diff --git a/src/CodeCoverage/Generators/CloverXMLGenerator.php b/src/CodeCoverage/Generators/CloverXMLGenerator.php index a85038e1..bd88ec88 100644 --- a/src/CodeCoverage/Generators/CloverXMLGenerator.php +++ b/src/CodeCoverage/Generators/CloverXMLGenerator.php @@ -114,6 +114,9 @@ protected function renderSelf(): void 'coveredConditionalCount' => 0, ]; + // Prepare metrics for lines outside class/struct definitions + $structuralLines = array_fill(1, $code->linesOfCode + 1, true); + foreach (array_merge($code->classes, $code->traits) as $name => $info) { // TODO: interfaces? $elClass = $elFile->appendChild($doc->createElement('class')); if (($tmp = strrpos($name, '\\')) === false) { @@ -125,10 +128,20 @@ protected function renderSelf(): void $elClassMetrics = $elClass->appendChild($doc->createElement('metrics')); $classMetrics = $this->calculateClassMetrics($info, $coverageData); + + // mark all lines inside iterated class as non-structurals + for ($index = $info->start + 1; $index <= $info->end; $index++) { // + 1 to skip function name + unset($structuralLines[$index]); + } + self::setMetricAttributes($elClassMetrics, $classMetrics); self::appendMetrics($fileMetrics, $classMetrics); } + //plain metrics - procedural style + $structMetrics = $this->calculateStructuralMetrics($structuralLines, $coverageData); + self::appendMetrics($fileMetrics, $structMetrics); + self::setMetricAttributes($elFileMetrics, $fileMetrics); @@ -150,7 +163,6 @@ protected function renderSelf(): void self::appendMetrics($projectMetrics, $fileMetrics); } - // TODO: What about reported (covered) lines outside of class/trait definition? self::setMetricAttributes($elProjectMetrics, $projectMetrics); echo $doc->saveXML(); @@ -188,6 +200,35 @@ private function calculateClassMetrics(\stdClass $info, ?array $coverageData = n } + private function calculateStructuralMetrics(array $structuralLines, ?array $coverageData = null): \stdClass + { + $stats = (object) [ + 'statementCount' => 0, + 'coveredStatementCount' => 0, + 'elementCount' => null, + 'coveredElementCount' => null, + ]; + + if ($coverageData === null) { // Never loaded file should return empty stats + return $stats; + } + + foreach ($structuralLines as $line => $val) { + if (isset($coverageData[$line]) && $coverageData[$line] !== self::LineDead) { + $stats->statementCount++; + if ($coverageData[$line] > 0) { + $stats->coveredStatementCount++; + } + } + } + + $stats->elementCount = $stats->statementCount; + $stats->coveredElementCount = $stats->coveredStatementCount; + + return $stats; + } + + private static function analyzeMethod(\stdClass $info, ?array $coverageData = null): array { $count = 0; diff --git a/tests/CodeCoverage/CloverXMLGenerator.expected.xml b/tests/CodeCoverage/CloverXMLGenerator.expected.xml index 6186653b..64bfb1c6 100644 --- a/tests/CodeCoverage/CloverXMLGenerator.expected.xml +++ b/tests/CodeCoverage/CloverXMLGenerator.expected.xml @@ -1,9 +1,9 @@ - + - +