/vendor/phpunit/php-code-coverage/src/Report/Text.php

https://gitlab.com/madwanz64/laravel · PHP · 341 lines · 244 code · 56 blank · 41 comment · 18 complexity · 3f6976666b139c39f3dab9e7cb2e7090 MD5 · raw file

  1. <?php declare(strict_types=1);
  2. /*
  3. * This file is part of phpunit/php-code-coverage.
  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;
  11. use const PHP_EOL;
  12. use function array_map;
  13. use function date;
  14. use function ksort;
  15. use function max;
  16. use function sprintf;
  17. use function str_pad;
  18. use function strlen;
  19. use SebastianBergmann\CodeCoverage\CodeCoverage;
  20. use SebastianBergmann\CodeCoverage\Node\File;
  21. use SebastianBergmann\CodeCoverage\Util\Percentage;
  22. final class Text
  23. {
  24. /**
  25. * @var string
  26. */
  27. private const COLOR_GREEN = "\x1b[30;42m";
  28. /**
  29. * @var string
  30. */
  31. private const COLOR_YELLOW = "\x1b[30;43m";
  32. /**
  33. * @var string
  34. */
  35. private const COLOR_RED = "\x1b[37;41m";
  36. /**
  37. * @var string
  38. */
  39. private const COLOR_HEADER = "\x1b[1;37;40m";
  40. /**
  41. * @var string
  42. */
  43. private const COLOR_RESET = "\x1b[0m";
  44. /**
  45. * @var string
  46. */
  47. private const COLOR_EOL = "\x1b[2K";
  48. /**
  49. * @var int
  50. */
  51. private $lowUpperBound;
  52. /**
  53. * @var int
  54. */
  55. private $highLowerBound;
  56. /**
  57. * @var bool
  58. */
  59. private $showUncoveredFiles;
  60. /**
  61. * @var bool
  62. */
  63. private $showOnlySummary;
  64. public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, bool $showUncoveredFiles = false, bool $showOnlySummary = false)
  65. {
  66. $this->lowUpperBound = $lowUpperBound;
  67. $this->highLowerBound = $highLowerBound;
  68. $this->showUncoveredFiles = $showUncoveredFiles;
  69. $this->showOnlySummary = $showOnlySummary;
  70. }
  71. public function process(CodeCoverage $coverage, bool $showColors = false): string
  72. {
  73. $hasBranchCoverage = !empty($coverage->getData(true)->functionCoverage());
  74. $output = PHP_EOL . PHP_EOL;
  75. $report = $coverage->getReport();
  76. $colors = [
  77. 'header' => '',
  78. 'classes' => '',
  79. 'methods' => '',
  80. 'lines' => '',
  81. 'branches' => '',
  82. 'paths' => '',
  83. 'reset' => '',
  84. 'eol' => '',
  85. ];
  86. if ($showColors) {
  87. $colors['classes'] = $this->coverageColor(
  88. $report->numberOfTestedClassesAndTraits(),
  89. $report->numberOfClassesAndTraits()
  90. );
  91. $colors['methods'] = $this->coverageColor(
  92. $report->numberOfTestedMethods(),
  93. $report->numberOfMethods()
  94. );
  95. $colors['lines'] = $this->coverageColor(
  96. $report->numberOfExecutedLines(),
  97. $report->numberOfExecutableLines()
  98. );
  99. $colors['branches'] = $this->coverageColor(
  100. $report->numberOfExecutedBranches(),
  101. $report->numberOfExecutableBranches()
  102. );
  103. $colors['paths'] = $this->coverageColor(
  104. $report->numberOfExecutedPaths(),
  105. $report->numberOfExecutablePaths()
  106. );
  107. $colors['reset'] = self::COLOR_RESET;
  108. $colors['header'] = self::COLOR_HEADER;
  109. $colors['eol'] = self::COLOR_EOL;
  110. }
  111. $classes = sprintf(
  112. ' Classes: %6s (%d/%d)',
  113. Percentage::fromFractionAndTotal(
  114. $report->numberOfTestedClassesAndTraits(),
  115. $report->numberOfClassesAndTraits()
  116. )->asString(),
  117. $report->numberOfTestedClassesAndTraits(),
  118. $report->numberOfClassesAndTraits()
  119. );
  120. $methods = sprintf(
  121. ' Methods: %6s (%d/%d)',
  122. Percentage::fromFractionAndTotal(
  123. $report->numberOfTestedMethods(),
  124. $report->numberOfMethods(),
  125. )->asString(),
  126. $report->numberOfTestedMethods(),
  127. $report->numberOfMethods()
  128. );
  129. $paths = '';
  130. $branches = '';
  131. if ($hasBranchCoverage) {
  132. $paths = sprintf(
  133. ' Paths: %6s (%d/%d)',
  134. Percentage::fromFractionAndTotal(
  135. $report->numberOfExecutedPaths(),
  136. $report->numberOfExecutablePaths(),
  137. )->asString(),
  138. $report->numberOfExecutedPaths(),
  139. $report->numberOfExecutablePaths()
  140. );
  141. $branches = sprintf(
  142. ' Branches: %6s (%d/%d)',
  143. Percentage::fromFractionAndTotal(
  144. $report->numberOfExecutedBranches(),
  145. $report->numberOfExecutableBranches(),
  146. )->asString(),
  147. $report->numberOfExecutedBranches(),
  148. $report->numberOfExecutableBranches()
  149. );
  150. }
  151. $lines = sprintf(
  152. ' Lines: %6s (%d/%d)',
  153. Percentage::fromFractionAndTotal(
  154. $report->numberOfExecutedLines(),
  155. $report->numberOfExecutableLines(),
  156. )->asString(),
  157. $report->numberOfExecutedLines(),
  158. $report->numberOfExecutableLines()
  159. );
  160. $padding = max(array_map('strlen', [$classes, $methods, $lines]));
  161. if ($this->showOnlySummary) {
  162. $title = 'Code Coverage Report Summary:';
  163. $padding = max($padding, strlen($title));
  164. $output .= $this->format($colors['header'], $padding, $title);
  165. } else {
  166. $date = date(' Y-m-d H:i:s');
  167. $title = 'Code Coverage Report:';
  168. $output .= $this->format($colors['header'], $padding, $title);
  169. $output .= $this->format($colors['header'], $padding, $date);
  170. $output .= $this->format($colors['header'], $padding, '');
  171. $output .= $this->format($colors['header'], $padding, ' Summary:');
  172. }
  173. $output .= $this->format($colors['classes'], $padding, $classes);
  174. $output .= $this->format($colors['methods'], $padding, $methods);
  175. if ($hasBranchCoverage) {
  176. $output .= $this->format($colors['paths'], $padding, $paths);
  177. $output .= $this->format($colors['branches'], $padding, $branches);
  178. }
  179. $output .= $this->format($colors['lines'], $padding, $lines);
  180. if ($this->showOnlySummary) {
  181. return $output . PHP_EOL;
  182. }
  183. $classCoverage = [];
  184. foreach ($report as $item) {
  185. if (!$item instanceof File) {
  186. continue;
  187. }
  188. $classes = $item->classesAndTraits();
  189. foreach ($classes as $className => $class) {
  190. $classExecutableLines = 0;
  191. $classExecutedLines = 0;
  192. $classExecutableBranches = 0;
  193. $classExecutedBranches = 0;
  194. $classExecutablePaths = 0;
  195. $classExecutedPaths = 0;
  196. $coveredMethods = 0;
  197. $classMethods = 0;
  198. foreach ($class['methods'] as $method) {
  199. if ($method['executableLines'] == 0) {
  200. continue;
  201. }
  202. $classMethods++;
  203. $classExecutableLines += $method['executableLines'];
  204. $classExecutedLines += $method['executedLines'];
  205. $classExecutableBranches += $method['executableBranches'];
  206. $classExecutedBranches += $method['executedBranches'];
  207. $classExecutablePaths += $method['executablePaths'];
  208. $classExecutedPaths += $method['executedPaths'];
  209. if ($method['coverage'] == 100) {
  210. $coveredMethods++;
  211. }
  212. }
  213. $classCoverage[$className] = [
  214. 'namespace' => $class['namespace'],
  215. 'className' => $className,
  216. 'methodsCovered' => $coveredMethods,
  217. 'methodCount' => $classMethods,
  218. 'statementsCovered' => $classExecutedLines,
  219. 'statementCount' => $classExecutableLines,
  220. 'branchesCovered' => $classExecutedBranches,
  221. 'branchesCount' => $classExecutableBranches,
  222. 'pathsCovered' => $classExecutedPaths,
  223. 'pathsCount' => $classExecutablePaths,
  224. ];
  225. }
  226. }
  227. ksort($classCoverage);
  228. $methodColor = '';
  229. $pathsColor = '';
  230. $branchesColor = '';
  231. $linesColor = '';
  232. $resetColor = '';
  233. foreach ($classCoverage as $fullQualifiedPath => $classInfo) {
  234. if ($this->showUncoveredFiles || $classInfo['statementsCovered'] != 0) {
  235. if ($showColors) {
  236. $methodColor = $this->coverageColor($classInfo['methodsCovered'], $classInfo['methodCount']);
  237. $pathsColor = $this->coverageColor($classInfo['pathsCovered'], $classInfo['pathsCount']);
  238. $branchesColor = $this->coverageColor($classInfo['branchesCovered'], $classInfo['branchesCount']);
  239. $linesColor = $this->coverageColor($classInfo['statementsCovered'], $classInfo['statementCount']);
  240. $resetColor = $colors['reset'];
  241. }
  242. $output .= PHP_EOL . $fullQualifiedPath . PHP_EOL
  243. . ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' ';
  244. if ($hasBranchCoverage) {
  245. $output .= ' ' . $pathsColor . 'Paths: ' . $this->printCoverageCounts($classInfo['pathsCovered'], $classInfo['pathsCount'], 3) . $resetColor . ' '
  246. . ' ' . $branchesColor . 'Branches: ' . $this->printCoverageCounts($classInfo['branchesCovered'], $classInfo['branchesCount'], 3) . $resetColor . ' ';
  247. }
  248. $output .= ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor;
  249. }
  250. }
  251. return $output . PHP_EOL;
  252. }
  253. private function coverageColor(int $numberOfCoveredElements, int $totalNumberOfElements): string
  254. {
  255. $coverage = Percentage::fromFractionAndTotal(
  256. $numberOfCoveredElements,
  257. $totalNumberOfElements
  258. );
  259. if ($coverage->asFloat() >= $this->highLowerBound) {
  260. return self::COLOR_GREEN;
  261. }
  262. if ($coverage->asFloat() > $this->lowUpperBound) {
  263. return self::COLOR_YELLOW;
  264. }
  265. return self::COLOR_RED;
  266. }
  267. private function printCoverageCounts(int $numberOfCoveredElements, int $totalNumberOfElements, int $precision): string
  268. {
  269. $format = '%' . $precision . 's';
  270. return Percentage::fromFractionAndTotal(
  271. $numberOfCoveredElements,
  272. $totalNumberOfElements
  273. )->asFixedWidthString() .
  274. ' (' . sprintf($format, $numberOfCoveredElements) . '/' .
  275. sprintf($format, $totalNumberOfElements) . ')';
  276. }
  277. /**
  278. * @param false|string $string
  279. */
  280. private function format(string $color, int $padding, $string): string
  281. {
  282. $reset = $color ? self::COLOR_RESET : '';
  283. return $color . str_pad((string) $string, $padding) . $reset . PHP_EOL;
  284. }
  285. }