Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions phpunit.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="driver" type="xs:string"/>
<xs:attribute name="pathCoverage" type="xs:boolean" default="false"/>
<xs:attribute name="includeUncoveredFiles" type="xs:boolean" default="true"/>
<xs:attribute name="ignoreDeprecatedCodeUnits" type="xs:boolean" default="false"/>
Expand Down
71 changes: 68 additions & 3 deletions src/Runner/CodeCoverage.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
namespace PHPUnit\Runner;

use function assert;
use function class_exists;
use function is_subclass_of;
use function sprintf;
use function sys_get_temp_dir;
use DateTimeImmutable;
Expand All @@ -19,6 +21,7 @@
use PHPUnit\TextUI\Configuration\Configuration;
use PHPUnit\TextUI\Output\Printer;
use PHPUnit\Util\Filesystem;
use ReflectionClass;
use SebastianBergmann\CodeCoverage\Driver\Driver;
use SebastianBergmann\CodeCoverage\Driver\Selector;
use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException;
Expand Down Expand Up @@ -76,7 +79,11 @@ public function init(Configuration $configuration, CodeCoverageFilterRegistry $c
return CodeCoverageInitializationStatus::NOT_REQUESTED;
}

$this->activate($codeCoverageFilterRegistry->get(), $configuration->pathCoverage());
$this->activate(
$codeCoverageFilterRegistry->get(),
$configuration->pathCoverage(),
$configuration->hasCoverageDriver() ? $configuration->coverageDriver() : null,
);

if (!$this->isActive()) {
return CodeCoverageInitializationStatus::FAILED;
Expand Down Expand Up @@ -428,10 +435,12 @@ public function generateReports(Printer $printer, Configuration $configuration):
}
}

private function activate(Filter $filter, bool $pathCoverage): void
private function activate(Filter $filter, bool $pathCoverage, ?string $driverClass = null): void
{
try {
if ($pathCoverage) {
if ($driverClass !== null) {
$this->driver = $this->instantiateDriver($driverClass, $filter, $pathCoverage);
} elseif ($pathCoverage) {
$this->driver = (new Selector)->forLineAndPathCoverage($filter);
} else {
$this->driver = (new Selector)->forLineCoverage($filter);
Expand All @@ -448,6 +457,62 @@ private function activate(Filter $filter, bool $pathCoverage): void
}
}

/**
* @phpstan-ignore return.internalClass
*/
private function instantiateDriver(string $driverClass, Filter $filter, bool $pathCoverage): Driver
{
if (!class_exists($driverClass)) {
throw new CodeCoverageDriverException(
sprintf(
'Configured code coverage driver class "%s" does not exist',
$driverClass,
),
);
}

/** @phpstan-ignore classConstant.internalClass */
if (!is_subclass_of($driverClass, Driver::class)) {
throw new CodeCoverageDriverException(
sprintf(
'Configured code coverage driver class "%s" does not extend %s',
$driverClass,
/** @phpstan-ignore classConstant.internalClass */
Driver::class,
),
);
}

$reflection = new ReflectionClass($driverClass);

if (!$reflection->isInstantiable()) {
throw new CodeCoverageDriverException(
sprintf(
'Configured code coverage driver class "%s" is not instantiable',
$driverClass,
),
);
}

$constructor = $reflection->getConstructor();

if ($constructor !== null && $constructor->getNumberOfRequiredParameters() > 0) {
$driver = $reflection->newInstance($filter);
} else {
$driver = $reflection->newInstance();
}

/** @phpstan-ignore instanceof.internalClass */
assert($driver instanceof Driver);

if ($pathCoverage) {
/** @phpstan-ignore method.internalClass */
$driver->enableBranchAndPathCoverage();
}

return $driver;
}

private function codeCoverageGenerationStart(Printer $printer, string $format): void
{
$printer->print(
Expand Down
21 changes: 21 additions & 0 deletions src/Runner/Exception/CodeCoverageDriverException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?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\Runner;

use RuntimeException;

/**
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class CodeCoverageDriverException extends RuntimeException implements Exception
{
}
17 changes: 16 additions & 1 deletion src/TextUI/Configuration/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
private ?string $coverageCacheDirectory;
private Source $source;
private bool $pathCoverage;
private ?string $coverageDriver;
private ?string $coverageClover;
private ?string $coverageCobertura;
private ?string $coverageCrap4j;
Expand Down Expand Up @@ -232,7 +233,7 @@
* @param null|non-empty-string $generateBaseline
* @param non-negative-int $shortenArraysForExportThreshold
*/
public function __construct(array $cliArguments, ?string $testFilesFile, ?string $configurationFile, ?string $bootstrap, array $bootstrapForTestSuite, bool $cacheResult, ?string $cacheDirectory, ?string $coverageCacheDirectory, Source $source, string $testResultCacheFile, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4j, int $coverageCrap4jThreshold, ?string $coverageHtml, int $coverageHtmlLowUpperBound, int $coverageHtmlHighLowerBound, string $coverageHtmlColorSuccessLow, string $coverageHtmlColorSuccessLowDark, string $coverageHtmlColorSuccessMedium, string $coverageHtmlColorSuccessMediumDark, string $coverageHtmlColorSuccessHigh, string $coverageHtmlColorSuccessHighDark, string $coverageHtmlColorSuccessBar, string $coverageHtmlColorSuccessBarDark, string $coverageHtmlColorWarning, string $coverageHtmlColorWarningDark, string $coverageHtmlColorWarningBar, string $coverageHtmlColorWarningBarDark, string $coverageHtmlColorDanger, string $coverageHtmlColorDangerDark, string $coverageHtmlColorDangerBar, string $coverageHtmlColorDangerBarDark, string $coverageHtmlColorBreadcrumbs, string $coverageHtmlColorBreadcrumbsDark, ?string $coverageHtmlCustomCssFile, ?string $coverageOpenClover, ?string $coveragePhp, ?string $coverageText, bool $coverageTextShowUncoveredFiles, bool $coverageTextShowOnlySummary, ?string $coverageXml, bool $coverageXmlIncludeSource, bool $pathCoverage, bool $ignoreDeprecatedCodeUnitsFromCodeCoverage, bool $disableCodeCoverageIgnore, bool $failOnAllIssues, bool $failOnDeprecation, bool $failOnPhpunitDeprecation, bool $failOnPhpunitNotice, bool $failOnPhpunitWarning, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnNotice, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $doNotFailOnDeprecation, bool $doNotFailOnPhpunitDeprecation, bool $doNotFailOnPhpunitNotice, bool $doNotFailOnPhpunitWarning, bool $doNotFailOnEmptyTestSuite, bool $doNotFailOnIncomplete, bool $doNotFailOnNotice, bool $doNotFailOnRisky, bool $doNotFailOnSkipped, bool $doNotFailOnWarning, bool $stopOnDefect, bool $stopOnDeprecation, ?string $specificDeprecationToStopOn, bool $stopOnError, bool $stopOnFailure, bool $stopOnIncomplete, bool $stopOnNotice, bool $stopOnRisky, bool $stopOnSkipped, bool $stopOnWarning, bool $outputToStandardErrorStream, int $columns, bool $noExtensions, ?string $pharExtensionDirectory, array $extensionBootstrappers, bool $backupGlobals, bool $backupStaticProperties, bool $beStrictAboutChangesToGlobalState, bool $colors, bool $processIsolation, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, bool $reportUselessTests, bool $strictCoverage, bool $disallowTestOutput, bool $displayDetailsOnAllIssues, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnPhpunitDeprecations, bool $displayDetailsOnPhpunitNotices, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $reverseDefectList, bool $requireCoverageMetadata, bool $requireSealedMockObjects, bool $noProgress, bool $noResults, bool $noOutput, int $executionOrder, int $executionOrderDefects, bool $resolveDependencies, ?string $logfileTeamcity, ?string $logfileJunit, ?string $logfileOtr, bool $includeGitInformation, bool $includeGitInformationInOtrLogfile, ?string $logfileTestdoxHtml, ?string $logfileTestdoxText, ?string $logEventsText, ?string $logEventsVerboseText, bool $teamCityOutput, bool $testDoxOutput, bool $testDoxOutputSummary, ?array $testsCovering, ?array $testsUsing, ?array $testsRequiringPhpExtension, ?string $filter, ?string $excludeFilter, array $groups, array $excludeGroups, int $randomOrderSeed, bool $includeUncoveredFiles, TestSuiteCollection $testSuite, string $includeTestSuite, string $excludeTestSuite, ?string $defaultTestSuite, bool $ignoreTestSelectionInXmlConfiguration, array $testSuffixes, Php $php, bool $controlGarbageCollector, int $numberOfTestsBeforeGarbageCollection, ?string $generateBaseline, bool $debug, bool $withTelemetry, int $shortenArraysForExportThreshold)
public function __construct(array $cliArguments, ?string $testFilesFile, ?string $configurationFile, ?string $bootstrap, array $bootstrapForTestSuite, bool $cacheResult, ?string $cacheDirectory, ?string $coverageCacheDirectory, Source $source, string $testResultCacheFile, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4j, int $coverageCrap4jThreshold, ?string $coverageHtml, int $coverageHtmlLowUpperBound, int $coverageHtmlHighLowerBound, string $coverageHtmlColorSuccessLow, string $coverageHtmlColorSuccessLowDark, string $coverageHtmlColorSuccessMedium, string $coverageHtmlColorSuccessMediumDark, string $coverageHtmlColorSuccessHigh, string $coverageHtmlColorSuccessHighDark, string $coverageHtmlColorSuccessBar, string $coverageHtmlColorSuccessBarDark, string $coverageHtmlColorWarning, string $coverageHtmlColorWarningDark, string $coverageHtmlColorWarningBar, string $coverageHtmlColorWarningBarDark, string $coverageHtmlColorDanger, string $coverageHtmlColorDangerDark, string $coverageHtmlColorDangerBar, string $coverageHtmlColorDangerBarDark, string $coverageHtmlColorBreadcrumbs, string $coverageHtmlColorBreadcrumbsDark, ?string $coverageHtmlCustomCssFile, ?string $coverageOpenClover, ?string $coveragePhp, ?string $coverageText, bool $coverageTextShowUncoveredFiles, bool $coverageTextShowOnlySummary, ?string $coverageXml, bool $coverageXmlIncludeSource, bool $pathCoverage, ?string $coverageDriver, bool $ignoreDeprecatedCodeUnitsFromCodeCoverage, bool $disableCodeCoverageIgnore, bool $failOnAllIssues, bool $failOnDeprecation, bool $failOnPhpunitDeprecation, bool $failOnPhpunitNotice, bool $failOnPhpunitWarning, bool $failOnEmptyTestSuite, bool $failOnIncomplete, bool $failOnNotice, bool $failOnRisky, bool $failOnSkipped, bool $failOnWarning, bool $doNotFailOnDeprecation, bool $doNotFailOnPhpunitDeprecation, bool $doNotFailOnPhpunitNotice, bool $doNotFailOnPhpunitWarning, bool $doNotFailOnEmptyTestSuite, bool $doNotFailOnIncomplete, bool $doNotFailOnNotice, bool $doNotFailOnRisky, bool $doNotFailOnSkipped, bool $doNotFailOnWarning, bool $stopOnDefect, bool $stopOnDeprecation, ?string $specificDeprecationToStopOn, bool $stopOnError, bool $stopOnFailure, bool $stopOnIncomplete, bool $stopOnNotice, bool $stopOnRisky, bool $stopOnSkipped, bool $stopOnWarning, bool $outputToStandardErrorStream, int $columns, bool $noExtensions, ?string $pharExtensionDirectory, array $extensionBootstrappers, bool $backupGlobals, bool $backupStaticProperties, bool $beStrictAboutChangesToGlobalState, bool $colors, bool $processIsolation, bool $enforceTimeLimit, int $defaultTimeLimit, int $timeoutForSmallTests, int $timeoutForMediumTests, int $timeoutForLargeTests, bool $reportUselessTests, bool $strictCoverage, bool $disallowTestOutput, bool $displayDetailsOnAllIssues, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnPhpunitDeprecations, bool $displayDetailsOnPhpunitNotices, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $reverseDefectList, bool $requireCoverageMetadata, bool $requireSealedMockObjects, bool $noProgress, bool $noResults, bool $noOutput, int $executionOrder, int $executionOrderDefects, bool $resolveDependencies, ?string $logfileTeamcity, ?string $logfileJunit, ?string $logfileOtr, bool $includeGitInformation, bool $includeGitInformationInOtrLogfile, ?string $logfileTestdoxHtml, ?string $logfileTestdoxText, ?string $logEventsText, ?string $logEventsVerboseText, bool $teamCityOutput, bool $testDoxOutput, bool $testDoxOutputSummary, ?array $testsCovering, ?array $testsUsing, ?array $testsRequiringPhpExtension, ?string $filter, ?string $excludeFilter, array $groups, array $excludeGroups, int $randomOrderSeed, bool $includeUncoveredFiles, TestSuiteCollection $testSuite, string $includeTestSuite, string $excludeTestSuite, ?string $defaultTestSuite, bool $ignoreTestSelectionInXmlConfiguration, array $testSuffixes, Php $php, bool $controlGarbageCollector, int $numberOfTestsBeforeGarbageCollection, ?string $generateBaseline, bool $debug, bool $withTelemetry, int $shortenArraysForExportThreshold)
{
$this->cliArguments = $cliArguments;
$this->testFilesFile = $testFilesFile;
Expand Down Expand Up @@ -278,6 +279,7 @@ public function __construct(array $cliArguments, ?string $testFilesFile, ?string
$this->coverageXml = $coverageXml;
$this->coverageXmlIncludeSource = $coverageXmlIncludeSource;
$this->pathCoverage = $pathCoverage;
$this->coverageDriver = $coverageDriver;
$this->ignoreDeprecatedCodeUnitsFromCodeCoverage = $ignoreDeprecatedCodeUnitsFromCodeCoverage;
$this->disableCodeCoverageIgnore = $disableCodeCoverageIgnore;
$this->failOnAllIssues = $failOnAllIssues;
Expand Down Expand Up @@ -537,6 +539,19 @@ public function pathCoverage(): bool
return $this->pathCoverage;
}

/**
* @phpstan-assert-if-true !null $this->coverageDriver
*/
public function hasCoverageDriver(): bool
{
return $this->coverageDriver !== null;
}

public function coverageDriver(): string
{
return $this->coverageDriver;
}

public function hasCoverageReport(): bool
{
return $this->hasCoverageClover() ||
Expand Down
7 changes: 7 additions & 0 deletions src/TextUI/Configuration/Merger.php
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,12 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC
$pathCoverage = $xmlConfiguration->codeCoverage()->pathCoverage();
}

$coverageDriver = null;

if ($xmlConfiguration->codeCoverage()->hasDriver()) {
$coverageDriver = $xmlConfiguration->codeCoverage()->driver();
}

$defaultColors = Colors::default();
$defaultThresholds = Thresholds::default();

Expand Down Expand Up @@ -1020,6 +1026,7 @@ public function merge(CliConfiguration $cliConfiguration, XmlConfiguration $xmlC
$coverageXml,
$coverageXmlIncludeSource,
$pathCoverage,
$coverageDriver,
$xmlConfiguration->codeCoverage()->ignoreDeprecatedCodeUnits(),
$disableCodeCoverageIgnore,
$failOnAllIssues,
Expand Down
26 changes: 25 additions & 1 deletion src/TextUI/Configuration/Xml/CodeCoverage/CodeCoverage.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
*/
final readonly class CodeCoverage
{
private ?string $driver;
private bool $pathCoverage;
private bool $includeUncoveredFiles;
private bool $ignoreDeprecatedCodeUnits;
Expand All @@ -41,8 +42,9 @@
private ?Text $text;
private ?Xml $xml;

public function __construct(bool $pathCoverage, bool $includeUncoveredFiles, bool $ignoreDeprecatedCodeUnits, bool $disableCodeCoverageIgnore, ?Clover $clover, ?Cobertura $cobertura, ?Crap4j $crap4j, ?Html $html, ?OpenClover $openClover, ?Php $php, ?Text $text, ?Xml $xml)
public function __construct(?string $driver, bool $pathCoverage, bool $includeUncoveredFiles, bool $ignoreDeprecatedCodeUnits, bool $disableCodeCoverageIgnore, ?Clover $clover, ?Cobertura $cobertura, ?Crap4j $crap4j, ?Html $html, ?OpenClover $openClover, ?Php $php, ?Text $text, ?Xml $xml)
{
$this->driver = $driver;
$this->pathCoverage = $pathCoverage;
$this->includeUncoveredFiles = $includeUncoveredFiles;
$this->ignoreDeprecatedCodeUnits = $ignoreDeprecatedCodeUnits;
Expand All @@ -57,6 +59,28 @@ public function __construct(bool $pathCoverage, bool $includeUncoveredFiles, boo
$this->xml = $xml;
}

/**
* @phpstan-assert-if-true !null $this->driver
*/
public function hasDriver(): bool
{
return $this->driver !== null;
}

/**
* @throws Exception
*/
public function driver(): string
{
if (!$this->hasDriver()) {
throw new Exception(
'Code Coverage driver has not been configured',
);
}

return $this->driver;
}

public function pathCoverage(): bool
{
return $this->pathCoverage;
Expand Down
1 change: 1 addition & 0 deletions src/TextUI/Configuration/Xml/DefaultConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public static function create(): self
true,
),
new CodeCoverage(
null,
false,
true,
false,
Expand Down
4 changes: 4 additions & 0 deletions src/TextUI/Configuration/Xml/Loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ private function source(string $filename, DOMXPath $xpath): Source

private function codeCoverage(string $filename, DOMXPath $xpath): CodeCoverage
{
$driver = null;
$pathCoverage = false;
$includeUncoveredFiles = true;
$ignoreDeprecatedCodeUnits = false;
Expand All @@ -411,6 +412,8 @@ private function codeCoverage(string $filename, DOMXPath $xpath): CodeCoverage
$element = $this->element($xpath, 'coverage');

if ($element !== null) {
$driver = $this->parseStringAttribute($element, 'driver');

$pathCoverage = $this->parseBooleanAttribute(
$element,
'pathCoverage',
Expand Down Expand Up @@ -582,6 +585,7 @@ private function codeCoverage(string $filename, DOMXPath $xpath): CodeCoverage
}

return new CodeCoverage(
$driver,
$pathCoverage,
$includeUncoveredFiles,
$ignoreDeprecatedCodeUnits,
Expand Down
15 changes: 15 additions & 0 deletions tests/_files/configuration_codecoverage_driver.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../phpunit.xsd">
<source>
<include>
<directory suffix=".php">/path/to/files</directory>
</include>
</source>

<coverage driver="My\Custom\Driver">
<report>
<clover outputFile="clover.xml"/>
</report>
</coverage>
</phpunit>
Loading
Loading