summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci/pipeline.yml4
-rw-r--r--core/.phpunit-next.xml164
-rw-r--r--core/lib/Drupal/Core/Test/PhpUnitTestDiscovery.php346
-rw-r--r--core/lib/Drupal/Core/Test/RunTests/TestFileParser.php7
-rw-r--r--core/lib/Drupal/Core/Test/TestDiscovery.php41
-rw-r--r--core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageNegotiationSettingsTest.php1
-rw-r--r--core/modules/link/tests/src/Functional/LinkFieldUITest.php1
-rw-r--r--core/modules/package_manager/tests/src/Kernel/SupportedReleaseValidatorTest.php1
-rw-r--r--core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php1
-rw-r--r--core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php11
-rw-r--r--core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeTest.php1
-rw-r--r--core/modules/workflows/tests/src/Kernel/WorkflowAccessControlHandlerTest.php11
-rwxr-xr-xcore/scripts/run-tests.sh222
-rw-r--r--core/tests/Drupal/KernelTests/Core/Test/PhpUnitApiFindAllClassFilesTest.php72
-rw-r--r--core/tests/Drupal/KernelTests/Core/Test/PhpUnitApiGetTestClassesTest.php109
-rw-r--r--core/tests/Drupal/KernelTests/Core/Test/PhpUnitTestDiscoveryTest.php28
-rw-r--r--core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php7
-rw-r--r--core/tests/Drupal/Tests/Core/Test/RunTests/TestFileParserTest.php18
-rw-r--r--core/tests/Drupal/Tests/Core/Test/TestDiscoveryTest.php40
19 files changed, 968 insertions, 117 deletions
diff --git a/.gitlab-ci/pipeline.yml b/.gitlab-ci/pipeline.yml
index c311b1a03a6..06f5c83acbc 100644
--- a/.gitlab-ci/pipeline.yml
+++ b/.gitlab-ci/pipeline.yml
@@ -104,7 +104,7 @@ variables:
script:
- sudo -u www-data -E -H composer run-script drupal-phpunit-upgrade-check
# Need to pass this along directly.
- - sudo -u www-data -E -H php ./core/scripts/run-tests.sh --color --keep-results --types "$TESTSUITE" --concurrency "$CONCURRENCY" --repeat "1" --sqlite "./sites/default/files/tests.sqlite" --dburl $SIMPLETEST_DB --url $SIMPLETEST_BASE_URL --verbose --non-html --all --ci-parallel-node-index $CI_PARALLEL_NODE_INDEX --ci-parallel-node-total $CI_PARALLEL_NODE_TOTAL
+ - sudo -u www-data -E -H php ./core/scripts/run-tests.sh --debug-discovery --color --keep-results --types "$TESTSUITE" --concurrency "$CONCURRENCY" --repeat "1" --sqlite "./sites/default/files/tests.sqlite" --dburl $SIMPLETEST_DB --url $SIMPLETEST_BASE_URL --verbose --non-html --all --ci-parallel-node-index $CI_PARALLEL_NODE_INDEX --ci-parallel-node-total $CI_PARALLEL_NODE_TOTAL
.run-repeat-class-test: &run-repeat-class-test
script:
@@ -234,7 +234,7 @@ variables:
script:
- sudo -u www-data -E -H composer run-script drupal-phpunit-upgrade-check
# Run a small subset of tests to prove non W3C testing still works.
- - sudo -u www-data -E -H php ./core/scripts/run-tests.sh --color --keep-results --types "$TESTSUITE" --concurrency "$CONCURRENCY" --repeat "1" --sqlite "./sites/default/files/tests.sqlite" --dburl $SIMPLETEST_DB --url $SIMPLETEST_BASE_URL --verbose --non-html javascript
+ - sudo -u www-data -E -H php ./core/scripts/run-tests.sh --debug-discovery --color --keep-results --types "$TESTSUITE" --concurrency "$CONCURRENCY" --repeat "1" --sqlite "./sites/default/files/tests.sqlite" --dburl $SIMPLETEST_DB --url $SIMPLETEST_BASE_URL --verbose --non-html javascript
after_script:
- sed -i "s#$CI_PROJECT_DIR/##" ./sites/default/files/simpletest/phpunit-*.xml || true
diff --git a/core/.phpunit-next.xml b/core/.phpunit-next.xml
new file mode 100644
index 00000000000..944ec9dfadd
--- /dev/null
+++ b/core/.phpunit-next.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+<!-- This is a near-copy of phpunit.xml.dist, used to test with upcoming
+ PHPUnit versions.
+
+ Current differences:
+ * for PHPUnit 11: removed duplicate directories from <testsuites>,
+ required in PHPUnit 10, but that cause duplication warnings in
+ PHPUnit 11.
+-->
+
+
+<!-- For how to customize PHPUnit configuration, see core/tests/README.md. -->
+<!-- TODO set checkForUnintentionallyCoveredCode="true" once https://www.drupal.org/node/2626832 is resolved. -->
+<!-- PHPUnit expects functional tests to be run with either a privileged user
+ or your current system user. See core/tests/README.md and
+ https://www.drupal.org/node/2116263 for details.
+-->
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ bootstrap="tests/bootstrap.php"
+ colors="true"
+ beStrictAboutTestsThatDoNotTestAnything="true"
+ beStrictAboutOutputDuringTests="true"
+ beStrictAboutChangesToGlobalState="true"
+ failOnRisky="true"
+ failOnWarning="true"
+ displayDetailsOnTestsThatTriggerErrors="true"
+ displayDetailsOnTestsThatTriggerWarnings="true"
+ displayDetailsOnTestsThatTriggerDeprecations="true"
+ cacheResult="false"
+ xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
+ cacheDirectory=".phpunit.cache">
+ <php>
+ <!-- Set error reporting to E_ALL. -->
+ <ini name="error_reporting" value="32767"/>
+ <!-- Do not limit the amount of memory tests take to run. -->
+ <ini name="memory_limit" value="-1"/>
+ <!-- Example SIMPLETEST_BASE_URL value: http://localhost -->
+ <env name="SIMPLETEST_BASE_URL" value=""/>
+ <!-- Example SIMPLETEST_DB value: mysql://username:password@localhost/database_name#table_prefix -->
+ <env name="SIMPLETEST_DB" value=""/>
+ <!-- By default, browser tests will output links that use the base URL set
+ in SIMPLETEST_BASE_URL. However, if your SIMPLETEST_BASE_URL is an internal
+ path (such as may be the case in a virtual or Docker-based environment),
+ you can set the base URL used in the browser test output links to something
+ reachable from your host machine here. This will allow you to follow them
+ directly and view the output. -->
+ <env name="BROWSERTEST_OUTPUT_BASE_URL" value=""/>
+ <!-- The environment variable SYMFONY_DEPRECATIONS_HELPER is used to configure
+ the behavior of the deprecation tests.
+ Drupal core's testing framework is setting this variable to its defaults.
+ Projects with their own requirements need to manage this variable
+ explicitly.
+ -->
+ <!-- To disable deprecation testing completely uncomment the next line. -->
+ <!-- <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled"/> -->
+ <!-- Deprecation errors can be selectively ignored by specifying a file of
+ regular expression patterns for exclusion.
+ Uncomment the line below to specify a custom deprecations ignore file.
+ NOTE: it may be required to specify the full path to the file to run tests
+ correctly.
+ -->
+ <!-- <env name="SYMFONY_DEPRECATIONS_HELPER" value="ignoreFile=.deprecation-ignore.txt"/> -->
+ <!-- Example for changing the driver class for mink tests MINK_DRIVER_CLASS value: 'Drupal\FunctionalJavascriptTests\DrupalSelenium2Driver' -->
+ <env name="MINK_DRIVER_CLASS" value=""/>
+ <!-- Example for changing the driver args to mink tests MINK_DRIVER_ARGS value: '["http://127.0.0.1:8510"]' -->
+ <env name="MINK_DRIVER_ARGS" value=""/>
+ <!-- Example for changing the driver args to webdriver tests MINK_DRIVER_ARGS_WEBDRIVER value: '["chrome", { "goog:chromeOptions": { "w3c": false } }, "http://localhost:4444/wd/hub"]' For using the Firefox browser, replace "chrome" with "firefox" -->
+ <env name="MINK_DRIVER_ARGS_WEBDRIVER" value=""/>
+ </php>
+ <extensions>
+ <!-- Functional tests HTML output logging. -->
+ <bootstrap class="Drupal\TestTools\Extension\HtmlLogging\HtmlOutputLogger">
+ <!-- The directory where the browser output will be stored. If a relative
+ path is specified, it will be relative to the current working directory
+ of the process running the PHPUnit CLI. In CI environments, this can be
+ overridden by the value set for the "BROWSERTEST_OUTPUT_DIRECTORY"
+ environment variable.
+ -->
+ <parameter name="outputDirectory" value="sites/simpletest/browser_output"/>
+ <!-- By default browser tests print the individual links in the test run
+ report. To avoid overcrowding the output in CI environments, you can
+ set the "verbose" parameter or the "BROWSERTEST_OUTPUT_VERBOSE"
+ environment variable to "false". In GitLabCI, the output is saved
+ anyway as an artifact that can be browsed or downloaded from Gitlab.
+ -->
+ <parameter name="verbose" value="true"/>
+ </bootstrap>
+ <!-- Debug dump() printer. -->
+ <bootstrap class="Drupal\TestTools\Extension\Dump\DebugDump">
+ <parameter name="colors" value="true"/>
+ <parameter name="printCaller" value="true"/>
+ </bootstrap>
+ </extensions>
+ <testsuites>
+ <testsuite name="unit-component">
+ <directory>tests/Drupal/Tests/Component</directory>
+ </testsuite>
+ <testsuite name="unit">
+ <directory>tests/Drupal/Tests</directory>
+ <exclude>tests/Drupal/Tests/Component</exclude>
+ <directory>modules/**/tests/src/Unit</directory>
+ <directory>profiles/**/tests/src/Unit</directory>
+ <directory>themes/**/tests/src/Unit</directory>
+ <directory>../modules/**/tests/src/Unit</directory>
+ <directory>../profiles/**/tests/src/Unit</directory>
+ <directory>../themes/**/tests/src/Unit</directory>
+ </testsuite>
+ <testsuite name="kernel">
+ <directory>tests/Drupal/KernelTests</directory>
+ <directory>modules/**/tests/src/Kernel</directory>
+ <directory>recipes/*/tests/src/Kernel</directory>
+ <directory>profiles/**/tests/src/Kernel</directory>
+ <directory>themes/**/tests/src/Kernel</directory>
+ <directory>../modules/**/tests/src/Kernel</directory>
+ <directory>../profiles/**/tests/src/Kernel</directory>
+ <directory>../themes/**/tests/src/Kernel</directory>
+ </testsuite>
+ <testsuite name="functional">
+ <directory>tests/Drupal/FunctionalTests</directory>
+ <directory>modules/**/tests/src/Functional</directory>
+ <directory>profiles/**/tests/src/Functional</directory>
+ <directory>recipes/*/tests/src/Functional</directory>
+ <directory>themes/**/tests/src/Functional</directory>
+ <directory>../modules/**/tests/src/Functional</directory>
+ <directory>../profiles/**/tests/src/Functional</directory>
+ <directory>../themes/**/tests/src/Functional</directory>
+ </testsuite>
+ <testsuite name="functional-javascript">
+ <directory>tests/Drupal/FunctionalJavascriptTests</directory>
+ <directory>modules/**/tests/src/FunctionalJavascript</directory>
+ <directory>recipes/*/tests/src/FunctionalJavascript</directory>
+ <directory>profiles/**/tests/src/FunctionalJavascript</directory>
+ <directory>themes/**/tests/src/FunctionalJavascript</directory>
+ <directory>../modules/**/tests/src/FunctionalJavascript</directory>
+ <directory>../profiles/**/tests/src/FunctionalJavascript</directory>
+ <directory>../themes/**/tests/src/FunctionalJavascript</directory>
+ </testsuite>
+ <testsuite name="build">
+ <directory>tests/Drupal/BuildTests</directory>
+ <directory>modules/**/tests/src/Build</directory>
+ </testsuite>
+ </testsuites>
+ <!-- Settings for coverage reports. -->
+ <source ignoreSuppressionOfDeprecations="true">
+ <include>
+ <directory>./includes</directory>
+ <directory>./lib</directory>
+ <directory>./modules</directory>
+ <directory>../modules</directory>
+ <directory>../sites</directory>
+ </include>
+ <exclude>
+ <directory>./lib/Drupal/Component</directory>
+ <directory>./modules/*/src/Tests</directory>
+ <directory>./modules/*/tests</directory>
+ <directory>../modules/*/src/Tests</directory>
+ <directory>../modules/*/tests</directory>
+ <directory>../modules/*/*/src/Tests</directory>
+ <directory>../modules/*/*/tests</directory>
+ </exclude>
+ </source>
+</phpunit>
diff --git a/core/lib/Drupal/Core/Test/PhpUnitTestDiscovery.php b/core/lib/Drupal/Core/Test/PhpUnitTestDiscovery.php
new file mode 100644
index 00000000000..d9737028821
--- /dev/null
+++ b/core/lib/Drupal/Core/Test/PhpUnitTestDiscovery.php
@@ -0,0 +1,346 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Core\Test;
+
+use Drupal\Core\Test\Exception\MissingGroupException;
+use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion;
+use PHPUnit\Framework\DataProviderTestSuite;
+use PHPUnit\Framework\Test;
+use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\TestSuite;
+use PHPUnit\TextUI\Configuration\Builder;
+use PHPUnit\TextUI\Configuration\TestSuiteBuilder;
+
+/**
+ * Discovers available tests using the PHPUnit API.
+ *
+ * @internal
+ */
+class PhpUnitTestDiscovery {
+
+ /**
+ * The map of legacy test suite identifiers to phpunit.xml ones.
+ *
+ * @var array<string,string>
+ */
+ private array $map = [
+ 'PHPUnit-FunctionalJavascript' => 'functional-javascript',
+ 'PHPUnit-Functional' => 'functional',
+ 'PHPUnit-Kernel' => 'kernel',
+ 'PHPUnit-Unit' => 'unit',
+ 'PHPUnit-Unit-Component' => 'unit-component',
+ 'PHPUnit-Build' => 'build',
+ ];
+
+ /**
+ * The reverse map of legacy test suite identifiers to phpunit.xml ones.
+ *
+ * @var array<string,string>
+ */
+ private array $reverseMap;
+
+ /**
+ * The warnings generated during the discovery.
+ *
+ * @var list<string>
+ */
+ private array $warnings = [];
+
+ public function __construct(
+ private string $configurationFilePath,
+ ) {
+ $this->reverseMap = array_flip($this->map);
+ }
+
+ /**
+ * Discovers available tests.
+ *
+ * @param string|null $extension
+ * (optional) The name of an extension to limit discovery to; e.g., 'node'.
+ * @param list<string> $testSuites
+ * (optional) An array of PHPUnit test suites to filter the discovery for.
+ * @param string|null $directory
+ * (optional) Limit discovered tests to a specific directory.
+ *
+ * @return array<string<array<class-string, array{name: class-string, description: string, group: string|int, groups: list<string|int>, type: string, file: string, tests_count: positive-int}>>>
+ * An array of test groups keyed by the group name. Each test group is an
+ * array of test class information arrays as returned by
+ * ::getTestClassInfo(), keyed by test class. If a test class belongs to
+ * multiple groups, it will appear under all group keys it belongs to.
+ */
+ public function getTestClasses(?string $extension = NULL, array $testSuites = [], ?string $directory = NULL): array {
+ $this->warnings = [];
+
+ $args = ['--configuration', $this->configurationFilePath];
+
+ if (!empty($testSuites)) {
+ // Convert $testSuites from Drupal's legacy syntax to the syntax used in
+ // phpunit.xml, that is necessary to PHPUnit to be able to apply the
+ // test suite filter. For example, 'PHPUnit-Unit' to 'unit'.
+ $tmp = [];
+ foreach ($testSuites as $i) {
+ if (!is_string($i)) {
+ throw new \InvalidArgumentException("Test suite must be a string");
+ }
+ if (str_contains($i, ' ')) {
+ throw new \InvalidArgumentException("Test suite name '{$i}' is invalid");
+ }
+ $tmp[] = $this->map[$i] ?? $i;
+ }
+ $args[] = '--testsuite=' . implode(',', $tmp);
+ }
+
+ if ($directory !== NULL) {
+ $args[] = $directory;
+ }
+
+ $phpUnitConfiguration = (new Builder())->build($args);
+
+ // TestSuiteBuilder calls the test data providers during the discovery.
+ // Data providers may be changing the Drupal service container, which leads
+ // to potential issues. We save the current container before running the
+ // discovery, and in case a change is detected, reset it and raise
+ // warnings so that developers can tune their data provider code.
+ if (\Drupal::hasContainer()) {
+ $container = \Drupal::getContainer();
+ $containerObjectId = spl_object_id($container);
+ }
+ $phpUnitTestSuite = (new TestSuiteBuilder())->build($phpUnitConfiguration);
+ if (isset($containerObjectId) && $containerObjectId !== spl_object_id(\Drupal::getContainer())) {
+ $this->warnings[] = '*** The service container was changed during the test discovery ***';
+ $this->warnings[] = 'Probably a test data provider method called \\Drupal::setContainer.';
+ $this->warnings[] = 'Ensure that all the data providers restore the original container before returning data.';
+ assert(isset($container));
+ \Drupal::setContainer($container);
+ }
+
+ $list = $directory === NULL ?
+ $this->getTestList($phpUnitTestSuite, $extension) :
+ $this->getTestListLimitedToDirectory($phpUnitTestSuite, $extension, $testSuites);
+
+ // Sort the groups and tests within the groups by name.
+ uksort($list, 'strnatcasecmp');
+ foreach ($list as &$tests) {
+ uksort($tests, 'strnatcasecmp');
+ }
+
+ return $list;
+ }
+
+ /**
+ * Discovers all class files in all available extensions.
+ *
+ * @param string|null $extension
+ * (optional) The name of an extension to limit discovery to; e.g., 'node'.
+ * @param string|null $directory
+ * (optional) Limit discovered tests to a specific directory.
+ *
+ * @return array
+ * A classmap containing all discovered class files; i.e., a map of
+ * fully-qualified classnames to path names.
+ */
+ public function findAllClassFiles(?string $extension = NULL, ?string $directory = NULL): array {
+ $testClasses = $this->getTestClasses($extension, [], $directory);
+ $classMap = [];
+ foreach ($testClasses as $group) {
+ foreach ($group as $className => $info) {
+ $classMap[$className] = $info['file'];
+ }
+ }
+ return $classMap;
+ }
+
+ /**
+ * Returns the warnings generated during the discovery.
+ *
+ * @return list<string>
+ * The warnings.
+ */
+ public function getWarnings(): array {
+ return $this->warnings;
+ }
+
+ /**
+ * Returns a list of tests from a TestSuite object.
+ *
+ * @param \PHPUnit\Framework\TestSuite $phpUnitTestSuite
+ * The TestSuite object returned by PHPUnit test discovery.
+ * @param string|null $extension
+ * The name of an extension to limit discovery to; e.g., 'node'.
+ *
+ * @return array<string<array<class-string, array{name: class-string, description: string, group: string|int, groups: list<string|int>, type: string, file: string, tests_count: positive-int}>>>
+ * An array of test groups keyed by the group name. Each test group is an
+ * array of test class information arrays as returned by
+ * ::getTestClassInfo(), keyed by test class. If a test class belongs to
+ * multiple groups, it will appear under all group keys it belongs to.
+ */
+ private function getTestList(TestSuite $phpUnitTestSuite, ?string $extension): array {
+ $list = [];
+ foreach ($phpUnitTestSuite->tests() as $testSuite) {
+ foreach ($testSuite->tests() as $testClass) {
+ if ($extension !== NULL && !str_starts_with($testClass->name(), "Drupal\\Tests\\{$extension}\\")) {
+ continue;
+ }
+
+ $item = $this->getTestClassInfo(
+ $testClass,
+ $this->reverseMap[$testSuite->name()] ?? $testSuite->name(),
+ );
+
+ foreach ($item['groups'] as $group) {
+ $list[$group][$item['name']] = $item;
+ }
+ }
+ }
+ return $list;
+ }
+
+ /**
+ * Returns a list of tests from a TestSuite object limited to a directory.
+ *
+ * @param \PHPUnit\Framework\TestSuite $phpUnitTestSuite
+ * The TestSuite object returned by PHPUnit test discovery.
+ * @param string|null $extension
+ * The name of an extension to limit discovery to; e.g., 'node'.
+ * @param list<string> $testSuites
+ * An array of PHPUnit test suites to filter the discovery for.
+ *
+ * @return array<string<array<class-string, array{name: class-string, description: string, group: string|int, groups: list<string|int>, type: string, file: string, tests_count: positive-int}>>>
+ * An array of test groups keyed by the group name. Each test group is an
+ * array of test class information arrays as returned by
+ * ::getTestClassInfo(), keyed by test class. If a test class belongs to
+ * multiple groups, it will appear under all group keys it belongs to.
+ */
+ private function getTestListLimitedToDirectory(TestSuite $phpUnitTestSuite, ?string $extension, array $testSuites): array {
+ $list = [];
+
+ // In this case, PHPUnit found a single test class to run tests for.
+ if ($phpUnitTestSuite->isForTestClass()) {
+ if ($extension !== NULL && !str_starts_with($phpUnitTestSuite->name(), "Drupal\\Tests\\{$extension}\\")) {
+ return [];
+ }
+
+ // Take the test suite name from the class namespace.
+ $testSuite = 'PHPUnit-' . TestDiscovery::getPhpunitTestSuite($phpUnitTestSuite->name());
+ if (!empty($testSuites) && !in_array($testSuite, $testSuites, TRUE)) {
+ return [];
+ }
+
+ $item = $this->getTestClassInfo($phpUnitTestSuite, $testSuite);
+
+ foreach ($item['groups'] as $group) {
+ $list[$group][$item['name']] = $item;
+ }
+ return $list;
+ }
+
+ // Multiple test classes were found.
+ $list = [];
+ foreach ($phpUnitTestSuite->tests() as $testClass) {
+ if ($extension !== NULL && !str_starts_with($testClass->name(), "Drupal\\Tests\\{$extension}\\")) {
+ continue;
+ }
+
+ // Take the test suite name from the class namespace.
+ $testSuite = 'PHPUnit-' . TestDiscovery::getPhpunitTestSuite($testClass->name());
+ if (!empty($testSuites) && !in_array($testSuite, $testSuites, TRUE)) {
+ continue;
+ }
+
+ $item = $this->getTestClassInfo($testClass, $testSuite);
+
+ foreach ($item['groups'] as $group) {
+ $list[$group][$item['name']] = $item;
+ }
+ }
+ return $list;
+
+ }
+
+ /**
+ * Returns the test class information.
+ *
+ * @param \PHPUnit\Framework\Test $testClass
+ * The test class.
+ * @param string $testSuite
+ * The test suite of this test class.
+ *
+ * @return array{name: class-string, description: string, group: string|int, groups: list<string|int>, type: string, file: string, tests_count: positive-int}
+ * The test class information.
+ */
+ private function getTestClassInfo(Test $testClass, string $testSuite): array {
+ $reflection = new \ReflectionClass($testClass->name());
+
+ // Let PHPUnit API return the groups, as it will deal transparently with
+ // annotations or attributes, but skip groups generated by PHPUnit
+ // internally and starting with a double underscore prefix.
+ if (RunnerVersion::getMajor() < 11) {
+ $groups = array_filter($testClass->groups(), function (string $value): bool {
+ return !str_starts_with($value, '__phpunit');
+ });
+ }
+ else {
+ // In PHPUnit 11+, we need to coalesce the groups from individual tests
+ // as they may not be available from the test class level (when tests are
+ // backed by data providers).
+ $tmp = [];
+ foreach ($testClass as $test) {
+ if ($test instanceof DataProviderTestSuite) {
+ foreach ($test as $testWithData) {
+ $tmp = array_merge($tmp, $testWithData->groups());
+ }
+ }
+ else {
+ $tmp = array_merge($tmp, $test->groups());
+ }
+ }
+ $groups = array_filter(array_unique($tmp), function (string $value): bool {
+ return !str_starts_with($value, '__phpunit');
+ });
+ }
+ if (empty($groups)) {
+ throw new MissingGroupException(sprintf('Missing group metadata in test class %s', $testClass->name()));
+ }
+
+ // Let PHPUnit API return the class coverage information.
+ $test = $testClass;
+ while (!$test instanceof TestCase) {
+ $test = $test->tests()[0];
+ }
+ if (($metadata = $test->valueObjectForEvents()->metadata()->isCoversClass()) && $metadata->isNotEmpty()) {
+ $description = sprintf('Tests %s.', $metadata->asArray()[0]->className());
+ }
+ elseif (($metadata = $test->valueObjectForEvents()->metadata()->isCoversDefaultClass()) && $metadata->isNotEmpty()) {
+ $description = sprintf('Tests %s.', $metadata->asArray()[0]->className());
+ }
+ else {
+ $description = TestDiscovery::parseTestClassSummary($reflection->getDocComment());
+ }
+
+ // Find the test cases count.
+ $count = 0;
+ foreach ($testClass->tests() as $testCase) {
+ if ($testCase instanceof TestCase) {
+ // If it's a straight test method, counts 1.
+ $count++;
+ }
+ else {
+ // It's a data provider test suite, count 1 per data set provided.
+ $count += count($testCase->tests());
+ }
+ }
+
+ return [
+ 'name' => $testClass->name(),
+ 'group' => $groups[0],
+ 'groups' => $groups,
+ 'type' => $testSuite,
+ 'description' => $description,
+ 'file' => $reflection->getFileName(),
+ 'tests_count' => $count,
+ ];
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Test/RunTests/TestFileParser.php b/core/lib/Drupal/Core/Test/RunTests/TestFileParser.php
index 12aa757e57e..8ab5260aa66 100644
--- a/core/lib/Drupal/Core/Test/RunTests/TestFileParser.php
+++ b/core/lib/Drupal/Core/Test/RunTests/TestFileParser.php
@@ -4,9 +4,16 @@ namespace Drupal\Core\Test\RunTests;
use PHPUnit\Framework\TestCase;
+@trigger_error('Drupal\Core\Test\RunTests\TestFileParser is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. See https://www.drupal.org/node/3447698', E_USER_DEPRECATED);
+
/**
* Parses class names from PHP files without loading them.
*
+ * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no
+ * replacement.
+ *
+ * @see https://www.drupal.org/node/3447698
+ *
* @internal
*/
class TestFileParser {
diff --git a/core/lib/Drupal/Core/Test/TestDiscovery.php b/core/lib/Drupal/Core/Test/TestDiscovery.php
index 1347d0c583f..468256779b3 100644
--- a/core/lib/Drupal/Core/Test/TestDiscovery.php
+++ b/core/lib/Drupal/Core/Test/TestDiscovery.php
@@ -7,6 +7,7 @@ use Drupal\Component\Annotation\Reflection\MockFileFinder;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Extension\ExtensionDiscovery;
use Drupal\Core\Test\Exception\MissingGroupException;
+use PHPUnit\Framework\Attributes\Group;
/**
* Discovers available tests.
@@ -26,6 +27,11 @@ class TestDiscovery {
* Statically cached list of test classes.
*
* @var array
+ *
+ * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is
+ * no replacement.
+ *
+ * @see https://www.drupal.org/node/3447698
*/
protected $testClasses;
@@ -149,8 +155,14 @@ class TestDiscovery {
*
* @todo Remove singular grouping; retain list of groups in 'group' key.
* @see https://www.drupal.org/node/2296615
+ *
+ * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
+ * PhpUnitTestDiscovery::getTestClasses() instead.
+ *
+ * @see https://www.drupal.org/node/3447698
*/
public function getTestClasses($extension = NULL, array $types = [], ?string $directory = NULL) {
+ @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use PhpUnitTestDiscovery::getTestClasses() instead. See https://www.drupal.org/node/3447698', E_USER_DEPRECATED);
if (!isset($extension) && empty($types)) {
if (!empty($this->testClasses)) {
return $this->testClasses;
@@ -175,6 +187,15 @@ class TestDiscovery {
catch (MissingGroupException $e) {
// If the class name ends in Test and is not a migrate table dump.
if (str_ends_with($classname, 'Test') && !str_contains($classname, 'migrate_drupal\Tests\Table')) {
+ $reflection = new \ReflectionClass($classname);
+ $groupAttributes = $reflection->getAttributes(Group::class, \ReflectionAttribute::IS_INSTANCEOF);
+ if (!empty($groupAttributes)) {
+ $group = '##no-group-annotations';
+ $info['group'] = $group;
+ $info['groups'] = [$group];
+ $list[$group][$classname] = $info;
+ continue;
+ }
throw $e;
}
// If the class is @group annotation just skip it. Most likely it is an
@@ -216,8 +237,14 @@ class TestDiscovery {
* @return array
* A classmap containing all discovered class files; i.e., a map of
* fully-qualified classnames to path names.
+ *
+ * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
+ * PhpUnitTestDiscovery::findAllClassFiles() instead.
+ *
+ * @see https://www.drupal.org/node/3447698
*/
public function findAllClassFiles($extension = NULL, ?string $directory = NULL) {
+ @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use PhpUnitTestDiscovery::findAllClassFiles() instead. See https://www.drupal.org/node/3447698', E_USER_DEPRECATED);
$classmap = [];
$namespaces = $this->registerTestNamespaces();
if (isset($extension)) {
@@ -256,8 +283,14 @@ class TestDiscovery {
*
* @todo Limit to '*Test.php' files (~10% less files to reflect/introspect).
* @see https://www.drupal.org/node/2296635
+ *
+ * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is
+ * no replacement.
+ *
+ * @see https://www.drupal.org/node/3447698
*/
public static function scanDirectory($namespace_prefix, $path) {
+ @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. See https://www.drupal.org/node/3447698', E_USER_DEPRECATED);
if (!str_ends_with($namespace_prefix, '\\')) {
throw new \InvalidArgumentException("Namespace prefix for $path must contain a trailing namespace separator.");
}
@@ -312,8 +345,14 @@ class TestDiscovery {
*
* @throws \Drupal\Core\Test\Exception\MissingGroupException
* If the class does not have a @group annotation.
+ *
+ * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is
+ * no replacement.
+ *
+ * @see https://www.drupal.org/node/3447698
*/
public static function getTestInfo($classname, $doc_comment = NULL) {
+ @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. See https://www.drupal.org/node/3447698', E_USER_DEPRECATED);
if ($doc_comment === NULL) {
$reflection = new \ReflectionClass($classname);
$doc_comment = $reflection->getDocComment();
@@ -350,7 +389,7 @@ class TestDiscovery {
$info['type'] = 'PHPUnit-' . static::getPhpunitTestSuite($classname);
if (!empty($annotations['coversDefaultClass'])) {
- $info['description'] = 'Tests ' . $annotations['coversDefaultClass'] . '.';
+ $info['description'] = 'Tests ' . ltrim($annotations['coversDefaultClass']) . '.';
}
else {
$info['description'] = static::parseTestClassSummary($doc_comment);
diff --git a/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageNegotiationSettingsTest.php b/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageNegotiationSettingsTest.php
index 54fdf4b0d21..fe5ff0ca062 100644
--- a/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageNegotiationSettingsTest.php
+++ b/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageNegotiationSettingsTest.php
@@ -10,6 +10,7 @@ use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests the migration of language negotiation.
*
+ * @group #slow
* @group migrate_drupal_7
*/
class MigrateLanguageNegotiationSettingsTest extends MigrateDrupal7TestBase {
diff --git a/core/modules/link/tests/src/Functional/LinkFieldUITest.php b/core/modules/link/tests/src/Functional/LinkFieldUITest.php
index 694fb6b3677..5c78abc2391 100644
--- a/core/modules/link/tests/src/Functional/LinkFieldUITest.php
+++ b/core/modules/link/tests/src/Functional/LinkFieldUITest.php
@@ -15,6 +15,7 @@ use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
* Tests link field UI functionality.
*
* @group link
+ * @group #slow
*/
class LinkFieldUITest extends BrowserTestBase {
diff --git a/core/modules/package_manager/tests/src/Kernel/SupportedReleaseValidatorTest.php b/core/modules/package_manager/tests/src/Kernel/SupportedReleaseValidatorTest.php
index 90348cdfdd3..2e9a0977fa3 100644
--- a/core/modules/package_manager/tests/src/Kernel/SupportedReleaseValidatorTest.php
+++ b/core/modules/package_manager/tests/src/Kernel/SupportedReleaseValidatorTest.php
@@ -13,7 +13,6 @@ use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait;
* @coversDefaultClass \Drupal\package_manager\Validator\SupportedReleaseValidator
* @group #slow
* @group package_manager
- * @group #slow
* @internal
*/
class SupportedReleaseValidatorTest extends PackageManagerKernelTestBase {
diff --git a/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php b/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php
index d5cc9759ab1..be5d811fe54 100644
--- a/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php
+++ b/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php
@@ -13,6 +13,7 @@ use Drupal\Tests\Traits\Core\PathAliasTestTrait;
/**
* URL alias migration.
*
+ * @group #slow
* @group migrate_drupal_6
*/
class MigrateUrlAliasTest extends MigrateDrupal6TestBase {
diff --git a/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php b/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php
index 6c8c42da59e..82d866e985e 100644
--- a/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php
+++ b/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php
@@ -77,6 +77,8 @@ class DateFormatAccessControlHandlerTest extends KernelTestBase {
* An array of test cases.
*/
public static function providerTestAccess(): array {
+ $originalContainer = \Drupal::hasContainer() ? \Drupal::getContainer() : NULL;
+
$c = new ContainerBuilder();
$cache_contexts_manager = (new Prophet())->prophesize(CacheContextsManager::class);
$cache_contexts_manager->assertValidTokens()->willReturn(TRUE);
@@ -84,7 +86,7 @@ class DateFormatAccessControlHandlerTest extends KernelTestBase {
$c->set('cache_contexts_manager', $cache_contexts_manager);
\Drupal::setContainer($c);
- return [
+ $data = [
'No permission + unlocked' => [
[],
'unlocked',
@@ -122,6 +124,13 @@ class DateFormatAccessControlHandlerTest extends KernelTestBase {
AccessResult::allowed()->addCacheContexts(['user.permissions']),
],
];
+
+ // Restore the original container if needed.
+ if ($originalContainer) {
+ \Drupal::setContainer($originalContainer);
+ }
+
+ return $data;
}
}
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeTest.php
index 1d9654dd505..511778daf20 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeTest.php
@@ -10,6 +10,7 @@ use Drupal\node\Entity\Node;
/**
* Upgrade taxonomy term node associations.
*
+ * @group #slow
* @group migrate_drupal_6
*/
class MigrateTermNodeTest extends MigrateDrupal6TestBase {
diff --git a/core/modules/workflows/tests/src/Kernel/WorkflowAccessControlHandlerTest.php b/core/modules/workflows/tests/src/Kernel/WorkflowAccessControlHandlerTest.php
index e46fbcf417b..180edc868f6 100644
--- a/core/modules/workflows/tests/src/Kernel/WorkflowAccessControlHandlerTest.php
+++ b/core/modules/workflows/tests/src/Kernel/WorkflowAccessControlHandlerTest.php
@@ -124,6 +124,8 @@ class WorkflowAccessControlHandlerTest extends KernelTestBase {
* An array of test data.
*/
public static function checkAccessProvider() {
+ $originalContainer = \Drupal::hasContainer() ? \Drupal::getContainer() : NULL;
+
$container = new ContainerBuilder();
$cache_contexts_manager = (new Prophet())->prophesize(CacheContextsManager::class);
$cache_contexts_manager->assertValidTokens()->willReturn(TRUE);
@@ -131,7 +133,7 @@ class WorkflowAccessControlHandlerTest extends KernelTestBase {
$container->set('cache_contexts_manager', $cache_contexts_manager);
\Drupal::setContainer($container);
- return [
+ $data = [
'Admin view' => [
'adminUser',
'view',
@@ -275,6 +277,13 @@ class WorkflowAccessControlHandlerTest extends KernelTestBase {
AccessResult::allowed()->addCacheContexts(['user.permissions']),
],
];
+
+ // Restore the original container if needed.
+ if ($originalContainer) {
+ \Drupal::setContainer($originalContainer);
+ }
+
+ return $data;
}
}
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 4545a00cc3a..96f16eed9c7 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -17,14 +17,13 @@ use Drupal\Component\Utility\Timer;
use Drupal\Core\Composer\Composer;
use Drupal\Core\Database\Database;
use Drupal\Core\Test\EnvironmentCleaner;
+use Drupal\Core\Test\PhpUnitTestDiscovery;
use Drupal\Core\Test\PhpUnitTestRunner;
use Drupal\Core\Test\SimpletestTestRunResultsStorage;
-use Drupal\Core\Test\RunTests\TestFileParser;
use Drupal\Core\Test\TestDatabase;
use Drupal\Core\Test\TestRun;
use Drupal\Core\Test\TestRunnerKernel;
use Drupal\Core\Test\TestRunResultsStorageInterface;
-use Drupal\Core\Test\TestDiscovery;
use Drupal\BuildTests\Framework\BuildTestBase;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\KernelTests\KernelTestBase;
@@ -49,6 +48,10 @@ const SIMPLETEST_SCRIPT_COLOR_EXCEPTION = 33;
const SIMPLETEST_SCRIPT_COLOR_YELLOW = 33;
// A refreshing cyan.
const SIMPLETEST_SCRIPT_COLOR_CYAN = 36;
+// A fainting gray.
+const SIMPLETEST_SCRIPT_COLOR_GRAY = 90;
+// A notable white.
+const SIMPLETEST_SCRIPT_COLOR_BRIGHT_WHITE = "1;97";
// Restricting the chunk of queries prevents memory exhaustion.
const SIMPLETEST_SCRIPT_SQLITE_VARIABLE_LIMIT = 350;
@@ -86,12 +89,12 @@ if ($args['list']) {
// Display all available tests organized by one @group annotation.
echo "\nAvailable test groups & classes\n";
echo "-------------------------------\n\n";
- $test_discovery = new TestDiscovery(
- \Drupal::root(),
- \Drupal::service('class_loader')
- );
+ $test_discovery = new PhpUnitTestDiscovery(\Drupal::root() . \DIRECTORY_SEPARATOR . 'core');
try {
$groups = $test_discovery->getTestClasses($args['module']);
+ foreach ($test_discovery->getWarnings() as $warning) {
+ simpletest_script_print($warning . "\n", SIMPLETEST_SCRIPT_COLOR_EXCEPTION);
+ }
}
catch (Exception $e) {
error_log((string) $e);
@@ -119,11 +122,8 @@ if ($args['list']) {
// @see https://www.drupal.org/node/2569585
if ($args['list-files'] || $args['list-files-json']) {
// List all files which could be run as tests.
- $test_discovery = new TestDiscovery(
- \Drupal::root(),
- \Drupal::service('class_loader')
- );
- // TestDiscovery::findAllClassFiles() gives us a classmap similar to a
+ $test_discovery = new PhpUnitTestDiscovery(\Drupal::root() . \DIRECTORY_SEPARATOR . 'core');
+ // PhpUnitTestDiscovery::findAllClassFiles() gives us a classmap similar to a
// Composer 'classmap' array.
$test_classes = $test_discovery->findAllClassFiles();
// JSON output is the easiest.
@@ -177,6 +177,15 @@ if (!Composer::upgradePHPUnitCheck(Version::id())) {
exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);
}
+echo "\n";
+echo "Drupal test run\n\n";
+echo sprintf("Drupal Version: %s\n", \Drupal::VERSION);
+echo sprintf("PHP Version: %s\n", \PHP_VERSION);
+echo sprintf("PHP Binary: %s\n", $php ?? getenv('_'));
+echo sprintf("PHPUnit Version: %s\n", Version::id());
+echo "-------------------------------\n";
+echo "\n";
+
$test_list = simpletest_script_get_test_list();
// Try to allocate unlimited time to run the tests.
@@ -355,6 +364,11 @@ All arguments are long options.
The index of the job in the job set.
+ --debug-discovery
+
+ If provided, dumps detailed information on the tests selected
+ for execution, before the execution starts.
+
<test1>[,<test2>[,<test3> ...]]
One or more tests to be run. By default, these are interpreted
@@ -427,6 +441,7 @@ function simpletest_script_parse_args() {
'non-html' => FALSE,
'ci-parallel-node-index' => 1,
'ci-parallel-node-total' => 1,
+ 'debug-discovery' => FALSE,
];
// Override with set values.
@@ -919,17 +934,15 @@ function simpletest_script_command(TestRun $test_run, string $test_class): array
function simpletest_script_get_test_list() {
global $args;
- $test_discovery = new TestDiscovery(
- \Drupal::root(),
- \Drupal::service('class_loader')
- );
- $types_processed = empty($args['types']);
+ $test_discovery = new PhpUnitTestDiscovery(\Drupal::root() . \DIRECTORY_SEPARATOR . 'core');
$test_list = [];
$slow_tests = [];
if ($args['all'] || $args['module'] || $args['directory']) {
try {
$groups = $test_discovery->getTestClasses($args['module'], $args['types'], $args['directory']);
- $types_processed = TRUE;
+ foreach ($test_discovery->getWarnings() as $warning) {
+ simpletest_script_print($warning . "\n", SIMPLETEST_SCRIPT_COLOR_EXCEPTION);
+ }
}
catch (Exception $e) {
echo (string) $e;
@@ -938,30 +951,34 @@ function simpletest_script_get_test_list() {
// Ensure that tests marked explicitly as @group #slow are run at the
// beginning of each job.
if (key($groups) === '#slow') {
- $slow_tests = array_keys(array_shift($groups));
+ $slow_tests = array_shift($groups);
}
$not_slow_tests = [];
foreach ($groups as $group => $tests) {
- $not_slow_tests = array_merge($not_slow_tests, array_keys($tests));
+ $not_slow_tests = array_merge($not_slow_tests, $tests);
}
// Filter slow tests out of the not slow tests and ensure a unique list
// since tests may appear in more than one group.
- $not_slow_tests = array_unique(array_diff($not_slow_tests, $slow_tests));
+ $not_slow_tests = array_diff_key($not_slow_tests, $slow_tests);
// If the tests are not being run in parallel, then ensure slow tests run
// all together first.
if ((int) $args['ci-parallel-node-total'] <= 1 ) {
sort_tests_by_type_and_methods($slow_tests);
sort_tests_by_type_and_methods($not_slow_tests);
- $test_list = array_merge($slow_tests, $not_slow_tests);
+ $all_tests_list = array_merge($slow_tests, $not_slow_tests);
+ assign_tests_sequence($all_tests_list);
+ dump_tests_sequence($all_tests_list, $args);
+ $test_list = array_keys($all_tests_list);
}
else {
- // Sort all tests by the number of public methods on the test class.
- // This is a proxy for the approximate time taken to run the test,
- // which is used in combination with @group #slow to start the slowest tests
- // first and distribute tests between test runners.
+ // Sort all tests by the number of test cases on the test class.
+ // This is used in combination with @group #slow to start the slowest
+ // tests first and distribute tests between test runners.
sort_tests_by_public_method_count($slow_tests);
sort_tests_by_public_method_count($not_slow_tests);
+ $all_tests_list = array_merge($slow_tests, $not_slow_tests);
+ assign_tests_sequence($all_tests_list);
// Now set up a bin per test runner.
$bin_count = (int) $args['ci-parallel-node-total'];
@@ -975,6 +992,8 @@ function simpletest_script_get_test_list() {
$binned_other_tests = place_tests_into_bins($not_slow_tests, $bin_count);
$other_tests_for_job = $binned_other_tests[$args['ci-parallel-node-index'] - 1];
$test_list = array_merge($slow_tests_for_job, $other_tests_for_job);
+ dump_bin_tests_sequence($args['ci-parallel-node-index'], $all_tests_list, $test_list, $args);
+ $test_list = array_keys($test_list);
}
}
else {
@@ -988,6 +1007,9 @@ function simpletest_script_get_test_list() {
else {
try {
$groups = $test_discovery->getTestClasses(NULL, $args['types']);
+ foreach ($test_discovery->getWarnings() as $warning) {
+ simpletest_script_print($warning . "\n", SIMPLETEST_SCRIPT_COLOR_EXCEPTION);
+ }
}
catch (Exception $e) {
echo (string) $e;
@@ -1005,19 +1027,24 @@ function simpletest_script_get_test_list() {
}
elseif ($args['file']) {
// Extract test case class names from specified files.
- $parser = new TestFileParser();
foreach ($args['test_names'] as $file) {
- if (!file_exists($file)) {
+ if (!file_exists($file) || is_dir($file)) {
simpletest_script_print_error('File not found: ' . $file);
exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);
}
- $test_list = array_merge($test_list, $parser->getTestListFromFile($file));
+ $fileTests = current($test_discovery->getTestClasses(NULL, [], $file));
+ $test_list = array_merge($test_list, $fileTests);
}
+ assign_tests_sequence($test_list);
+ dump_tests_sequence($test_list, $args);
+ $test_list = array_keys($test_list);
}
else {
try {
$groups = $test_discovery->getTestClasses(NULL, $args['types']);
- $types_processed = TRUE;
+ foreach ($test_discovery->getWarnings() as $warning) {
+ simpletest_script_print($warning . "\n", SIMPLETEST_SCRIPT_COLOR_EXCEPTION);
+ }
}
catch (Exception $e) {
echo (string) $e;
@@ -1034,22 +1061,15 @@ function simpletest_script_get_test_list() {
}
// Merge the tests from the groups together.
foreach ($args['test_names'] as $group_name) {
- $test_list = array_merge($test_list, array_keys($groups[$group_name]));
+ $test_list = array_merge($test_list, $groups[$group_name]);
}
+ assign_tests_sequence($test_list);
+ dump_tests_sequence($test_list, $args);
// Ensure our list of tests contains only one entry for each test.
- $test_list = array_unique($test_list);
+ $test_list = array_keys($test_list);
}
}
- // If the test list creation does not automatically limit by test type then
- // we need to do so here.
- if (!$types_processed) {
- $test_list = array_filter($test_list, function ($test_class) use ($args) {
- $test_info = TestDiscovery::getTestInfo($test_class);
- return in_array($test_info['type'], $args['types'], TRUE);
- });
- }
-
if (empty($test_list)) {
simpletest_script_print_error('No valid tests were specified.');
exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);
@@ -1062,11 +1082,11 @@ function simpletest_script_get_test_list() {
* Sort tests by test type and number of public methods.
*/
function sort_tests_by_type_and_methods(array &$tests): void {
- usort($tests, function ($a, $b) {
- if (get_test_type_weight($a) === get_test_type_weight($b)) {
- return get_test_class_method_count($b) <=> get_test_class_method_count($a);
+ uasort($tests, function ($a, $b) {
+ if (get_test_type_weight($a['name']) === get_test_type_weight($b['name'])) {
+ return $b['tests_count'] <=> $a['tests_count'];
}
- return get_test_type_weight($b) <=> get_test_type_weight($a);
+ return get_test_type_weight($b['name']) <=> get_test_type_weight($a['name']);
});
}
@@ -1083,8 +1103,8 @@ function sort_tests_by_type_and_methods(array &$tests): void {
* An array of test class names.
*/
function sort_tests_by_public_method_count(array &$tests): void {
- usort($tests, function ($a, $b) {
- return get_test_class_method_count($b) <=> get_test_class_method_count($a);
+ uasort($tests, function ($a, $b) {
+ return $b['tests_count'] <=> $a['tests_count'];
});
}
@@ -1105,28 +1125,46 @@ function get_test_type_weight(string $class): int {
}
/**
- * Get an approximate test method count for a test class.
+ * Assigns the test sequence.
*
- * @param string $class
- * The test class name.
+ * @param array $tests
+ * The array of test class info.
*/
-function get_test_class_method_count(string $class): int {
- $reflection = new \ReflectionClass($class);
- $count = 0;
- foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
- // If a method uses a dataProvider, increase the count by 20 since data
- // providers result in a single method running multiple times.
- $comments = $method->getDocComment();
- preg_match_all('#@(.*?)\n#s', $comments, $annotations);
- foreach ($annotations[1] as $annotation) {
- if (str_starts_with($annotation, 'dataProvider')) {
- $count = $count + 20;
- continue;
- }
- }
- $count++;
+function assign_tests_sequence(array &$tests): void {
+ $i = 0;
+ foreach ($tests as &$testInfo) {
+ $testInfo['sequence'] = ++$i;
}
- return $count;
+}
+
+/**
+ * Dumps the list of tests in order of execution after sorting.
+ *
+ * @param array $tests
+ * The array of test class info.
+ * @param array $args
+ * The command line arguments.
+ */
+function dump_tests_sequence(array $tests, array $args): void {
+ if ($args['debug-discovery'] === FALSE) {
+ return;
+ }
+ echo "Test execution sequence\n";
+ echo "-----------------------\n\n";
+ echo " Seq Slow? Group Cnt Class\n";
+ echo "-----------------------------------------\n";
+ $i = 0;
+ foreach ($tests as $testInfo) {
+ echo sprintf(
+ "%4d %5s %15s %4d %s\n",
+ $testInfo['sequence'],
+ in_array('#slow', $testInfo['groups']) ? '#slow' : '',
+ trim_with_ellipsis($testInfo['group'], 15, \STR_PAD_RIGHT),
+ $testInfo['tests_count'],
+ trim_with_ellipsis($testInfo['name'], 60, \STR_PAD_LEFT),
+ );
+ }
+ echo "-----------------------------------------\n\n";
}
/**
@@ -1149,13 +1187,51 @@ function place_tests_into_bins(array $tests, int $bin_count) {
// Create a bin corresponding to each parallel test job.
$bins = array_fill(0, $bin_count, []);
// Go through each test and add them to one bin at a time.
+ $i = 0;
foreach ($tests as $key => $test) {
- $bins[($key % $bin_count)][] = $test;
+ $bins[($i++ % $bin_count)][$key] = $test;
}
return $bins;
}
/**
+ * Dumps the list of tests in order of execution for a bin.
+ *
+ * @param int $bin
+ * The bin.
+ * @param array $allTests
+ * The list of all test classes discovered.
+ * @param array $tests
+ * The list of test class to run for this bin.
+ * @param array $args
+ * The command line arguments.
+ */
+function dump_bin_tests_sequence(int $bin, array $allTests, array $tests, array $args): void {
+ if ($args['debug-discovery'] === FALSE) {
+ return;
+ }
+ echo "Test execution sequence. ";
+ echo "Tests marked *** will be executed in this PARALLEL BIN #{$bin}.\n";
+ echo "-------------------------------------------------------------------------------------\n\n";
+ echo "Bin Seq Slow? Group Cnt Class\n";
+ echo "--------------------------------------------\n";
+ foreach ($allTests as $testInfo) {
+ $inBin = isset($tests[$testInfo['name']]);
+ $message = sprintf(
+ "%s %4d %5s %15s %4d %s\n",
+ $inBin ? "***" : " ",
+ $testInfo['sequence'],
+ in_array('#slow', $testInfo['groups']) ? '#slow' : '',
+ trim_with_ellipsis($testInfo['group'], 15, \STR_PAD_RIGHT),
+ $testInfo['tests_count'],
+ trim_with_ellipsis($testInfo['name'], 60, \STR_PAD_LEFT),
+ );
+ simpletest_script_print($message, $inBin ? SIMPLETEST_SCRIPT_COLOR_BRIGHT_WHITE : SIMPLETEST_SCRIPT_COLOR_GRAY);
+ }
+ echo "-------------------------------------------------\n\n";
+}
+
+/**
* Initialize the reporter.
*/
function simpletest_script_reporter_init(): void {
@@ -1170,12 +1246,6 @@ function simpletest_script_reporter_init(): void {
'debug' => 'Log',
];
- echo "\n";
- echo "Drupal test run\n";
- echo "Using PHP Binary: $php\n";
- echo "---------------\n";
- echo "\n";
-
// Tell the user about what tests are to be run.
if ($args['all']) {
echo "All tests will run.\n\n";
@@ -1378,7 +1448,7 @@ function simpletest_script_reporter_display_results(TestRunResultsStorageInterfa
function simpletest_script_format_result($result): void {
global $args, $results_map, $color;
- $summary = sprintf("%-9.9s %9.3fs %-80.80s\n", $results_map[$result->status], $result->time, trim_with_ellipsis($result->function, 80, STR_PAD_LEFT));
+ $summary = sprintf("%-9.9s %9.3fs %s\n", $results_map[$result->status], $result->time, trim_with_ellipsis($result->function, 80, STR_PAD_LEFT));
simpletest_script_print($summary, simpletest_script_color_code($result->status));
@@ -1540,12 +1610,12 @@ function simpletest_script_load_messages_by_test_id(TestRunResultsStorageInterfa
*/
function trim_with_ellipsis(string $input, int $length, int $side): string {
if (strlen($input) < $length) {
- return str_pad($input, $length, ' ', STR_PAD_RIGHT);
+ return str_pad($input, $length, ' ', \STR_PAD_RIGHT);
}
elseif (strlen($input) > $length) {
return match($side) {
- STR_PAD_RIGHT => substr($input, 0, $length - 3) . '...',
- default => '...' . substr($input, -$length + 3),
+ \STR_PAD_RIGHT => substr($input, 0, $length - 1) . '…',
+ default => '…' . substr($input, -$length + 1),
};
}
return $input;
diff --git a/core/tests/Drupal/KernelTests/Core/Test/PhpUnitApiFindAllClassFilesTest.php b/core/tests/Drupal/KernelTests/Core/Test/PhpUnitApiFindAllClassFilesTest.php
new file mode 100644
index 00000000000..feb6addef2a
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Test/PhpUnitApiFindAllClassFilesTest.php
@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\KernelTests\Core\Test;
+
+use Drupal\Core\Test\PhpUnitTestDiscovery;
+use Drupal\Core\Test\TestDiscovery;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\IgnoreDeprecations;
+
+/**
+ * Tests ::findAllClassFiles() between TestDiscovery and PhpUnitTestDiscovery.
+ *
+ * PhpUnitTestDiscovery uses PHPUnit API to build the list of test classes,
+ * while TestDiscovery uses Drupal legacy code.
+ */
+#[CoversClass(PhpUnitTestDiscovery::class)]
+#[Group('TestSuites')]
+#[Group('Test')]
+#[Group('#slow')]
+class PhpUnitApiFindAllClassFilesTest extends KernelTestBase {
+
+ /**
+ * Checks that Drupal legacy and PHPUnit API based discoveries are equal.
+ */
+ #[DataProvider('argumentsProvider')]
+ #[IgnoreDeprecations]
+ public function testEquality(?string $extension = NULL, ?string $directory = NULL): void {
+ $testDiscovery = new TestDiscovery(
+ $this->container->getParameter('app.root'),
+ $this->container->get('class_loader')
+ );
+ $internalList = $testDiscovery->findAllClassFiles($extension, $directory);
+
+ // Location of PHPUnit configuration file.
+ $configurationFilePath = $this->container->getParameter('app.root') . \DIRECTORY_SEPARATOR . 'core';
+ // @todo once PHPUnit 10 is no longer used, remove the condition.
+ // @see https://www.drupal.org/project/drupal/issues/3497116
+ if (RunnerVersion::getMajor() >= 11) {
+ $configurationFilePath .= \DIRECTORY_SEPARATOR . '.phpunit-next.xml';
+ }
+
+ $phpUnitTestDiscovery = new PhpUnitTestDiscovery($configurationFilePath);
+
+ $phpUnitList = $phpUnitTestDiscovery->findAllClassFiles($extension, $directory);
+
+ // Downgrade results to make them comparable, working around bugs and
+ // additions.
+ // 1. TestDiscovery discovers non-test classes that PHPUnit does not.
+ $internalList = array_intersect_key($internalList, $phpUnitList);
+
+ $this->assertEquals($internalList, $phpUnitList);
+ }
+
+ /**
+ * Provides test data to ::testEquality.
+ */
+ public static function argumentsProvider(): \Generator {
+ yield 'All tests' => [];
+ yield 'Extension: system' => ['extension' => 'system'];
+ yield 'Extension: system, directory' => [
+ 'extension' => 'system',
+ 'directory' => 'core/modules/system/tests/src',
+ ];
+ }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Test/PhpUnitApiGetTestClassesTest.php b/core/tests/Drupal/KernelTests/Core/Test/PhpUnitApiGetTestClassesTest.php
new file mode 100644
index 00000000000..32d21e3e6c3
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Test/PhpUnitApiGetTestClassesTest.php
@@ -0,0 +1,109 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\KernelTests\Core\Test;
+
+use Drupal\Core\Test\PhpUnitTestDiscovery;
+use Drupal\Core\Test\TestDiscovery;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\IgnoreDeprecations;
+
+/**
+ * Tests ::getTestClasses() between TestDiscovery and PhpPUnitTestDiscovery.
+ *
+ * PhpPUnitTestDiscovery uses PHPUnit API to build the list of test classes,
+ * while TestDiscovery uses Drupal legacy code.
+ */
+#[CoversClass(PhpUnitTestDiscovery::class)]
+#[Group('TestSuites')]
+#[Group('Test')]
+#[Group('#slow')]
+class PhpUnitApiGetTestClassesTest extends KernelTestBase {
+
+ /**
+ * Checks that Drupal legacy and PHPUnit API based discoveries are equal.
+ */
+ #[DataProvider('argumentsProvider')]
+ #[IgnoreDeprecations]
+ public function testEquality(array $suites, ?string $extension = NULL, ?string $directory = NULL): void {
+ $testDiscovery = new TestDiscovery(
+ $this->container->getParameter('app.root'),
+ $this->container->get('class_loader')
+ );
+ $internalList = $testDiscovery->getTestClasses($extension, $suites, $directory);
+
+ // Location of PHPUnit configuration file.
+ $configurationFilePath = $this->container->getParameter('app.root') . \DIRECTORY_SEPARATOR . 'core';
+ // @todo once PHPUnit 10 is no longer used, remove the condition.
+ // @see https://www.drupal.org/project/drupal/issues/3497116
+ if (RunnerVersion::getMajor() >= 11) {
+ $configurationFilePath .= \DIRECTORY_SEPARATOR . '.phpunit-next.xml';
+ }
+
+ $phpUnitTestDiscovery = new PhpUnitTestDiscovery($configurationFilePath);
+
+ $phpUnitList = $phpUnitTestDiscovery->getTestClasses($extension, $suites, $directory);
+
+ // Downgrade results to make them comparable, working around bugs and
+ // additions.
+ // 1. Remove TestDiscovery empty groups.
+ $internalList = array_filter($internalList);
+ // 2. Remove TestDiscovery '##no-group-annotations' group.
+ unset($internalList['##no-group-annotations']);
+ // 3. Remove 'file' and 'tests_count' keys from PHPUnit results.
+ foreach ($phpUnitList as &$group) {
+ foreach ($group as &$testClass) {
+ unset($testClass['file']);
+ unset($testClass['tests_count']);
+ }
+ }
+ // 4. Remove from PHPUnit results groups not found by TestDiscovery.
+ $phpUnitList = array_intersect_key($phpUnitList, $internalList);
+ // 5. Remove from PHPUnit groups classes not found by TestDiscovery.
+ foreach ($phpUnitList as $groupName => &$group) {
+ $group = array_intersect_key($group, $internalList[$groupName]);
+ }
+ // 6. Remove from PHPUnit test classes groups not found by TestDiscovery.
+ foreach ($phpUnitList as $groupName => &$group) {
+ foreach ($group as $testClassName => &$testClass) {
+ $testClass['groups'] = array_intersect_key($testClass['groups'], $internalList[$groupName][$testClassName]['groups']);
+ }
+ }
+
+ $this->assertEquals($internalList, $phpUnitList);
+ }
+
+ /**
+ * Provides test data to ::testEquality.
+ */
+ public static function argumentsProvider(): \Generator {
+ yield 'All tests' => ['suites' => []];
+ yield 'Testsuite: functional-javascript' => ['suites' => ['PHPUnit-FunctionalJavascript']];
+ yield 'Testsuite: functional' => ['suites' => ['PHPUnit-Functional']];
+ yield 'Testsuite: kernel' => ['suites' => ['PHPUnit-Kernel']];
+ yield 'Testsuite: unit' => ['suites' => ['PHPUnit-Unit']];
+ yield 'Testsuite: unit-component' => ['suites' => ['PHPUnit-Unit-Component']];
+ yield 'Testsuite: build' => ['suites' => ['PHPUnit-Build']];
+ yield 'Extension: system' => ['suites' => [], 'extension' => 'system'];
+ yield 'Extension: system, testsuite: unit' => [
+ 'suites' => ['PHPUnit-Unit'],
+ 'extension' => 'system',
+ ];
+ yield 'Extension: system, directory' => [
+ 'suites' => [],
+ 'extension' => 'system',
+ 'directory' => 'core/modules/system/tests/src',
+ ];
+ yield 'Extension: system, testsuite: unit, directory' => [
+ 'suites' => ['PHPUnit-Unit'],
+ 'extension' => 'system',
+ 'directory' => 'core/modules/system/tests/src',
+ ];
+ }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Test/PhpUnitTestDiscoveryTest.php b/core/tests/Drupal/KernelTests/Core/Test/PhpUnitTestDiscoveryTest.php
index 705981f7507..d8534458049 100644
--- a/core/tests/Drupal/KernelTests/Core/Test/PhpUnitTestDiscoveryTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Test/PhpUnitTestDiscoveryTest.php
@@ -7,6 +7,8 @@ namespace Drupal\KernelTests\Core\Test;
use Drupal\Core\Test\TestDiscovery;
use Drupal\KernelTests\KernelTestBase;
use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion;
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\IgnoreDeprecations;
use PHPUnit\TextUI\Configuration\Builder;
use PHPUnit\TextUI\Configuration\TestSuiteBuilder;
use Symfony\Component\Process\Process;
@@ -22,11 +24,10 @@ use Symfony\Component\Process\Process;
* list thus generated, with the list generated by
* \Drupal\Core\Test\TestDiscovery, which is used by run-tests.sh, to ensure
* both methods will run the same tests,
- *
- * @group TestSuites
- * @group Test
- * @group #slow
*/
+#[Group('TestSuites')]
+#[Group('Test')]
+#[Group('#slow')]
class PhpUnitTestDiscoveryTest extends KernelTestBase {
private const TEST_LIST_MISMATCH_MESSAGE =
@@ -61,6 +62,7 @@ class PhpUnitTestDiscoveryTest extends KernelTestBase {
/**
* Tests equality of test discovery between run-tests.sh and PHPUnit CLI.
*/
+ #[IgnoreDeprecations]
public function testPhpUnitTestDiscoveryEqualsInternal(): void {
// Drupal's test discovery, used by run-tests.sh.
$testDiscovery = new TestDiscovery(
@@ -76,11 +78,19 @@ class PhpUnitTestDiscoveryTest extends KernelTestBase {
$internalList = array_unique($internalList);
asort($internalList);
+ // Location of PHPUnit configuration file.
+ $configurationFilePath = $this->root . \DIRECTORY_SEPARATOR . 'core';
+ // @todo once PHPUnit 10 is no longer used, remove the condition.
+ // @see https://www.drupal.org/project/drupal/issues/3497116
+ if (RunnerVersion::getMajor() >= 11) {
+ $configurationFilePath .= \DIRECTORY_SEPARATOR . '.phpunit-next.xml';
+ }
+
// PHPUnit's test discovery - via CLI execution.
$process = new Process([
'vendor/bin/phpunit',
'--configuration',
- 'core',
+ $configurationFilePath,
'--list-tests-xml',
$this->xmlOutputFile,
], $this->root);
@@ -113,14 +123,8 @@ class PhpUnitTestDiscoveryTest extends KernelTestBase {
// Check against Drupal's discovery.
$this->assertEquals(implode("\n", $phpUnitClientList), implode("\n", $internalList), self::TEST_LIST_MISMATCH_MESSAGE);
- // @todo once PHPUnit 10 is no longer used re-enable the rest of the test.
- // @see https://www.drupal.org/project/drupal/issues/3497116
- if (RunnerVersion::getMajor() >= 11) {
- $this->markTestIncomplete('On PHPUnit 11+ the test triggers warnings due to phpunit.xml setup. Re-enable in https://www.drupal.org/project/drupal/issues/3497116.');
- }
-
// PHPUnit's test discovery - via API.
- $phpUnitConfiguration = (new Builder())->build(['--configuration', 'core']);
+ $phpUnitConfiguration = (new Builder())->build(['--configuration', $configurationFilePath]);
$phpUnitTestSuite = (new TestSuiteBuilder())->build($phpUnitConfiguration);
$phpUnitApiList = [];
foreach ($phpUnitTestSuite->tests() as $testSuite) {
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
index 08fa2eceaf5..d0759a4bf08 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
@@ -203,6 +203,8 @@ class LocalActionManagerTest extends UnitTestCase {
}
public static function getActionsForRouteProvider() {
+ $originalContainer = \Drupal::hasContainer() ? \Drupal::getContainer() : NULL;
+
$cache_contexts_manager = (new Prophet())->prophesize(CacheContextsManager::class);
$cache_contexts_manager->assertValidTokens(Argument::any())
->willReturn(TRUE);
@@ -384,6 +386,11 @@ class LocalActionManagerTest extends UnitTestCase {
],
];
+ // Restore the original container if needed.
+ if ($originalContainer) {
+ \Drupal::setContainer($originalContainer);
+ }
+
return $data;
}
diff --git a/core/tests/Drupal/Tests/Core/Test/RunTests/TestFileParserTest.php b/core/tests/Drupal/Tests/Core/Test/RunTests/TestFileParserTest.php
index 957b2f61f97..9138d54523d 100644
--- a/core/tests/Drupal/Tests/Core/Test/RunTests/TestFileParserTest.php
+++ b/core/tests/Drupal/Tests/Core/Test/RunTests/TestFileParserTest.php
@@ -6,12 +6,18 @@ namespace Drupal\Tests\Core\Test\RunTests;
use Drupal\Core\Test\RunTests\TestFileParser;
use Drupal\Tests\UnitTestCase;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\IgnoreDeprecations;
/**
- * @coversDefaultClass \Drupal\Core\Test\RunTests\TestFileParser
- * @group Test
- * @group RunTests
+ * Tests for the deprecated TestFileParser class.
*/
+#[CoversClass(TestFileParser::class)]
+#[Group('Test')]
+#[Group('RunTest')]
+#[IgnoreDeprecations]
class TestFileParserTest extends UnitTestCase {
public static function provideTestFileContents() {
@@ -66,9 +72,9 @@ COMPOUND
}
/**
- * @covers ::parseContents
- * @dataProvider provideTestFileContents
+ * @legacy-covers ::parseContents
*/
+ #[DataProvider('provideTestFileContents')]
public function testParseContents($expected, $contents): void {
$parser = new TestFileParser();
@@ -78,7 +84,7 @@ COMPOUND
}
/**
- * @covers ::getTestListFromFile
+ * @legacy-covers ::getTestListFromFile
*/
public function testGetTestListFromFile(): void {
$parser = new TestFileParser();
diff --git a/core/tests/Drupal/Tests/Core/Test/TestDiscoveryTest.php b/core/tests/Drupal/Tests/Core/Test/TestDiscoveryTest.php
index bfbb4ca2e40..0fb55e6c7f8 100644
--- a/core/tests/Drupal/Tests/Core/Test/TestDiscoveryTest.php
+++ b/core/tests/Drupal/Tests/Core/Test/TestDiscoveryTest.php
@@ -13,17 +13,23 @@ use Drupal\Core\Test\Exception\MissingGroupException;
use Drupal\Core\Test\TestDiscovery;
use Drupal\Tests\UnitTestCase;
use org\bovigo\vfs\vfsStream;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\IgnoreDeprecations;
/**
- * @coversDefaultClass \Drupal\Core\Test\TestDiscovery
- * @group Test
+ * Unit tests for TestDiscovery.
*/
+#[CoversClass(TestDiscovery::class)]
+#[Group('Test')]
+#[IgnoreDeprecations]
class TestDiscoveryTest extends UnitTestCase {
/**
- * @covers ::getTestInfo
- * @dataProvider infoParserProvider
+ * @legacy-covers ::getTestInfo
*/
+ #[DataProvider('infoParserProvider')]
public function testTestInfoParser($expected, $classname, $doc_comment = NULL): void {
$info = TestDiscovery::getTestInfo($classname, $doc_comment);
$this->assertEquals($expected, $info);
@@ -34,14 +40,14 @@ class TestDiscoveryTest extends UnitTestCase {
$tests[] = [
// Expected result.
[
- 'name' => static::class,
+ 'name' => TestDatabaseTest::class,
'group' => 'Test',
- 'groups' => ['Test'],
- 'description' => 'Tests \Drupal\Core\Test\TestDiscovery.',
+ 'groups' => ['Test', 'simpletest', 'Template'],
+ 'description' => 'Tests \Drupal\Core\Test\TestDatabase.',
'type' => 'PHPUnit-Unit',
],
// Classname.
- static::class,
+ TestDatabaseTest::class,
];
// A core unit test.
@@ -217,7 +223,7 @@ class TestDiscoveryTest extends UnitTestCase {
}
/**
- * @covers ::getTestInfo
+ * @legacy-covers ::getTestInfo
*/
public function testTestInfoParserMissingGroup(): void {
$classname = 'Drupal\KernelTests\field\BulkDeleteTest';
@@ -232,7 +238,7 @@ EOT;
}
/**
- * @covers ::getTestInfo
+ * @legacy-covers ::getTestInfo
*/
public function testTestInfoParserMissingSummary(): void {
$classname = 'Drupal\KernelTests\field\BulkDeleteTest';
@@ -311,7 +317,7 @@ EOF;
}
/**
- * @covers ::getTestClasses
+ * @legacy-covers ::getTestClasses
*/
public function testGetTestClasses(): void {
$this->setupVfsWithTestClasses();
@@ -380,7 +386,7 @@ EOF;
}
/**
- * @covers ::getTestClasses
+ * @legacy-covers ::getTestClasses
*/
public function testGetTestClassesWithSelectedTypes(): void {
$this->setupVfsWithTestClasses();
@@ -425,7 +431,7 @@ EOF;
}
/**
- * @covers ::getTestClasses
+ * @legacy-covers ::getTestClasses
*/
public function testGetTestsInProfiles(): void {
$this->setupVfsWithTestClasses();
@@ -454,9 +460,9 @@ EOF;
}
/**
- * @covers ::getPhpunitTestSuite
- * @dataProvider providerTestGetPhpunitTestSuite
+ * @legacy-covers ::getPhpunitTestSuite
*/
+ #[DataProvider('providerTestGetPhpunitTestSuite')]
public function testGetPhpunitTestSuite($classname, $expected): void {
$this->assertEquals($expected, TestDiscovery::getPhpunitTestSuite($classname));
}
@@ -482,7 +488,7 @@ EOF;
/**
* Ensure that classes are not reflected when the docblock is empty.
*
- * @covers ::getTestInfo
+ * @legacy-covers ::getTestInfo
*/
public function testGetTestInfoEmptyDocblock(): void {
// If getTestInfo() performed reflection, it won't be able to find the
@@ -497,7 +503,7 @@ EOF;
/**
* Ensure TestDiscovery::scanDirectory() ignores certain abstract file types.
*
- * @covers ::scanDirectory
+ * @legacy-covers ::scanDirectory
*/
public function testScanDirectoryNoAbstract(): void {
$this->setupVfsWithTestClasses();