Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
namespace NunoMaduro\Collision;
6
 
7
use SebastianBergmann\CodeCoverage\CodeCoverage;
8
use SebastianBergmann\CodeCoverage\Node\Directory;
9
use SebastianBergmann\CodeCoverage\Node\File;
10
use SebastianBergmann\Environment\Runtime;
11
use Symfony\Component\Console\Output\OutputInterface;
12
use Symfony\Component\Console\Terminal;
13
 
14
/**
15
 * @internal
16
 */
17
final class Coverage
18
{
19
    /**
20
     * Returns the coverage path.
21
     */
22
    public static function getPath(): string
23
    {
24
        return implode(DIRECTORY_SEPARATOR, [
25
            dirname(__DIR__),
26
            '.temp',
27
            'coverage',
28
        ]);
29
    }
30
 
31
    /**
32
     * Runs true there is any code coverage driver available.
33
     */
34
    public static function isAvailable(): bool
35
    {
36
        if (! (new Runtime())->canCollectCodeCoverage()) {
37
            return false;
38
        }
39
 
40
        if (static::usingXdebug()) {
41
            $mode = getenv('XDEBUG_MODE') ?: ini_get('xdebug.mode');
42
 
43
            return $mode && in_array('coverage', explode(',', $mode), true);
44
        }
45
 
46
        return true;
47
    }
48
 
49
    /**
50
     * If the user is using Xdebug.
51
     */
52
    public static function usingXdebug(): bool
53
    {
54
        return (new Runtime())->hasXdebug();
55
    }
56
 
57
    /**
58
     * Reports the code coverage report to the
59
     * console and returns the result in float.
60
     */
61
    public static function report(OutputInterface $output): float
62
    {
63
        if (! file_exists($reportPath = self::getPath())) {
64
            if (self::usingXdebug()) {
65
                $output->writeln(
66
                    "  <fg=black;bg=yellow;options=bold> WARN </> Unable to get coverage using Xdebug. Did you set <href=https://xdebug.org/docs/code_coverage#mode>Xdebug's coverage mode</>?</>",
67
                );
68
 
69
                return 0.0;
70
            }
71
 
72
            $output->writeln(
73
                '  <fg=black;bg=yellow;options=bold> WARN </> No coverage driver detected.</>',
74
            );
75
 
76
            return 0.0;
77
        }
78
 
79
        /** @var CodeCoverage $codeCoverage */
80
        $codeCoverage = require $reportPath;
81
        unlink($reportPath);
82
 
83
        $totalCoverage = $codeCoverage->getReport()->percentageOfExecutedLines();
84
 
85
        $totalWidth = (new Terminal())->getWidth();
86
 
87
        $dottedLineLength = $totalWidth;
88
 
89
        /** @var Directory<File|Directory> $report */
90
        $report = $codeCoverage->getReport();
91
 
92
        foreach ($report->getIterator() as $file) {
93
            if (! $file instanceof File) {
94
                continue;
95
            }
96
            $dirname = dirname($file->id());
97
            $basename = basename($file->id(), '.php');
98
 
99
            $name = $dirname === '.' ? $basename : implode(DIRECTORY_SEPARATOR, [
100
                $dirname,
101
                $basename,
102
            ]);
103
            $rawName = $dirname === '.' ? $basename : implode(DIRECTORY_SEPARATOR, [
104
                $dirname,
105
                $basename,
106
            ]);
107
 
108
            $linesExecutedTakenSize = 0;
109
 
110
            if ($file->percentageOfExecutedLines()->asString() != '0.00%') {
111
                $linesExecutedTakenSize = strlen($uncoveredLines = trim(implode(', ', self::getMissingCoverage($file)))) + 1;
112
                $name .= sprintf(' <fg=red>%s</>', $uncoveredLines);
113
            }
114
 
115
            $percentage = $file->numberOfExecutableLines() === 0
116
                ? '100.0'
117
                : number_format($file->percentageOfExecutedLines()->asFloat(), 1, '.', '');
118
 
119
            $takenSize = strlen($rawName.$percentage) + 8 + $linesExecutedTakenSize; // adding 3 space and percent sign
120
 
121
            $percentage = sprintf(
122
                '<fg=%s%s>%s</>',
123
                $percentage === '100.0' ? 'green' : ($percentage === '0.0' ? 'red' : 'yellow'),
124
                $percentage === '100.0' ? ';options=bold' : '',
125
                $percentage
126
            );
127
 
128
            $output->writeln(sprintf(
129
                '  <fg=white>%s</> <fg=#6C7280>%s</> %s <fg=#6C7280>%%</>',
130
                $name,
131
                str_repeat('.', max($dottedLineLength - $takenSize, 1)),
132
                $percentage
133
            ));
134
        }
135
 
136
        $output->writeln('');
137
 
138
        $rawName = 'Total Coverage';
139
 
140
        $takenSize = strlen($rawName.$totalCoverage->asString()) + 6;
141
 
142
        $output->writeln(sprintf(
143
            '  <fg=white;options=bold>%s</> <fg=#6C7280>%s</> %s <fg=#6C7280>%%</>',
144
            $rawName,
145
            str_repeat('.', max($dottedLineLength - $takenSize, 1)),
146
            number_format($totalCoverage->asFloat(), 1, '.', '')
147
        ));
148
 
149
        return $totalCoverage->asFloat();
150
    }
151
 
152
    /**
153
     * Generates an array of missing coverage on the following format:.
154
     *
155
     * ```
156
     * ['11', '20..25', '50', '60..80'];
157
     * ```
158
     *
159
     * @param  File  $file
160
     * @return array<int, string>
161
     */
162
    public static function getMissingCoverage($file): array
163
    {
164
        $shouldBeNewLine = true;
165
 
166
        $eachLine = function (array $array, array $tests, int $line) use (&$shouldBeNewLine): array {
167
            if (count($tests) > 0) {
168
                $shouldBeNewLine = true;
169
 
170
                return $array;
171
            }
172
 
173
            if ($shouldBeNewLine) {
174
                $array[] = (string) $line;
175
                $shouldBeNewLine = false;
176
 
177
                return $array;
178
            }
179
 
180
            $lastKey = count($array) - 1;
181
 
182
            if (array_key_exists($lastKey, $array) && str_contains($array[$lastKey], '..')) {
183
                [$from] = explode('..', $array[$lastKey]);
184
                $array[$lastKey] = $line > $from ? sprintf('%s..%s', $from, $line) : sprintf('%s..%s', $line, $from);
185
 
186
                return $array;
187
            }
188
 
189
            $array[$lastKey] = sprintf('%s..%s', $array[$lastKey], $line);
190
 
191
            return $array;
192
        };
193
 
194
        $array = [];
195
        foreach (array_filter($file->lineCoverageData(), 'is_array') as $line => $tests) {
196
            $array = $eachLine($array, $tests, $line);
197
        }
198
 
199
        return $array;
200
    }
201
}