summaryrefslogtreecommitdiff
path: root/vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php')
-rw-r--r--vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php255
1 files changed, 255 insertions, 0 deletions
diff --git a/vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php b/vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php
new file mode 100644
index 000000000..1ed29ad52
--- /dev/null
+++ b/vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php
@@ -0,0 +1,255 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of phpunit/php-code-coverage.
+ *
+ * (c) Sebastian Bergmann <[email protected]>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace SebastianBergmann\CodeCoverage;
+
+use function array_key_exists;
+use function array_keys;
+use function array_merge;
+use function array_unique;
+use function count;
+use function is_array;
+use function ksort;
+use SebastianBergmann\CodeCoverage\Driver\Driver;
+
+/**
+ * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
+ */
+final class ProcessedCodeCoverageData
+{
+ /**
+ * Line coverage data.
+ * An array of filenames, each having an array of linenumbers, each executable line having an array of testcase ids.
+ *
+ * @var array
+ */
+ private $lineCoverage = [];
+
+ /**
+ * Function coverage data.
+ * Maintains base format of raw data (@see https://xdebug.org/docs/code_coverage), but each 'hit' entry is an array
+ * of testcase ids.
+ *
+ * @var array
+ */
+ private $functionCoverage = [];
+
+ public function initializeUnseenData(RawCodeCoverageData $rawData): void
+ {
+ foreach ($rawData->lineCoverage() as $file => $lines) {
+ if (!isset($this->lineCoverage[$file])) {
+ $this->lineCoverage[$file] = [];
+
+ foreach ($lines as $k => $v) {
+ $this->lineCoverage[$file][$k] = $v === Driver::LINE_NOT_EXECUTABLE ? null : [];
+ }
+ }
+ }
+
+ foreach ($rawData->functionCoverage() as $file => $functions) {
+ foreach ($functions as $functionName => $functionData) {
+ if (isset($this->functionCoverage[$file][$functionName])) {
+ $this->initPreviouslySeenFunction($file, $functionName, $functionData);
+ } else {
+ $this->initPreviouslyUnseenFunction($file, $functionName, $functionData);
+ }
+ }
+ }
+ }
+
+ public function markCodeAsExecutedByTestCase(string $testCaseId, RawCodeCoverageData $executedCode): void
+ {
+ foreach ($executedCode->lineCoverage() as $file => $lines) {
+ foreach ($lines as $k => $v) {
+ if ($v === Driver::LINE_EXECUTED) {
+ $this->lineCoverage[$file][$k][] = $testCaseId;
+ }
+ }
+ }
+
+ foreach ($executedCode->functionCoverage() as $file => $functions) {
+ foreach ($functions as $functionName => $functionData) {
+ foreach ($functionData['branches'] as $branchId => $branchData) {
+ if ($branchData['hit'] === Driver::BRANCH_HIT) {
+ $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'][] = $testCaseId;
+ }
+ }
+
+ foreach ($functionData['paths'] as $pathId => $pathData) {
+ if ($pathData['hit'] === Driver::BRANCH_HIT) {
+ $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'][] = $testCaseId;
+ }
+ }
+ }
+ }
+ }
+
+ public function setLineCoverage(array $lineCoverage): void
+ {
+ $this->lineCoverage = $lineCoverage;
+ }
+
+ public function lineCoverage(): array
+ {
+ ksort($this->lineCoverage);
+
+ return $this->lineCoverage;
+ }
+
+ public function setFunctionCoverage(array $functionCoverage): void
+ {
+ $this->functionCoverage = $functionCoverage;
+ }
+
+ public function functionCoverage(): array
+ {
+ ksort($this->functionCoverage);
+
+ return $this->functionCoverage;
+ }
+
+ public function coveredFiles(): array
+ {
+ ksort($this->lineCoverage);
+
+ return array_keys($this->lineCoverage);
+ }
+
+ public function renameFile(string $oldFile, string $newFile): void
+ {
+ $this->lineCoverage[$newFile] = $this->lineCoverage[$oldFile];
+
+ if (isset($this->functionCoverage[$oldFile])) {
+ $this->functionCoverage[$newFile] = $this->functionCoverage[$oldFile];
+ }
+
+ unset($this->lineCoverage[$oldFile], $this->functionCoverage[$oldFile]);
+ }
+
+ public function merge(self $newData): void
+ {
+ foreach ($newData->lineCoverage as $file => $lines) {
+ if (!isset($this->lineCoverage[$file])) {
+ $this->lineCoverage[$file] = $lines;
+
+ continue;
+ }
+
+ // we should compare the lines if any of two contains data
+ $compareLineNumbers = array_unique(
+ array_merge(
+ array_keys($this->lineCoverage[$file]),
+ array_keys($newData->lineCoverage[$file])
+ )
+ );
+
+ foreach ($compareLineNumbers as $line) {
+ $thatPriority = $this->priorityForLine($newData->lineCoverage[$file], $line);
+ $thisPriority = $this->priorityForLine($this->lineCoverage[$file], $line);
+
+ if ($thatPriority > $thisPriority) {
+ $this->lineCoverage[$file][$line] = $newData->lineCoverage[$file][$line];
+ } elseif ($thatPriority === $thisPriority && is_array($this->lineCoverage[$file][$line])) {
+ $this->lineCoverage[$file][$line] = array_unique(
+ array_merge($this->lineCoverage[$file][$line], $newData->lineCoverage[$file][$line])
+ );
+ }
+ }
+ }
+
+ foreach ($newData->functionCoverage as $file => $functions) {
+ if (!isset($this->functionCoverage[$file])) {
+ $this->functionCoverage[$file] = $functions;
+
+ continue;
+ }
+
+ foreach ($functions as $functionName => $functionData) {
+ if (isset($this->functionCoverage[$file][$functionName])) {
+ $this->initPreviouslySeenFunction($file, $functionName, $functionData);
+ } else {
+ $this->initPreviouslyUnseenFunction($file, $functionName, $functionData);
+ }
+
+ foreach ($functionData['branches'] as $branchId => $branchData) {
+ $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'], $branchData['hit']));
+ }
+
+ foreach ($functionData['paths'] as $pathId => $pathData) {
+ $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'], $pathData['hit']));
+ }
+ }
+ }
+ }
+
+ /**
+ * Determine the priority for a line.
+ *
+ * 1 = the line is not set
+ * 2 = the line has not been tested
+ * 3 = the line is dead code
+ * 4 = the line has been tested
+ *
+ * During a merge, a higher number is better.
+ */
+ private function priorityForLine(array $data, int $line): int
+ {
+ if (!array_key_exists($line, $data)) {
+ return 1;
+ }
+
+ if (is_array($data[$line]) && count($data[$line]) === 0) {
+ return 2;
+ }
+
+ if ($data[$line] === null) {
+ return 3;
+ }
+
+ return 4;
+ }
+
+ /**
+ * For a function we have never seen before, copy all data over and simply init the 'hit' array.
+ */
+ private function initPreviouslyUnseenFunction(string $file, string $functionName, array $functionData): void
+ {
+ $this->functionCoverage[$file][$functionName] = $functionData;
+
+ foreach (array_keys($functionData['branches']) as $branchId) {
+ $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = [];
+ }
+
+ foreach (array_keys($functionData['paths']) as $pathId) {
+ $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = [];
+ }
+ }
+
+ /**
+ * For a function we have seen before, only copy over and init the 'hit' array for any unseen branches and paths.
+ * Techniques such as mocking and where the contents of a file are different vary during tests (e.g. compiling
+ * containers) mean that the functions inside a file cannot be relied upon to be static.
+ */
+ private function initPreviouslySeenFunction(string $file, string $functionName, array $functionData): void
+ {
+ foreach ($functionData['branches'] as $branchId => $branchData) {
+ if (!isset($this->functionCoverage[$file][$functionName]['branches'][$branchId])) {
+ $this->functionCoverage[$file][$functionName]['branches'][$branchId] = $branchData;
+ $this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = [];
+ }
+ }
+
+ foreach ($functionData['paths'] as $pathId => $pathData) {
+ if (!isset($this->functionCoverage[$file][$functionName]['paths'][$pathId])) {
+ $this->functionCoverage[$file][$functionName]['paths'][$pathId] = $pathData;
+ $this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = [];
+ }
+ }
+ }
+}