Mercurial > hg > isophonics-drupal-site
comparison vendor/phpunit/php-code-coverage/src/CodeCoverage.php @ 14:1fec387a4317
Update Drupal core to 8.5.2 via Composer
author | Chris Cannam |
---|---|
date | Mon, 23 Apr 2018 09:46:53 +0100 |
parents | 4c8ae668cc8c |
children |
comparison
equal
deleted
inserted
replaced
13:5fb285c0d0e3 | 14:1fec387a4317 |
---|---|
1 <?php | 1 <?php |
2 /* | 2 /* |
3 * This file is part of the PHP_CodeCoverage package. | 3 * This file is part of the php-code-coverage package. |
4 * | 4 * |
5 * (c) Sebastian Bergmann <sebastian@phpunit.de> | 5 * (c) Sebastian Bergmann <sebastian@phpunit.de> |
6 * | 6 * |
7 * For the full copyright and license information, please view the LICENSE | 7 * For the full copyright and license information, please view the LICENSE |
8 * file that was distributed with this source code. | 8 * file that was distributed with this source code. |
9 */ | 9 */ |
10 | 10 |
11 namespace SebastianBergmann\CodeCoverage; | |
12 | |
13 use PHPUnit\Framework\TestCase; | |
14 use PHPUnit\Runner\PhptTestCase; | |
15 use SebastianBergmann\CodeCoverage\Driver\Driver; | |
16 use SebastianBergmann\CodeCoverage\Driver\HHVM; | |
17 use SebastianBergmann\CodeCoverage\Driver\PHPDBG; | |
18 use SebastianBergmann\CodeCoverage\Driver\Xdebug; | |
19 use SebastianBergmann\CodeCoverage\Node\Builder; | |
20 use SebastianBergmann\CodeCoverage\Node\Directory; | |
21 use SebastianBergmann\CodeUnitReverseLookup\Wizard; | |
11 use SebastianBergmann\Environment\Runtime; | 22 use SebastianBergmann\Environment\Runtime; |
12 | 23 |
13 /** | 24 /** |
14 * Provides collection functionality for PHP code coverage information. | 25 * Provides collection functionality for PHP code coverage information. |
15 * | |
16 * @since Class available since Release 1.0.0 | |
17 */ | 26 */ |
18 class PHP_CodeCoverage | 27 class CodeCoverage |
19 { | 28 { |
20 /** | 29 /** |
21 * @var PHP_CodeCoverage_Driver | 30 * @var Driver |
22 */ | 31 */ |
23 private $driver; | 32 private $driver; |
24 | 33 |
25 /** | 34 /** |
26 * @var PHP_CodeCoverage_Filter | 35 * @var Filter |
27 */ | 36 */ |
28 private $filter; | 37 private $filter; |
29 | 38 |
30 /** | 39 /** |
40 * @var Wizard | |
41 */ | |
42 private $wizard; | |
43 | |
44 /** | |
31 * @var bool | 45 * @var bool |
32 */ | 46 */ |
33 private $cacheTokens = false; | 47 private $cacheTokens = false; |
34 | 48 |
35 /** | 49 /** |
43 private $forceCoversAnnotation = false; | 57 private $forceCoversAnnotation = false; |
44 | 58 |
45 /** | 59 /** |
46 * @var bool | 60 * @var bool |
47 */ | 61 */ |
48 private $mapTestClassNameToCoveredClassName = false; | 62 private $checkForUnexecutedCoveredCode = false; |
49 | 63 |
50 /** | 64 /** |
51 * @var bool | 65 * @var bool |
52 */ | 66 */ |
67 private $checkForMissingCoversAnnotation = false; | |
68 | |
69 /** | |
70 * @var bool | |
71 */ | |
53 private $addUncoveredFilesFromWhitelist = true; | 72 private $addUncoveredFilesFromWhitelist = true; |
54 | 73 |
55 /** | 74 /** |
56 * @var bool | 75 * @var bool |
57 */ | 76 */ |
58 private $processUncoveredFilesFromWhitelist = false; | 77 private $processUncoveredFilesFromWhitelist = false; |
59 | 78 |
60 /** | 79 /** |
80 * @var bool | |
81 */ | |
82 private $ignoreDeprecatedCode = false; | |
83 | |
84 /** | |
61 * @var mixed | 85 * @var mixed |
62 */ | 86 */ |
63 private $currentId; | 87 private $currentId; |
64 | 88 |
65 /** | 89 /** |
66 * Code coverage data. | 90 * Code coverage data. |
67 * | 91 * |
68 * @var array | 92 * @var array |
69 */ | 93 */ |
70 private $data = array(); | 94 private $data = []; |
71 | 95 |
72 /** | 96 /** |
73 * @var array | 97 * @var array |
74 */ | 98 */ |
75 private $ignoredLines = array(); | 99 private $ignoredLines = []; |
76 | 100 |
77 /** | 101 /** |
78 * @var bool | 102 * @var bool |
79 */ | 103 */ |
80 private $disableIgnoredLines = false; | 104 private $disableIgnoredLines = false; |
82 /** | 106 /** |
83 * Test data. | 107 * Test data. |
84 * | 108 * |
85 * @var array | 109 * @var array |
86 */ | 110 */ |
87 private $tests = array(); | 111 private $tests = []; |
112 | |
113 /** | |
114 * @var string[] | |
115 */ | |
116 private $unintentionallyCoveredSubclassesWhitelist = []; | |
117 | |
118 /** | |
119 * Determine if the data has been initialized or not | |
120 * | |
121 * @var bool | |
122 */ | |
123 private $isInitialized = false; | |
124 | |
125 /** | |
126 * Determine whether we need to check for dead and unused code on each test | |
127 * | |
128 * @var bool | |
129 */ | |
130 private $shouldCheckForDeadAndUnused = true; | |
131 | |
132 /** | |
133 * @var Directory | |
134 */ | |
135 private $report; | |
88 | 136 |
89 /** | 137 /** |
90 * Constructor. | 138 * Constructor. |
91 * | 139 * |
92 * @param PHP_CodeCoverage_Driver $driver | 140 * @param Driver $driver |
93 * @param PHP_CodeCoverage_Filter $filter | 141 * @param Filter $filter |
94 * @throws PHP_CodeCoverage_Exception | 142 * |
95 */ | 143 * @throws RuntimeException |
96 public function __construct(PHP_CodeCoverage_Driver $driver = null, PHP_CodeCoverage_Filter $filter = null) | 144 */ |
145 public function __construct(Driver $driver = null, Filter $filter = null) | |
97 { | 146 { |
98 if ($driver === null) { | 147 if ($driver === null) { |
99 $driver = $this->selectDriver(); | 148 $driver = $this->selectDriver(); |
100 } | 149 } |
101 | 150 |
102 if ($filter === null) { | 151 if ($filter === null) { |
103 $filter = new PHP_CodeCoverage_Filter; | 152 $filter = new Filter; |
104 } | 153 } |
105 | 154 |
106 $this->driver = $driver; | 155 $this->driver = $driver; |
107 $this->filter = $filter; | 156 $this->filter = $filter; |
108 } | 157 |
109 | 158 $this->wizard = new Wizard; |
110 /** | 159 } |
111 * Returns the PHP_CodeCoverage_Report_Node_* object graph | 160 |
112 * for this PHP_CodeCoverage object. | 161 /** |
113 * | 162 * Returns the code coverage information as a graph of node objects. |
114 * @return PHP_CodeCoverage_Report_Node_Directory | 163 * |
115 * @since Method available since Release 1.1.0 | 164 * @return Directory |
116 */ | 165 */ |
117 public function getReport() | 166 public function getReport() |
118 { | 167 { |
119 $factory = new PHP_CodeCoverage_Report_Factory; | 168 if ($this->report === null) { |
120 | 169 $builder = new Builder; |
121 return $factory->create($this); | 170 |
171 $this->report = $builder->build($this); | |
172 } | |
173 | |
174 return $this->report; | |
122 } | 175 } |
123 | 176 |
124 /** | 177 /** |
125 * Clears collected code coverage data. | 178 * Clears collected code coverage data. |
126 */ | 179 */ |
127 public function clear() | 180 public function clear() |
128 { | 181 { |
129 $this->currentId = null; | 182 $this->isInitialized = false; |
130 $this->data = array(); | 183 $this->currentId = null; |
131 $this->tests = array(); | 184 $this->data = []; |
132 } | 185 $this->tests = []; |
133 | 186 $this->report = null; |
134 /** | 187 } |
135 * Returns the PHP_CodeCoverage_Filter used. | 188 |
136 * | 189 /** |
137 * @return PHP_CodeCoverage_Filter | 190 * Returns the filter object used. |
191 * | |
192 * @return Filter | |
138 */ | 193 */ |
139 public function filter() | 194 public function filter() |
140 { | 195 { |
141 return $this->filter; | 196 return $this->filter; |
142 } | 197 } |
143 | 198 |
144 /** | 199 /** |
145 * Returns the collected code coverage data. | 200 * Returns the collected code coverage data. |
146 * Set $raw = true to bypass all filters. | 201 * Set $raw = true to bypass all filters. |
147 * | 202 * |
148 * @param bool $raw | 203 * @param bool $raw |
204 * | |
149 * @return array | 205 * @return array |
150 * @since Method available since Release 1.1.0 | |
151 */ | 206 */ |
152 public function getData($raw = false) | 207 public function getData($raw = false) |
153 { | 208 { |
154 if (!$raw && $this->addUncoveredFilesFromWhitelist) { | 209 if (!$raw && $this->addUncoveredFilesFromWhitelist) { |
155 $this->addUncoveredFilesFromWhitelist(); | 210 $this->addUncoveredFilesFromWhitelist(); |
156 } | 211 } |
157 | 212 |
158 // We need to apply the blacklist filter a second time | |
159 // when no whitelist is used. | |
160 if (!$raw && !$this->filter->hasWhitelist()) { | |
161 $this->applyListsFilter($this->data); | |
162 } | |
163 | |
164 return $this->data; | 213 return $this->data; |
165 } | 214 } |
166 | 215 |
167 /** | 216 /** |
168 * Sets the coverage data. | 217 * Sets the coverage data. |
169 * | 218 * |
170 * @param array $data | 219 * @param array $data |
171 * @since Method available since Release 2.0.0 | |
172 */ | 220 */ |
173 public function setData(array $data) | 221 public function setData(array $data) |
174 { | 222 { |
175 $this->data = $data; | 223 $this->data = $data; |
224 $this->report = null; | |
176 } | 225 } |
177 | 226 |
178 /** | 227 /** |
179 * Returns the test data. | 228 * Returns the test data. |
180 * | 229 * |
181 * @return array | 230 * @return array |
182 * @since Method available since Release 1.1.0 | |
183 */ | 231 */ |
184 public function getTests() | 232 public function getTests() |
185 { | 233 { |
186 return $this->tests; | 234 return $this->tests; |
187 } | 235 } |
188 | 236 |
189 /** | 237 /** |
190 * Sets the test data. | 238 * Sets the test data. |
191 * | 239 * |
192 * @param array $tests | 240 * @param array $tests |
193 * @since Method available since Release 2.0.0 | |
194 */ | 241 */ |
195 public function setTests(array $tests) | 242 public function setTests(array $tests) |
196 { | 243 { |
197 $this->tests = $tests; | 244 $this->tests = $tests; |
198 } | 245 } |
199 | 246 |
200 /** | 247 /** |
201 * Start collection of code coverage information. | 248 * Start collection of code coverage information. |
202 * | 249 * |
203 * @param mixed $id | 250 * @param mixed $id |
204 * @param bool $clear | 251 * @param bool $clear |
205 * @throws PHP_CodeCoverage_Exception | 252 * |
253 * @throws InvalidArgumentException | |
206 */ | 254 */ |
207 public function start($id, $clear = false) | 255 public function start($id, $clear = false) |
208 { | 256 { |
209 if (!is_bool($clear)) { | 257 if (!\is_bool($clear)) { |
210 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( | 258 throw InvalidArgumentException::create( |
211 1, | 259 1, |
212 'boolean' | 260 'boolean' |
213 ); | 261 ); |
214 } | 262 } |
215 | 263 |
216 if ($clear) { | 264 if ($clear) { |
217 $this->clear(); | 265 $this->clear(); |
218 } | 266 } |
219 | 267 |
268 if ($this->isInitialized === false) { | |
269 $this->initializeData(); | |
270 } | |
271 | |
220 $this->currentId = $id; | 272 $this->currentId = $id; |
221 | 273 |
222 $this->driver->start(); | 274 $this->driver->start($this->shouldCheckForDeadAndUnused); |
223 } | 275 } |
224 | 276 |
225 /** | 277 /** |
226 * Stop collection of code coverage information. | 278 * Stop collection of code coverage information. |
227 * | 279 * |
228 * @param bool $append | 280 * @param bool $append |
229 * @param mixed $linesToBeCovered | 281 * @param mixed $linesToBeCovered |
230 * @param array $linesToBeUsed | 282 * @param array $linesToBeUsed |
283 * @param bool $ignoreForceCoversAnnotation | |
284 * | |
231 * @return array | 285 * @return array |
232 * @throws PHP_CodeCoverage_Exception | 286 * |
233 */ | 287 * @throws \SebastianBergmann\CodeCoverage\RuntimeException |
234 public function stop($append = true, $linesToBeCovered = array(), array $linesToBeUsed = array()) | 288 * @throws InvalidArgumentException |
235 { | 289 */ |
236 if (!is_bool($append)) { | 290 public function stop($append = true, $linesToBeCovered = [], array $linesToBeUsed = [], $ignoreForceCoversAnnotation = false) |
237 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( | 291 { |
292 if (!\is_bool($append)) { | |
293 throw InvalidArgumentException::create( | |
238 1, | 294 1, |
239 'boolean' | 295 'boolean' |
240 ); | 296 ); |
241 } | 297 } |
242 | 298 |
243 if (!is_array($linesToBeCovered) && $linesToBeCovered !== false) { | 299 if (!\is_array($linesToBeCovered) && $linesToBeCovered !== false) { |
244 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( | 300 throw InvalidArgumentException::create( |
245 2, | 301 2, |
246 'array or false' | 302 'array or false' |
247 ); | 303 ); |
248 } | 304 } |
249 | 305 |
250 $data = $this->driver->stop(); | 306 $data = $this->driver->stop(); |
251 $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed); | 307 $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed, $ignoreForceCoversAnnotation); |
252 | 308 |
253 $this->currentId = null; | 309 $this->currentId = null; |
254 | 310 |
255 return $data; | 311 return $data; |
256 } | 312 } |
257 | 313 |
258 /** | 314 /** |
259 * Appends code coverage data. | 315 * Appends code coverage data. |
260 * | 316 * |
261 * @param array $data | 317 * @param array $data |
262 * @param mixed $id | 318 * @param mixed $id |
263 * @param bool $append | 319 * @param bool $append |
264 * @param mixed $linesToBeCovered | 320 * @param mixed $linesToBeCovered |
265 * @param array $linesToBeUsed | 321 * @param array $linesToBeUsed |
266 * @throws PHP_CodeCoverage_Exception | 322 * @param bool $ignoreForceCoversAnnotation |
267 */ | 323 * |
268 public function append(array $data, $id = null, $append = true, $linesToBeCovered = array(), array $linesToBeUsed = array()) | 324 * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException |
325 * @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException | |
326 * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException | |
327 * @throws \ReflectionException | |
328 * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException | |
329 * @throws RuntimeException | |
330 */ | |
331 public function append(array $data, $id = null, $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], $ignoreForceCoversAnnotation = false) | |
269 { | 332 { |
270 if ($id === null) { | 333 if ($id === null) { |
271 $id = $this->currentId; | 334 $id = $this->currentId; |
272 } | 335 } |
273 | 336 |
274 if ($id === null) { | 337 if ($id === null) { |
275 throw new PHP_CodeCoverage_Exception; | 338 throw new RuntimeException; |
276 } | 339 } |
277 | 340 |
278 $this->applyListsFilter($data); | 341 $this->applyListsFilter($data); |
279 $this->applyIgnoredLinesFilter($data); | 342 $this->applyIgnoredLinesFilter($data); |
280 $this->initializeFilesThatAreSeenTheFirstTime($data); | 343 $this->initializeFilesThatAreSeenTheFirstTime($data); |
281 | 344 |
282 if (!$append) { | 345 if (!$append) { |
283 return; | 346 return; |
284 } | 347 } |
285 | 348 |
286 if ($id != 'UNCOVERED_FILES_FROM_WHITELIST') { | 349 if ($id !== 'UNCOVERED_FILES_FROM_WHITELIST') { |
287 $this->applyCoversAnnotationFilter( | 350 $this->applyCoversAnnotationFilter( |
288 $data, | 351 $data, |
289 $linesToBeCovered, | 352 $linesToBeCovered, |
290 $linesToBeUsed | 353 $linesToBeUsed, |
354 $ignoreForceCoversAnnotation | |
291 ); | 355 ); |
292 } | 356 } |
293 | 357 |
294 if (empty($data)) { | 358 if (empty($data)) { |
295 return; | 359 return; |
296 } | 360 } |
297 | 361 |
298 $size = 'unknown'; | 362 $size = 'unknown'; |
299 $status = null; | 363 $status = null; |
300 | 364 |
301 if ($id instanceof PHPUnit_Framework_TestCase) { | 365 if ($id instanceof TestCase) { |
302 $_size = $id->getSize(); | 366 $_size = $id->getSize(); |
303 | 367 |
304 if ($_size == PHPUnit_Util_Test::SMALL) { | 368 if ($_size === \PHPUnit\Util\Test::SMALL) { |
305 $size = 'small'; | 369 $size = 'small'; |
306 } elseif ($_size == PHPUnit_Util_Test::MEDIUM) { | 370 } elseif ($_size === \PHPUnit\Util\Test::MEDIUM) { |
307 $size = 'medium'; | 371 $size = 'medium'; |
308 } elseif ($_size == PHPUnit_Util_Test::LARGE) { | 372 } elseif ($_size === \PHPUnit\Util\Test::LARGE) { |
309 $size = 'large'; | 373 $size = 'large'; |
310 } | 374 } |
311 | 375 |
312 $status = $id->getStatus(); | 376 $status = $id->getStatus(); |
313 $id = get_class($id) . '::' . $id->getName(); | 377 $id = \get_class($id) . '::' . $id->getName(); |
314 } elseif ($id instanceof PHPUnit_Extensions_PhptTestCase) { | 378 } elseif ($id instanceof PhptTestCase) { |
315 $size = 'large'; | 379 $size = 'large'; |
316 $id = $id->getName(); | 380 $id = $id->getName(); |
317 } | 381 } |
318 | 382 |
319 $this->tests[$id] = array('size' => $size, 'status' => $status); | 383 $this->tests[$id] = ['size' => $size, 'status' => $status]; |
320 | 384 |
321 foreach ($data as $file => $lines) { | 385 foreach ($data as $file => $lines) { |
322 if (!$this->filter->isFile($file)) { | 386 if (!$this->filter->isFile($file)) { |
323 continue; | 387 continue; |
324 } | 388 } |
325 | 389 |
326 foreach ($lines as $k => $v) { | 390 foreach ($lines as $k => $v) { |
327 if ($v == PHP_CodeCoverage_Driver::LINE_EXECUTED) { | 391 if ($v === Driver::LINE_EXECUTED) { |
328 if (empty($this->data[$file][$k]) || !in_array($id, $this->data[$file][$k])) { | 392 if (empty($this->data[$file][$k]) || !\in_array($id, $this->data[$file][$k])) { |
329 $this->data[$file][$k][] = $id; | 393 $this->data[$file][$k][] = $id; |
330 } | 394 } |
331 } | 395 } |
332 } | 396 } |
333 } | 397 } |
334 } | 398 |
335 | 399 $this->report = null; |
336 /** | 400 } |
337 * Merges the data from another instance of PHP_CodeCoverage. | 401 |
338 * | 402 /** |
339 * @param PHP_CodeCoverage $that | 403 * Merges the data from another instance. |
340 */ | 404 * |
341 public function merge(PHP_CodeCoverage $that) | 405 * @param CodeCoverage $that |
342 { | 406 */ |
343 $this->filter->setBlacklistedFiles( | 407 public function merge(self $that) |
344 array_merge($this->filter->getBlacklistedFiles(), $that->filter()->getBlacklistedFiles()) | 408 { |
345 ); | |
346 | |
347 $this->filter->setWhitelistedFiles( | 409 $this->filter->setWhitelistedFiles( |
348 array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles()) | 410 \array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles()) |
349 ); | 411 ); |
350 | 412 |
351 foreach ($that->data as $file => $lines) { | 413 foreach ($that->data as $file => $lines) { |
352 if (!isset($this->data[$file])) { | 414 if (!isset($this->data[$file])) { |
353 if (!$this->filter->isFiltered($file)) { | 415 if (!$this->filter->isFiltered($file)) { |
360 foreach ($lines as $line => $data) { | 422 foreach ($lines as $line => $data) { |
361 if ($data !== null) { | 423 if ($data !== null) { |
362 if (!isset($this->data[$file][$line])) { | 424 if (!isset($this->data[$file][$line])) { |
363 $this->data[$file][$line] = $data; | 425 $this->data[$file][$line] = $data; |
364 } else { | 426 } else { |
365 $this->data[$file][$line] = array_unique( | 427 $this->data[$file][$line] = \array_unique( |
366 array_merge($this->data[$file][$line], $data) | 428 \array_merge($this->data[$file][$line], $data) |
367 ); | 429 ); |
368 } | 430 } |
369 } | 431 } |
370 } | 432 } |
371 } | 433 } |
372 | 434 |
373 $this->tests = array_merge($this->tests, $that->getTests()); | 435 $this->tests = \array_merge($this->tests, $that->getTests()); |
374 | 436 $this->report = null; |
375 } | 437 } |
376 | 438 |
377 /** | 439 /** |
378 * @param bool $flag | 440 * @param bool $flag |
379 * @throws PHP_CodeCoverage_Exception | 441 * |
380 * @since Method available since Release 1.1.0 | 442 * @throws InvalidArgumentException |
381 */ | 443 */ |
382 public function setCacheTokens($flag) | 444 public function setCacheTokens($flag) |
383 { | 445 { |
384 if (!is_bool($flag)) { | 446 if (!\is_bool($flag)) { |
385 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( | 447 throw InvalidArgumentException::create( |
386 1, | 448 1, |
387 'boolean' | 449 'boolean' |
388 ); | 450 ); |
389 } | 451 } |
390 | 452 |
391 $this->cacheTokens = $flag; | 453 $this->cacheTokens = $flag; |
392 } | 454 } |
393 | 455 |
394 /** | 456 /** |
395 * @since Method available since Release 1.1.0 | 457 * @return bool |
396 */ | 458 */ |
397 public function getCacheTokens() | 459 public function getCacheTokens() |
398 { | 460 { |
399 return $this->cacheTokens; | 461 return $this->cacheTokens; |
400 } | 462 } |
401 | 463 |
402 /** | 464 /** |
403 * @param bool $flag | 465 * @param bool $flag |
404 * @throws PHP_CodeCoverage_Exception | 466 * |
405 * @since Method available since Release 2.0.0 | 467 * @throws InvalidArgumentException |
406 */ | 468 */ |
407 public function setCheckForUnintentionallyCoveredCode($flag) | 469 public function setCheckForUnintentionallyCoveredCode($flag) |
408 { | 470 { |
409 if (!is_bool($flag)) { | 471 if (!\is_bool($flag)) { |
410 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( | 472 throw InvalidArgumentException::create( |
411 1, | 473 1, |
412 'boolean' | 474 'boolean' |
413 ); | 475 ); |
414 } | 476 } |
415 | 477 |
416 $this->checkForUnintentionallyCoveredCode = $flag; | 478 $this->checkForUnintentionallyCoveredCode = $flag; |
417 } | 479 } |
418 | 480 |
419 /** | 481 /** |
420 * @param bool $flag | 482 * @param bool $flag |
421 * @throws PHP_CodeCoverage_Exception | 483 * |
484 * @throws InvalidArgumentException | |
422 */ | 485 */ |
423 public function setForceCoversAnnotation($flag) | 486 public function setForceCoversAnnotation($flag) |
424 { | 487 { |
425 if (!is_bool($flag)) { | 488 if (!\is_bool($flag)) { |
426 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( | 489 throw InvalidArgumentException::create( |
427 1, | 490 1, |
428 'boolean' | 491 'boolean' |
429 ); | 492 ); |
430 } | 493 } |
431 | 494 |
432 $this->forceCoversAnnotation = $flag; | 495 $this->forceCoversAnnotation = $flag; |
433 } | 496 } |
434 | 497 |
435 /** | 498 /** |
436 * @param bool $flag | 499 * @param bool $flag |
437 * @throws PHP_CodeCoverage_Exception | 500 * |
438 */ | 501 * @throws InvalidArgumentException |
439 public function setMapTestClassNameToCoveredClassName($flag) | 502 */ |
440 { | 503 public function setCheckForMissingCoversAnnotation($flag) |
441 if (!is_bool($flag)) { | 504 { |
442 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( | 505 if (!\is_bool($flag)) { |
506 throw InvalidArgumentException::create( | |
443 1, | 507 1, |
444 'boolean' | 508 'boolean' |
445 ); | 509 ); |
446 } | 510 } |
447 | 511 |
448 $this->mapTestClassNameToCoveredClassName = $flag; | 512 $this->checkForMissingCoversAnnotation = $flag; |
449 } | 513 } |
450 | 514 |
451 /** | 515 /** |
452 * @param bool $flag | 516 * @param bool $flag |
453 * @throws PHP_CodeCoverage_Exception | 517 * |
454 */ | 518 * @throws InvalidArgumentException |
455 public function setAddUncoveredFilesFromWhitelist($flag) | 519 */ |
456 { | 520 public function setCheckForUnexecutedCoveredCode($flag) |
457 if (!is_bool($flag)) { | 521 { |
458 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( | 522 if (!\is_bool($flag)) { |
523 throw InvalidArgumentException::create( | |
459 1, | 524 1, |
460 'boolean' | 525 'boolean' |
461 ); | 526 ); |
462 } | 527 } |
463 | 528 |
464 $this->addUncoveredFilesFromWhitelist = $flag; | 529 $this->checkForUnexecutedCoveredCode = $flag; |
465 } | 530 } |
466 | 531 |
467 /** | 532 /** |
468 * @param bool $flag | 533 * @deprecated |
469 * @throws PHP_CodeCoverage_Exception | 534 * |
470 */ | 535 * @param bool $flag |
471 public function setProcessUncoveredFilesFromWhitelist($flag) | 536 * |
472 { | 537 * @throws InvalidArgumentException |
473 if (!is_bool($flag)) { | 538 */ |
474 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( | 539 public function setMapTestClassNameToCoveredClassName($flag) |
540 { | |
541 } | |
542 | |
543 /** | |
544 * @param bool $flag | |
545 * | |
546 * @throws InvalidArgumentException | |
547 */ | |
548 public function setAddUncoveredFilesFromWhitelist($flag) | |
549 { | |
550 if (!\is_bool($flag)) { | |
551 throw InvalidArgumentException::create( | |
475 1, | 552 1, |
476 'boolean' | 553 'boolean' |
477 ); | 554 ); |
478 } | 555 } |
479 | 556 |
480 $this->processUncoveredFilesFromWhitelist = $flag; | 557 $this->addUncoveredFilesFromWhitelist = $flag; |
481 } | 558 } |
482 | 559 |
483 /** | 560 /** |
484 * @param bool $flag | 561 * @param bool $flag |
485 * @throws PHP_CodeCoverage_Exception | 562 * |
486 */ | 563 * @throws InvalidArgumentException |
487 public function setDisableIgnoredLines($flag) | 564 */ |
488 { | 565 public function setProcessUncoveredFilesFromWhitelist($flag) |
489 if (!is_bool($flag)) { | 566 { |
490 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( | 567 if (!\is_bool($flag)) { |
568 throw InvalidArgumentException::create( | |
491 1, | 569 1, |
492 'boolean' | 570 'boolean' |
493 ); | 571 ); |
494 } | 572 } |
495 | 573 |
574 $this->processUncoveredFilesFromWhitelist = $flag; | |
575 } | |
576 | |
577 /** | |
578 * @param bool $flag | |
579 * | |
580 * @throws InvalidArgumentException | |
581 */ | |
582 public function setDisableIgnoredLines($flag) | |
583 { | |
584 if (!\is_bool($flag)) { | |
585 throw InvalidArgumentException::create( | |
586 1, | |
587 'boolean' | |
588 ); | |
589 } | |
590 | |
496 $this->disableIgnoredLines = $flag; | 591 $this->disableIgnoredLines = $flag; |
497 } | 592 } |
498 | 593 |
499 /** | 594 /** |
595 * @param bool $flag | |
596 * | |
597 * @throws InvalidArgumentException | |
598 */ | |
599 public function setIgnoreDeprecatedCode($flag) | |
600 { | |
601 if (!\is_bool($flag)) { | |
602 throw InvalidArgumentException::create( | |
603 1, | |
604 'boolean' | |
605 ); | |
606 } | |
607 | |
608 $this->ignoreDeprecatedCode = $flag; | |
609 } | |
610 | |
611 /** | |
612 * @param array $whitelist | |
613 */ | |
614 public function setUnintentionallyCoveredSubclassesWhitelist(array $whitelist) | |
615 { | |
616 $this->unintentionallyCoveredSubclassesWhitelist = $whitelist; | |
617 } | |
618 | |
619 /** | |
500 * Applies the @covers annotation filtering. | 620 * Applies the @covers annotation filtering. |
501 * | 621 * |
502 * @param array $data | 622 * @param array $data |
503 * @param mixed $linesToBeCovered | 623 * @param mixed $linesToBeCovered |
504 * @param array $linesToBeUsed | 624 * @param array $linesToBeUsed |
505 * @throws PHP_CodeCoverage_Exception_UnintentionallyCoveredCode | 625 * @param bool $ignoreForceCoversAnnotation |
506 */ | 626 * |
507 private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed) | 627 * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException |
628 * @throws \ReflectionException | |
629 * @throws MissingCoversAnnotationException | |
630 * @throws UnintentionallyCoveredCodeException | |
631 */ | |
632 private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed, $ignoreForceCoversAnnotation) | |
508 { | 633 { |
509 if ($linesToBeCovered === false || | 634 if ($linesToBeCovered === false || |
510 ($this->forceCoversAnnotation && empty($linesToBeCovered))) { | 635 ($this->forceCoversAnnotation && empty($linesToBeCovered) && !$ignoreForceCoversAnnotation)) { |
511 $data = array(); | 636 if ($this->checkForMissingCoversAnnotation) { |
637 throw new MissingCoversAnnotationException; | |
638 } | |
639 | |
640 $data = []; | |
512 | 641 |
513 return; | 642 return; |
514 } | 643 } |
515 | 644 |
516 if (empty($linesToBeCovered)) { | 645 if (empty($linesToBeCovered)) { |
517 return; | 646 return; |
518 } | 647 } |
519 | 648 |
520 if ($this->checkForUnintentionallyCoveredCode) { | 649 if ($this->checkForUnintentionallyCoveredCode && |
521 $this->performUnintentionallyCoveredCodeCheck( | 650 (!$this->currentId instanceof TestCase || |
522 $data, | 651 (!$this->currentId->isMedium() && !$this->currentId->isLarge()))) { |
523 $linesToBeCovered, | 652 $this->performUnintentionallyCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); |
524 $linesToBeUsed | 653 } |
525 ); | 654 |
526 } | 655 if ($this->checkForUnexecutedCoveredCode) { |
527 | 656 $this->performUnexecutedCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); |
528 $data = array_intersect_key($data, $linesToBeCovered); | 657 } |
529 | 658 |
530 foreach (array_keys($data) as $filename) { | 659 $data = \array_intersect_key($data, $linesToBeCovered); |
531 $_linesToBeCovered = array_flip($linesToBeCovered[$filename]); | 660 |
532 | 661 foreach (\array_keys($data) as $filename) { |
533 $data[$filename] = array_intersect_key( | 662 $_linesToBeCovered = \array_flip($linesToBeCovered[$filename]); |
534 $data[$filename], | 663 $data[$filename] = \array_intersect_key($data[$filename], $_linesToBeCovered); |
535 $_linesToBeCovered | 664 } |
536 ); | 665 } |
537 } | 666 |
538 } | 667 /** |
539 | 668 * Applies the whitelist filtering. |
540 /** | |
541 * Applies the blacklist/whitelist filtering. | |
542 * | 669 * |
543 * @param array $data | 670 * @param array $data |
544 */ | 671 */ |
545 private function applyListsFilter(array &$data) | 672 private function applyListsFilter(array &$data) |
546 { | 673 { |
547 foreach (array_keys($data) as $filename) { | 674 foreach (\array_keys($data) as $filename) { |
548 if ($this->filter->isFiltered($filename)) { | 675 if ($this->filter->isFiltered($filename)) { |
549 unset($data[$filename]); | 676 unset($data[$filename]); |
550 } | 677 } |
551 } | 678 } |
552 } | 679 } |
553 | 680 |
554 /** | 681 /** |
555 * Applies the "ignored lines" filtering. | 682 * Applies the "ignored lines" filtering. |
556 * | 683 * |
557 * @param array $data | 684 * @param array $data |
685 * | |
686 * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException | |
558 */ | 687 */ |
559 private function applyIgnoredLinesFilter(array &$data) | 688 private function applyIgnoredLinesFilter(array &$data) |
560 { | 689 { |
561 foreach (array_keys($data) as $filename) { | 690 foreach (\array_keys($data) as $filename) { |
562 if (!$this->filter->isFile($filename)) { | 691 if (!$this->filter->isFile($filename)) { |
563 continue; | 692 continue; |
564 } | 693 } |
565 | 694 |
566 foreach ($this->getLinesToBeIgnored($filename) as $line) { | 695 foreach ($this->getLinesToBeIgnored($filename) as $line) { |
569 } | 698 } |
570 } | 699 } |
571 | 700 |
572 /** | 701 /** |
573 * @param array $data | 702 * @param array $data |
574 * @since Method available since Release 1.1.0 | |
575 */ | 703 */ |
576 private function initializeFilesThatAreSeenTheFirstTime(array $data) | 704 private function initializeFilesThatAreSeenTheFirstTime(array $data) |
577 { | 705 { |
578 foreach ($data as $file => $lines) { | 706 foreach ($data as $file => $lines) { |
579 if ($this->filter->isFile($file) && !isset($this->data[$file])) { | 707 if (!isset($this->data[$file]) && $this->filter->isFile($file)) { |
580 $this->data[$file] = array(); | 708 $this->data[$file] = []; |
581 | 709 |
582 foreach ($lines as $k => $v) { | 710 foreach ($lines as $k => $v) { |
583 $this->data[$file][$k] = $v == -2 ? null : array(); | 711 $this->data[$file][$k] = $v === -2 ? null : []; |
584 } | 712 } |
585 } | 713 } |
586 } | 714 } |
587 } | 715 } |
588 | 716 |
589 /** | 717 /** |
590 * Processes whitelisted files that are not covered. | 718 * Processes whitelisted files that are not covered. |
591 */ | 719 */ |
592 private function addUncoveredFilesFromWhitelist() | 720 private function addUncoveredFilesFromWhitelist() |
593 { | 721 { |
594 $data = array(); | 722 $data = []; |
595 $uncoveredFiles = array_diff( | 723 $uncoveredFiles = \array_diff( |
596 $this->filter->getWhitelist(), | 724 $this->filter->getWhitelist(), |
597 array_keys($this->data) | 725 \array_keys($this->data) |
598 ); | 726 ); |
599 | 727 |
600 foreach ($uncoveredFiles as $uncoveredFile) { | 728 foreach ($uncoveredFiles as $uncoveredFile) { |
601 if (!file_exists($uncoveredFile)) { | 729 if (!\file_exists($uncoveredFile)) { |
602 continue; | 730 continue; |
603 } | 731 } |
604 | 732 |
605 if ($this->processUncoveredFilesFromWhitelist) { | 733 $data[$uncoveredFile] = []; |
606 $this->processUncoveredFileFromWhitelist( | 734 |
607 $uncoveredFile, | 735 $lines = \count(\file($uncoveredFile)); |
608 $data, | 736 |
609 $uncoveredFiles | 737 for ($i = 1; $i <= $lines; $i++) { |
610 ); | 738 $data[$uncoveredFile][$i] = Driver::LINE_NOT_EXECUTED; |
611 } else { | |
612 $data[$uncoveredFile] = array(); | |
613 | |
614 $lines = count(file($uncoveredFile)); | |
615 | |
616 for ($i = 1; $i <= $lines; $i++) { | |
617 $data[$uncoveredFile][$i] = PHP_CodeCoverage_Driver::LINE_NOT_EXECUTED; | |
618 } | |
619 } | 739 } |
620 } | 740 } |
621 | 741 |
622 $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); | 742 $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); |
623 } | 743 } |
624 | 744 |
625 /** | 745 /** |
626 * @param string $uncoveredFile | |
627 * @param array $data | |
628 * @param array $uncoveredFiles | |
629 */ | |
630 private function processUncoveredFileFromWhitelist($uncoveredFile, array &$data, array $uncoveredFiles) | |
631 { | |
632 $this->driver->start(); | |
633 include_once $uncoveredFile; | |
634 $coverage = $this->driver->stop(); | |
635 | |
636 foreach ($coverage as $file => $fileCoverage) { | |
637 if (!isset($data[$file]) && | |
638 in_array($file, $uncoveredFiles)) { | |
639 foreach (array_keys($fileCoverage) as $key) { | |
640 if ($fileCoverage[$key] == PHP_CodeCoverage_Driver::LINE_EXECUTED) { | |
641 $fileCoverage[$key] = PHP_CodeCoverage_Driver::LINE_NOT_EXECUTED; | |
642 } | |
643 } | |
644 | |
645 $data[$file] = $fileCoverage; | |
646 } | |
647 } | |
648 } | |
649 | |
650 /** | |
651 * Returns the lines of a source file that should be ignored. | 746 * Returns the lines of a source file that should be ignored. |
652 * | 747 * |
653 * @param string $filename | 748 * @param string $filename |
749 * | |
654 * @return array | 750 * @return array |
655 * @throws PHP_CodeCoverage_Exception | 751 * |
656 * @since Method available since Release 2.0.0 | 752 * @throws InvalidArgumentException |
657 */ | 753 */ |
658 private function getLinesToBeIgnored($filename) | 754 private function getLinesToBeIgnored($filename) |
659 { | 755 { |
660 if (!is_string($filename)) { | 756 if (!\is_string($filename)) { |
661 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory( | 757 throw InvalidArgumentException::create( |
662 1, | 758 1, |
663 'string' | 759 'string' |
664 ); | 760 ); |
665 } | 761 } |
666 | 762 |
667 if (!isset($this->ignoredLines[$filename])) { | 763 if (isset($this->ignoredLines[$filename])) { |
668 $this->ignoredLines[$filename] = array(); | 764 return $this->ignoredLines[$filename]; |
669 | 765 } |
670 if ($this->disableIgnoredLines) { | 766 |
671 return $this->ignoredLines[$filename]; | 767 $this->ignoredLines[$filename] = []; |
672 } | 768 |
673 | 769 $lines = \file($filename); |
674 $ignore = false; | 770 |
675 $stop = false; | 771 foreach ($lines as $index => $line) { |
676 $lines = file($filename); | 772 if (!\trim($line)) { |
677 $numLines = count($lines); | 773 $this->ignoredLines[$filename][] = $index + 1; |
678 | 774 } |
679 foreach ($lines as $index => $line) { | 775 } |
680 if (!trim($line)) { | 776 |
681 $this->ignoredLines[$filename][] = $index + 1; | 777 if ($this->cacheTokens) { |
778 $tokens = \PHP_Token_Stream_CachingFactory::get($filename); | |
779 } else { | |
780 $tokens = new \PHP_Token_Stream($filename); | |
781 } | |
782 | |
783 foreach ($tokens->getInterfaces() as $interface) { | |
784 $interfaceStartLine = $interface['startLine']; | |
785 $interfaceEndLine = $interface['endLine']; | |
786 | |
787 foreach (\range($interfaceStartLine, $interfaceEndLine) as $line) { | |
788 $this->ignoredLines[$filename][] = $line; | |
789 } | |
790 } | |
791 | |
792 foreach (\array_merge($tokens->getClasses(), $tokens->getTraits()) as $classOrTrait) { | |
793 $classOrTraitStartLine = $classOrTrait['startLine']; | |
794 $classOrTraitEndLine = $classOrTrait['endLine']; | |
795 | |
796 if (empty($classOrTrait['methods'])) { | |
797 foreach (\range($classOrTraitStartLine, $classOrTraitEndLine) as $line) { | |
798 $this->ignoredLines[$filename][] = $line; | |
682 } | 799 } |
683 } | 800 |
684 | 801 continue; |
685 if ($this->cacheTokens) { | 802 } |
686 $tokens = PHP_Token_Stream_CachingFactory::get($filename); | 803 |
687 } else { | 804 $firstMethod = \array_shift($classOrTrait['methods']); |
688 $tokens = new PHP_Token_Stream($filename); | 805 $firstMethodStartLine = $firstMethod['startLine']; |
689 } | 806 $firstMethodEndLine = $firstMethod['endLine']; |
690 | 807 $lastMethodEndLine = $firstMethodEndLine; |
691 $classes = array_merge($tokens->getClasses(), $tokens->getTraits()); | 808 |
692 $tokens = $tokens->tokens(); | 809 do { |
693 | 810 $lastMethod = \array_pop($classOrTrait['methods']); |
694 foreach ($tokens as $token) { | 811 } while ($lastMethod !== null && 0 === \strpos($lastMethod['signature'], 'anonymousFunction')); |
695 switch (get_class($token)) { | 812 |
696 case 'PHP_Token_COMMENT': | 813 if ($lastMethod !== null) { |
697 case 'PHP_Token_DOC_COMMENT': | 814 $lastMethodEndLine = $lastMethod['endLine']; |
698 $_token = trim($token); | 815 } |
699 $_line = trim($lines[$token->getLine() - 1]); | 816 |
700 | 817 foreach (\range($classOrTraitStartLine, $firstMethodStartLine) as $line) { |
701 if ($_token == '// @codeCoverageIgnore' || | 818 $this->ignoredLines[$filename][] = $line; |
702 $_token == '//@codeCoverageIgnore') { | 819 } |
703 $ignore = true; | 820 |
704 $stop = true; | 821 foreach (\range($lastMethodEndLine + 1, $classOrTraitEndLine) as $line) { |
705 } elseif ($_token == '// @codeCoverageIgnoreStart' || | 822 $this->ignoredLines[$filename][] = $line; |
706 $_token == '//@codeCoverageIgnoreStart') { | 823 } |
707 $ignore = true; | 824 } |
708 } elseif ($_token == '// @codeCoverageIgnoreEnd' || | 825 |
709 $_token == '//@codeCoverageIgnoreEnd') { | 826 if ($this->disableIgnoredLines) { |
710 $stop = true; | 827 $this->ignoredLines[$filename] = array_unique($this->ignoredLines[$filename]); |
828 \sort($this->ignoredLines[$filename]); | |
829 | |
830 return $this->ignoredLines[$filename]; | |
831 } | |
832 | |
833 $ignore = false; | |
834 $stop = false; | |
835 | |
836 foreach ($tokens->tokens() as $token) { | |
837 switch (\get_class($token)) { | |
838 case \PHP_Token_COMMENT::class: | |
839 case \PHP_Token_DOC_COMMENT::class: | |
840 $_token = \trim($token); | |
841 $_line = \trim($lines[$token->getLine() - 1]); | |
842 | |
843 if ($_token === '// @codeCoverageIgnore' || | |
844 $_token === '//@codeCoverageIgnore') { | |
845 $ignore = true; | |
846 $stop = true; | |
847 } elseif ($_token === '// @codeCoverageIgnoreStart' || | |
848 $_token === '//@codeCoverageIgnoreStart') { | |
849 $ignore = true; | |
850 } elseif ($_token === '// @codeCoverageIgnoreEnd' || | |
851 $_token === '//@codeCoverageIgnoreEnd') { | |
852 $stop = true; | |
853 } | |
854 | |
855 if (!$ignore) { | |
856 $start = $token->getLine(); | |
857 $end = $start + \substr_count($token, "\n"); | |
858 | |
859 // Do not ignore the first line when there is a token | |
860 // before the comment | |
861 if (0 !== \strpos($_token, $_line)) { | |
862 $start++; | |
711 } | 863 } |
712 | 864 |
713 if (!$ignore) { | 865 for ($i = $start; $i < $end; $i++) { |
714 $start = $token->getLine(); | 866 $this->ignoredLines[$filename][] = $i; |
715 $end = $start + substr_count($token, "\n"); | |
716 | |
717 // Do not ignore the first line when there is a token | |
718 // before the comment | |
719 if (0 !== strpos($_token, $_line)) { | |
720 $start++; | |
721 } | |
722 | |
723 for ($i = $start; $i < $end; $i++) { | |
724 $this->ignoredLines[$filename][] = $i; | |
725 } | |
726 | |
727 // A DOC_COMMENT token or a COMMENT token starting with "/*" | |
728 // does not contain the final \n character in its text | |
729 if (isset($lines[$i-1]) && 0 === strpos($_token, '/*') && '*/' === substr(trim($lines[$i-1]), -2)) { | |
730 $this->ignoredLines[$filename][] = $i; | |
731 } | |
732 } | 867 } |
733 break; | 868 |
734 | 869 // A DOC_COMMENT token or a COMMENT token starting with "/*" |
735 case 'PHP_Token_INTERFACE': | 870 // does not contain the final \n character in its text |
736 case 'PHP_Token_TRAIT': | 871 if (isset($lines[$i - 1]) && 0 === \strpos($_token, '/*') && '*/' === \substr(\trim($lines[$i - 1]), -2)) { |
737 case 'PHP_Token_CLASS': | 872 $this->ignoredLines[$filename][] = $i; |
738 case 'PHP_Token_FUNCTION': | |
739 $docblock = $token->getDocblock(); | |
740 | |
741 $this->ignoredLines[$filename][] = $token->getLine(); | |
742 | |
743 if (strpos($docblock, '@codeCoverageIgnore') || strpos($docblock, '@deprecated')) { | |
744 $endLine = $token->getEndLine(); | |
745 | |
746 for ($i = $token->getLine(); $i <= $endLine; $i++) { | |
747 $this->ignoredLines[$filename][] = $i; | |
748 } | |
749 } elseif ($token instanceof PHP_Token_INTERFACE || | |
750 $token instanceof PHP_Token_TRAIT || | |
751 $token instanceof PHP_Token_CLASS) { | |
752 if (empty($classes[$token->getName()]['methods'])) { | |
753 for ($i = $token->getLine(); | |
754 $i <= $token->getEndLine(); | |
755 $i++) { | |
756 $this->ignoredLines[$filename][] = $i; | |
757 } | |
758 } else { | |
759 $firstMethod = array_shift( | |
760 $classes[$token->getName()]['methods'] | |
761 ); | |
762 | |
763 do { | |
764 $lastMethod = array_pop( | |
765 $classes[$token->getName()]['methods'] | |
766 ); | |
767 } while ($lastMethod !== null && | |
768 substr($lastMethod['signature'], 0, 18) == 'anonymous function'); | |
769 | |
770 if ($lastMethod === null) { | |
771 $lastMethod = $firstMethod; | |
772 } | |
773 | |
774 for ($i = $token->getLine(); | |
775 $i < $firstMethod['startLine']; | |
776 $i++) { | |
777 $this->ignoredLines[$filename][] = $i; | |
778 } | |
779 | |
780 for ($i = $token->getEndLine(); | |
781 $i > $lastMethod['endLine']; | |
782 $i--) { | |
783 $this->ignoredLines[$filename][] = $i; | |
784 } | |
785 } | |
786 } | 873 } |
787 break; | 874 } |
788 | 875 |
789 case 'PHP_Token_NAMESPACE': | 876 break; |
790 $this->ignoredLines[$filename][] = $token->getEndLine(); | 877 |
791 | 878 case \PHP_Token_INTERFACE::class: |
792 // Intentional fallthrough | 879 case \PHP_Token_TRAIT::class: |
793 case 'PHP_Token_OPEN_TAG': | 880 case \PHP_Token_CLASS::class: |
794 case 'PHP_Token_CLOSE_TAG': | 881 case \PHP_Token_FUNCTION::class: |
795 case 'PHP_Token_USE': | 882 /* @var \PHP_Token_Interface $token */ |
796 $this->ignoredLines[$filename][] = $token->getLine(); | 883 |
797 break; | 884 $docblock = $token->getDocblock(); |
885 | |
886 $this->ignoredLines[$filename][] = $token->getLine(); | |
887 | |
888 if (\strpos($docblock, '@codeCoverageIgnore') || ($this->ignoreDeprecatedCode && \strpos($docblock, '@deprecated'))) { | |
889 $endLine = $token->getEndLine(); | |
890 | |
891 for ($i = $token->getLine(); $i <= $endLine; $i++) { | |
892 $this->ignoredLines[$filename][] = $i; | |
893 } | |
894 } | |
895 | |
896 break; | |
897 | |
898 case \PHP_Token_ENUM::class: | |
899 $this->ignoredLines[$filename][] = $token->getLine(); | |
900 | |
901 break; | |
902 | |
903 case \PHP_Token_NAMESPACE::class: | |
904 $this->ignoredLines[$filename][] = $token->getEndLine(); | |
905 | |
906 // Intentional fallthrough | |
907 case \PHP_Token_DECLARE::class: | |
908 case \PHP_Token_OPEN_TAG::class: | |
909 case \PHP_Token_CLOSE_TAG::class: | |
910 case \PHP_Token_USE::class: | |
911 $this->ignoredLines[$filename][] = $token->getLine(); | |
912 | |
913 break; | |
914 } | |
915 | |
916 if ($ignore) { | |
917 $this->ignoredLines[$filename][] = $token->getLine(); | |
918 | |
919 if ($stop) { | |
920 $ignore = false; | |
921 $stop = false; | |
798 } | 922 } |
799 | 923 } |
800 if ($ignore) { | 924 } |
801 $this->ignoredLines[$filename][] = $token->getLine(); | 925 |
802 | 926 $this->ignoredLines[$filename][] = \count($lines) + 1; |
803 if ($stop) { | 927 |
804 $ignore = false; | 928 $this->ignoredLines[$filename] = \array_unique( |
805 $stop = false; | 929 $this->ignoredLines[$filename] |
806 } | 930 ); |
807 } | 931 |
808 } | 932 $this->ignoredLines[$filename] = array_unique($this->ignoredLines[$filename]); |
809 | 933 \sort($this->ignoredLines[$filename]); |
810 $this->ignoredLines[$filename][] = $numLines + 1; | |
811 | |
812 $this->ignoredLines[$filename] = array_unique( | |
813 $this->ignoredLines[$filename] | |
814 ); | |
815 | |
816 sort($this->ignoredLines[$filename]); | |
817 } | |
818 | 934 |
819 return $this->ignoredLines[$filename]; | 935 return $this->ignoredLines[$filename]; |
820 } | 936 } |
821 | 937 |
822 /** | 938 /** |
823 * @param array $data | 939 * @param array $data |
824 * @param array $linesToBeCovered | 940 * @param array $linesToBeCovered |
825 * @param array $linesToBeUsed | 941 * @param array $linesToBeUsed |
826 * @throws PHP_CodeCoverage_Exception_UnintentionallyCoveredCode | 942 * |
827 * @since Method available since Release 2.0.0 | 943 * @throws \ReflectionException |
944 * @throws UnintentionallyCoveredCodeException | |
828 */ | 945 */ |
829 private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed) | 946 private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed) |
830 { | 947 { |
831 $allowedLines = $this->getAllowedLines( | 948 $allowedLines = $this->getAllowedLines( |
832 $linesToBeCovered, | 949 $linesToBeCovered, |
833 $linesToBeUsed | 950 $linesToBeUsed |
834 ); | 951 ); |
835 | 952 |
836 $message = ''; | 953 $unintentionallyCoveredUnits = []; |
837 | 954 |
838 foreach ($data as $file => $_data) { | 955 foreach ($data as $file => $_data) { |
839 foreach ($_data as $line => $flag) { | 956 foreach ($_data as $line => $flag) { |
840 if ($flag == 1 && | 957 if ($flag === 1 && !isset($allowedLines[$file][$line])) { |
841 (!isset($allowedLines[$file]) || | 958 $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line); |
842 !isset($allowedLines[$file][$line]))) { | |
843 $message .= sprintf( | |
844 '- %s:%d' . PHP_EOL, | |
845 $file, | |
846 $line | |
847 ); | |
848 } | 959 } |
849 } | 960 } |
850 } | 961 } |
851 | 962 |
963 $unintentionallyCoveredUnits = $this->processUnintentionallyCoveredUnits($unintentionallyCoveredUnits); | |
964 | |
965 if (!empty($unintentionallyCoveredUnits)) { | |
966 throw new UnintentionallyCoveredCodeException( | |
967 $unintentionallyCoveredUnits | |
968 ); | |
969 } | |
970 } | |
971 | |
972 /** | |
973 * @param array $data | |
974 * @param array $linesToBeCovered | |
975 * @param array $linesToBeUsed | |
976 * | |
977 * @throws CoveredCodeNotExecutedException | |
978 */ | |
979 private function performUnexecutedCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed) | |
980 { | |
981 $executedCodeUnits = $this->coverageToCodeUnits($data); | |
982 $message = ''; | |
983 | |
984 foreach ($this->linesToCodeUnits($linesToBeCovered) as $codeUnit) { | |
985 if (!\in_array($codeUnit, $executedCodeUnits)) { | |
986 $message .= \sprintf( | |
987 '- %s is expected to be executed (@covers) but was not executed' . "\n", | |
988 $codeUnit | |
989 ); | |
990 } | |
991 } | |
992 | |
993 foreach ($this->linesToCodeUnits($linesToBeUsed) as $codeUnit) { | |
994 if (!\in_array($codeUnit, $executedCodeUnits)) { | |
995 $message .= \sprintf( | |
996 '- %s is expected to be executed (@uses) but was not executed' . "\n", | |
997 $codeUnit | |
998 ); | |
999 } | |
1000 } | |
1001 | |
852 if (!empty($message)) { | 1002 if (!empty($message)) { |
853 throw new PHP_CodeCoverage_Exception_UnintentionallyCoveredCode( | 1003 throw new CoveredCodeNotExecutedException($message); |
854 $message | 1004 } |
855 ); | 1005 } |
856 } | 1006 |
857 } | 1007 /** |
858 | 1008 * @param array $linesToBeCovered |
859 /** | 1009 * @param array $linesToBeUsed |
860 * @param array $linesToBeCovered | 1010 * |
861 * @param array $linesToBeUsed | |
862 * @return array | 1011 * @return array |
863 * @since Method available since Release 2.0.0 | |
864 */ | 1012 */ |
865 private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed) | 1013 private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed) |
866 { | 1014 { |
867 $allowedLines = array(); | 1015 $allowedLines = []; |
868 | 1016 |
869 foreach (array_keys($linesToBeCovered) as $file) { | 1017 foreach (\array_keys($linesToBeCovered) as $file) { |
870 if (!isset($allowedLines[$file])) { | 1018 if (!isset($allowedLines[$file])) { |
871 $allowedLines[$file] = array(); | 1019 $allowedLines[$file] = []; |
872 } | 1020 } |
873 | 1021 |
874 $allowedLines[$file] = array_merge( | 1022 $allowedLines[$file] = \array_merge( |
875 $allowedLines[$file], | 1023 $allowedLines[$file], |
876 $linesToBeCovered[$file] | 1024 $linesToBeCovered[$file] |
877 ); | 1025 ); |
878 } | 1026 } |
879 | 1027 |
880 foreach (array_keys($linesToBeUsed) as $file) { | 1028 foreach (\array_keys($linesToBeUsed) as $file) { |
881 if (!isset($allowedLines[$file])) { | 1029 if (!isset($allowedLines[$file])) { |
882 $allowedLines[$file] = array(); | 1030 $allowedLines[$file] = []; |
883 } | 1031 } |
884 | 1032 |
885 $allowedLines[$file] = array_merge( | 1033 $allowedLines[$file] = \array_merge( |
886 $allowedLines[$file], | 1034 $allowedLines[$file], |
887 $linesToBeUsed[$file] | 1035 $linesToBeUsed[$file] |
888 ); | 1036 ); |
889 } | 1037 } |
890 | 1038 |
891 foreach (array_keys($allowedLines) as $file) { | 1039 foreach (\array_keys($allowedLines) as $file) { |
892 $allowedLines[$file] = array_flip( | 1040 $allowedLines[$file] = \array_flip( |
893 array_unique($allowedLines[$file]) | 1041 \array_unique($allowedLines[$file]) |
894 ); | 1042 ); |
895 } | 1043 } |
896 | 1044 |
897 return $allowedLines; | 1045 return $allowedLines; |
898 } | 1046 } |
899 | 1047 |
900 /** | 1048 /** |
901 * @return PHP_CodeCoverage_Driver | 1049 * @return Driver |
902 * @throws PHP_CodeCoverage_Exception | 1050 * |
1051 * @throws RuntimeException | |
903 */ | 1052 */ |
904 private function selectDriver() | 1053 private function selectDriver() |
905 { | 1054 { |
906 $runtime = new Runtime; | 1055 $runtime = new Runtime; |
907 | 1056 |
908 if (!$runtime->canCollectCodeCoverage()) { | 1057 if (!$runtime->canCollectCodeCoverage()) { |
909 throw new PHP_CodeCoverage_Exception('No code coverage driver available'); | 1058 throw new RuntimeException('No code coverage driver available'); |
910 } | 1059 } |
911 | 1060 |
912 if ($runtime->isHHVM()) { | 1061 if ($runtime->isHHVM()) { |
913 return new PHP_CodeCoverage_Driver_HHVM; | 1062 return new HHVM; |
914 } elseif ($runtime->isPHPDBG()) { | 1063 } |
915 return new PHP_CodeCoverage_Driver_PHPDBG; | 1064 |
916 } else { | 1065 if ($runtime->isPHPDBG()) { |
917 return new PHP_CodeCoverage_Driver_Xdebug; | 1066 return new PHPDBG; |
918 } | 1067 } |
1068 | |
1069 return new Xdebug; | |
1070 } | |
1071 | |
1072 /** | |
1073 * @param array $unintentionallyCoveredUnits | |
1074 * | |
1075 * @return array | |
1076 * | |
1077 * @throws \ReflectionException | |
1078 */ | |
1079 private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits) | |
1080 { | |
1081 $unintentionallyCoveredUnits = \array_unique($unintentionallyCoveredUnits); | |
1082 \sort($unintentionallyCoveredUnits); | |
1083 | |
1084 foreach (\array_keys($unintentionallyCoveredUnits) as $k => $v) { | |
1085 $unit = \explode('::', $unintentionallyCoveredUnits[$k]); | |
1086 | |
1087 if (\count($unit) !== 2) { | |
1088 continue; | |
1089 } | |
1090 | |
1091 $class = new \ReflectionClass($unit[0]); | |
1092 | |
1093 foreach ($this->unintentionallyCoveredSubclassesWhitelist as $whitelisted) { | |
1094 if ($class->isSubclassOf($whitelisted)) { | |
1095 unset($unintentionallyCoveredUnits[$k]); | |
1096 | |
1097 break; | |
1098 } | |
1099 } | |
1100 } | |
1101 | |
1102 return \array_values($unintentionallyCoveredUnits); | |
1103 } | |
1104 | |
1105 /** | |
1106 * If we are processing uncovered files from whitelist, | |
1107 * we can initialize the data before we start to speed up the tests | |
1108 * | |
1109 * @throws \SebastianBergmann\CodeCoverage\RuntimeException | |
1110 */ | |
1111 protected function initializeData() | |
1112 { | |
1113 $this->isInitialized = true; | |
1114 | |
1115 if ($this->processUncoveredFilesFromWhitelist) { | |
1116 $this->shouldCheckForDeadAndUnused = false; | |
1117 | |
1118 $this->driver->start(true); | |
1119 | |
1120 foreach ($this->filter->getWhitelist() as $file) { | |
1121 if ($this->filter->isFile($file)) { | |
1122 include_once($file); | |
1123 } | |
1124 } | |
1125 | |
1126 $data = []; | |
1127 $coverage = $this->driver->stop(); | |
1128 | |
1129 foreach ($coverage as $file => $fileCoverage) { | |
1130 if ($this->filter->isFiltered($file)) { | |
1131 continue; | |
1132 } | |
1133 | |
1134 foreach (\array_keys($fileCoverage) as $key) { | |
1135 if ($fileCoverage[$key] === Driver::LINE_EXECUTED) { | |
1136 $fileCoverage[$key] = Driver::LINE_NOT_EXECUTED; | |
1137 } | |
1138 } | |
1139 | |
1140 $data[$file] = $fileCoverage; | |
1141 } | |
1142 | |
1143 $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); | |
1144 } | |
1145 } | |
1146 | |
1147 /** | |
1148 * @param array $data | |
1149 * | |
1150 * @return array | |
1151 */ | |
1152 private function coverageToCodeUnits(array $data) | |
1153 { | |
1154 $codeUnits = []; | |
1155 | |
1156 foreach ($data as $filename => $lines) { | |
1157 foreach ($lines as $line => $flag) { | |
1158 if ($flag === 1) { | |
1159 $codeUnits[] = $this->wizard->lookup($filename, $line); | |
1160 } | |
1161 } | |
1162 } | |
1163 | |
1164 return \array_unique($codeUnits); | |
1165 } | |
1166 | |
1167 /** | |
1168 * @param array $data | |
1169 * | |
1170 * @return array | |
1171 */ | |
1172 private function linesToCodeUnits(array $data) | |
1173 { | |
1174 $codeUnits = []; | |
1175 | |
1176 foreach ($data as $filename => $lines) { | |
1177 foreach ($lines as $line) { | |
1178 $codeUnits[] = $this->wizard->lookup($filename, $line); | |
1179 } | |
1180 } | |
1181 | |
1182 return \array_unique($codeUnits); | |
919 } | 1183 } |
920 } | 1184 } |