diff --git a/core/modules/simpletest/src/TestDiscovery.php b/core/modules/simpletest/src/TestDiscovery.php
index 02bf7071fb..53f15ade2d 100644
--- a/core/modules/simpletest/src/TestDiscovery.php
+++ b/core/modules/simpletest/src/TestDiscovery.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\simpletest;
 
-use Doctrine\Common\Annotations\SimpleAnnotationReader;
 use Doctrine\Common\Reflection\StaticReflectionParser;
 use Drupal\Component\Annotation\Reflection\MockFileFinder;
 use Drupal\Component\Utility\NestedArray;
@@ -148,6 +147,7 @@ public function registerTestNamespaces() {
    *         'name' => 'Drupal\block\Tests\BlockTest',
    *         'description' => 'Tests block UI CRUD functionality.',
    *         'group' => 'block',
+   *         'groups' => ['block', 'group2', 'group3'],
    *       ),
    *     );
    * @endcode
@@ -156,9 +156,6 @@ public function registerTestNamespaces() {
    * @see https://www.drupal.org/node/2296615
    */
   public function getTestClasses($extension = NULL, array $types = []) {
-    $reader = new SimpleAnnotationReader();
-    $reader->addNamespace('Drupal\\simpletest\\Annotation');
-
     if (!isset($extension)) {
       if ($this->cacheBackend && $cache = $this->cacheBackend->get('simpletest:discovery:classes')) {
         return $cache->data;
@@ -200,7 +197,9 @@ public function getTestClasses($extension = NULL, array $types = []) {
         }
       }
 
-      $list[$info['group']][$classname] = $info;
+      foreach ($info['groups'] as $group) {
+        $list[$group][$classname] = $info;
+      }
     }
 
     // Sort the groups and tests within the groups by name.
@@ -320,6 +319,8 @@ public static function scanDirectory($namespace_prefix, $path) {
    *   - name: The test class name.
    *   - description: The test (PHPDoc) summary.
    *   - group: The test's first @group (parsed from PHPDoc annotations).
+   *   - groups: All of the test's @group annotations, as an array (parsed from
+   *     PHPDoc annotations).
    *   - requires: An associative array containing test requirements parsed from
    *     PHPDoc annotations:
    *     - module: List of Drupal module extension names the test depends on.
@@ -341,9 +342,14 @@ public static function getTestInfo($classname, $doc_comment = NULL) {
     preg_match_all('/^[ ]*\* \@([^\s]*) (.*$)/m', $doc_comment, $matches);
     if (isset($matches[1])) {
       foreach ($matches[1] as $key => $annotation) {
+        // For historical reasons, there is a single-value 'group' result key
+        // and a 'groups' key as an array.
+        if ($annotation == 'group') {
+          $annotations['groups'][] = $matches[2][$key];
+        }
         if (!empty($annotations[$annotation])) {
-          // Only have the first match per annotation. This deals with
-          // multiple @group annotations.
+          // Only @group is allowed to have more than one annotation, in the
+          // 'groups' key. Other annotations only have one value per key.
           continue;
         }
         $annotations[$annotation] = $matches[2][$key];
@@ -355,7 +361,9 @@ public static function getTestInfo($classname, $doc_comment = NULL) {
       throw new MissingGroupException(sprintf('Missing @group annotation in %s', $classname));
     }
     $info['group'] = $annotations['group'];
-    // Put PHPUnit test suites into their own custom groups.
+    $info['groups'] = $annotations['groups'];
+
+    // Sort out PHPUnit-runnable tests by type.
     if ($testsuite = static::getPhpunitTestSuite($classname)) {
       $info['type'] = 'PHPUnit-' . $testsuite;
     }
diff --git a/core/modules/simpletest/tests/src/Unit/TestInfoParsingTest.php b/core/modules/simpletest/tests/src/Unit/TestInfoParsingTest.php
index df98377d15..9f5f402c8e 100644
--- a/core/modules/simpletest/tests/src/Unit/TestInfoParsingTest.php
+++ b/core/modules/simpletest/tests/src/Unit/TestInfoParsingTest.php
@@ -36,6 +36,7 @@ public function infoParserProvider() {
       [
         'name' => 'Drupal\Tests\simpletest\Unit\TestInfoParsingTest',
         'group' => 'simpletest',
+        'groups' => ['simpletest'],
         'description' => 'Tests \Drupal\simpletest\TestDiscovery.',
         'type' => 'PHPUnit-Unit',
       ],
@@ -49,6 +50,7 @@ public function infoParserProvider() {
       [
         'name' => 'Drupal\Tests\Core\DrupalTest',
         'group' => 'DrupalTest',
+        'groups' => ['DrupalTest'],
         'description' => 'Tests \Drupal.',
         'type' => 'PHPUnit-Unit',
       ],
@@ -62,6 +64,7 @@ public function infoParserProvider() {
       [
         'name' => 'Drupal\FunctionalTests\BrowserTestBaseTest',
         'group' => 'browsertestbase',
+        'groups' => ['browsertestbase'],
         'description' => 'Tests BrowserTestBase functionality.',
         'type' => 'PHPUnit-Functional',
       ],
@@ -75,6 +78,7 @@ public function infoParserProvider() {
       [
         'name' => '\Drupal\Tests\file\Kernel\FileItemValidationTest',
         'group' => 'file',
+        'groups' => ['file'],
         'description' => 'Tests that files referenced in file and image fields are always validated.',
         'type' => 'PHPUnit-Kernel',
       ],
@@ -89,6 +93,7 @@ public function infoParserProvider() {
       [
         'name' => 'Drupal\simpletest\Tests\ExampleSimpleTest',
         'group' => 'simpletest',
+        'groups' => ['simpletest'],
         'description' => 'Tests the Simpletest UI internal browser.',
         'type' => 'Simpletest',
       ],
@@ -109,6 +114,7 @@ public function infoParserProvider() {
       [
         'name' => 'Drupal\simpletest\Tests\ExampleSimpleTest',
         'group' => 'simpletest',
+        'groups' => ['simpletest'],
         'description' => 'Tests the Simpletest UI internal browser.',
         'type' => 'Simpletest',
       ],
@@ -131,6 +137,7 @@ public function infoParserProvider() {
       [
         'name' => 'Drupal\simpletest\Tests\ExampleSimpleTest',
         'group' => 'simpletest',
+        'groups' => ['simpletest'],
         'description' => 'Tests the Simpletest UI internal browser. * @',
         'type' => 'Simpletest',
       ],
@@ -151,6 +158,7 @@ public function infoParserProvider() {
       [
         'name' => 'Drupal\simpletest\Tests\ExampleSimpleTest',
         'group' => 'Test',
+        'groups' => ['Test', 'simpletest'],
         'description' => 'Tests the Simpletest UI internal browser.',
         'type' => 'Simpletest',
       ],
@@ -175,6 +183,7 @@ public function infoParserProvider() {
         'type' => 'Simpletest',
         'requires' => ['module' => ['test']],
         'group' => 'simpletest',
+        'groups' => ['simpletest'],
       ],
       // Classname.
       'Drupal\simpletest\Tests\ExampleSimpleTest',
@@ -197,6 +206,7 @@ public function infoParserProvider() {
         'type' => 'Simpletest',
         'requires' => ['module' => ['test', 'test1', 'test2']],
         'group' => 'simpletest',
+        'groups' => ['simpletest'],
       ],
       // Classname.
       'Drupal\simpletest\Tests\ExampleSimpleTest',
@@ -218,6 +228,7 @@ public function infoParserProvider() {
         'description' => 'Tests the Simpletest UI internal browser. And the summary line continues an there is no gap to the annotation.',
         'type' => 'Simpletest',
         'group' => 'simpletest',
+        'groups' => ['simpletest'],
       ],
       // Classname.
       'Drupal\simpletest\Tests\ExampleSimpleTest',
@@ -316,6 +327,7 @@ public function testGetTestClasses() {
           'name' => 'Drupal\Tests\test_module\Functional\FunctionalExampleTest',
           'description' => 'Test description',
           'group' => 'example',
+          'groups' => ['example'],
           'type' => 'PHPUnit-Functional',
         ],
       ],
@@ -324,12 +336,14 @@ public function testGetTestClasses() {
           'name' => 'Drupal\Tests\test_module\Functional\FunctionalExampleTest2',
           'description' => 'Test description',
           'group' => 'example2',
+          'groups' => ['example2'],
           'type' => 'PHPUnit-Functional',
         ],
         'Drupal\Tests\test_module\Kernel\KernelExampleTest3' => [
           'name' => 'Drupal\Tests\test_module\Kernel\KernelExampleTest3',
           'description' => 'Test description',
           'group' => 'example2',
+          'groups' => ['example2'],
           'type' => 'PHPUnit-Kernel',
         ],
       ],
@@ -360,6 +374,7 @@ public function testGetTestClassesWithSelectedTypes() {
           'name' => 'Drupal\Tests\test_module\Kernel\KernelExampleTest3',
           'description' => 'Test description',
           'group' => 'example2',
+          'groups' => ['example2'],
           'type' => 'PHPUnit-Kernel',
         ],
       ],
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 9e832a5bad..e51ca8f0b8 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -69,7 +69,7 @@
 }
 
 if ($args['list']) {
-  // Display all available tests.
+  // Display all available tests organized by one @group annotation.
   echo "\nAvailable test groups & classes\n";
   echo "-------------------------------\n\n";
   try {
@@ -80,10 +80,24 @@
     echo (string) $e;
     exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION);
   }
-  foreach ($groups as $group => $tests) {
+
+  // A given class can appear in multiple groups. For historical reasons, we
+  // need to present each test only once, under its first group.
+  $classes = [];
+  $list = [];
+  foreach($groups as $group => $tests) {
+    foreach (array_keys($tests) as $class) {
+      if (!in_array($class, $classes)) {
+        $list[$group][] = $class;
+        $classes[] = $class;
+      }
+    }
+  }
+
+  foreach ($list as $group => $tests) {
     echo $group . "\n";
-    foreach ($tests as $class => $info) {
-      echo " - $class\n";
+    foreach ($tests as $test) {
+      echo " - $test\n";
     }
   }
   exit(SIMPLETEST_SCRIPT_EXIT_SUCCESS);
