Skip to content

Commit 800227d

Browse files
sebastianbergmannMaks3w
authored andcommitted
Merge feature/path-coverage
27c330b
1 parent c8a0477 commit 800227d

File tree

7 files changed

+193
-120
lines changed

7 files changed

+193
-120
lines changed

src/CodeCoverage.php

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -86,25 +86,39 @@ class PHP_CodeCoverage
8686
*/
8787
private $tests = [];
8888

89+
/**
90+
* @var bool
91+
*/
92+
private $pathCoverage = false;
93+
8994
/**
9095
* Constructor.
9196
*
9297
* @param PHP_CodeCoverage_Driver $driver
9398
* @param PHP_CodeCoverage_Filter $filter
99+
* @param bool $pathCoverage
94100
* @throws PHP_CodeCoverage_RuntimeException
95101
*/
96-
public function __construct(PHP_CodeCoverage_Driver $driver = null, PHP_CodeCoverage_Filter $filter = null)
102+
public function __construct(PHP_CodeCoverage_Driver $driver = null, PHP_CodeCoverage_Filter $filter = null, $pathCoverage = true)
97103
{
104+
if (!is_bool($pathCoverage)) {
105+
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
106+
3,
107+
'boolean'
108+
);
109+
}
110+
98111
if ($driver === null) {
99-
$driver = $this->selectDriver();
112+
$driver = $this->selectDriver($pathCoverage);
100113
}
101114

102115
if ($filter === null) {
103116
$filter = new PHP_CodeCoverage_Filter;
104117
}
105118

106-
$this->driver = $driver;
107-
$this->filter = $filter;
119+
$this->driver = $driver;
120+
$this->filter = $filter;
121+
$this->pathCoverage = $pathCoverage;
108122
}
109123

110124
/**
@@ -312,15 +326,15 @@ public function append(array $data, $id = null, $append = true, $linesToBeCovere
312326

313327
$this->tests[$id] = ['size' => $size, 'status' => $status];
314328

315-
foreach ($data as $file => $lines) {
329+
foreach ($data as $file => $fileData) {
316330
if (!$this->filter->isFile($file)) {
317331
continue;
318332
}
319333

320-
foreach ($lines as $k => $v) {
334+
foreach ($fileData['lines'] as $k => $v) {
321335
if ($v == PHP_CodeCoverage_Driver::LINE_EXECUTED) {
322-
if (empty($this->data[$file][$k]) || !in_array($id, $this->data[$file][$k])) {
323-
$this->data[$file][$k][] = $id;
336+
if (empty($this->data[$file]['lines'][$k]) || !in_array($id, $this->data[$file]['lines'][$k])) {
337+
$this->data[$file]['lines'][$k][] = $id;
324338
}
325339
}
326340
}
@@ -338,22 +352,22 @@ public function merge(PHP_CodeCoverage $that)
338352
array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles())
339353
);
340354

341-
foreach ($that->data as $file => $lines) {
355+
foreach ($that->getData() as $file => $fileData) {
342356
if (!isset($this->data[$file])) {
343-
if (!$this->filter->isFiltered($file)) {
344-
$this->data[$file] = $lines;
357+
if (!$that->filter()->isFiltered($file)) {
358+
$this->data[$file] = ['lines' => $fileData['lines']];
345359
}
346360

347361
continue;
348362
}
349363

350-
foreach ($lines as $line => $data) {
364+
foreach ($fileData['lines'] as $line => $data) {
351365
if ($data !== null) {
352-
if (!isset($this->data[$file][$line])) {
353-
$this->data[$file][$line] = $data;
366+
if (!isset($this->data[$file]['lines'][$line])) {
367+
$this->data[$file]['lines'][$line] = $data;
354368
} else {
355-
$this->data[$file][$line] = array_unique(
356-
array_merge($this->data[$file][$line], $data)
369+
$this->data[$file]['lines'][$line] = array_unique(
370+
array_merge($this->data[$file]['lines'][$line], $data)
357371
);
358372
}
359373
}
@@ -498,7 +512,7 @@ private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, ar
498512
{
499513
if ($linesToBeCovered === false ||
500514
($this->forceCoversAnnotation && empty($linesToBeCovered))) {
501-
$data = [];
515+
$data = ['lines' => []];
502516

503517
return;
504518
}
@@ -520,8 +534,8 @@ private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, ar
520534
foreach (array_keys($data) as $filename) {
521535
$_linesToBeCovered = array_flip($linesToBeCovered[$filename]);
522536

523-
$data[$filename] = array_intersect_key(
524-
$data[$filename],
537+
$data[$filename]['lines'] = array_intersect_key(
538+
$data[$filename]['lines'],
525539
$_linesToBeCovered
526540
);
527541
}
@@ -554,7 +568,7 @@ private function applyIgnoredLinesFilter(array &$data)
554568
}
555569

556570
foreach ($this->getLinesToBeIgnored($filename) as $line) {
557-
unset($data[$filename][$line]);
571+
unset($data[$filename]['lines'][$line]);
558572
}
559573
}
560574
}
@@ -565,12 +579,37 @@ private function applyIgnoredLinesFilter(array &$data)
565579
*/
566580
private function initializeFilesThatAreSeenTheFirstTime(array $data)
567581
{
568-
foreach ($data as $file => $lines) {
582+
foreach ($data as $file => $fileData) {
569583
if ($this->filter->isFile($file) && !isset($this->data[$file])) {
570-
$this->data[$file] = [];
584+
$this->data[$file] = ['lines' => []];
585+
586+
if ($this->pathCoverage) {
587+
$this->data[$file]['branches'] = [];
588+
$this->data[$file]['paths'] = [];
589+
590+
foreach ($fileData['functions'] as $functionName => $functionData) {
591+
$this->data[$file]['branches'][$functionName] = [];
592+
$this->data[$file]['paths'][$functionName] = [];
593+
594+
foreach ($functionData['branches'] as $branch) {
595+
$this->data[$file]['branches'][$functionName][] = [
596+
'line_start' => $branch['line_start'],
597+
'line_end' => $branch['line_end'],
598+
'tests' => []
599+
];
600+
}
601+
602+
foreach ($functionData['paths'] as $path) {
603+
$this->data[$file]['paths'][$functionName][] = [
604+
'path' => $path['path'],
605+
'tests' => []
606+
];
607+
}
608+
}
609+
}
571610

572-
foreach ($lines as $k => $v) {
573-
$this->data[$file][$k] = $v == -2 ? null : [];
611+
foreach ($fileData['lines'] as $lineNumber => $flag) {
612+
$this->data[$file]['lines'][$lineNumber] = $flag == -2 ? null : [];
574613
}
575614
}
576615
}
@@ -599,12 +638,12 @@ private function addUncoveredFilesFromWhitelist()
599638
$uncoveredFiles
600639
);
601640
} else {
602-
$data[$uncoveredFile] = [];
641+
$data[$uncoveredFile] = ['lines' => []];
603642

604643
$lines = count(file($uncoveredFile));
605644

606645
for ($i = 1; $i <= $lines; $i++) {
607-
$data[$uncoveredFile][$i] = PHP_CodeCoverage_Driver::LINE_NOT_EXECUTED;
646+
$data[$uncoveredFile]['lines'][$i] = PHP_CodeCoverage_Driver::LINE_NOT_EXECUTED;
608647
}
609648
}
610649
}
@@ -632,7 +671,7 @@ private function processUncoveredFileFromWhitelist($uncoveredFile, array &$data,
632671
}
633672
}
634673

635-
$data[$file] = $fileCoverage;
674+
$data[$file] = ['lines' => $fileCoverage];
636675
}
637676
}
638677
}
@@ -827,8 +866,8 @@ private function performUnintentionallyCoveredCodeCheck(array &$data, array $lin
827866

828867
$message = '';
829868

830-
foreach ($data as $file => $_data) {
831-
foreach ($_data as $line => $flag) {
869+
foreach ($data as $file => $fileData) {
870+
foreach ($fileData['lines'] as $line => $flag) {
832871
if ($flag == 1 &&
833872
(!isset($allowedLines[$file]) ||
834873
!isset($allowedLines[$file][$line]))) {
@@ -890,10 +929,11 @@ private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed)
890929
}
891930

892931
/**
932+
* @param bool $pathCoverage
893933
* @return PHP_CodeCoverage_Driver
894934
* @throws PHP_CodeCoverage_RuntimeException
895935
*/
896-
private function selectDriver()
936+
private function selectDriver($pathCoverage)
897937
{
898938
$runtime = new Runtime;
899939

@@ -906,7 +946,7 @@ private function selectDriver()
906946
} elseif ($runtime->isPHPDBG()) {
907947
return new PHP_CodeCoverage_Driver_PHPDBG;
908948
} else {
909-
return new PHP_CodeCoverage_Driver_Xdebug;
949+
return new PHP_CodeCoverage_Driver_Xdebug($pathCoverage);
910950
}
911951
}
912952
}

src/CodeCoverage/Driver/Xdebug.php

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,41 @@
1717
class PHP_CodeCoverage_Driver_Xdebug implements PHP_CodeCoverage_Driver
1818
{
1919
/**
20-
* Constructor.
20+
* @var int
2121
*/
22-
public function __construct()
22+
private $flags;
23+
24+
/**
25+
* @param bool $pathCoverage
26+
*/
27+
public function __construct($pathCoverage = true)
2328
{
24-
if (!extension_loaded('xdebug')) {
25-
throw new PHP_CodeCoverage_RuntimeException('This driver requires Xdebug');
29+
if (!extension_loaded('xdebug') ||
30+
version_compare(phpversion('xdebug'), '2.3.2', '<')) {
31+
throw new PHP_CodeCoverage_RuntimeException(
32+
'This driver requires Xdebug 2.3.2 (or newer)'
33+
);
2634
}
2735

28-
if (version_compare(phpversion('xdebug'), '2.2.0-dev', '>=') &&
29-
!ini_get('xdebug.coverage_enable')) {
36+
if (!ini_get('xdebug.coverage_enable')) {
3037
throw new PHP_CodeCoverage_RuntimeException(
3138
'xdebug.coverage_enable=On has to be set in php.ini'
3239
);
3340
}
41+
42+
$this->flags = XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE;
43+
44+
if ($pathCoverage) {
45+
$this->flags |= XDEBUG_CC_BRANCH_CHECK;
46+
}
3447
}
3548

3649
/**
3750
* Start collection of code coverage information.
3851
*/
3952
public function start()
4053
{
41-
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
54+
xdebug_start_code_coverage($this->flags);
4255
}
4356

4457
/**
@@ -62,14 +75,18 @@ public function stop()
6275
private function cleanup(array $data)
6376
{
6477
foreach (array_keys($data) as $file) {
65-
unset($data[$file][0]);
78+
if (!isset($data[$file]['lines'])) {
79+
$data[$file] = ['lines' => $data[$file]];
80+
}
81+
82+
unset($data[$file]['lines'][0]);
6683

6784
if ($file != 'xdebug://debug-eval' && file_exists($file)) {
6885
$numLines = $this->getNumberOfLinesInFile($file);
6986

70-
foreach (array_keys($data[$file]) as $line) {
71-
if (isset($data[$file][$line]) && $line > $numLines) {
72-
unset($data[$file][$line]);
87+
foreach (array_keys($data[$file]['lines']) as $line) {
88+
if (isset($data[$file]['lines'][$line]) && $line > $numLines) {
89+
unset($data[$file]['lines'][$line]);
7390
}
7491
}
7592
}

src/CodeCoverage/Report/Clover.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ public function process(PHP_CodeCoverage $coverage, $target = null, $name = null
7979
for ($i = $method['startLine'];
8080
$i <= $method['endLine'];
8181
$i++) {
82-
if (isset($coverage[$i]) && ($coverage[$i] !== null)) {
83-
$methodCount = max($methodCount, count($coverage[$i]));
82+
if (isset($coverage['lines'][$i]) && ($coverage['lines'][$i] !== null)) {
83+
$methodCount = max($methodCount, count($coverage['lines'][$i]));
8484
}
8585
}
8686

@@ -158,7 +158,7 @@ public function process(PHP_CodeCoverage $coverage, $target = null, $name = null
158158
$xmlClass->appendChild($xmlMetrics);
159159
}
160160

161-
foreach ($coverage as $line => $data) {
161+
foreach ($coverage['lines'] as $line => $data) {
162162
if ($data === null || isset($lines[$line])) {
163163
continue;
164164
}

src/CodeCoverage/Report/HTML/Renderer/File.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,10 @@ protected function renderSource(PHP_CodeCoverage_Report_Node_File $node)
309309
$popoverContent = '';
310310
$popoverTitle = '';
311311

312-
if (array_key_exists($i, $coverageData)) {
313-
$numTests = count($coverageData[$i]);
312+
if (array_key_exists($i, $coverageData['lines'])) {
313+
$numTests = count($coverageData['lines'][$i]);
314314

315-
if ($coverageData[$i] === null) {
315+
if ($coverageData['lines'][$i] === null) {
316316
$trClass = ' class="warning"';
317317
} elseif ($numTests == 0) {
318318
$trClass = ' class="danger"';
@@ -326,7 +326,7 @@ protected function renderSource(PHP_CodeCoverage_Report_Node_File $node)
326326
$popoverTitle = '1 test covers line ' . $i;
327327
}
328328

329-
foreach ($coverageData[$i] as $test) {
329+
foreach ($coverageData['lines'][$i] as $test) {
330330
if ($lineCss == 'covered-by-large-tests' && $testData[$test]['size'] == 'medium') {
331331
$lineCss = 'covered-by-medium-tests';
332332
} elseif ($testData[$test]['size'] == 'small') {

src/CodeCoverage/Report/Node/File.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ protected function calculateStatistics()
390390
}
391391
}
392392

393-
if (isset($this->coverageData[$lineNumber])) {
393+
if (isset($this->coverageData['lines'][$lineNumber])) {
394394
if (isset($currentClass)) {
395395
$currentClass['executableLines']++;
396396
}
@@ -409,7 +409,7 @@ protected function calculateStatistics()
409409

410410
$this->numExecutableLines++;
411411

412-
if (count($this->coverageData[$lineNumber]) > 0) {
412+
if (count($this->coverageData['lines'][$lineNumber]) > 0) {
413413
if (isset($currentClass)) {
414414
$currentClass['executedLines']++;
415415
}

src/CodeCoverage/Report/XML.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ private function processFile(PHP_CodeCoverage_Report_Node_File $file, PHP_CodeCo
114114
$this->processFunction($function, $fileReport);
115115
}
116116

117-
foreach ($file->getCoverageData() as $line => $tests) {
117+
$fileData = $file->getCoverageData();
118+
119+
foreach ($fileData['lines'] as $line => $tests) {
118120
if (!is_array($tests) || count($tests) == 0) {
119121
continue;
120122
}

0 commit comments

Comments
 (0)