From 3203df5982abe22ec19b9c685cc78b4676f0f2b5 Mon Sep 17 00:00:00 2001 From: Doug Wright Date: Wed, 13 May 2020 15:11:00 +0100 Subject: [PATCH] Encapsulate raw coverage data from drivers --- src/CodeCoverage.php | 78 +++---- src/Driver/Driver.php | 4 +- src/Driver/PCOV.php | 5 +- src/Driver/PHPDBG.php | 5 +- src/Driver/Xdebug.php | 5 +- .../UnknownCoverageDataFormatException.php | 26 +++ src/RawCodeCoverageData.php | 71 ++++++ tests/TestCase.php | 64 ++--- tests/tests/CodeCoverageTest.php | 10 +- tests/tests/RawCodeCoverageDataTest.php | 218 ++++++++++++++++++ 10 files changed, 405 insertions(+), 81 deletions(-) create mode 100644 src/Exception/UnknownCoverageDataFormatException.php create mode 100644 src/RawCodeCoverageData.php create mode 100644 tests/tests/RawCodeCoverageDataTest.php diff --git a/src/CodeCoverage.php b/src/CodeCoverage.php index 9e7396214..757a293c6 100644 --- a/src/CodeCoverage.php +++ b/src/CodeCoverage.php @@ -255,7 +255,7 @@ public function start($id, bool $clear = false): void * @throws InvalidArgumentException * @throws \ReflectionException */ - public function stop(bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): array + public function stop(bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): RawCodeCoverageData { if (!\is_array($linesToBeCovered) && $linesToBeCovered !== false) { throw InvalidArgumentException::create( @@ -285,7 +285,7 @@ public function stop(bool $append = true, $linesToBeCovered = [], array $linesTo * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException * @throws RuntimeException */ - public function append(array $data, $id = null, bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): void + public function append(RawCodeCoverageData $rawData, $id = null, bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): void { if ($id === null) { $id = $this->currentId; @@ -295,9 +295,9 @@ public function append(array $data, $id = null, bool $append = true, $linesToBeC throw new RuntimeException; } - $this->applyWhitelistFilter($data); - $this->applyIgnoredLinesFilter($data); - $this->initializeFilesThatAreSeenTheFirstTime($data); + $this->applyWhitelistFilter($rawData); + $this->applyIgnoredLinesFilter($rawData); + $this->initializeFilesThatAreSeenTheFirstTime($rawData); if (!$append) { return; @@ -305,14 +305,14 @@ public function append(array $data, $id = null, bool $append = true, $linesToBeC if ($id !== 'UNCOVERED_FILES_FROM_WHITELIST') { $this->applyCoversAnnotationFilter( - $data, + $rawData, $linesToBeCovered, $linesToBeUsed, $ignoreForceCoversAnnotation ); } - if (empty($data)) { + if (empty($rawData->getLineData())) { return; } @@ -339,7 +339,7 @@ public function append(array $data, $id = null, bool $append = true, $linesToBeC $this->tests[$id] = ['size' => $size, 'status' => $status]; - foreach ($data as $file => $lines) { + foreach ($rawData->getLineData() as $file => $lines) { if (!$this->filter->isFile($file)) { continue; } @@ -499,7 +499,7 @@ private function getLinePriority($data, $line) * @throws MissingCoversAnnotationException * @throws UnintentionallyCoveredCodeException */ - private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed, bool $ignoreForceCoversAnnotation): void + private function applyCoversAnnotationFilter(RawCodeCoverageData $rawData, $linesToBeCovered, array $linesToBeUsed, bool $ignoreForceCoversAnnotation): void { if ($linesToBeCovered === false || ($this->forceCoversAnnotation && empty($linesToBeCovered) && !$ignoreForceCoversAnnotation)) { @@ -507,7 +507,7 @@ private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, ar throw new MissingCoversAnnotationException; } - $data = []; + $rawData->clear(); return; } @@ -519,26 +519,32 @@ private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, ar if ($this->checkForUnintentionallyCoveredCode && (!$this->currentId instanceof TestCase || (!$this->currentId->isMedium() && !$this->currentId->isLarge()))) { - $this->performUnintentionallyCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); + $this->performUnintentionallyCoveredCodeCheck($rawData, $linesToBeCovered, $linesToBeUsed); } if ($this->checkForUnexecutedCoveredCode) { - $this->performUnexecutedCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); + $this->performUnexecutedCoveredCodeCheck($rawData, $linesToBeCovered, $linesToBeUsed); } - $data = \array_intersect_key($data, $linesToBeCovered); + $rawLineData = $rawData->getLineData(); + $filesWithNoCoverage = \array_diff_key($rawLineData, $linesToBeCovered); - foreach (\array_keys($data) as $filename) { - $_linesToBeCovered = \array_flip($linesToBeCovered[$filename]); - $data[$filename] = \array_intersect_key($data[$filename], $_linesToBeCovered); + foreach (\array_keys($filesWithNoCoverage) as $fileWithNoCoverage) { + $rawData->removeCoverageDataForFile($fileWithNoCoverage); + } + + if (\is_array($linesToBeCovered)) { + foreach ($linesToBeCovered as $fileToBeCovered => $includedLines) { + $rawData->keepCoverageDataOnlyForLines($fileToBeCovered, $includedLines); + } } } - private function applyWhitelistFilter(array &$data): void + private function applyWhitelistFilter(RawCodeCoverageData $data): void { - foreach (\array_keys($data) as $filename) { + foreach (\array_keys($data->getLineData()) as $filename) { if ($this->filter->isFiltered($filename)) { - unset($data[$filename]); + $data->removeCoverageDataForFile($filename); } } } @@ -546,22 +552,16 @@ private function applyWhitelistFilter(array &$data): void /** * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException */ - private function applyIgnoredLinesFilter(array &$data): void + private function applyIgnoredLinesFilter(RawCodeCoverageData $data): void { - foreach (\array_keys($data) as $filename) { - if (!$this->filter->isFile($filename)) { - continue; - } - - foreach ($this->getLinesToBeIgnored($filename) as $line) { - unset($data[$filename][$line]); - } + foreach (\array_keys($data->getLineData()) as $filename) { + $data->removeCoverageDataForLines($filename, $this->getLinesToBeIgnored($filename)); } } - private function initializeFilesThatAreSeenTheFirstTime(array $data): void + private function initializeFilesThatAreSeenTheFirstTime(RawCodeCoverageData $data): void { - foreach ($data as $file => $lines) { + foreach ($data->getLineData() as $file => $lines) { if (!isset($this->data[$file]) && $this->filter->isFile($file)) { $this->data[$file] = []; @@ -602,7 +602,7 @@ private function addUncoveredFilesFromWhitelist(): void } } - $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); + $this->append(new RawCodeCoverageData($data), 'UNCOVERED_FILES_FROM_WHITELIST'); } private function getLinesToBeIgnored(string $fileName): array @@ -793,7 +793,7 @@ private function getLinesToBeIgnoredInner(string $fileName): array * @throws \ReflectionException * @throws UnintentionallyCoveredCodeException */ - private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void + private function performUnintentionallyCoveredCodeCheck(RawCodeCoverageData $data, array $linesToBeCovered, array $linesToBeUsed): void { $allowedLines = $this->getAllowedLines( $linesToBeCovered, @@ -802,7 +802,7 @@ private function performUnintentionallyCoveredCodeCheck(array &$data, array $lin $unintentionallyCoveredUnits = []; - foreach ($data as $file => $_data) { + foreach ($data->getLineData() as $file => $_data) { foreach ($_data as $line => $flag) { if ($flag === 1 && !isset($allowedLines[$file][$line])) { $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line); @@ -822,9 +822,9 @@ private function performUnintentionallyCoveredCodeCheck(array &$data, array $lin /** * @throws CoveredCodeNotExecutedException */ - private function performUnexecutedCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void + private function performUnexecutedCoveredCodeCheck(RawCodeCoverageData $rawData, array $linesToBeCovered, array $linesToBeUsed): void { - $executedCodeUnits = $this->coverageToCodeUnits($data); + $executedCodeUnits = $this->coverageToCodeUnits($rawData); $message = ''; foreach ($this->linesToCodeUnits($linesToBeCovered) as $codeUnit) { @@ -958,7 +958,7 @@ private function initializeData(): void $data = []; - foreach ($this->driver->stop() as $file => $fileCoverage) { + foreach ($this->driver->stop()->getLineData() as $file => $fileCoverage) { if ($this->filter->isFiltered($file)) { continue; } @@ -972,15 +972,15 @@ private function initializeData(): void $data[$file] = $fileCoverage; } - $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); + $this->append(new RawCodeCoverageData($data), 'UNCOVERED_FILES_FROM_WHITELIST'); } } - private function coverageToCodeUnits(array $data): array + private function coverageToCodeUnits(RawCodeCoverageData $rawData): array { $codeUnits = []; - foreach ($data as $filename => $lines) { + foreach ($rawData->getLineData() as $filename => $lines) { foreach ($lines as $line => $flag) { if ($flag === 1) { $codeUnits[] = $this->wizard->lookup($filename, $line); diff --git a/src/Driver/Driver.php b/src/Driver/Driver.php index 8a5f4bf08..aba17b011 100644 --- a/src/Driver/Driver.php +++ b/src/Driver/Driver.php @@ -9,6 +9,8 @@ */ namespace SebastianBergmann\CodeCoverage\Driver; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; + /** * Interface for code coverage drivers. */ @@ -43,5 +45,5 @@ public function start(bool $determineUnusedAndDead = true): void; /** * Stop collection of code coverage information. */ - public function stop(): array; + public function stop(): RawCodeCoverageData; } diff --git a/src/Driver/PCOV.php b/src/Driver/PCOV.php index 1ee2e9b3d..af830c34c 100644 --- a/src/Driver/PCOV.php +++ b/src/Driver/PCOV.php @@ -10,6 +10,7 @@ namespace SebastianBergmann\CodeCoverage\Driver; use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; /** * Driver for PCOV code coverage functionality. @@ -37,7 +38,7 @@ public function start(bool $determineUnusedAndDead = true): void /** * Stop collection of code coverage information. */ - public function stop(): array + public function stop(): RawCodeCoverageData { \pcov\stop(); @@ -45,6 +46,6 @@ public function stop(): array \pcov\clear(); - return $collect; + return new RawCodeCoverageData($collect); } } diff --git a/src/Driver/PHPDBG.php b/src/Driver/PHPDBG.php index 4739ae41f..57871d6cb 100644 --- a/src/Driver/PHPDBG.php +++ b/src/Driver/PHPDBG.php @@ -9,6 +9,7 @@ */ namespace SebastianBergmann\CodeCoverage\Driver; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; use SebastianBergmann\CodeCoverage\RuntimeException; /** @@ -45,7 +46,7 @@ public function start(bool $determineUnusedAndDead = true): void /** * Stop collection of code coverage information. */ - public function stop(): array + public function stop(): RawCodeCoverageData { static $fetchedLines = []; @@ -71,7 +72,7 @@ public function stop(): array $fetchedLines = \array_merge($fetchedLines, $sourceLines); - return $this->detectExecutedLines($fetchedLines, $dbgData); + return new RawCodeCoverageData($this->detectExecutedLines($fetchedLines, $dbgData)); } /** diff --git a/src/Driver/Xdebug.php b/src/Driver/Xdebug.php index 793bb9972..1264b21eb 100644 --- a/src/Driver/Xdebug.php +++ b/src/Driver/Xdebug.php @@ -10,6 +10,7 @@ namespace SebastianBergmann\CodeCoverage\Driver; use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\RawCodeCoverageData; use SebastianBergmann\CodeCoverage\RuntimeException; /** @@ -48,12 +49,12 @@ public function start(bool $determineUnusedAndDead = true): void /** * Stop collection of code coverage information. */ - public function stop(): array + public function stop(): RawCodeCoverageData { $data = \xdebug_get_code_coverage(); \xdebug_stop_code_coverage(); - return $data; + return new RawCodeCoverageData($data); } } diff --git a/src/Exception/UnknownCoverageDataFormatException.php b/src/Exception/UnknownCoverageDataFormatException.php new file mode 100644 index 000000000..f8cd7dc5a --- /dev/null +++ b/src/Exception/UnknownCoverageDataFormatException.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +/** + * Exception that is raised when a driver supplies coverage data in a format that cannot be handled. + */ +final class UnknownCoverageDataFormatException extends RuntimeException +{ + public static function create(string $filename): self + { + return new self( + \sprintf( + 'Coverage data for file "%s" must be in Xdebug-compatible format, see https://xdebug.org/docs/code_coverage', + $filename + ) + ); + } +} diff --git a/src/RawCodeCoverageData.php b/src/RawCodeCoverageData.php new file mode 100644 index 000000000..f56312ee0 --- /dev/null +++ b/src/RawCodeCoverageData.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +/** + * Raw context-less code coverage data for SUT. + */ +final class RawCodeCoverageData +{ + /** + * Line coverage data. + * + * @see https://xdebug.org/docs/code_coverage for format + * + * @var array + */ + private $lineData = []; + + public function __construct(array $rawCoverage = []) + { + foreach ($rawCoverage as $file => $fileCoverageData) { + $hasOnlyIntegerKeys = \count(\array_filter(\array_keys($fileCoverageData), 'is_int')) === \count($fileCoverageData); + + if ($hasOnlyIntegerKeys) { + $this->lineData[$file] = $fileCoverageData; + } elseif (\count($fileCoverageData) === 2 && isset($fileCoverageData['lines'], $fileCoverageData['functions'])) { + $this->lineData[$file] = $fileCoverageData['lines']; + } else { + throw UnknownCoverageDataFormatException::create($file); + } + } + } + + public function clear(): void + { + $this->lineData = []; + } + + public function getLineData(): array + { + return $this->lineData; + } + + public function removeCoverageDataForFile(string $filename): void + { + unset($this->lineData[$filename]); + } + + /** + * @param int[] $lines + */ + public function keepCoverageDataOnlyForLines(string $filename, array $lines): void + { + $this->lineData[$filename] = \array_intersect_key($this->lineData[$filename], \array_flip($lines)); + } + + /** + * @param int[] $lines + */ + public function removeCoverageDataForLines(string $filename, array $lines): void + { + $this->lineData[$filename] = \array_diff_key($this->lineData[$filename], \array_flip($lines)); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index a1c9efb00..45e12dd2a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -23,7 +23,7 @@ public static function setUpBeforeClass(): void protected function getXdebugDataForBankAccount() { return [ - [ + new RawCodeCoverageData([ TEST_FILES_PATH . 'BankAccount.php' => [ 8 => 1, 9 => -2, @@ -39,24 +39,24 @@ protected function getXdebugDataForBankAccount() 31 => -1, 32 => -2, ], - ], - [ + ]), + new RawCodeCoverageData([ TEST_FILES_PATH . 'BankAccount.php' => [ 8 => 1, 13 => 1, 16 => 1, 29 => 1, ], - ], - [ + ]), + new RawCodeCoverageData([ TEST_FILES_PATH . 'BankAccount.php' => [ 8 => 1, 13 => 1, 16 => 1, 22 => 1, ], - ], - [ + ]), + new RawCodeCoverageData([ TEST_FILES_PATH . 'BankAccount.php' => [ 8 => 1, 13 => 1, @@ -68,7 +68,7 @@ protected function getXdebugDataForBankAccount() 29 => 1, 31 => 1, ], - ], + ]), ]; } @@ -314,14 +314,16 @@ protected function setUpXdebugStubForFileWithIgnoredLines(): Driver $stub->expects($this->any()) ->method('stop') ->will($this->returnValue( - [ - TEST_FILES_PATH . 'source_with_ignore.php' => [ - 2 => 1, - 4 => -1, - 6 => -1, - 7 => 1, - ], - ] + new RawCodeCoverageData( + [ + TEST_FILES_PATH . 'source_with_ignore.php' => [ + 2 => 1, + 4 => -1, + 6 => -1, + 7 => 1, + ], + ] + ) )); return $stub; @@ -350,19 +352,21 @@ protected function setUpXdebugStubForClassWithAnonymousFunction(): Driver $stub->expects($this->any()) ->method('stop') ->will($this->returnValue( - [ - TEST_FILES_PATH . 'source_with_class_and_anonymous_function.php' => [ - 7 => 1, - 9 => 1, - 10 => -1, - 11 => 1, - 12 => 1, - 13 => 1, - 14 => 1, - 17 => 1, - 18 => 1, - ], - ] + new RawCodeCoverageData( + [ + TEST_FILES_PATH . 'source_with_class_and_anonymous_function.php' => [ + 7 => 1, + 9 => 1, + 10 => -1, + 11 => 1, + 12 => 1, + 13 => 1, + 14 => 1, + 17 => 1, + 18 => 1, + ], + ] + ) )); return $stub; @@ -386,7 +390,7 @@ protected function setUpXdebugStubForCrashParsing(): Driver $stub->expects($this->any()) ->method('stop') - ->will($this->returnValue([])); + ->will($this->returnValue(new RawCodeCoverageData([]))); return $stub; } diff --git a/tests/tests/CodeCoverageTest.php b/tests/tests/CodeCoverageTest.php index b76fa43d9..3b7c1ae0d 100644 --- a/tests/tests/CodeCoverageTest.php +++ b/tests/tests/CodeCoverageTest.php @@ -44,7 +44,7 @@ public function testCannotAppendWithInvalidArgument(): void { $this->expectException(Exception::class); - $this->coverage->append([], null); + $this->coverage->append(new RawCodeCoverageData([]), null); } public function testCollect(): void @@ -290,12 +290,12 @@ public function testAppendThrowsExceptionIfCoveredCodeWasNotExecuted(): void $this->coverage->filter()->addDirectoryToWhitelist(TEST_FILES_PATH); $this->coverage->setCheckForUnexecutedCoveredCode(true); - $data = [ + $data = new RawCodeCoverageData([ TEST_FILES_PATH . 'BankAccount.php' => [ 29 => -1, 31 => -1, ], - ]; + ]); $linesToBeCovered = [ TEST_FILES_PATH . 'BankAccount.php' => [ @@ -316,12 +316,12 @@ public function testAppendThrowsExceptionIfUsedCodeWasNotExecuted(): void $this->coverage->filter()->addDirectoryToWhitelist(TEST_FILES_PATH); $this->coverage->setCheckForUnexecutedCoveredCode(true); - $data = [ + $data = new RawCodeCoverageData([ TEST_FILES_PATH . 'BankAccount.php' => [ 29 => -1, 31 => -1, ], - ]; + ]); $linesToBeCovered = [ TEST_FILES_PATH . 'BankAccount.php' => [ diff --git a/tests/tests/RawCodeCoverageDataTest.php b/tests/tests/RawCodeCoverageDataTest.php new file mode 100644 index 000000000..7d7a4984b --- /dev/null +++ b/tests/tests/RawCodeCoverageDataTest.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +class RawCodeCoverageDataTest extends TestCase +{ + /** + * In the standard XDebug format, there is only line data. Therefore output should match input. + */ + public function testLineDataFromStandardXDebugFormat(): void + { + $lineDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + ]; + + $dataObject = new RawCodeCoverageData($lineDataFromDriver); + $this->assertEquals($lineDataFromDriver, $dataObject->getLineData()); + } + + /** + * In the branch-check XDebug format, the line data exists inside a "lines" array key. + */ + public function testLineDataFromBranchCheckXDebugFormat(): void + { + $rawDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 'lines' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + 'functions' => [ + + ], + ], + ]; + + $lineData = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + ]; + + $dataObject = new RawCodeCoverageData($rawDataFromDriver); + $this->assertEquals($lineData, $dataObject->getLineData()); + } + + /** + * Coverage data that does not match a known format should throw an exception. + */ + public function testDataFromUnknownFormat(): void + { + $this->expectException(UnknownCoverageDataFormatException::class); + + $lineDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 'executedLines' => [ + 8, + ], + 'unExecutedLines' => [ + 13, + ], + 'nonExecutableLines' => [ + 9, + ], + ], + ]; + + $dataObject = new RawCodeCoverageData($lineDataFromDriver); + } + + public function testClear(): void + { + $lineDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + ]; + + $dataObject = new RawCodeCoverageData($lineDataFromDriver); + $dataObject->clear(); + $this->assertEmpty($dataObject->getLineData()); + } + + public function testRemoveCoverageDataForFile(): void + { + $lineDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + '/some/path/SomeOtherClass.php' => [ + 18 => 1, + 19 => -2, + 113 => -1, + ], + '/some/path/AnotherClass.php' => [ + 28 => 1, + 29 => -2, + 213 => -1, + ], + ]; + + $expectedFilterResult = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + '/some/path/AnotherClass.php' => [ + 28 => 1, + 29 => -2, + 213 => -1, + ], + ]; + + $dataObject = new RawCodeCoverageData($lineDataFromDriver); + $dataObject->removeCoverageDataForFile('/some/path/SomeOtherClass.php'); + $this->assertEquals($expectedFilterResult, $dataObject->getLineData()); + } + + public function testKeepCoverageDataOnlyForLines(): void + { + $lineDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + '/some/path/SomeOtherClass.php' => [ + 18 => 1, + 19 => -2, + 113 => -1, + ], + '/some/path/AnotherClass.php' => [ + 28 => 1, + 29 => -2, + 213 => -1, + ], + ]; + + $expectedFilterResult = [ + '/some/path/SomeClass.php' => [ + 9 => -2, + 13 => -1, + ], + '/some/path/SomeOtherClass.php' => [ + ], + '/some/path/AnotherClass.php' => [ + 28 => 1, + ], + ]; + + $dataObject = new RawCodeCoverageData($lineDataFromDriver); + $dataObject->keepCoverageDataOnlyForLines('/some/path/SomeClass.php', [9, 13]); + $dataObject->keepCoverageDataOnlyForLines('/some/path/SomeOtherClass.php', [999]); + $dataObject->keepCoverageDataOnlyForLines('/some/path/AnotherClass.php', [28]); + $this->assertEquals($expectedFilterResult, $dataObject->getLineData()); + } + + public function testRemoveCoverageDataForLines(): void + { + $lineDataFromDriver = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + 9 => -2, + 13 => -1, + ], + '/some/path/SomeOtherClass.php' => [ + 18 => 1, + 19 => -2, + 113 => -1, + ], + '/some/path/AnotherClass.php' => [ + 28 => 1, + 29 => -2, + 213 => -1, + ], + ]; + + $expectedFilterResult = [ + '/some/path/SomeClass.php' => [ + 8 => 1, + ], + '/some/path/SomeOtherClass.php' => [ + 18 => 1, + 19 => -2, + 113 => -1, + ], + '/some/path/AnotherClass.php' => [ + 29 => -2, + 213 => -1, + ], + ]; + + $dataObject = new RawCodeCoverageData($lineDataFromDriver); + $dataObject->removeCoverageDataForLines('/some/path/SomeClass.php', [9, 13]); + $dataObject->removeCoverageDataForLines('/some/path/SomeOtherClass.php', [999]); + $dataObject->removeCoverageDataForLines('/some/path/AnotherClass.php', [28]); + $this->assertEquals($expectedFilterResult, $dataObject->getLineData()); + } +}