From 94b717e9202617b4d0bec7c3c019d08cfdd42866 Mon Sep 17 00:00:00 2001
From: Dave Long <dave@longwaveconsulting.com>
Date: Fri, 20 Sep 2024 18:27:29 +0100
Subject: [PATCH] Issue #3470179 by catch, quietone: Account for test type,
 number of methods, and data providers in test ordering

---
 .../Kernel/BlockContentAccessHandlerTest.php  |   1 -
 .../src/Kernel/CKEditor5PluginManagerTest.php |   1 -
 .../tests/src/Kernel/ValidatorsTest.php       |   1 -
 .../src/Kernel/ContactFormValidationTest.php  |   1 -
 .../src/Kernel/ContentModerationStateTest.php |   1 -
 .../tests/src/Kernel/DateTimeItemTest.php     |   1 -
 .../tests/src/Kernel/EditorValidationTest.php |   1 -
 .../Entity/FieldConfigValidationTest.php      |   1 -
 .../FieldStorageConfigValidationTest.php      |   1 -
 .../src/Kernel/Number/NumberItemTest.php      |   1 -
 .../src/Kernel/FilterFormatValidationTest.php |   1 -
 .../src/Kernel/ImageStyleValidationTest.php   |   1 -
 .../ConfigurableLanguageValidationTest.php    |   1 -
 .../ContentLanguageSettingsValidationTest.php |   1 -
 .../Kernel/MediaAccessControlHandlerTest.php  |   1 -
 .../tests/src/Kernel/MediaEmbedFilterTest.php |   1 -
 .../src/Kernel/MediaTypeValidationTest.php    |   1 -
 .../src/Kernel/MediaLibraryStateTest.php      |   1 -
 .../ResponsiveImageStyleValidationTest.php    |   1 -
 .../RestResourceConfigValidationTest.php      |   1 -
 .../EntityResourceRestTestCoverageTest.php    |   1 +
 .../src/Kernel/SearchPageValidationTest.php   |   1 -
 .../src/Kernel/ShortcutSetValidationTest.php  |   1 -
 .../src/Kernel/Entity/MenuValidationTest.php  |   1 -
 .../tests/src/Kernel/RoleValidationTest.php   |   1 -
 .../src/Kernel/Entity/ViewValidationTest.php  |   1 -
 .../src/Kernel/WorkflowValidationTest.php     |   1 -
 .../src/Kernel/WorkspaceIntegrationTest.php   |   1 -
 core/scripts/run-tests.sh                     | 144 +++++++++++-------
 ...solvedLibraryDefinitionsFilesMatchTest.php |   1 +
 .../Config/SimpleConfigValidationTest.php     |   1 -
 .../EntityViewDisplayValidationTest.php       |   1 -
 .../KernelTests/Core/Image/ToolkitGdTest.php  |   1 -
 .../Core/Routing/RouteProviderTest.php        |   1 -
 .../Theme/Stable9TemplateOverrideTest.php     |   1 +
 .../Core/Theme/ThemeInstallerTest.php         |   1 -
 36 files changed, 95 insertions(+), 84 deletions(-)

diff --git a/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php b/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php
index 893ae0c73a70..6252aa736156 100644
--- a/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php
+++ b/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php
@@ -23,7 +23,6 @@
  * @coversDefaultClass \Drupal\block_content\BlockContentAccessControlHandler
  *
  * @group block_content
- * @group #slow
  */
 class BlockContentAccessHandlerTest extends KernelTestBase {
 
diff --git a/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php b/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php
index 0a4cf8a901c3..7c0dd5adc91b 100644
--- a/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php
+++ b/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php
@@ -28,7 +28,6 @@
  * Tests different ways of enabling CKEditor 5 plugins.
  *
  * @group ckeditor5
- * @group #slow
  * @internal
  */
 class CKEditor5PluginManagerTest extends KernelTestBase {
diff --git a/core/modules/ckeditor5/tests/src/Kernel/ValidatorsTest.php b/core/modules/ckeditor5/tests/src/Kernel/ValidatorsTest.php
index 78272fff453d..03ac68cc76e0 100644
--- a/core/modules/ckeditor5/tests/src/Kernel/ValidatorsTest.php
+++ b/core/modules/ckeditor5/tests/src/Kernel/ValidatorsTest.php
@@ -24,7 +24,6 @@
  * @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\FundamentalCompatibilityConstraintValidator
  * @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\CKEditor5MediaAndFilterSettingsInSyncConstraintValidator
  * @group ckeditor5
- * @group #slow
  */
 class ValidatorsTest extends KernelTestBase {
 
diff --git a/core/modules/contact/tests/src/Kernel/ContactFormValidationTest.php b/core/modules/contact/tests/src/Kernel/ContactFormValidationTest.php
index a79a9b2b84b6..71fe9aae9531 100644
--- a/core/modules/contact/tests/src/Kernel/ContactFormValidationTest.php
+++ b/core/modules/contact/tests/src/Kernel/ContactFormValidationTest.php
@@ -12,7 +12,6 @@
  * Tests validation of contact_form entities.
  *
  * @group contact
- * @group #slow
  */
 class ContactFormValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
index cbfd0a61db6f..30d12fbb8109 100644
--- a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
+++ b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
@@ -23,7 +23,6 @@
  * Tests links between a content entity and a content_moderation_state entity.
  *
  * @group content_moderation
- * @group #slow
  */
 class ContentModerationStateTest extends KernelTestBase {
 
diff --git a/core/modules/datetime/tests/src/Kernel/DateTimeItemTest.php b/core/modules/datetime/tests/src/Kernel/DateTimeItemTest.php
index 428f56b8d7eb..887763a01ca3 100644
--- a/core/modules/datetime/tests/src/Kernel/DateTimeItemTest.php
+++ b/core/modules/datetime/tests/src/Kernel/DateTimeItemTest.php
@@ -18,7 +18,6 @@
  * Tests the new entity API for the date field type.
  *
  * @group datetime
- * @group #slow
  */
 class DateTimeItemTest extends FieldKernelTestBase {
 
diff --git a/core/modules/editor/tests/src/Kernel/EditorValidationTest.php b/core/modules/editor/tests/src/Kernel/EditorValidationTest.php
index cf45fb8a4143..956177d94a1f 100644
--- a/core/modules/editor/tests/src/Kernel/EditorValidationTest.php
+++ b/core/modules/editor/tests/src/Kernel/EditorValidationTest.php
@@ -13,7 +13,6 @@
  * Tests validation of editor entities.
  *
  * @group editor
- * @group #slow
  */
 class EditorValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/field/tests/src/Kernel/Entity/FieldConfigValidationTest.php b/core/modules/field/tests/src/Kernel/Entity/FieldConfigValidationTest.php
index 65f2a4664489..c4fb51e2a65e 100644
--- a/core/modules/field/tests/src/Kernel/Entity/FieldConfigValidationTest.php
+++ b/core/modules/field/tests/src/Kernel/Entity/FieldConfigValidationTest.php
@@ -14,7 +14,6 @@
  * Tests validation of field_config entities.
  *
  * @group field
- * @group #slow
  */
 class FieldConfigValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/field/tests/src/Kernel/Entity/FieldStorageConfigValidationTest.php b/core/modules/field/tests/src/Kernel/Entity/FieldStorageConfigValidationTest.php
index d595782757a0..47532f40c84b 100644
--- a/core/modules/field/tests/src/Kernel/Entity/FieldStorageConfigValidationTest.php
+++ b/core/modules/field/tests/src/Kernel/Entity/FieldStorageConfigValidationTest.php
@@ -11,7 +11,6 @@
  * Tests validation of field_storage_config entities.
  *
  * @group field
- * @group #slow
  */
 class FieldStorageConfigValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php b/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php
index 4827439e237e..e7f742b7bad3 100644
--- a/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php
+++ b/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php
@@ -17,7 +17,6 @@
  * Tests the new entity API for the number field type.
  *
  * @group field
- * @group #slow
  */
 class NumberItemTest extends FieldKernelTestBase {
 
diff --git a/core/modules/filter/tests/src/Kernel/FilterFormatValidationTest.php b/core/modules/filter/tests/src/Kernel/FilterFormatValidationTest.php
index 03241f26f2bb..5fd481e5bcfc 100644
--- a/core/modules/filter/tests/src/Kernel/FilterFormatValidationTest.php
+++ b/core/modules/filter/tests/src/Kernel/FilterFormatValidationTest.php
@@ -11,7 +11,6 @@
  * Tests validation of filter_format entities.
  *
  * @group filter
- * @group #slow
  */
 class FilterFormatValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/image/tests/src/Kernel/ImageStyleValidationTest.php b/core/modules/image/tests/src/Kernel/ImageStyleValidationTest.php
index 573998b84ef9..8458c907605d 100644
--- a/core/modules/image/tests/src/Kernel/ImageStyleValidationTest.php
+++ b/core/modules/image/tests/src/Kernel/ImageStyleValidationTest.php
@@ -11,7 +11,6 @@
  * Tests validation of image_style entities.
  *
  * @group image
- * @group #slow
  */
 class ImageStyleValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/language/tests/src/Kernel/ConfigurableLanguageValidationTest.php b/core/modules/language/tests/src/Kernel/ConfigurableLanguageValidationTest.php
index 86235659d7ef..f0369ff7a79b 100644
--- a/core/modules/language/tests/src/Kernel/ConfigurableLanguageValidationTest.php
+++ b/core/modules/language/tests/src/Kernel/ConfigurableLanguageValidationTest.php
@@ -11,7 +11,6 @@
  * Tests validation of configurable_language entities.
  *
  * @group language
- * @group #slow
  */
 class ConfigurableLanguageValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/language/tests/src/Kernel/ContentLanguageSettingsValidationTest.php b/core/modules/language/tests/src/Kernel/ContentLanguageSettingsValidationTest.php
index a0e73fcfefeb..003bdac683c3 100644
--- a/core/modules/language/tests/src/Kernel/ContentLanguageSettingsValidationTest.php
+++ b/core/modules/language/tests/src/Kernel/ContentLanguageSettingsValidationTest.php
@@ -13,7 +13,6 @@
  * Tests validation of content_language_settings entities.
  *
  * @group language
- * @group #slow
  */
 class ContentLanguageSettingsValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/media/tests/src/Kernel/MediaAccessControlHandlerTest.php b/core/modules/media/tests/src/Kernel/MediaAccessControlHandlerTest.php
index f4b7ec987a51..10c163c40aa9 100644
--- a/core/modules/media/tests/src/Kernel/MediaAccessControlHandlerTest.php
+++ b/core/modules/media/tests/src/Kernel/MediaAccessControlHandlerTest.php
@@ -13,7 +13,6 @@
  * Tests the media access control handler.
  *
  * @group media
- * @group #slow
  *
  * @coversDefaultClass \Drupal\media\MediaAccessControlHandler
  */
diff --git a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php
index f67c940c6d38..e283aae92bb1 100644
--- a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php
+++ b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php
@@ -12,7 +12,6 @@
 /**
  * @coversDefaultClass \Drupal\media\Plugin\Filter\MediaEmbed
  * @group media
- * @group #slow
  */
 class MediaEmbedFilterTest extends MediaEmbedFilterTestBase {
 
diff --git a/core/modules/media/tests/src/Kernel/MediaTypeValidationTest.php b/core/modules/media/tests/src/Kernel/MediaTypeValidationTest.php
index 1b070de3517c..43be795011fe 100644
--- a/core/modules/media/tests/src/Kernel/MediaTypeValidationTest.php
+++ b/core/modules/media/tests/src/Kernel/MediaTypeValidationTest.php
@@ -11,7 +11,6 @@
  * Tests validation of media_type entities.
  *
  * @group media
- * @group #slow
  */
 class MediaTypeValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/media_library/tests/src/Kernel/MediaLibraryStateTest.php b/core/modules/media_library/tests/src/Kernel/MediaLibraryStateTest.php
index 3256e914cd25..a861d5348924 100644
--- a/core/modules/media_library/tests/src/Kernel/MediaLibraryStateTest.php
+++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryStateTest.php
@@ -16,7 +16,6 @@
  * Tests the media library state value object.
  *
  * @group media_library
- * @group #slow
  *
  * @coversDefaultClass \Drupal\media_library\MediaLibraryState
  */
diff --git a/core/modules/responsive_image/tests/src/Kernel/ResponsiveImageStyleValidationTest.php b/core/modules/responsive_image/tests/src/Kernel/ResponsiveImageStyleValidationTest.php
index fe0e0cf5f1bf..6ba1184a41cb 100644
--- a/core/modules/responsive_image/tests/src/Kernel/ResponsiveImageStyleValidationTest.php
+++ b/core/modules/responsive_image/tests/src/Kernel/ResponsiveImageStyleValidationTest.php
@@ -11,7 +11,6 @@
  * Tests validation of responsive_image_style entities.
  *
  * @group responsive_image
- * @group #slow
  */
 class ResponsiveImageStyleValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/rest/tests/src/Kernel/Entity/RestResourceConfigValidationTest.php b/core/modules/rest/tests/src/Kernel/Entity/RestResourceConfigValidationTest.php
index 673003d51aec..56e423b3fe38 100644
--- a/core/modules/rest/tests/src/Kernel/Entity/RestResourceConfigValidationTest.php
+++ b/core/modules/rest/tests/src/Kernel/Entity/RestResourceConfigValidationTest.php
@@ -12,7 +12,6 @@
  * Tests validation of rest_resource_config entities.
  *
  * @group rest
- * @group #slow
  */
 class RestResourceConfigValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/rest/tests/src/Kernel/EntityResource/EntityResourceRestTestCoverageTest.php b/core/modules/rest/tests/src/Kernel/EntityResource/EntityResourceRestTestCoverageTest.php
index 60f65f990a51..300a80592fc7 100644
--- a/core/modules/rest/tests/src/Kernel/EntityResource/EntityResourceRestTestCoverageTest.php
+++ b/core/modules/rest/tests/src/Kernel/EntityResource/EntityResourceRestTestCoverageTest.php
@@ -20,6 +20,7 @@
  * Additionally, every entity type must have the correct parent test class.
  *
  * @group rest
+ * @group #slow
  */
 class EntityResourceRestTestCoverageTest extends KernelTestBase {
 
diff --git a/core/modules/search/tests/src/Kernel/SearchPageValidationTest.php b/core/modules/search/tests/src/Kernel/SearchPageValidationTest.php
index 890371ab8494..7ee86ec18120 100644
--- a/core/modules/search/tests/src/Kernel/SearchPageValidationTest.php
+++ b/core/modules/search/tests/src/Kernel/SearchPageValidationTest.php
@@ -13,7 +13,6 @@
  * Tests validation of search_page entities.
  *
  * @group search
- * @group #slow
  */
 class SearchPageValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/shortcut/tests/src/Kernel/ShortcutSetValidationTest.php b/core/modules/shortcut/tests/src/Kernel/ShortcutSetValidationTest.php
index 6976a76b4086..f61b03aff6c3 100644
--- a/core/modules/shortcut/tests/src/Kernel/ShortcutSetValidationTest.php
+++ b/core/modules/shortcut/tests/src/Kernel/ShortcutSetValidationTest.php
@@ -11,7 +11,6 @@
  * Tests validation of shortcut_set entities.
  *
  * @group shortcut
- * @group #slow
  */
 class ShortcutSetValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/system/tests/src/Kernel/Entity/MenuValidationTest.php b/core/modules/system/tests/src/Kernel/Entity/MenuValidationTest.php
index 9dff686d7280..906960a0d683 100644
--- a/core/modules/system/tests/src/Kernel/Entity/MenuValidationTest.php
+++ b/core/modules/system/tests/src/Kernel/Entity/MenuValidationTest.php
@@ -11,7 +11,6 @@
  * Tests validation of menu entities.
  *
  * @group system
- * @group #slow
  */
 class MenuValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/user/tests/src/Kernel/RoleValidationTest.php b/core/modules/user/tests/src/Kernel/RoleValidationTest.php
index 841dc6abe7ae..07e43497115a 100644
--- a/core/modules/user/tests/src/Kernel/RoleValidationTest.php
+++ b/core/modules/user/tests/src/Kernel/RoleValidationTest.php
@@ -11,7 +11,6 @@
  * Tests validation of user_role entities.
  *
  * @group user
- * @group #slow
  */
 class RoleValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/views/tests/src/Kernel/Entity/ViewValidationTest.php b/core/modules/views/tests/src/Kernel/Entity/ViewValidationTest.php
index 642694890041..28ba787fb1af 100644
--- a/core/modules/views/tests/src/Kernel/Entity/ViewValidationTest.php
+++ b/core/modules/views/tests/src/Kernel/Entity/ViewValidationTest.php
@@ -12,7 +12,6 @@
  * Tests validation of view entities.
  *
  * @group views
- * @group #slow
  */
 class ViewValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/workflows/tests/src/Kernel/WorkflowValidationTest.php b/core/modules/workflows/tests/src/Kernel/WorkflowValidationTest.php
index 4136a5f531dd..dba4eb5bef5b 100644
--- a/core/modules/workflows/tests/src/Kernel/WorkflowValidationTest.php
+++ b/core/modules/workflows/tests/src/Kernel/WorkflowValidationTest.php
@@ -11,7 +11,6 @@
  * Tests validation of workflow entities.
  *
  * @group workflows
- * @group #slow
  */
 class WorkflowValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php b/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php
index 1c832c78c366..341f37ed41d2 100644
--- a/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php
+++ b/core/modules/workspaces/tests/src/Kernel/WorkspaceIntegrationTest.php
@@ -23,7 +23,6 @@
 /**
  * Tests a complete publishing scenario across different workspaces.
  *
- * @group #slow
  * @group workspaces
  */
 class WorkspaceIntegrationTest extends KernelTestBase {
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 2042c24dd41a..f3e0223b4d5a 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -25,6 +25,11 @@
 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;
+use Drupal\Tests\BrowserTestBase;
+
 use PHPUnit\Framework\TestCase;
 use PHPUnit\Runner\Version;
 use Symfony\Component\Console\Output\ConsoleOutput;
@@ -922,33 +927,42 @@ function simpletest_script_get_test_list() {
         $slow_tests = array_keys(array_shift($groups));
       }
     }
-    $all_tests = [];
+    $not_slow_tests = [];
     foreach ($groups as $group => $tests) {
-      if ($group === '#slow') {
-        $slow_group = $tests;
-      }
-      else {
-        $all_tests = array_merge($all_tests, array_keys($tests));
-      }
+      $not_slow_tests = array_merge($not_slow_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);
+    // Filter slow tests out of the not slow tests since they may appear in more
+    // than one group.
+    $not_slow_tests = array_diff($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_unique(array_merge($slow_tests, $not_slow_tests));
+    }
+    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_tests_by_public_method_count($slow_tests);
+      sort_tests_by_public_method_count($not_slow_tests);
+
+      // Now set up a bin per test runner.
+      $bin_count = (int) $args['ci-parallel-node-total'];
+
+      // Now loop over the slow tests and add them to a bin one by one, this
+      // distributes the tests evenly across the bins.
+      $binned_slow_tests = place_tests_into_bins($slow_tests, $bin_count);
+      $slow_tests_for_job = $binned_slow_tests[$args['ci-parallel-node-index'] - 1];
+
+      // And the same for the rest of the tests.
+      $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_unique(array_merge($slow_tests_for_job, $other_tests_for_job));
     }
-    $test_list = array_unique($all_tests);
-    $test_list = array_diff($test_list, $slow_tests);
   }
   else {
     if ($args['class']) {
@@ -1028,32 +1042,21 @@ function simpletest_script_get_test_list() {
     exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);
   }
 
-  if ((int) $args['ci-parallel-node-total'] > 1) {
-    // 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_tests_by_public_method_count($slow_tests);
-    sort_tests_by_public_method_count($test_list);
-
-    // Now set up a bin per test runner.
-    $bin_count = (int) $args['ci-parallel-node-total'];
-
-    // Now loop over the slow tests and add them to a bin one by one, this
-    // distributes the tests evenly across the bins.
-    $binned_slow_tests = place_tests_into_bins($slow_tests, $bin_count);
-    $slow_tests_for_job = $binned_slow_tests[$args['ci-parallel-node-index'] - 1];
-
-    // And the same for the rest of the tests.
-    $binned_other_tests = place_tests_into_bins($test_list, $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);
-  }
-
   return $test_list;
 }
 
+/**
+ * Sort tests by test type and number of public methods.
+ */
+function sort_tests_by_type_and_methods(array &$tests) {
+  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);
+    }
+    return get_test_type_weight($b) <=> get_test_type_weight($a);
+  });
+}
+
 /**
  * Sort tests by the number of public methods in the test class.
  *
@@ -1068,14 +1071,51 @@ function simpletest_script_get_test_list() {
  */
 function sort_tests_by_public_method_count(array &$tests): void {
   usort($tests, function ($a, $b) {
-    $method_count = function ($class) {
-      $reflection = new \ReflectionClass($class);
-      return count($reflection->getMethods(\ReflectionMethod::IS_PUBLIC));
-    };
-    return $method_count($b) <=> $method_count($a);
+    return get_test_class_method_count($b) <=> get_test_class_method_count($a);
   });
 }
 
+/**
+ * Weights a test class based on which test base class it extends.
+ *
+ * @param string $class
+ *   The test class name.
+ */
+function get_test_type_weight(string $class): int {
+  return match(TRUE) {
+    is_subclass_of($class, WebDriverTestBase::class) => 3,
+    is_subclass_of($class, BrowserTestBase::class) => 2,
+    is_subclass_of($class, BuildTestBase::class) => 2,
+    is_subclass_of($class, KernelTestBase::class) => 1,
+    default => 0,
+  };
+}
+
+/**
+ * Get an approximate test method count for a test class.
+ *
+ * @param string $class
+ *   The test class name.
+ */
+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++;
+  }
+  return $count;
+}
+
 /**
  * Distribute tests into bins.
  *
diff --git a/core/tests/Drupal/KernelTests/Core/Asset/ResolvedLibraryDefinitionsFilesMatchTest.php b/core/tests/Drupal/KernelTests/Core/Asset/ResolvedLibraryDefinitionsFilesMatchTest.php
index 74efc95ab407..186121a181f4 100644
--- a/core/tests/Drupal/KernelTests/Core/Asset/ResolvedLibraryDefinitionsFilesMatchTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Asset/ResolvedLibraryDefinitionsFilesMatchTest.php
@@ -15,6 +15,7 @@
  * applied.
  *
  * @group Asset
+ * @group #slow
  */
 class ResolvedLibraryDefinitionsFilesMatchTest extends KernelTestBase {
 
diff --git a/core/tests/Drupal/KernelTests/Core/Config/SimpleConfigValidationTest.php b/core/tests/Drupal/KernelTests/Core/Config/SimpleConfigValidationTest.php
index 0e3ec7f1cc09..3855de27ca39 100644
--- a/core/tests/Drupal/KernelTests/Core/Config/SimpleConfigValidationTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Config/SimpleConfigValidationTest.php
@@ -11,7 +11,6 @@
  *
  * @group config
  * @group Validation
- * @group #slow
  */
 class SimpleConfigValidationTest extends KernelTestBase {
 
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityViewDisplayValidationTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityViewDisplayValidationTest.php
index 02d371a00130..607458810ccf 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityViewDisplayValidationTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityViewDisplayValidationTest.php
@@ -17,7 +17,6 @@
  *
  * @group Entity
  * @group Validation
- * @group #slow
  */
 class EntityViewDisplayValidationTest extends ConfigEntityValidationTestBase {
 
diff --git a/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php
index 9f52d7e8de87..793bbd66bd85 100644
--- a/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php
@@ -16,7 +16,6 @@
  *
  * @coversDefaultClass \Drupal\system\Plugin\ImageToolkit\GDToolkit
  * @group Image
- * @group #slow
  * @requires extension gd
  */
 class ToolkitGdTest extends KernelTestBase {
diff --git a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
index 8f25feb458c0..7727303b6324 100644
--- a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
@@ -29,7 +29,6 @@
  * Confirm that the default route provider is working correctly.
  *
  * @group Routing
- * @group #slow
  */
 class RouteProviderTest extends KernelTestBase {
 
diff --git a/core/tests/Drupal/KernelTests/Core/Theme/Stable9TemplateOverrideTest.php b/core/tests/Drupal/KernelTests/Core/Theme/Stable9TemplateOverrideTest.php
index 2e50db7e9b72..36602f4f1d25 100644
--- a/core/tests/Drupal/KernelTests/Core/Theme/Stable9TemplateOverrideTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Theme/Stable9TemplateOverrideTest.php
@@ -12,6 +12,7 @@
  * Tests Stable 9's template overrides.
  *
  * @group Theme
+ * @group #slow
  */
 class Stable9TemplateOverrideTest extends KernelTestBase {
 
diff --git a/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php b/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php
index 512992ccaa22..452f53c0cd29 100644
--- a/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php
@@ -17,7 +17,6 @@
  * Tests installing and uninstalling of themes.
  *
  * @group Extension
- * @group #slow
  */
 class ThemeInstallerTest extends KernelTestBase {
 
-- 
GitLab