Skip to content

Commit

Permalink
Closes #4464
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastianbergmann committed Sep 23, 2020
1 parent cfc60ae commit 2745ac0
Show file tree
Hide file tree
Showing 18 changed files with 265 additions and 15 deletions.
4 changes: 3 additions & 1 deletion .psalm/baseline.xml
Expand Up @@ -1666,7 +1666,9 @@
</InternalMethod>
<InvalidCatch occurrences="1"/>
<InvalidStringClass occurrences="1"/>
<MissingThrowsDocblock occurrences="6">
<MissingThrowsDocblock occurrences="8">
<code>addFilter</code>
<code>addFilter</code>
<code>addFilter</code>
<code>addFilter</code>
<code>addFilter</code>
Expand Down
4 changes: 4 additions & 0 deletions ChangeLog-9.4.md
Expand Up @@ -4,6 +4,10 @@ All notable changes of the PHPUnit 9.4 release series are documented in this fil

## [9.4.0] - 2020-10-02

### Added

* [#4464](https://github.com/sebastianbergmann/phpunit/issues/4464): Filter based on covered (`@covers`) / used (`@uses`) units of code

### Changed

* The PHPUnit XML configuration generator (that is invoked using the `--generate-configuration` CLI option) now asks for a cache directory (default: `.phpunit.cache`)
Expand Down
15 changes: 13 additions & 2 deletions src/Framework/TestSuite.php
Expand Up @@ -280,8 +280,8 @@ public function addTest(Test $test, $groups = []): void
$groups = $test->getGroups();
}

if (empty($groups)) {
$groups = ['default'];
if ($this->containsOnlyVirtualGroups($groups)) {
$groups[] = 'default';
}

foreach ($groups as $group) {
Expand Down Expand Up @@ -906,4 +906,15 @@ private function clearCaches(): void
$this->providedTests = null;
$this->requiredTests = null;
}

private function containsOnlyVirtualGroups(array $groups): bool
{
foreach ($groups as $group) {
if (strpos($group, '__phpunit_') !== 0) {
return false;
}
}

return true;
}
}
17 changes: 17 additions & 0 deletions src/TextUI/CliArguments/Builder.php
Expand Up @@ -9,6 +9,7 @@
*/
namespace PHPUnit\TextUI\CliArguments;

use function array_map;
use function array_merge;
use function class_exists;
use function explode;
Expand Down Expand Up @@ -60,6 +61,8 @@ final class Builder
'generate-configuration',
'globals-backup',
'group=',
'covers=',
'uses=',
'help',
'resolve-dependencies',
'ignore-dependencies',
Expand Down Expand Up @@ -179,6 +182,8 @@ public function fromParameters(array $parameters, array $additionalLongOptions):
$generateConfiguration = null;
$migrateConfiguration = null;
$groups = null;
$testsCovering = null;
$testsUsing = null;
$help = null;
$includePath = null;
$iniSettings = [];
Expand Down Expand Up @@ -374,6 +379,16 @@ public function fromParameters(array $parameters, array $additionalLongOptions):

break;

case '--covers':
$testsCovering = array_map('strtolower', explode(',', $option[1]));

break;

case '--uses':
$testsUsing = array_map('strtolower', explode(',', $option[1]));

break;

case '--test-suffix':
$testSuffixes = explode(',', $option[1]);

Expand Down Expand Up @@ -811,6 +826,8 @@ public function fromParameters(array $parameters, array $additionalLongOptions):
$generateConfiguration,
$migrateConfiguration,
$groups,
$testsCovering,
$testsUsing,
$help,
$includePath,
$iniSettings,
Expand Down
48 changes: 47 additions & 1 deletion src/TextUI/CliArguments/Configuration.php
Expand Up @@ -242,6 +242,16 @@ final class Configuration
*/
private $groups;

/**
* @var null|string[]
*/
private $testsCovering;

/**
* @var null|string[]
*/
private $testsUsing;

/**
* @var ?bool
*/
Expand Down Expand Up @@ -460,7 +470,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 $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, ?array $testsCovering, ?array $testsUsing, ?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 Down Expand Up @@ -507,6 +517,8 @@ public function __construct(?string $argument, ?string $atLeastVersion, ?bool $b
$this->generateConfiguration = $generateConfiguration;
$this->migrateConfiguration = $migrateConfiguration;
$this->groups = $groups;
$this->testsCovering = $testsCovering;
$this->testsUsing = $testsUsing;
$this->help = $help;
$this->includePath = $includePath;
$this->iniSettings = $iniSettings;
Expand Down Expand Up @@ -1283,6 +1295,40 @@ public function groups(): array
return $this->groups;
}

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

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

return $this->testsCovering;
}

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

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

return $this->testsUsing;
}

public function hasHelp(): bool
{
return $this->help !== null;
Expand Down
8 changes: 8 additions & 0 deletions src/TextUI/CliArguments/Mapper.php
Expand Up @@ -124,6 +124,14 @@ public function mapToLegacyArray(Configuration $arguments): array
$result['excludeGroups'] = $arguments->excludeGroups();
}

if ($arguments->hasTestsCovering()) {
$result['testsCovering'] = $arguments->testsCovering();
}

if ($arguments->hasTestsUsing()) {
$result['testsUsing'] = $arguments->testsUsing();
}

if ($arguments->hasTestSuffixes()) {
$result['testSuffixes'] = $arguments->testSuffixes();
}
Expand Down
5 changes: 5 additions & 0 deletions src/TextUI/Command.php
Expand Up @@ -32,6 +32,7 @@
use function sort;
use function sprintf;
use function stream_resolve_include_path;
use function strpos;
use function trim;
use function version_compare;
use PharIo\Manifest\ApplicationName;
Expand Down Expand Up @@ -651,6 +652,10 @@ private function handleListGroups(TestSuite $suite, bool $exit): int
sort($groups);

foreach ($groups as $group) {
if (strpos($group, '__phpunit_') === 0) {
continue;
}

printf(
' - %s' . PHP_EOL,
$group
Expand Down
8 changes: 5 additions & 3 deletions src/TextUI/Help.php
Expand Up @@ -60,14 +60,16 @@ final class Help
],

'Test Selection Options' => [
['arg' => '--filter <pattern>', 'desc' => 'Filter which tests to run'],
['arg' => '--list-suites', 'desc' => 'List available test suites'],
['arg' => '--testsuite <name>', 'desc' => 'Filter which testsuite to run'],
['arg' => '--list-groups', 'desc' => 'List available test groups'],
['arg' => '--group <name>', 'desc' => 'Only runs tests from the specified group(s)'],
['arg' => '--exclude-group <name>', 'desc' => 'Exclude tests from the specified group(s)'],
['arg' => '--list-groups', 'desc' => 'List available test groups'],
['arg' => '--list-suites', 'desc' => 'List available test suites'],
['arg' => '--covers <name>', 'desc' => 'Only runs tests annotated with "@covers <name>"'],
['arg' => '--uses <name>', 'desc' => 'Only runs tests annotated with "@uses <name>"'],
['arg' => '--list-tests', 'desc' => 'List available tests'],
['arg' => '--list-tests-xml <file>', 'desc' => 'List available tests in XML format'],
['arg' => '--filter <pattern>', 'desc' => 'Filter which tests to run'],
['arg' => '--test-suffix <suffixes>', 'desc' => 'Only search for test in files with specified suffix(es). Default: Test.php,.phpt'],
],

Expand Down
29 changes: 28 additions & 1 deletion src/TextUI/TestRunner.php
Expand Up @@ -13,6 +13,7 @@
use const PHP_SAPI;
use const PHP_VERSION;
use function array_diff;
use function array_map;
use function assert;
use function class_exists;
use function count;
Expand Down Expand Up @@ -1118,7 +1119,9 @@ private function processSuiteFilters(TestSuite $suite, array $arguments): void
{
if (!$arguments['filter'] &&
empty($arguments['groups']) &&
empty($arguments['excludeGroups'])) {
empty($arguments['excludeGroups']) &&
empty($arguments['testsCovering']) &&
empty($arguments['testsUsing'])) {
return;
}

Expand All @@ -1138,6 +1141,30 @@ private function processSuiteFilters(TestSuite $suite, array $arguments): void
);
}

if (!empty($arguments['testsCovering'])) {
$filterFactory->addFilter(
new ReflectionClass(IncludeGroupFilterIterator::class),
array_map(
static function (string $name): string {
return '__phpunit_covers_' . $name;
},
$arguments['testsCovering']
)
);
}

if (!empty($arguments['testsUsing'])) {
$filterFactory->addFilter(
new ReflectionClass(IncludeGroupFilterIterator::class),
array_map(
static function (string $name): string {
return '__phpunit_uses_' . $name;
},
$arguments['testsUsing']
)
);
}

if ($arguments['filter']) {
$filterFactory->addFilter(
new ReflectionClass(NameFilterIterator::class),
Expand Down
21 changes: 21 additions & 0 deletions src/Util/Test.php
Expand Up @@ -34,6 +34,8 @@
use function sprintf;
use function strncmp;
use function strpos;
use function strtolower;
use function trim;
use function version_compare;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\CodeCoverageException;
Expand Down Expand Up @@ -439,6 +441,20 @@ public static function getGroups(string $className, ?string $methodName = ''): a
}
}

foreach (['method', 'class'] as $element) {
if (isset($annotations[$element]['covers'])) {
foreach ($annotations[$element]['covers'] as $coversTarget) {
$groups[] = ['__phpunit_covers_' . self::canonicalizeName($coversTarget)];
}
}

if (isset($annotations[$element]['uses'])) {
foreach ($annotations[$element]['uses'] as $usesTarget) {
$groups[] = ['__phpunit_uses_' . self::canonicalizeName($usesTarget)];
}
}
}

return array_unique(array_merge([], ...$groups));
}

Expand Down Expand Up @@ -753,4 +769,9 @@ private static function mergeArraysRecursively(array $a, array $b): array

return $a;
}

private static function canonicalizeName(string $name): string
{
return strtolower(trim($name, '\\'));
}
}
3 changes: 2 additions & 1 deletion src/Util/TestDox/XmlResultPrinter.php
Expand Up @@ -12,6 +12,7 @@
use function array_filter;
use function get_class;
use function implode;
use function strpos;
use DOMDocument;
use DOMElement;
use PHPUnit\Framework\AssertionFailedError;
Expand Down Expand Up @@ -159,7 +160,7 @@ public function endTest(Test $test, float $time): void
$groups = array_filter(
$test->getGroups(),
static function ($group) {
return !($group === 'small' || $group === 'medium' || $group === 'large');
return !($group === 'small' || $group === 'medium' || $group === 'large' || strpos($group, '__phpunit_') === 0);
}
);

Expand Down
10 changes: 7 additions & 3 deletions tests/end-to-end/cli/_files/output-cli-help-color.txt
Expand Up @@ -37,15 +37,19 @@
--no-logging  Ignore logging configuration

Test Selection Options:
--filter <pattern>  Filter which tests to run
--list-suites  List available test suites
--testsuite <name>  Filter which testsuite to run
--list-groups  List available test groups
--group <name>  Only runs tests from the specified
group(s)
--exclude-group <name>  Exclude tests from the specified group(s)
--list-groups  List available test groups
--list-suites  List available test suites
--covers <name>  Only runs tests annotated with "@covers
<name>"
--uses <name>  Only runs tests annotated with "@uses
<name>"
--list-tests  List available tests
--list-tests-xml <file>  List available tests in XML format
--filter <pattern>  Filter which tests to run
--test-suffix <suffixes>  Only search for test in files with
specified suffix(es). Default:
Test.php,.phpt
Expand Down
8 changes: 5 additions & 3 deletions tests/end-to-end/cli/_files/output-cli-usage.txt
Expand Up @@ -31,14 +31,16 @@ Logging Options:

Test Selection Options:

--filter <pattern> Filter which tests to run
--list-suites List available test suites
--testsuite <name> Filter which testsuite to run
--list-groups List available test groups
--group <name> Only runs tests from the specified group(s)
--exclude-group <name> Exclude tests from the specified group(s)
--list-groups List available test groups
--list-suites List available test suites
--covers <name> Only runs tests annotated with "@covers <name>"
--uses <name> Only runs tests annotated with "@uses <name>"
--list-tests List available tests
--list-tests-xml <file> List available tests in XML format
--filter <pattern> Filter which tests to run
--test-suffix <suffixes> Only search for test in files with specified suffix(es). Default: Test.php,.phpt

Test Execution Options:
Expand Down

0 comments on commit 2745ac0

Please sign in to comment.