Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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