Skip to content

Commit e85b58e

Browse files
Implement --validate-configuration CLI option
1 parent d0b86f0 commit e85b58e

14 files changed

Lines changed: 277 additions & 1 deletion

File tree

ChangeLog-13.2.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ All notable changes of the PHPUnit 13.2 release series are documented in this fi
2626
* [#6577](https://github.com/sebastianbergmann/phpunit/issues/6577): `--run-test-id <test-id>` CLI option that accepts a single test ID for exact matching
2727
* [#6579](https://github.com/sebastianbergmann/phpunit/pull/6579): Properly handle issues triggered outside of tests
2828
* The `executionOrder` attribute in the XML configuration file now accepts `defects` combined with any main order, as well as three-way combinations of `depends`/`no-depends`, `defects`, and a main order (for example, `depends,defects,duration-ascending`)
29+
* `--validate-configuration` CLI option to validate an XML configuration file for PHPUnit
2930

3031
### Changed
3132

src/TextUI/Application.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
use PHPUnit\TextUI\Command\Result;
8787
use PHPUnit\TextUI\Command\ShowHelpCommand;
8888
use PHPUnit\TextUI\Command\ShowVersionCommand;
89+
use PHPUnit\TextUI\Command\ValidateConfigurationCommand;
8990
use PHPUnit\TextUI\Command\VersionCheckCommand;
9091
use PHPUnit\TextUI\Command\WarmCodeCoverageCacheCommand;
9192
use PHPUnit\TextUI\Configuration\BootstrapLoader;
@@ -468,6 +469,14 @@ private function executeCommandsThatOnlyRequireCliConfiguration(CliConfiguration
468469
$this->execute(new MigrateConfigurationCommand(realpath($configurationFile)));
469470
}
470471

472+
if ($cliConfiguration->validateConfiguration()) {
473+
if ($configurationFile === false) {
474+
$this->exitWithErrorMessage('No configuration file found to validate');
475+
}
476+
477+
$this->execute(new ValidateConfigurationCommand(realpath($configurationFile)));
478+
}
479+
471480
if ($cliConfiguration->hasAtLeastVersion()) {
472481
$this->execute(new AtLeastVersionCommand($cliConfiguration->atLeastVersion()));
473482
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\TextUI\Command;
11+
12+
use const PHP_EOL;
13+
use function assert;
14+
use function realpath;
15+
use function sprintf;
16+
use PHPUnit\Runner\Version;
17+
use PHPUnit\TextUI\XmlConfiguration\CannotFindSchemaException;
18+
use PHPUnit\TextUI\XmlConfiguration\SchemaFinder;
19+
use PHPUnit\TextUI\XmlConfiguration\Validator;
20+
use PHPUnit\Util\Xml\Loader as XmlLoader;
21+
use PHPUnit\Util\Xml\XmlException;
22+
23+
/**
24+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
25+
*
26+
* @internal This class is not covered by the backward compatibility promise for PHPUnit
27+
*/
28+
final readonly class ValidateConfigurationCommand implements Command
29+
{
30+
private string $filename;
31+
32+
public function __construct(string $filename)
33+
{
34+
$this->filename = $filename;
35+
}
36+
37+
public function execute(): Result
38+
{
39+
try {
40+
$document = (new XmlLoader)->loadFile($this->filename);
41+
} catch (XmlException $e) {
42+
return Result::from(
43+
sprintf(
44+
'Cannot load XML configuration file %s:%s%s%s',
45+
$this->filename,
46+
PHP_EOL,
47+
$e->getMessage(),
48+
PHP_EOL,
49+
),
50+
Result::FAILURE,
51+
);
52+
}
53+
54+
try {
55+
$xsdFilename = (new SchemaFinder)->find(Version::series());
56+
} catch (CannotFindSchemaException $e) {
57+
return Result::from(
58+
sprintf(
59+
'Cannot find schema for PHPUnit %s:%s%s%s',
60+
Version::series(),
61+
PHP_EOL,
62+
$e->getMessage(),
63+
PHP_EOL,
64+
),
65+
Result::FAILURE,
66+
);
67+
}
68+
69+
$validationResult = (new Validator)->validate($document, $xsdFilename);
70+
71+
if (!$validationResult->hasValidationErrors()) {
72+
$configurationFileRealpath = realpath($this->filename);
73+
74+
assert($configurationFileRealpath !== false && $configurationFileRealpath !== '');
75+
76+
return Result::from(
77+
sprintf(
78+
'XML configuration file %s is valid%s',
79+
$configurationFileRealpath,
80+
PHP_EOL,
81+
),
82+
);
83+
}
84+
85+
$configurationFileRealpath = realpath($this->filename);
86+
87+
assert($configurationFileRealpath !== false && $configurationFileRealpath !== '');
88+
89+
return Result::from(
90+
sprintf(
91+
'XML configuration file %s does not validate against the PHPUnit %s schema:%s%s',
92+
$configurationFileRealpath,
93+
Version::series(),
94+
PHP_EOL,
95+
$validationResult->asString(),
96+
),
97+
Result::FAILURE,
98+
);
99+
}
100+
}

src/TextUI/Configuration/Cli/Builder.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ final class Builder
102102
'include-git-information',
103103
'log-teamcity=',
104104
'migrate-configuration',
105+
'validate-configuration',
105106
'no-configuration',
106107
'no-coverage',
107108
'no-logging',
@@ -214,6 +215,7 @@ final class Builder
214215
'--list-tests',
215216
'--list-tests-xml',
216217
'--migrate-configuration',
218+
'--validate-configuration',
217219
'--version',
218220
'--warm-coverage-cache',
219221
];
@@ -326,6 +328,7 @@ public function fromParameters(array $parameters): Configuration
326328
$ignoreBaseline = false;
327329
$generateConfiguration = false;
328330
$migrateConfiguration = false;
331+
$validateConfiguration = false;
329332
$groups = null;
330333
$testsCovering = null;
331334
$testsUsing = null;
@@ -591,6 +594,11 @@ public function fromParameters(array $parameters): Configuration
591594

592595
break;
593596

597+
case '--validate-configuration':
598+
$validateConfiguration = true;
599+
600+
break;
601+
594602
case '--group':
595603
if ($groups === null) {
596604
$groups = [];
@@ -1328,6 +1336,7 @@ public function fromParameters(array $parameters): Configuration
13281336
$ignoreBaseline,
13291337
$generateConfiguration,
13301338
$migrateConfiguration,
1339+
$validateConfiguration,
13311340
$groups,
13321341
$testsCovering,
13331342
$testsUsing,

src/TextUI/Configuration/Cli/Configuration.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
private bool $ignoreBaseline;
111111
private bool $generateConfiguration;
112112
private bool $migrateConfiguration;
113+
private bool $validateConfiguration;
113114

114115
/**
115116
* @var ?non-empty-list<non-empty-string>
@@ -207,7 +208,7 @@
207208
* @param ?non-empty-list<non-empty-string> $coverageFilter
208209
* @param ?non-empty-list<non-empty-string> $extensions
209210
*/
210-
public function __construct(array $arguments, ?string $testFilesFile, ?string $testIdFile, ?string $testIdFilter, ?bool $all, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticProperties, ?bool $beStrictAboutChangesToGlobalState, ?string $bootstrap, ?string $cacheDirectory, ?bool $cacheResult, bool $checkPhpConfiguration, bool $checkVersion, ?string $colors, null|int|string $columns, ?string $configurationFile, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coverageOpenClover, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $coverageXmlIncludeSource, ?bool $pathCoverage, bool $warmCoverageCache, ?int $defaultTimeLimit, ?int $diffContext, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?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, ?int $stopOnDefect, ?int $stopOnDeprecation, ?string $specificDeprecationToStopOn, ?int $stopOnError, ?int $stopOnFailure, ?int $stopOnIncomplete, ?int $stopOnNotice, ?int $stopOnRisky, ?int $stopOnSkipped, ?int $stopOnWarning, ?string $filter, ?string $excludeFilter, ?string $generateBaseline, ?string $useBaseline, bool $ignoreBaseline, bool $generateConfiguration, bool $migrateConfiguration, ?array $groups, ?array $testsCovering, ?array $testsUsing, ?array $testsRequiringPhpExtension, bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, ?string $otrLogfile, ?bool $includeGitInformation, bool $listGroups, bool $listSuites, bool $listTestFiles, bool $listTestIds, bool $listTests, ?string $listTestsXml, ?bool $noCoverage, ?bool $noExtensions, ?bool $noOutput, ?bool $noProgress, ?bool $noResults, ?bool $noLogging, ?bool $processIsolation, ?int $randomOrderSeed, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?bool $requireCoverageContribution, ?string $teamcityLogfile, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?array $testSuffixes, ?string $testSuite, ?string $excludeTestSuite, bool $useDefaultConfiguration, ?bool $displayDetailsOnAllIssues, ?bool $displayDetailsOnIncompleteTests, ?bool $displayDetailsOnSkippedTests, ?bool $displayDetailsOnTestsThatTriggerDeprecations, ?bool $displayDetailsOnPhpunitDeprecations, ?bool $displayDetailsOnPhpunitNotices, ?bool $displayDetailsOnTestsThatTriggerErrors, ?bool $displayDetailsOnTestsThatTriggerNotices, ?bool $displayDetailsOnTestsThatTriggerWarnings, bool $version, ?array $coverageFilter, ?string $logEventsText, ?string $logEventsVerboseText, ?bool $printerTeamCity, ?bool $testdoxPrinter, ?bool $testdoxPrinterSummary, bool $debug, bool $withTelemetry, ?array $extensions)
211+
public function __construct(array $arguments, ?string $testFilesFile, ?string $testIdFile, ?string $testIdFilter, ?bool $all, ?string $atLeastVersion, ?bool $backupGlobals, ?bool $backupStaticProperties, ?bool $beStrictAboutChangesToGlobalState, ?string $bootstrap, ?string $cacheDirectory, ?bool $cacheResult, bool $checkPhpConfiguration, bool $checkVersion, ?string $colors, null|int|string $columns, ?string $configurationFile, ?string $coverageClover, ?string $coverageCobertura, ?string $coverageCrap4J, ?string $coverageHtml, ?string $coverageOpenClover, ?string $coveragePhp, ?string $coverageText, ?bool $coverageTextShowUncoveredFiles, ?bool $coverageTextShowOnlySummary, ?string $coverageXml, ?bool $coverageXmlIncludeSource, ?bool $pathCoverage, bool $warmCoverageCache, ?int $defaultTimeLimit, ?int $diffContext, ?bool $disableCodeCoverageIgnore, ?bool $disallowTestOutput, ?bool $enforceTimeLimit, ?array $excludeGroups, ?int $executionOrder, ?int $executionOrderDefects, ?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, ?int $stopOnDefect, ?int $stopOnDeprecation, ?string $specificDeprecationToStopOn, ?int $stopOnError, ?int $stopOnFailure, ?int $stopOnIncomplete, ?int $stopOnNotice, ?int $stopOnRisky, ?int $stopOnSkipped, ?int $stopOnWarning, ?string $filter, ?string $excludeFilter, ?string $generateBaseline, ?string $useBaseline, bool $ignoreBaseline, bool $generateConfiguration, bool $migrateConfiguration, bool $validateConfiguration, ?array $groups, ?array $testsCovering, ?array $testsUsing, ?array $testsRequiringPhpExtension, bool $help, ?string $includePath, ?array $iniSettings, ?string $junitLogfile, ?string $otrLogfile, ?bool $includeGitInformation, bool $listGroups, bool $listSuites, bool $listTestFiles, bool $listTestIds, bool $listTests, ?string $listTestsXml, ?bool $noCoverage, ?bool $noExtensions, ?bool $noOutput, ?bool $noProgress, ?bool $noResults, ?bool $noLogging, ?bool $processIsolation, ?int $randomOrderSeed, ?bool $reportUselessTests, ?bool $resolveDependencies, ?bool $reverseList, ?bool $stderr, ?bool $strictCoverage, ?bool $requireCoverageContribution, ?string $teamcityLogfile, ?string $testdoxHtmlFile, ?string $testdoxTextFile, ?array $testSuffixes, ?string $testSuite, ?string $excludeTestSuite, bool $useDefaultConfiguration, ?bool $displayDetailsOnAllIssues, ?bool $displayDetailsOnIncompleteTests, ?bool $displayDetailsOnSkippedTests, ?bool $displayDetailsOnTestsThatTriggerDeprecations, ?bool $displayDetailsOnPhpunitDeprecations, ?bool $displayDetailsOnPhpunitNotices, ?bool $displayDetailsOnTestsThatTriggerErrors, ?bool $displayDetailsOnTestsThatTriggerNotices, ?bool $displayDetailsOnTestsThatTriggerWarnings, bool $version, ?array $coverageFilter, ?string $logEventsText, ?string $logEventsVerboseText, ?bool $printerTeamCity, ?bool $testdoxPrinter, ?bool $testdoxPrinterSummary, bool $debug, bool $withTelemetry, ?array $extensions)
211212
{
212213
$this->arguments = $arguments;
213214
$this->testFilesFile = $testFilesFile;
@@ -286,6 +287,7 @@ public function __construct(array $arguments, ?string $testFilesFile, ?string $t
286287
$this->ignoreBaseline = $ignoreBaseline;
287288
$this->generateConfiguration = $generateConfiguration;
288289
$this->migrateConfiguration = $migrateConfiguration;
290+
$this->validateConfiguration = $validateConfiguration;
289291
$this->groups = $groups;
290292
$this->testsCovering = $testsCovering;
291293
$this->testsUsing = $testsUsing;
@@ -1787,6 +1789,11 @@ public function migrateConfiguration(): bool
17871789
return $this->migrateConfiguration;
17881790
}
17891791

1792+
public function validateConfiguration(): bool
1793+
{
1794+
return $this->validateConfiguration;
1795+
}
1796+
17901797
/**
17911798
* @phpstan-assert-if-true !null $this->groups
17921799
*/

src/TextUI/Help.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ private function elements(): array
183183
['arg' => '--cache-directory <dir>', 'desc' => 'Specify cache directory'],
184184
['arg' => '--generate-configuration', 'desc' => 'Generate configuration file with suggested settings'],
185185
['arg' => '--migrate-configuration', 'desc' => 'Migrate configuration file to current format'],
186+
['arg' => '--validate-configuration', 'desc' => 'Validate XML configuration file'],
186187
['arg' => '--generate-baseline <file>', 'desc' => 'Generate baseline for issues'],
187188
['arg' => '--use-baseline <file>', 'desc' => 'Use baseline to ignore issues'],
188189
['arg' => '--ignore-baseline', 'desc' => 'Do not use baseline to ignore issues'],

tests/end-to-end/_files/output-cli-help-color.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
suggested settings
2020
--migrate-configuration  Migrate configuration file to current
2121
format
22+
--validate-configuration  Validate XML configuration file
2223
--generate-baseline <file>  Generate baseline for issues
2324
--use-baseline <file>  Use baseline to ignore issues
2425
--ignore-baseline  Do not use baseline to ignore issues

tests/end-to-end/_files/output-cli-usage.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Configuration:
1515
--cache-directory <dir> Specify cache directory
1616
--generate-configuration Generate configuration file with suggested settings
1717
--migrate-configuration Migrate configuration file to current format
18+
--validate-configuration Validate XML configuration file
1819
--generate-baseline <file> Generate baseline for issues
1920
--use-baseline <file> Use baseline to ignore issues
2021
--ignore-baseline Do not use baseline to ignore issues
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/13.2/phpunit.xsd"
4+
invalidAttribute="true"
5+
>
6+
<invalidElement/>
7+
<testsuites>
8+
<testsuite name="default">
9+
<directory>tests</directory>
10+
</testsuite>
11+
</testsuites>
12+
</phpunit>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/13.2/phpunit.xsd"
4+
>
5+
<testsuites>
6+
<testsuite name="default">
7+
<directory>tests</directory>
8+
</testsuite>
9+
</testsuites>
10+
</phpunit>

0 commit comments

Comments
 (0)