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;
11
 
12
use function array_key_exists;
13
use function array_keys;
14
use function array_merge;
15
use function array_unique;
16
use function count;
17
use function is_array;
18
use function ksort;
19
use SebastianBergmann\CodeCoverage\Driver\Driver;
20
 
21
/**
22
 * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
23
 */
24
final class ProcessedCodeCoverageData
25
{
26
    /**
27
     * Line coverage data.
28
     * An array of filenames, each having an array of linenumbers, each executable line having an array of testcase ids.
29
     *
30
     * @var array
31
     */
32
    private $lineCoverage = [];
33
 
34
    /**
35
     * Function coverage data.
36
     * Maintains base format of raw data (@see https://xdebug.org/docs/code_coverage), but each 'hit' entry is an array
37
     * of testcase ids.
38
     *
39
     * @var array
40
     */
41
    private $functionCoverage = [];
42
 
43
    public function initializeUnseenData(RawCodeCoverageData $rawData): void
44
    {
45
        foreach ($rawData->lineCoverage() as $file => $lines) {
46
            if (!isset($this->lineCoverage[$file])) {
47
                $this->lineCoverage[$file] = [];
48
 
49
                foreach ($lines as $k => $v) {
50
                    $this->lineCoverage[$file][$k] = $v === Driver::LINE_NOT_EXECUTABLE ? null : [];
51
                }
52
            }
53
        }
54
 
55
        foreach ($rawData->functionCoverage() as $file => $functions) {
56
            foreach ($functions as $functionName => $functionData) {
57
                if (isset($this->functionCoverage[$file][$functionName])) {
58
                    $this->initPreviouslySeenFunction($file, $functionName, $functionData);
59
                } else {
60
                    $this->initPreviouslyUnseenFunction($file, $functionName, $functionData);
61
                }
62
            }
63
        }
64
    }
65
 
66
    public function markCodeAsExecutedByTestCase(string $testCaseId, RawCodeCoverageData $executedCode): void
67
    {
68
        foreach ($executedCode->lineCoverage() as $file => $lines) {
69
            foreach ($lines as $k => $v) {
70
                if ($v === Driver::LINE_EXECUTED) {
71
                    $this->lineCoverage[$file][$k][] = $testCaseId;
72
                }
73
            }
74
        }
75
 
76
        foreach ($executedCode->functionCoverage() as $file => $functions) {
77
            foreach ($functions as $functionName => $functionData) {
78
                foreach ($functionData['branches'] as $branchId => $branchData) {
79
                    if ($branchData['hit'] === Driver::BRANCH_HIT) {
80
                        $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'][] = $testCaseId;
81
                    }
82
                }
83
 
84
                foreach ($functionData['paths'] as $pathId => $pathData) {
85
                    if ($pathData['hit'] === Driver::BRANCH_HIT) {
86
                        $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'][] = $testCaseId;
87
                    }
88
                }
89
            }
90
        }
91
    }
92
 
93
    public function setLineCoverage(array $lineCoverage): void
94
    {
95
        $this->lineCoverage = $lineCoverage;
96
    }
97
 
98
    public function lineCoverage(): array
99
    {
100
        ksort($this->lineCoverage);
101
 
102
        return $this->lineCoverage;
103
    }
104
 
105
    public function setFunctionCoverage(array $functionCoverage): void
106
    {
107
        $this->functionCoverage = $functionCoverage;
108
    }
109
 
110
    public function functionCoverage(): array
111
    {
112
        ksort($this->functionCoverage);
113
 
114
        return $this->functionCoverage;
115
    }
116
 
117
    public function coveredFiles(): array
118
    {
119
        ksort($this->lineCoverage);
120
 
121
        return array_keys($this->lineCoverage);
122
    }
123
 
124
    public function renameFile(string $oldFile, string $newFile): void
125
    {
126
        $this->lineCoverage[$newFile] = $this->lineCoverage[$oldFile];
127
 
128
        if (isset($this->functionCoverage[$oldFile])) {
129
            $this->functionCoverage[$newFile] = $this->functionCoverage[$oldFile];
130
        }
131
 
132
        unset($this->lineCoverage[$oldFile], $this->functionCoverage[$oldFile]);
133
    }
134
 
135
    public function merge(self $newData): void
136
    {
137
        foreach ($newData->lineCoverage as $file => $lines) {
138
            if (!isset($this->lineCoverage[$file])) {
139
                $this->lineCoverage[$file] = $lines;
140
 
141
                continue;
142
            }
143
 
144
            // we should compare the lines if any of two contains data
145
            $compareLineNumbers = array_unique(
146
                array_merge(
147
                    array_keys($this->lineCoverage[$file]),
148
                    array_keys($newData->lineCoverage[$file])
149
                )
150
            );
151
 
152
            foreach ($compareLineNumbers as $line) {
153
                $thatPriority = $this->priorityForLine($newData->lineCoverage[$file], $line);
154
                $thisPriority = $this->priorityForLine($this->lineCoverage[$file], $line);
155
 
156
                if ($thatPriority > $thisPriority) {
157
                    $this->lineCoverage[$file][$line] = $newData->lineCoverage[$file][$line];
158
                } elseif ($thatPriority === $thisPriority && is_array($this->lineCoverage[$file][$line])) {
159
                    $this->lineCoverage[$file][$line] = array_unique(
160
                        array_merge($this->lineCoverage[$file][$line], $newData->lineCoverage[$file][$line])
161
                    );
162
                }
163
            }
164
        }
165
 
166
        foreach ($newData->functionCoverage as $file => $functions) {
167
            if (!isset($this->functionCoverage[$file])) {
168
                $this->functionCoverage[$file] = $functions;
169
 
170
                continue;
171
            }
172
 
173
            foreach ($functions as $functionName => $functionData) {
174
                if (isset($this->functionCoverage[$file][$functionName])) {
175
                    $this->initPreviouslySeenFunction($file, $functionName, $functionData);
176
                } else {
177
                    $this->initPreviouslyUnseenFunction($file, $functionName, $functionData);
178
                }
179
 
180
                foreach ($functionData['branches'] as $branchId => $branchData) {
181
                    $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'], $branchData['hit']));
182
                }
183
 
184
                foreach ($functionData['paths'] as $pathId => $pathData) {
185
                    $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'], $pathData['hit']));
186
                }
187
            }
188
        }
189
    }
190
 
191
    /**
192
     * Determine the priority for a line.
193
     *
194
     * 1 = the line is not set
195
     * 2 = the line has not been tested
196
     * 3 = the line is dead code
197
     * 4 = the line has been tested
198
     *
199
     * During a merge, a higher number is better.
200
     */
201
    private function priorityForLine(array $data, int $line): int
202
    {
203
        if (!array_key_exists($line, $data)) {
204
            return 1;
205
        }
206
 
207
        if (is_array($data[$line]) && count($data[$line]) === 0) {
208
            return 2;
209
        }
210
 
211
        if ($data[$line] === null) {
212
            return 3;
213
        }
214
 
215
        return 4;
216
    }
217
 
218
    /**
219
     * For a function we have never seen before, copy all data over and simply init the 'hit' array.
220
     */
221
    private function initPreviouslyUnseenFunction(string $file, string $functionName, array $functionData): void
222
    {
223
        $this->functionCoverage[$file][$functionName] = $functionData;
224
 
225
        foreach (array_keys($functionData['branches']) as $branchId) {
226
            $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = [];
227
        }
228
 
229
        foreach (array_keys($functionData['paths']) as $pathId) {
230
            $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = [];
231
        }
232
    }
233
 
234
    /**
235
     * For a function we have seen before, only copy over and init the 'hit' array for any unseen branches and paths.
236
     * Techniques such as mocking and where the contents of a file are different vary during tests (e.g. compiling
237
     * containers) mean that the functions inside a file cannot be relied upon to be static.
238
     */
239
    private function initPreviouslySeenFunction(string $file, string $functionName, array $functionData): void
240
    {
241
        foreach ($functionData['branches'] as $branchId => $branchData) {
242
            if (!isset($this->functionCoverage[$file][$functionName]['branches'][$branchId])) {
243
                $this->functionCoverage[$file][$functionName]['branches'][$branchId]        = $branchData;
244
                $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = [];
245
            }
246
        }
247
 
248
        foreach ($functionData['paths'] as $pathId => $pathData) {
249
            if (!isset($this->functionCoverage[$file][$functionName]['paths'][$pathId])) {
250
                $this->functionCoverage[$file][$functionName]['paths'][$pathId]        = $pathData;
251
                $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = [];
252
            }
253
        }
254
    }
255
}