/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php

https://bitbucket.org/alan_cordova/api-sb-map · PHP · 302 lines · 221 code · 35 blank · 46 comment · 20 complexity · b48004e149447790ad8854093f26c535 MD5 · raw file

  1. <?php
  2. /*
  3. * This file is part of the php-code-coverage package.
  4. *
  5. * (c) Sebastian Bergmann <sebastian@phpunit.de>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace SebastianBergmann\CodeCoverage\Report\Html;
  11. use SebastianBergmann\CodeCoverage\Node\AbstractNode;
  12. use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
  13. /**
  14. * Renders the dashboard for a directory node.
  15. */
  16. class Dashboard extends Renderer
  17. {
  18. /**
  19. * @param DirectoryNode $node
  20. * @param string $file
  21. */
  22. public function render(DirectoryNode $node, $file)
  23. {
  24. $classes = $node->getClassesAndTraits();
  25. $template = new \Text_Template(
  26. $this->templatePath . 'dashboard.html',
  27. '{{',
  28. '}}'
  29. );
  30. $this->setCommonTemplateVariables($template, $node);
  31. $baseLink = $node->getId() . '/';
  32. $complexity = $this->complexity($classes, $baseLink);
  33. $coverageDistribution = $this->coverageDistribution($classes);
  34. $insufficientCoverage = $this->insufficientCoverage($classes, $baseLink);
  35. $projectRisks = $this->projectRisks($classes, $baseLink);
  36. $template->setVar(
  37. [
  38. 'insufficient_coverage_classes' => $insufficientCoverage['class'],
  39. 'insufficient_coverage_methods' => $insufficientCoverage['method'],
  40. 'project_risks_classes' => $projectRisks['class'],
  41. 'project_risks_methods' => $projectRisks['method'],
  42. 'complexity_class' => $complexity['class'],
  43. 'complexity_method' => $complexity['method'],
  44. 'class_coverage_distribution' => $coverageDistribution['class'],
  45. 'method_coverage_distribution' => $coverageDistribution['method']
  46. ]
  47. );
  48. $template->renderTo($file);
  49. }
  50. /**
  51. * Returns the data for the Class/Method Complexity charts.
  52. *
  53. * @param array $classes
  54. * @param string $baseLink
  55. *
  56. * @return array
  57. */
  58. protected function complexity(array $classes, $baseLink)
  59. {
  60. $result = ['class' => [], 'method' => []];
  61. foreach ($classes as $className => $class) {
  62. foreach ($class['methods'] as $methodName => $method) {
  63. if ($className != '*') {
  64. $methodName = $className . '::' . $methodName;
  65. }
  66. $result['method'][] = [
  67. $method['coverage'],
  68. $method['ccn'],
  69. sprintf(
  70. '<a href="%s">%s</a>',
  71. str_replace($baseLink, '', $method['link']),
  72. $methodName
  73. )
  74. ];
  75. }
  76. $result['class'][] = [
  77. $class['coverage'],
  78. $class['ccn'],
  79. sprintf(
  80. '<a href="%s">%s</a>',
  81. str_replace($baseLink, '', $class['link']),
  82. $className
  83. )
  84. ];
  85. }
  86. return [
  87. 'class' => json_encode($result['class']),
  88. 'method' => json_encode($result['method'])
  89. ];
  90. }
  91. /**
  92. * Returns the data for the Class / Method Coverage Distribution chart.
  93. *
  94. * @param array $classes
  95. *
  96. * @return array
  97. */
  98. protected function coverageDistribution(array $classes)
  99. {
  100. $result = [
  101. 'class' => [
  102. '0%' => 0,
  103. '0-10%' => 0,
  104. '10-20%' => 0,
  105. '20-30%' => 0,
  106. '30-40%' => 0,
  107. '40-50%' => 0,
  108. '50-60%' => 0,
  109. '60-70%' => 0,
  110. '70-80%' => 0,
  111. '80-90%' => 0,
  112. '90-100%' => 0,
  113. '100%' => 0
  114. ],
  115. 'method' => [
  116. '0%' => 0,
  117. '0-10%' => 0,
  118. '10-20%' => 0,
  119. '20-30%' => 0,
  120. '30-40%' => 0,
  121. '40-50%' => 0,
  122. '50-60%' => 0,
  123. '60-70%' => 0,
  124. '70-80%' => 0,
  125. '80-90%' => 0,
  126. '90-100%' => 0,
  127. '100%' => 0
  128. ]
  129. ];
  130. foreach ($classes as $class) {
  131. foreach ($class['methods'] as $methodName => $method) {
  132. if ($method['coverage'] == 0) {
  133. $result['method']['0%']++;
  134. } elseif ($method['coverage'] == 100) {
  135. $result['method']['100%']++;
  136. } else {
  137. $key = floor($method['coverage'] / 10) * 10;
  138. $key = $key . '-' . ($key + 10) . '%';
  139. $result['method'][$key]++;
  140. }
  141. }
  142. if ($class['coverage'] == 0) {
  143. $result['class']['0%']++;
  144. } elseif ($class['coverage'] == 100) {
  145. $result['class']['100%']++;
  146. } else {
  147. $key = floor($class['coverage'] / 10) * 10;
  148. $key = $key . '-' . ($key + 10) . '%';
  149. $result['class'][$key]++;
  150. }
  151. }
  152. return [
  153. 'class' => json_encode(array_values($result['class'])),
  154. 'method' => json_encode(array_values($result['method']))
  155. ];
  156. }
  157. /**
  158. * Returns the classes / methods with insufficient coverage.
  159. *
  160. * @param array $classes
  161. * @param string $baseLink
  162. *
  163. * @return array
  164. */
  165. protected function insufficientCoverage(array $classes, $baseLink)
  166. {
  167. $leastTestedClasses = [];
  168. $leastTestedMethods = [];
  169. $result = ['class' => '', 'method' => ''];
  170. foreach ($classes as $className => $class) {
  171. foreach ($class['methods'] as $methodName => $method) {
  172. if ($method['coverage'] < $this->highLowerBound) {
  173. if ($className != '*') {
  174. $key = $className . '::' . $methodName;
  175. } else {
  176. $key = $methodName;
  177. }
  178. $leastTestedMethods[$key] = $method['coverage'];
  179. }
  180. }
  181. if ($class['coverage'] < $this->highLowerBound) {
  182. $leastTestedClasses[$className] = $class['coverage'];
  183. }
  184. }
  185. asort($leastTestedClasses);
  186. asort($leastTestedMethods);
  187. foreach ($leastTestedClasses as $className => $coverage) {
  188. $result['class'] .= sprintf(
  189. ' <tr><td><a href="%s">%s</a></td><td class="text-right">%d%%</td></tr>' . "\n",
  190. str_replace($baseLink, '', $classes[$className]['link']),
  191. $className,
  192. $coverage
  193. );
  194. }
  195. foreach ($leastTestedMethods as $methodName => $coverage) {
  196. list($class, $method) = explode('::', $methodName);
  197. $result['method'] .= sprintf(
  198. ' <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d%%</td></tr>' . "\n",
  199. str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
  200. $methodName,
  201. $method,
  202. $coverage
  203. );
  204. }
  205. return $result;
  206. }
  207. /**
  208. * Returns the project risks according to the CRAP index.
  209. *
  210. * @param array $classes
  211. * @param string $baseLink
  212. *
  213. * @return array
  214. */
  215. protected function projectRisks(array $classes, $baseLink)
  216. {
  217. $classRisks = [];
  218. $methodRisks = [];
  219. $result = ['class' => '', 'method' => ''];
  220. foreach ($classes as $className => $class) {
  221. foreach ($class['methods'] as $methodName => $method) {
  222. if ($method['coverage'] < $this->highLowerBound &&
  223. $method['ccn'] > 1) {
  224. if ($className != '*') {
  225. $key = $className . '::' . $methodName;
  226. } else {
  227. $key = $methodName;
  228. }
  229. $methodRisks[$key] = $method['crap'];
  230. }
  231. }
  232. if ($class['coverage'] < $this->highLowerBound &&
  233. $class['ccn'] > count($class['methods'])) {
  234. $classRisks[$className] = $class['crap'];
  235. }
  236. }
  237. arsort($classRisks);
  238. arsort($methodRisks);
  239. foreach ($classRisks as $className => $crap) {
  240. $result['class'] .= sprintf(
  241. ' <tr><td><a href="%s">%s</a></td><td class="text-right">%d</td></tr>' . "\n",
  242. str_replace($baseLink, '', $classes[$className]['link']),
  243. $className,
  244. $crap
  245. );
  246. }
  247. foreach ($methodRisks as $methodName => $crap) {
  248. list($class, $method) = explode('::', $methodName);
  249. $result['method'] .= sprintf(
  250. ' <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d</td></tr>' . "\n",
  251. str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
  252. $methodName,
  253. $method,
  254. $crap
  255. );
  256. }
  257. return $result;
  258. }
  259. protected function getActiveBreadcrumb(AbstractNode $node)
  260. {
  261. return sprintf(
  262. ' <li><a href="index.html">%s</a></li>' . "\n" .
  263. ' <li class="active">(Dashboard)</li>' . "\n",
  264. $node->getName()
  265. );
  266. }
  267. }