Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Cobertura XML report format #4462

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 12 additions & 1 deletion .psalm/baseline.xml
Expand Up @@ -1699,8 +1699,9 @@
</UnusedParam>
</file>
<file src="src/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php">
<PossiblyUnusedMethod occurrences="7">
<PossiblyUnusedMethod occurrences="8">
<code>clover</code>
<code>cobertura</code>
<code>crap4j</code>
<code>html</code>
<code>pathCoverage</code>
Expand Down Expand Up @@ -1732,6 +1733,11 @@
<code>target</code>
</PossiblyUnusedMethod>
</file>
<file src="src/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php">
<PossiblyUnusedMethod occurrences="1">
<code>target</code>
</PossiblyUnusedMethod>
</file>
<file src="src/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php">
<PossiblyUnusedMethod occurrences="2">
<code>target</code>
Expand Down Expand Up @@ -1874,6 +1880,11 @@
<code>createElement</code>
</PossiblyNullReference>
</file>
<file src="src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCoberturaToReport.php">
<PossiblyNullReference occurrences="1">
<code>createElement</code>
</PossiblyNullReference>
</file>
<file src="src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php">
<PossiblyNullReference occurrences="1">
<code>createElement</code>
Expand Down
1 change: 1 addition & 0 deletions phpunit.xsd
Expand Up @@ -287,6 +287,7 @@
<xs:group name="coverageReportGroup">
<xs:all>
<xs:element name="clover" type="logToFileType" minOccurs="0"/>
<xs:element name="cobertura" type="logToFileType" minOccurs="0"/>
<xs:element name="crap4j" type="coverageReportCrap4JType" minOccurs="0" />
<xs:element name="html" type="coverageReportHtmlType" minOccurs="0" />
<xs:element name="php" type="logToFileType" minOccurs="0" />
Expand Down
8 changes: 8 additions & 0 deletions src/TextUI/CliArguments/Builder.php
Expand Up @@ -42,6 +42,7 @@ final class Builder
'warm-coverage-cache',
'coverage-filter=',
'coverage-clover=',
'coverage-cobertura=',
'coverage-crap4j=',
'coverage-html=',
'coverage-php=',
Expand Down Expand Up @@ -151,6 +152,7 @@ public function fromParameters(array $parameters, array $additionalLongOptions):
$warmCoverageCache = null;
$coverageFilter = null;
$coverageClover = null;
$coverageCobertura = null;
$coverageCrap4J = null;
$coverageHtml = null;
$coveragePhp = null;
Expand Down Expand Up @@ -284,6 +286,11 @@ public function fromParameters(array $parameters, array $additionalLongOptions):

break;

case '--coverage-cobertura':
$coverageCobertura = $option[1];

break;

case '--coverage-crap4j':
$coverageCrap4J = $option[1];

Expand Down Expand Up @@ -781,6 +788,7 @@ public function fromParameters(array $parameters, array $additionalLongOptions):
$columns,
$configuration,
$coverageClover,
$coverageCobertura,
$coverageCrap4J,
$coverageHtml,
$coveragePhp,
Expand Down
25 changes: 24 additions & 1 deletion src/TextUI/CliArguments/Configuration.php
Expand Up @@ -92,6 +92,11 @@ final class Configuration
*/
private $coverageClover;

/**
* @var ?string
*/
private $coverageCobertura;

/**
* @var ?string
*/
Expand Down Expand Up @@ -460,7 +465,7 @@ final class Configuration
/**
* @param null|int|string $columns
*/
public function __construct(?string $argument, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticAttributes, ?bool $beStrictAboutChangesToGlobalState, ?bool $beStrictAboutResourceUsageDuringSmallTests, ?string $bootstrap, ?bool $cacheResult, ?string $cacheResultFile, ?bool $checkVersion, ?string $colors, $columns, ?string $configuration, ?string $coverageClover, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $pathCoverage, ?string $coverageCacheDirectory, ?bool $warmCoverageCache, ?bool $debug, ?int $defaultTimeLimit, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $disallowTodoAnnotatedTests, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?array $extensions, ?array $unavailableExtensions, ?bool $failOnEmptyTestSuite, ?bool $failOnIncomplete, ?bool $failOnRisky, ?bool $failOnSkipped, ?bool $failOnWarning, ?string $filter, ?bool $generateConfiguration, ?bool $migrateConfiguration, ?array $groups, ?bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, ?bool $listGroups, ?bool $listSuites, ?bool $listTests, ?string $listTestsXml, ?string $loader, ?bool $noCoverage, ?bool $noExtensions, ?bool $noInteraction, ?bool $noLogging, ?string $printer, ?bool $processIsolation, ?int $randomOrderSeed, ?int $repeat, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?bool $stopOnDefect, ?bool $stopOnError, ?bool $stopOnFailure, ?bool $stopOnIncomplete, ?bool $stopOnRisky, ?bool $stopOnSkipped, ?bool $stopOnWarning, ?string $teamcityLogfile, ?array $testdoxExcludeGroups, ?array $testdoxGroups, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?string $testdoxXmlFile, ?array $testSuffixes, ?string $testSuite, array $unrecognizedOptions, ?string $unrecognizedOrderBy, ?bool $useDefaultConfiguration, ?bool $verbose, ?bool $version, ?array $coverageFilter, ?string $xdebugFilterFile)
public function __construct(?string $argument, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticAttributes, ?bool $beStrictAboutChangesToGlobalState, ?bool $beStrictAboutResourceUsageDuringSmallTests, ?string $bootstrap, ?bool $cacheResult, ?string $cacheResultFile, ?bool $checkVersion, ?string $colors, $columns, ?string $configuration, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $pathCoverage, ?string $coverageCacheDirectory, ?bool $warmCoverageCache, ?bool $debug, ?int $defaultTimeLimit, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $disallowTodoAnnotatedTests, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?array $extensions, ?array $unavailableExtensions, ?bool $failOnEmptyTestSuite, ?bool $failOnIncomplete, ?bool $failOnRisky, ?bool $failOnSkipped, ?bool $failOnWarning, ?string $filter, ?bool $generateConfiguration, ?bool $migrateConfiguration, ?array $groups, ?bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, ?bool $listGroups, ?bool $listSuites, ?bool $listTests, ?string $listTestsXml, ?string $loader, ?bool $noCoverage, ?bool $noExtensions, ?bool $noInteraction, ?bool $noLogging, ?string $printer, ?bool $processIsolation, ?int $randomOrderSeed, ?int $repeat, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?bool $stopOnDefect, ?bool $stopOnError, ?bool $stopOnFailure, ?bool $stopOnIncomplete, ?bool $stopOnRisky, ?bool $stopOnSkipped, ?bool $stopOnWarning, ?string $teamcityLogfile, ?array $testdoxExcludeGroups, ?array $testdoxGroups, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?string $testdoxXmlFile, ?array $testSuffixes, ?string $testSuite, array $unrecognizedOptions, ?string $unrecognizedOrderBy, ?bool $useDefaultConfiguration, ?bool $verbose, ?bool $version, ?array $coverageFilter, ?string $xdebugFilterFile)
{
$this->argument = $argument;
$this->atLeastVersion = $atLeastVersion;
Expand All @@ -477,6 +482,7 @@ public function __construct(?string $argument, ?string $atLeastVersion, ?bool $b
$this->configuration = $configuration;
$this->coverageFilter = $coverageFilter;
$this->coverageClover = $coverageClover;
$this->coverageCobertura = $coverageCobertura;
$this->coverageCrap4J = $coverageCrap4J;
$this->coverageHtml = $coverageHtml;
$this->coveragePhp = $coveragePhp;
Expand Down Expand Up @@ -807,6 +813,23 @@ public function coverageClover(): string
return $this->coverageClover;
}

public function hasCoverageCobertura(): bool
{
return $this->coverageCobertura !== null;
}

/**
* @throws Exception
*/
public function coverageCobertura(): string
{
if ($this->coverageCobertura === null) {
throw new Exception;
}

return $this->coverageCobertura;
}

public function hasCoverageCrap4J(): bool
{
return $this->coverageCrap4J !== null;
Expand Down
4 changes: 4 additions & 0 deletions src/TextUI/CliArguments/Mapper.php
Expand Up @@ -68,6 +68,10 @@ public function mapToLegacyArray(Configuration $arguments): array
$result['coverageClover'] = $arguments->coverageClover();
}

if ($arguments->hasCoverageCobertura()) {
$result['coverageCobertura'] = $arguments->coverageCobertura();
}

if ($arguments->hasCoverageCrap4J()) {
$result['coverageCrap4J'] = $arguments->coverageCrap4J();
}
Expand Down
1 change: 1 addition & 0 deletions src/TextUI/Help.php
Expand Up @@ -36,6 +36,7 @@ final class Help

'Code Coverage Options' => [
['arg' => '--coverage-clover <file>', 'desc' => 'Generate code coverage report in Clover XML format'],
['arg' => '--coverage-cobertura <file>', 'desc' => 'Generate code coverage report in Cobertura XML format'],
['arg' => '--coverage-crap4j <file>', 'desc' => 'Generate code coverage report in Crap4J XML format'],
['arg' => '--coverage-html <dir>', 'desc' => 'Generate code coverage report in HTML format'],
['arg' => '--coverage-php <file>', 'desc' => 'Export PHP_CodeCoverage object to file'],
Expand Down
24 changes: 24 additions & 0 deletions src/TextUI/TestRunner.php
Expand Up @@ -69,6 +69,7 @@
use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException;
use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter;
use SebastianBergmann\CodeCoverage\Report\Clover as CloverReport;
use SebastianBergmann\CodeCoverage\Report\Cobertura as CoberturaReport;
use SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport;
use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport;
use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport;
Expand Down Expand Up @@ -389,6 +390,10 @@ public function run(TestSuite $suite, array $arguments = [], array $warnings = [
$codeCoverageReports++;
}

if (isset($arguments['coverageCobertura'])) {
$codeCoverageReports++;
}

if (isset($arguments['coverageCrap4J'])) {
$codeCoverageReports++;
}
Expand Down Expand Up @@ -691,6 +696,21 @@ public function run(TestSuite $suite, array $arguments = [], array $warnings = [
}
}

if (isset($arguments['coverageCobertura'])) {
$this->codeCoverageGenerationStart('Cobertura XML');

try {
$writer = new CoberturaReport;
$writer->process($codeCoverage, $arguments['coverageCobertura']);

$this->codeCoverageGenerationSucceeded();

unset($writer);
} catch (CodeCoverageException $e) {
$this->codeCoverageGenerationFailed($e);
}
}

if (isset($arguments['coverageCrap4J'])) {
$this->codeCoverageGenerationStart('Crap4J XML');

Expand Down Expand Up @@ -887,6 +907,10 @@ private function handleConfiguration(array &$arguments): void
$arguments['coverageClover'] = $codeCoverageConfiguration->clover()->target()->path();
}

if (!isset($arguments['coverageCobertura']) && $codeCoverageConfiguration->hasCobertura()) {
$arguments['coverageCobertura'] = $codeCoverageConfiguration->cobertura()->target()->path();
}

if (!isset($arguments['coverageCrap4J']) && $codeCoverageConfiguration->hasCrap4j()) {
$arguments['coverageCrap4J'] = $codeCoverageConfiguration->crap4j()->target()->path();

Expand Down
31 changes: 30 additions & 1 deletion src/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php
Expand Up @@ -12,6 +12,7 @@
use function count;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php;
Expand Down Expand Up @@ -82,6 +83,11 @@ final class CodeCoverage
*/
private $clover;

/**
* @var ?Cobertura
*/
private $cobertura;

/**
* @var ?Crap4j
*/
Expand All @@ -107,7 +113,7 @@ final class CodeCoverage
*/
private $xml;

public function __construct(?Directory $cacheDirectory, DirectoryCollection $directories, FileCollection $files, DirectoryCollection $excludeDirectories, FileCollection $excludeFiles, bool $pathCoverage, bool $includeUncoveredFiles, bool $processUncoveredFiles, bool $ignoreDeprecatedCodeUnits, bool $disableCodeCoverageIgnore, ?Clover $clover, ?Crap4j $crap4j, ?Html $html, ?Php $php, ?Text $text, ?Xml $xml)
public function __construct(?Directory $cacheDirectory, DirectoryCollection $directories, FileCollection $files, DirectoryCollection $excludeDirectories, FileCollection $excludeFiles, bool $pathCoverage, bool $includeUncoveredFiles, bool $processUncoveredFiles, bool $ignoreDeprecatedCodeUnits, bool $disableCodeCoverageIgnore, ?Clover $clover, ?Cobertura $cobertura, ?Crap4j $crap4j, ?Html $html, ?Php $php, ?Text $text, ?Xml $xml)
{
$this->cacheDirectory = $cacheDirectory;
$this->directories = $directories;
Expand All @@ -120,6 +126,7 @@ public function __construct(?Directory $cacheDirectory, DirectoryCollection $dir
$this->ignoreDeprecatedCodeUnits = $ignoreDeprecatedCodeUnits;
$this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore;
$this->clover = $clover;
$this->cobertura = $cobertura;
$this->crap4j = $crap4j;
$this->html = $html;
$this->php = $php;
Expand Down Expand Up @@ -221,6 +228,28 @@ public function clover(): Clover
return $this->clover;
}

/**
* @psalm-assert-if-true !null $this->cobertura
*/
public function hasCobertura(): bool
{
return $this->cobertura !== null;
}

/**
* @throws Exception
*/
public function cobertura(): Cobertura
{
if (!$this->hasCobertura()) {
throw new Exception(
'Code Coverage report "Cobertura XML" has not been configured'
);
}

return $this->cobertura;
}

/**
* @psalm-assert-if-true !null $this->crap4j
*/
Expand Down
34 changes: 34 additions & 0 deletions src/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php
@@ -0,0 +1,34 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report;

use PHPUnit\TextUI\XmlConfiguration\File;

/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
* @psalm-immutable
*/
final class Cobertura
{
/**
* @var File
*/
private $target;

public function __construct(File $target)
{
$this->target = $target;
}

public function target(): File
{
return $this->target;
}
}
37 changes: 31 additions & 6 deletions src/TextUI/XmlConfiguration/Loader.php
Expand Up @@ -35,6 +35,7 @@
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\Directory as FilterDirectory;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Filter\DirectoryCollection as FilterDirectoryCollection;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Clover;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Cobertura;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Crap4j;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Html as CodeCoverageHtml;
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\Report\Php as CodeCoveragePhp;
Expand Down Expand Up @@ -450,6 +451,20 @@ private function codeCoverage(string $filename, DOMXPath $xpath, DOMDocument $do
);
}

$cobertura = null;
$element = $this->element($xpath, 'coverage/report/cobertura');

if ($element) {
$cobertura = new Cobertura(
new File(
$this->toAbsolutePath(
$filename,
(string) $this->getStringAttribute($element, 'outputFile')
)
)
);
}

$crap4j = null;
$element = $this->element($xpath, 'coverage/report/crap4j');

Expand Down Expand Up @@ -537,6 +552,7 @@ private function codeCoverage(string $filename, DOMXPath $xpath, DOMDocument $do
$ignoreDeprecatedCodeUnits,
$disableCodeCoverageIgnore,
$clover,
$cobertura,
$crap4j,
$html,
$php,
Expand Down Expand Up @@ -583,12 +599,13 @@ private function legacyCodeCoverage(string $filename, DOMXPath $xpath, DOMDocume
}
}

$clover = null;
$crap4j = null;
$html = null;
$php = null;
$text = null;
$xml = null;
$clover = null;
$cobertura = null;
$crap4j = null;
$html = null;
$php = null;
$text = null;
$xml = null;

foreach ($xpath->query('logging/log') as $log) {
assert($log instanceof DOMElement);
Expand All @@ -610,6 +627,13 @@ private function legacyCodeCoverage(string $filename, DOMXPath $xpath, DOMDocume

break;

case 'coverage-cobertura':
$cobertura = new Cobertura(
new File($target)
);

break;

case 'coverage-crap4j':
$crap4j = new Crap4j(
new File($target),
Expand Down Expand Up @@ -664,6 +688,7 @@ private function legacyCodeCoverage(string $filename, DOMXPath $xpath, DOMDocume
$ignoreDeprecatedCodeUnits,
$disableCodeCoverageIgnore,
$clover,
$cobertura,
$crap4j,
$html,
$php,
Expand Down
1 change: 1 addition & 0 deletions src/TextUI/XmlConfiguration/Migration/MigrationBuilder.php
Expand Up @@ -32,6 +32,7 @@ final class MigrationBuilder
MoveWhitelistExcludesToCoverage::class,
RemoveEmptyFilter::class,
CoverageCloverToReport::class,
CoverageCoberturaToReport::class,
CoverageCrap4jToReport::class,
CoverageHtmlToReport::class,
CoveragePhpToReport::class,
Expand Down