Chris@14
|
1 <?php
|
Chris@14
|
2 /*
|
Chris@14
|
3 * This file is part of the php-code-coverage package.
|
Chris@14
|
4 *
|
Chris@14
|
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
|
Chris@14
|
6 *
|
Chris@14
|
7 * For the full copyright and license information, please view the LICENSE
|
Chris@14
|
8 * file that was distributed with this source code.
|
Chris@14
|
9 */
|
Chris@14
|
10
|
Chris@14
|
11 namespace SebastianBergmann\CodeCoverage\Report;
|
Chris@14
|
12
|
Chris@14
|
13 use SebastianBergmann\CodeCoverage\CodeCoverage;
|
Chris@14
|
14 use SebastianBergmann\CodeCoverage\Node\File;
|
Chris@14
|
15 use SebastianBergmann\CodeCoverage\Util;
|
Chris@14
|
16
|
Chris@14
|
17 /**
|
Chris@14
|
18 * Generates human readable output from a code coverage object.
|
Chris@14
|
19 *
|
Chris@14
|
20 * The output gets put into a text file our written to the CLI.
|
Chris@14
|
21 */
|
Chris@14
|
22 class Text
|
Chris@14
|
23 {
|
Chris@14
|
24 private $lowUpperBound;
|
Chris@14
|
25 private $highLowerBound;
|
Chris@14
|
26 private $showUncoveredFiles;
|
Chris@14
|
27 private $showOnlySummary;
|
Chris@14
|
28
|
Chris@14
|
29 private $colors = [
|
Chris@14
|
30 'green' => "\x1b[30;42m",
|
Chris@14
|
31 'yellow' => "\x1b[30;43m",
|
Chris@14
|
32 'red' => "\x1b[37;41m",
|
Chris@14
|
33 'header' => "\x1b[1;37;40m",
|
Chris@14
|
34 'reset' => "\x1b[0m",
|
Chris@14
|
35 'eol' => "\x1b[2K",
|
Chris@14
|
36 ];
|
Chris@14
|
37
|
Chris@14
|
38 /**
|
Chris@14
|
39 * @param int $lowUpperBound
|
Chris@14
|
40 * @param int $highLowerBound
|
Chris@14
|
41 * @param bool $showUncoveredFiles
|
Chris@14
|
42 * @param bool $showOnlySummary
|
Chris@14
|
43 */
|
Chris@14
|
44 public function __construct($lowUpperBound = 50, $highLowerBound = 90, $showUncoveredFiles = false, $showOnlySummary = false)
|
Chris@14
|
45 {
|
Chris@14
|
46 $this->lowUpperBound = $lowUpperBound;
|
Chris@14
|
47 $this->highLowerBound = $highLowerBound;
|
Chris@14
|
48 $this->showUncoveredFiles = $showUncoveredFiles;
|
Chris@14
|
49 $this->showOnlySummary = $showOnlySummary;
|
Chris@14
|
50 }
|
Chris@14
|
51
|
Chris@14
|
52 /**
|
Chris@14
|
53 * @param CodeCoverage $coverage
|
Chris@14
|
54 * @param bool $showColors
|
Chris@14
|
55 *
|
Chris@14
|
56 * @return string
|
Chris@14
|
57 */
|
Chris@14
|
58 public function process(CodeCoverage $coverage, $showColors = false)
|
Chris@14
|
59 {
|
Chris@14
|
60 $output = PHP_EOL . PHP_EOL;
|
Chris@14
|
61 $report = $coverage->getReport();
|
Chris@14
|
62 unset($coverage);
|
Chris@14
|
63
|
Chris@14
|
64 $colors = [
|
Chris@14
|
65 'header' => '',
|
Chris@14
|
66 'classes' => '',
|
Chris@14
|
67 'methods' => '',
|
Chris@14
|
68 'lines' => '',
|
Chris@14
|
69 'reset' => '',
|
Chris@14
|
70 'eol' => ''
|
Chris@14
|
71 ];
|
Chris@14
|
72
|
Chris@14
|
73 if ($showColors) {
|
Chris@14
|
74 $colors['classes'] = $this->getCoverageColor(
|
Chris@14
|
75 $report->getNumTestedClassesAndTraits(),
|
Chris@14
|
76 $report->getNumClassesAndTraits()
|
Chris@14
|
77 );
|
Chris@14
|
78 $colors['methods'] = $this->getCoverageColor(
|
Chris@14
|
79 $report->getNumTestedMethods(),
|
Chris@14
|
80 $report->getNumMethods()
|
Chris@14
|
81 );
|
Chris@14
|
82 $colors['lines'] = $this->getCoverageColor(
|
Chris@14
|
83 $report->getNumExecutedLines(),
|
Chris@14
|
84 $report->getNumExecutableLines()
|
Chris@14
|
85 );
|
Chris@14
|
86 $colors['reset'] = $this->colors['reset'];
|
Chris@14
|
87 $colors['header'] = $this->colors['header'];
|
Chris@14
|
88 $colors['eol'] = $this->colors['eol'];
|
Chris@14
|
89 }
|
Chris@14
|
90
|
Chris@14
|
91 $classes = \sprintf(
|
Chris@14
|
92 ' Classes: %6s (%d/%d)',
|
Chris@14
|
93 Util::percent(
|
Chris@14
|
94 $report->getNumTestedClassesAndTraits(),
|
Chris@14
|
95 $report->getNumClassesAndTraits(),
|
Chris@14
|
96 true
|
Chris@14
|
97 ),
|
Chris@14
|
98 $report->getNumTestedClassesAndTraits(),
|
Chris@14
|
99 $report->getNumClassesAndTraits()
|
Chris@14
|
100 );
|
Chris@14
|
101
|
Chris@14
|
102 $methods = \sprintf(
|
Chris@14
|
103 ' Methods: %6s (%d/%d)',
|
Chris@14
|
104 Util::percent(
|
Chris@14
|
105 $report->getNumTestedMethods(),
|
Chris@14
|
106 $report->getNumMethods(),
|
Chris@14
|
107 true
|
Chris@14
|
108 ),
|
Chris@14
|
109 $report->getNumTestedMethods(),
|
Chris@14
|
110 $report->getNumMethods()
|
Chris@14
|
111 );
|
Chris@14
|
112
|
Chris@14
|
113 $lines = \sprintf(
|
Chris@14
|
114 ' Lines: %6s (%d/%d)',
|
Chris@14
|
115 Util::percent(
|
Chris@14
|
116 $report->getNumExecutedLines(),
|
Chris@14
|
117 $report->getNumExecutableLines(),
|
Chris@14
|
118 true
|
Chris@14
|
119 ),
|
Chris@14
|
120 $report->getNumExecutedLines(),
|
Chris@14
|
121 $report->getNumExecutableLines()
|
Chris@14
|
122 );
|
Chris@14
|
123
|
Chris@14
|
124 $padding = \max(\array_map('strlen', [$classes, $methods, $lines]));
|
Chris@14
|
125
|
Chris@14
|
126 if ($this->showOnlySummary) {
|
Chris@14
|
127 $title = 'Code Coverage Report Summary:';
|
Chris@14
|
128 $padding = \max($padding, \strlen($title));
|
Chris@14
|
129
|
Chris@14
|
130 $output .= $this->format($colors['header'], $padding, $title);
|
Chris@14
|
131 } else {
|
Chris@14
|
132 $date = \date(' Y-m-d H:i:s', $_SERVER['REQUEST_TIME']);
|
Chris@14
|
133 $title = 'Code Coverage Report:';
|
Chris@14
|
134
|
Chris@14
|
135 $output .= $this->format($colors['header'], $padding, $title);
|
Chris@14
|
136 $output .= $this->format($colors['header'], $padding, $date);
|
Chris@14
|
137 $output .= $this->format($colors['header'], $padding, '');
|
Chris@14
|
138 $output .= $this->format($colors['header'], $padding, ' Summary:');
|
Chris@14
|
139 }
|
Chris@14
|
140
|
Chris@14
|
141 $output .= $this->format($colors['classes'], $padding, $classes);
|
Chris@14
|
142 $output .= $this->format($colors['methods'], $padding, $methods);
|
Chris@14
|
143 $output .= $this->format($colors['lines'], $padding, $lines);
|
Chris@14
|
144
|
Chris@14
|
145 if ($this->showOnlySummary) {
|
Chris@14
|
146 return $output . PHP_EOL;
|
Chris@14
|
147 }
|
Chris@14
|
148
|
Chris@14
|
149 $classCoverage = [];
|
Chris@14
|
150
|
Chris@14
|
151 foreach ($report as $item) {
|
Chris@14
|
152 if (!$item instanceof File) {
|
Chris@14
|
153 continue;
|
Chris@14
|
154 }
|
Chris@14
|
155
|
Chris@14
|
156 $classes = $item->getClassesAndTraits();
|
Chris@14
|
157
|
Chris@14
|
158 foreach ($classes as $className => $class) {
|
Chris@14
|
159 $classStatements = 0;
|
Chris@14
|
160 $coveredClassStatements = 0;
|
Chris@14
|
161 $coveredMethods = 0;
|
Chris@14
|
162 $classMethods = 0;
|
Chris@14
|
163
|
Chris@14
|
164 foreach ($class['methods'] as $method) {
|
Chris@14
|
165 if ($method['executableLines'] == 0) {
|
Chris@14
|
166 continue;
|
Chris@14
|
167 }
|
Chris@14
|
168
|
Chris@14
|
169 $classMethods++;
|
Chris@14
|
170 $classStatements += $method['executableLines'];
|
Chris@14
|
171 $coveredClassStatements += $method['executedLines'];
|
Chris@14
|
172 if ($method['coverage'] == 100) {
|
Chris@14
|
173 $coveredMethods++;
|
Chris@14
|
174 }
|
Chris@14
|
175 }
|
Chris@14
|
176
|
Chris@14
|
177 if (!empty($class['package']['namespace'])) {
|
Chris@14
|
178 $namespace = '\\' . $class['package']['namespace'] . '::';
|
Chris@14
|
179 } elseif (!empty($class['package']['fullPackage'])) {
|
Chris@14
|
180 $namespace = '@' . $class['package']['fullPackage'] . '::';
|
Chris@14
|
181 } else {
|
Chris@14
|
182 $namespace = '';
|
Chris@14
|
183 }
|
Chris@14
|
184
|
Chris@14
|
185 $classCoverage[$namespace . $className] = [
|
Chris@14
|
186 'namespace' => $namespace,
|
Chris@14
|
187 'className ' => $className,
|
Chris@14
|
188 'methodsCovered' => $coveredMethods,
|
Chris@14
|
189 'methodCount' => $classMethods,
|
Chris@14
|
190 'statementsCovered' => $coveredClassStatements,
|
Chris@14
|
191 'statementCount' => $classStatements,
|
Chris@14
|
192 ];
|
Chris@14
|
193 }
|
Chris@14
|
194 }
|
Chris@14
|
195
|
Chris@14
|
196 \ksort($classCoverage);
|
Chris@14
|
197
|
Chris@14
|
198 $methodColor = '';
|
Chris@14
|
199 $linesColor = '';
|
Chris@14
|
200 $resetColor = '';
|
Chris@14
|
201
|
Chris@14
|
202 foreach ($classCoverage as $fullQualifiedPath => $classInfo) {
|
Chris@14
|
203 if ($classInfo['statementsCovered'] != 0 ||
|
Chris@14
|
204 $this->showUncoveredFiles) {
|
Chris@14
|
205 if ($showColors) {
|
Chris@14
|
206 $methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']);
|
Chris@14
|
207 $linesColor = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']);
|
Chris@14
|
208 $resetColor = $colors['reset'];
|
Chris@14
|
209 }
|
Chris@14
|
210
|
Chris@14
|
211 $output .= PHP_EOL . $fullQualifiedPath . PHP_EOL
|
Chris@14
|
212 . ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' '
|
Chris@14
|
213 . ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor
|
Chris@14
|
214 ;
|
Chris@14
|
215 }
|
Chris@14
|
216 }
|
Chris@14
|
217
|
Chris@14
|
218 return $output . PHP_EOL;
|
Chris@14
|
219 }
|
Chris@14
|
220
|
Chris@14
|
221 protected function getCoverageColor($numberOfCoveredElements, $totalNumberOfElements)
|
Chris@14
|
222 {
|
Chris@14
|
223 $coverage = Util::percent(
|
Chris@14
|
224 $numberOfCoveredElements,
|
Chris@14
|
225 $totalNumberOfElements
|
Chris@14
|
226 );
|
Chris@14
|
227
|
Chris@14
|
228 if ($coverage >= $this->highLowerBound) {
|
Chris@14
|
229 return $this->colors['green'];
|
Chris@14
|
230 } elseif ($coverage > $this->lowUpperBound) {
|
Chris@14
|
231 return $this->colors['yellow'];
|
Chris@14
|
232 }
|
Chris@14
|
233
|
Chris@14
|
234 return $this->colors['red'];
|
Chris@14
|
235 }
|
Chris@14
|
236
|
Chris@14
|
237 protected function printCoverageCounts($numberOfCoveredElements, $totalNumberOfElements, $precision)
|
Chris@14
|
238 {
|
Chris@14
|
239 $format = '%' . $precision . 's';
|
Chris@14
|
240
|
Chris@14
|
241 return Util::percent(
|
Chris@14
|
242 $numberOfCoveredElements,
|
Chris@14
|
243 $totalNumberOfElements,
|
Chris@14
|
244 true,
|
Chris@14
|
245 true
|
Chris@14
|
246 ) .
|
Chris@14
|
247 ' (' . \sprintf($format, $numberOfCoveredElements) . '/' .
|
Chris@14
|
248 \sprintf($format, $totalNumberOfElements) . ')';
|
Chris@14
|
249 }
|
Chris@14
|
250
|
Chris@14
|
251 private function format($color, $padding, $string)
|
Chris@14
|
252 {
|
Chris@14
|
253 $reset = $color ? $this->colors['reset'] : '';
|
Chris@14
|
254
|
Chris@14
|
255 return $color . \str_pad($string, $padding) . $reset . PHP_EOL;
|
Chris@14
|
256 }
|
Chris@14
|
257 }
|