Skip to content
Snippets Groups Projects
Verified Commit af883167 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3450616 by catch, quietone, smustgrave: Optimize test order when --directory is used

parent 35ca6308
No related branches found
No related tags found
27 merge requests!11131[10.4.x-only-DO-NOT-MERGE]: Issue ##2842525 Ajax attached to Views exposed filter form does not trigger callbacks,!9470[10.3.x-only-DO-NOT-MERGE]: #3331771 Fix file_get_contents(): Passing null to parameter,!8736Update the Documention As per the Function uses.,!8540Issue #3457061: Bootstrap Modal dialog Not closing after 10.3.0 Update,!8528Issue #3456871 by Tim Bozeman: Support NULL services,!8513Issue #3453786: DefaultSelection should document why values for target_bundles NULL and [] behave as they do,!8126Added escape fucntionality on admintoolbar close icon,!3878Removed unused condition head title for views,!3818Issue #2140179: $entity->original gets stale between updates,!3742Issue #3328429: Create item list field formatter for displaying ordered and unordered lists,!3731Claro: role=button on status report items,!3651Issue #3347736: Create new SDC component for Olivero (header-search),!3531Issue #3336994: StringFormatter always displays links to entity even if the user in context does not have access,!3355Issue #3209129: Scrolling problems when adding a block via layout builder,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!3133core/modules/system/css/components/hidden.module.css,!2964Issue #2865710 : Dependencies from only one instance of a widget are used in display modes,!2812Issue #3312049: [Followup] Fix Drupal.Commenting.FunctionComment.MissingReturnType returns for NULL,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2062Issue #3246454: Add weekly granularity to views date sort,!1105Issue #3025039: New non translatable field on translatable content throws error,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!877Issue #2708101: Default value for link text is not saved,!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493
Pipeline #196397 canceled
Pipeline: drupal

#196400

    ...@@ -119,7 +119,9 @@ public function registerTestNamespaces() { ...@@ -119,7 +119,9 @@ public function registerTestNamespaces() {
    * @param string $extension * @param string $extension
    * (optional) The name of an extension to limit discovery to; e.g., 'node'. * (optional) The name of an extension to limit discovery to; e.g., 'node'.
    * @param string[] $types * @param string[] $types
    * An array of included test types. * (optional) An array of included test types.
    * @param string|null $directory
    * (optional) Limit discovered tests to a specific directory.
    * *
    * @return array * @return array
    * An array of tests keyed by the group name. If a test is annotated to * An array of tests keyed by the group name. If a test is annotated to
    ...@@ -140,7 +142,7 @@ public function registerTestNamespaces() { ...@@ -140,7 +142,7 @@ public function registerTestNamespaces() {
    * @todo Remove singular grouping; retain list of groups in 'group' key. * @todo Remove singular grouping; retain list of groups in 'group' key.
    * @see https://www.drupal.org/node/2296615 * @see https://www.drupal.org/node/2296615
    */ */
    public function getTestClasses($extension = NULL, array $types = []) { public function getTestClasses($extension = NULL, array $types = [], ?string $directory = NULL) {
    if (!isset($extension) && empty($types)) { if (!isset($extension) && empty($types)) {
    if (!empty($this->testClasses)) { if (!empty($this->testClasses)) {
    return $this->testClasses; return $this->testClasses;
    ...@@ -148,7 +150,7 @@ public function getTestClasses($extension = NULL, array $types = []) { ...@@ -148,7 +150,7 @@ public function getTestClasses($extension = NULL, array $types = []) {
    } }
    $list = []; $list = [];
    $classmap = $this->findAllClassFiles($extension); $classmap = $this->findAllClassFiles($extension, $directory);
    // Prevent expensive class loader lookups for each reflected test class by // Prevent expensive class loader lookups for each reflected test class by
    // registering the complete classmap of test classes to the class loader. // registering the complete classmap of test classes to the class loader.
    ...@@ -200,12 +202,14 @@ public function getTestClasses($extension = NULL, array $types = []) { ...@@ -200,12 +202,14 @@ public function getTestClasses($extension = NULL, array $types = []) {
    * *
    * @param string $extension * @param string $extension
    * (optional) The name of an extension to limit discovery to; e.g., 'node'. * (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 * @return array
    * A classmap containing all discovered class files; i.e., a map of * A classmap containing all discovered class files; i.e., a map of
    * fully-qualified classnames to path names. * fully-qualified classnames to path names.
    */ */
    public function findAllClassFiles($extension = NULL) { public function findAllClassFiles($extension = NULL, ?string $directory = NULL) {
    $classmap = []; $classmap = [];
    $namespaces = $this->registerTestNamespaces(); $namespaces = $this->registerTestNamespaces();
    if (isset($extension)) { if (isset($extension)) {
    ...@@ -215,7 +219,7 @@ public function findAllClassFiles($extension = NULL) { ...@@ -215,7 +219,7 @@ public function findAllClassFiles($extension = NULL) {
    } }
    foreach ($namespaces as $namespace => $paths) { foreach ($namespaces as $namespace => $paths) {
    foreach ($paths as $path) { foreach ($paths as $path) {
    if (!is_dir($path)) { if (!is_dir($path) || (!is_null($directory) && !str_contains($path, $directory))) {
    continue; continue;
    } }
    $classmap += static::scanDirectory($namespace, $path); $classmap += static::scanDirectory($namespace, $path);
    ......
    ...@@ -900,15 +900,17 @@ function simpletest_script_get_test_list() { ...@@ -900,15 +900,17 @@ function simpletest_script_get_test_list() {
    $types_processed = empty($args['types']); $types_processed = empty($args['types']);
    $test_list = []; $test_list = [];
    $slow_tests = []; $slow_tests = [];
    if ($args['all'] || $args['module']) { if ($args['all'] || $args['module'] || $args['directory']) {
    try { try {
    $groups = $test_discovery->getTestClasses($args['module'], $args['types']); $groups = $test_discovery->getTestClasses($args['module'], $args['types'], $args['directory']);
    $types_processed = TRUE; $types_processed = TRUE;
    } }
    catch (Exception $e) { catch (Exception $e) {
    echo (string) $e; echo (string) $e;
    exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION); exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION);
    } }
    // If the tests are run in parallel jobs, ensure that slow tests are
    // distributed between each job.
    if ((int) $args['ci-parallel-node-total'] > 1) { if ((int) $args['ci-parallel-node-total'] > 1) {
    if (key($groups) === '#slow') { if (key($groups) === '#slow') {
    $slow_tests = array_keys(array_shift($groups)); $slow_tests = array_keys(array_shift($groups));
    ...@@ -916,7 +918,28 @@ function simpletest_script_get_test_list() { ...@@ -916,7 +918,28 @@ function simpletest_script_get_test_list() {
    } }
    $all_tests = []; $all_tests = [];
    foreach ($groups as $group => $tests) { foreach ($groups as $group => $tests) {
    $all_tests = array_merge($all_tests, array_keys($tests)); if ($group === '#slow') {
    $slow_group = $tests;
    }
    else {
    $all_tests = array_merge($all_tests, array_keys($tests));
    }
    }
    // If no type has been set, order the tests alphabetically by test namespace
    // so that unit tests run last. This takes advantage of the fact that Build,
    // Functional, Functional JavaScript, Kernel, Unit roughly corresponds to
    // test time.
    usort($all_tests, function ($a, $b) {
    $slice = function ($class) {
    $parts = explode('\\', $class);
    return implode('\\', array_slice($parts, 3));
    };
    return $slice($a) > $slice($b) ? 1 : -1;
    });
    // 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 && !empty($slow_group)) {
    $all_tests = array_merge(array_keys($slow_group), $all_tests);
    } }
    $test_list = array_unique($all_tests); $test_list = array_unique($all_tests);
    $test_list = array_diff($test_list, $slow_tests); $test_list = array_diff($test_list, $slow_tests);
    ...@@ -958,42 +981,6 @@ function simpletest_script_get_test_list() { ...@@ -958,42 +981,6 @@ function simpletest_script_get_test_list() {
    $test_list = array_merge($test_list, $parser->getTestListFromFile($file)); $test_list = array_merge($test_list, $parser->getTestListFromFile($file));
    } }
    } }
    elseif ($args['directory']) {
    // Extract test case class names from specified directory.
    // Find all tests in the PSR-X structure; Drupal\$extension\Tests\*.php
    // Since we do not want to hard-code too many structural file/directory
    // assumptions about PSR-4 files and directories, we check for the
    // minimal conditions only; i.e., a '*.php' file that has '/Tests/' in
    // its path.
    // Ignore anything from third party vendors.
    $ignore = ['.', '..', 'vendor'];
    $files = [];
    if ($args['directory'][0] === '/') {
    $directory = $args['directory'];
    }
    else {
    $directory = DRUPAL_ROOT . "/" . $args['directory'];
    }
    foreach (\Drupal::service('file_system')->scanDirectory($directory, '/\.php$/', $ignore) as $file) {
    // '/Tests/' can be contained anywhere in the file's path (there can be
    // sub-directories below /Tests), but must be contained literally.
    // Case-insensitive to match all Simpletest and PHPUnit tests:
    // ./lib/Drupal/foo/Tests/Bar/Baz.php
    // ./foo/src/Tests/Bar/Baz.php
    // ./foo/tests/Drupal/foo/Tests/FooTest.php
    // ./foo/tests/src/FooTest.php
    // $file->filename doesn't give us a directory, so we use $file->uri
    // Strip the drupal root directory and trailing slash off the URI.
    $filename = substr($file->uri, strlen(DRUPAL_ROOT) + 1);
    if (stripos($filename, '/Tests/')) {
    $files[$filename] = $filename;
    }
    }
    $parser = new TestFileParser();
    foreach ($files as $file) {
    $test_list = array_merge($test_list, $parser->getTestListFromFile($file));
    }
    }
    else { else {
    try { try {
    $groups = $test_discovery->getTestClasses(NULL, $args['types']); $groups = $test_discovery->getTestClasses(NULL, $args['types']);
    ......
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment