diff --git a/core/lib/Drupal/Core/Test/RunTests/TestFileParser.php b/core/lib/Drupal/Core/Test/RunTests/TestFileParser.php new file mode 100644 index 00000000000..afd92df5e42 --- /dev/null +++ b/core/lib/Drupal/Core/Test/RunTests/TestFileParser.php @@ -0,0 +1,61 @@ +parseContents(file_get_contents($file)); + return array_filter($test_list, function ($class) { + return (is_subclass_of($class, TestCase::class) || is_subclass_of($class, TestBase::class)); + }); + } + + /** + * Parse class names out of PHP file contents. + * + * @param string $contents + * The contents of a PHP file. + * + * @return string[] + * Array of fully qualified class names within the PHP file contents. + */ + protected function parseContents($contents) { + // Extract a potential namespace. + $namespace = FALSE; + if (preg_match('@^\s*namespace ([^ ;]+)@m', $contents, $matches)) { + $namespace = $matches[1]; + } + $test_list = []; + // Extract all class names. Abstract classes are excluded on purpose. + preg_match_all('@^\s*(?!abstract\s+)(?:final\s+|\s*)class ([^ ]+)@m', $contents, $matches); + if (!$namespace) { + $test_list = $matches[1]; + } + else { + foreach ($matches[1] as $class_name) { + $namespace_class = $namespace . '\\' . $class_name; + $test_list[] = $namespace_class; + } + } + return $test_list; + } + +} diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index c3f9d1a73aa..9d3f3894eba 100755 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -16,6 +16,7 @@ use Drupal\Core\File\FileSystemInterface; use Drupal\Core\StreamWrapper\PublicStream; use Drupal\Core\Test\EnvironmentCleaner; use Drupal\Core\Test\PhpUnitTestRunner; +use Drupal\Core\Test\RunTests\TestFileParser; use Drupal\Core\Test\TestDatabase; use Drupal\Core\Test\TestRunnerKernel; use Drupal\simpletest\Form\SimpletestResultsForm; @@ -1054,31 +1055,13 @@ 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)) { simpletest_script_print_error('File not found: ' . $file); exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } - $content = file_get_contents($file); - // Extract a potential namespace. - $namespace = FALSE; - if (preg_match('@^namespace ([^ ;]+)@m', $content, $matches)) { - $namespace = $matches[1]; - } - // Extract all class names. - // Abstract classes are excluded on purpose. - preg_match_all('@^class ([^ ]+)@m', $content, $matches); - if (!$namespace) { - $test_list = array_merge($test_list, $matches[1]); - } - else { - foreach ($matches[1] as $class_name) { - $namespace_class = $namespace . '\\' . $class_name; - if (is_subclass_of($namespace_class, '\Drupal\simpletest\TestBase') || is_subclass_of($namespace_class, TestCase::class)) { - $test_list[] = $namespace_class; - } - } - } + $test_list = array_merge($test_list, $parser->getTestListFromFile($file)); } } elseif ($args['directory']) { @@ -1112,27 +1095,9 @@ function simpletest_script_get_test_list() { $files[$filename] = $filename; } } + $parser = new TestFileParser(); foreach ($files as $file) { - $content = file_get_contents($file); - // Extract a potential namespace. - $namespace = FALSE; - if (preg_match('@^\s*namespace ([^ ;]+)@m', $content, $matches)) { - $namespace = $matches[1]; - } - // Extract all class names. - // Abstract classes are excluded on purpose. - preg_match_all('@^\s*class ([^ ]+)@m', $content, $matches); - if (!$namespace) { - $test_list = array_merge($test_list, $matches[1]); - } - else { - foreach ($matches[1] as $class_name) { - $namespace_class = $namespace . '\\' . $class_name; - if (is_subclass_of($namespace_class, '\Drupal\simpletest\TestBase') || is_subclass_of($namespace_class, TestCase::class)) { - $test_list[] = $namespace_class; - } - } - } + $test_list = array_merge($test_list, $parser->getTestListFromFile($file)); } } else { diff --git a/core/tests/Drupal/Tests/Core/Test/RunTests/TestFileParserTest.php b/core/tests/Drupal/Tests/Core/Test/RunTests/TestFileParserTest.php new file mode 100644 index 00000000000..5ba0f83c489 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Test/RunTests/TestFileParserTest.php @@ -0,0 +1,99 @@ + [[], ''], + 'no-namespace' => [['ConcreteClass'], + <<< 'NO_NAMESPACE' + [['Namespace\Is\Complex\ConcreteClass'], + <<< 'CONCRETE_CLASS' + [[], + <<< 'ABSTRACT_CLASS' + [['Namespace\Is\Complex\FinalClass'], + <<< 'FINAL_CLASS' + [[ + 'Namespace\Is\Complex\FinalClass', + 'Namespace\Is\Complex\AnotherClass', + ], + <<< 'COMPOUND' +setAccessible(TRUE); + + $this->assertSame($expected, $ref_parse->invoke($parser, $contents)); + } + + /** + * @covers ::getTestListFromFile + */ + public function testGetTestListFromFile() { + $parser = new TestFileParser(); + $this->assertArrayEquals( + ['Drupal\Tests\Core\Test\RunTests\TestFileParserTest'], + $parser->getTestListFromFile(__FILE__) + ); + // This WebTestBase test will eventually move, so we'll need to update it. + $this->assertArrayEquals( + ['Drupal\simpletest\Tests\TimeZoneTest'], + $parser->getTestListFromFile(__DIR__ . '/../../../../../../modules/simpletest/src/Tests/TimeZoneTest.php') + ); + // Not a test. + $this->assertEmpty( + $parser->getTestListFromFile(__DIR__ . '/../../../AssertHelperTrait.php') + ); + } + +}