From 89e64c1fe0ef2bf6395453b372546ae58df9b59b Mon Sep 17 00:00:00 2001
From: Lee Rowlands <lee.rowlands@previousnext.com.au>
Date: Wed, 13 Dec 2017 06:55:11 +1000
Subject: [PATCH] Issue #2928249 by alexpott, mondrake: Introduce a PHPUnit 6+
 compatibility layer for Drupal\Tests\Listeners classes

---
 core/modules/simpletest/src/WebTestBase.php   |  4 +-
 core/phpunit.xml.dist                         |  9 +-
 ...tener.php => DeprecationListenerTrait.php} | 12 ++-
 ...p => DrupalComponentTestListenerTrait.php} | 18 ++--
 .../Drupal/Tests/Listeners/DrupalListener.php | 36 ++++++++
 ...r.php => DrupalStandardsListenerTrait.php} | 59 ++++++++++---
 .../Tests/Listeners/HtmlOutputPrinter.php     | 87 +++++++------------
 .../Listeners/HtmlOutputPrinterTrait.php      | 72 +++++++++++++++
 .../Tests/Listeners/Legacy/DrupalListener.php | 29 +++++++
 .../Listeners/Legacy/HtmlOutputPrinter.php    | 33 +++++++
 .../Tests/TestSuites/TestSuiteBaseTest.php    | 10 ++-
 core/tests/TestSuites/TestSuiteBase.php       | 10 ++-
 12 files changed, 290 insertions(+), 89 deletions(-)
 rename core/tests/Drupal/Tests/Listeners/{DeprecationListener.php => DeprecationListenerTrait.php} (97%)
 rename core/tests/Drupal/Tests/Listeners/{DrupalComponentTestListener.php => DrupalComponentTestListenerTrait.php} (50%)
 create mode 100644 core/tests/Drupal/Tests/Listeners/DrupalListener.php
 rename core/tests/Drupal/Tests/Listeners/{DrupalStandardsListener.php => DrupalStandardsListenerTrait.php} (78%)
 create mode 100644 core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php
 create mode 100644 core/tests/Drupal/Tests/Listeners/Legacy/DrupalListener.php
 create mode 100644 core/tests/Drupal/Tests/Listeners/Legacy/HtmlOutputPrinter.php

diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index 4b6f421e5524..441b2398f4b9 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -18,7 +18,7 @@
 use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
 use Drupal\Tests\EntityViewTrait;
 use Drupal\Tests\block\Traits\BlockCreationTrait as BaseBlockCreationTrait;
-use Drupal\Tests\Listeners\DeprecationListener;
+use Drupal\Tests\Listeners\DeprecationListenerTrait;
 use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
 use Drupal\Tests\node\Traits\NodeCreationTrait;
 use Drupal\Tests\Traits\Core\CronRunTrait;
@@ -698,7 +698,7 @@ protected function curlHeaderCallback($curlHandler, $header) {
       if ($parameters[1] === 'User deprecated function') {
         if (getenv('SYMFONY_DEPRECATIONS_HELPER') !== 'disabled') {
           $message = (string) $parameters[0];
-          if (!in_array($message, DeprecationListener::getSkippedDeprecations())) {
+          if (!in_array($message, DeprecationListenerTrait::getSkippedDeprecations())) {
             call_user_func_array([&$this, 'error'], $parameters);
           }
         }
diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist
index bfe74673b251..750c6e2b502e 100644
--- a/core/phpunit.xml.dist
+++ b/core/phpunit.xml.dist
@@ -49,16 +49,11 @@
     </testsuite>
   </testsuites>
   <listeners>
-    <listener class="\Drupal\Tests\Listeners\DeprecationListener">
+    <listener class="\Drupal\Tests\Listeners\DrupalListener">
     </listener>
-    <!-- The Symfony deprecation listener has to come after the Drupal
-    deprecation listener -->
+    <!-- The Symfony deprecation listener has to come after the Drupal listener -->
     <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener">
     </listener>
-    <listener class="\Drupal\Tests\Listeners\DrupalStandardsListener">
-    </listener>
-    <listener class="\Drupal\Tests\Listeners\DrupalComponentTestListener">
-    </listener>
   </listeners>
   <!-- Filter for coverage reports. -->
   <filter>
diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListener.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
similarity index 97%
rename from core/tests/Drupal/Tests/Listeners/DeprecationListener.php
rename to core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
index 80b3d31c97e1..c73b6e59df40 100644
--- a/core/tests/Drupal/Tests/Listeners/DeprecationListener.php
+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
@@ -9,12 +9,18 @@
  *   This class will be removed once all the deprecation notices have been
  *   fixed.
  */
-class DeprecationListener extends \PHPUnit_Framework_BaseTestListener {
+trait DeprecationListenerTrait {
 
   /**
-   * {@inheritdoc}
+   * Reacts to the end of a test.
+   *
+   * @param \PHPUnit\Framework\Test|\PHPUnit_Framework_Test $test
+   *   The test object that has ended its test run.
+   * @param float $time
+   *   The time the test took.
    */
-  public function endTest(\PHPUnit_Framework_Test $test, $time) {
+  protected function deprecationEndTest($test, $time) {
+    /** @var \PHPUnit\Framework\Test $test */
     // Need to edit the file of deprecations.
     if ($file = getenv('SYMFONY_DEPRECATIONS_SERIALIZE')) {
       $deprecations = file_get_contents($file);
diff --git a/core/tests/Drupal/Tests/Listeners/DrupalComponentTestListener.php b/core/tests/Drupal/Tests/Listeners/DrupalComponentTestListenerTrait.php
similarity index 50%
rename from core/tests/Drupal/Tests/Listeners/DrupalComponentTestListener.php
rename to core/tests/Drupal/Tests/Listeners/DrupalComponentTestListenerTrait.php
index c3496773057b..70fa23604141 100644
--- a/core/tests/Drupal/Tests/Listeners/DrupalComponentTestListener.php
+++ b/core/tests/Drupal/Tests/Listeners/DrupalComponentTestListenerTrait.php
@@ -5,20 +5,28 @@
 use Drupal\KernelTests\KernelTestBase;;
 use Drupal\Tests\BrowserTestBase;;
 use Drupal\Tests\UnitTestCase;
-use PHPUnit\Framework\BaseTestListener;
+use PHPUnit\Framework\AssertionFailedError;
 
 /**
  * Ensures that no component tests are extending a core test base class.
+ *
+ * @internal
  */
-class DrupalComponentTestListener extends BaseTestListener {
+trait DrupalComponentTestListenerTrait {
 
   /**
-   * {@inheritdoc}
+   * Reacts to the end of a test.
+   *
+   * @param \PHPUnit\Framework\Test|\PHPUnit_Framework_Test $test
+   *   The test object that has ended its test run.
+   * @param float $time
+   *   The time the test took.
    */
-  public function endTest(\PHPUnit_Framework_Test $test, $time) {
+  protected function componentEndTest($test, $time) {
+    /** @var \PHPUnit\Framework\Test $test */
     if (substr($test->toString(), 0, 22) == 'Drupal\Tests\Component') {
       if ($test instanceof BrowserTestBase || $test instanceof KernelTestBase || $test instanceof UnitTestCase) {
-        $error = new \PHPUnit_Framework_AssertionFailedError('Component tests should not extend a core test base class.');
+        $error = new AssertionFailedError('Component tests should not extend a core test base class.');
         $test->getTestResultObject()->addFailure($test, $error, $time);
       }
     }
diff --git a/core/tests/Drupal/Tests/Listeners/DrupalListener.php b/core/tests/Drupal/Tests/Listeners/DrupalListener.php
new file mode 100644
index 000000000000..9ed976f76c44
--- /dev/null
+++ b/core/tests/Drupal/Tests/Listeners/DrupalListener.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\Tests\Listeners;
+
+use PHPUnit\Framework\BaseTestListener;
+use PHPUnit\Framework\Test;
+
+if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) {
+  class_alias('Drupal\Tests\Listeners\Legacy\DrupalListener', 'Drupal\Tests\Listeners\DrupalListener');
+  // Using an early return instead of a else does not work when using the
+  // PHPUnit phar due to some weird PHP behavior (the class gets defined without
+  // executing the code before it and so the definition is not properly
+  // conditional).
+}
+else {
+  /**
+   * Listens to PHPUnit test runs.
+   *
+   * @internal
+   */
+  class DrupalListener extends BaseTestListener {
+    use DeprecationListenerTrait;
+    use DrupalComponentTestListenerTrait;
+    use DrupalStandardsListenerTrait;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function endTest(Test $test, $time) {
+      $this->deprecationEndTest($test, $time);
+      $this->componentEndTest($test, $time);
+      $this->standardsEndTest($test, $time);
+    }
+
+  }
+}
diff --git a/core/tests/Drupal/Tests/Listeners/DrupalStandardsListener.php b/core/tests/Drupal/Tests/Listeners/DrupalStandardsListenerTrait.php
similarity index 78%
rename from core/tests/Drupal/Tests/Listeners/DrupalStandardsListener.php
rename to core/tests/Drupal/Tests/Listeners/DrupalStandardsListenerTrait.php
index fd15f8218bc1..2676c46a4fe6 100644
--- a/core/tests/Drupal/Tests/Listeners/DrupalStandardsListener.php
+++ b/core/tests/Drupal/Tests/Listeners/DrupalStandardsListenerTrait.php
@@ -2,15 +2,18 @@
 
 namespace Drupal\Tests\Listeners;
 
-use PHPUnit\Framework\BaseTestListener;
+use PHPUnit\Framework\AssertionFailedError;
 use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\TestSuite;
 
 /**
  * Listens for PHPUnit tests and fails those with invalid coverage annotations.
  *
  * Enforces various coding standards within test runs.
+ *
+ * @internal
  */
-class DrupalStandardsListener extends BaseTestListener {
+trait DrupalStandardsListenerTrait {
 
   /**
    * Signals a coding standards failure to the user.
@@ -21,10 +24,10 @@ class DrupalStandardsListener extends BaseTestListener {
    *   The message to add to the failure notice. The test class name and test
    *   name will be appended to this message automatically.
    */
-  protected function fail(TestCase $test, $message) {
+  private function fail(TestCase $test, $message) {
     // Add the report to the test's results.
     $message .= ': ' . get_class($test) . '::' . $test->getName();
-    $fail = new \PHPUnit_Framework_AssertionFailedError($message);
+    $fail = new AssertionFailedError($message);
     $result = $test->getTestResultObject();
     $result->addFailure($test, $fail, 0);
   }
@@ -38,7 +41,7 @@ protected function fail(TestCase $test, $message) {
    * @return bool
    *   TRUE if the class exists, FALSE otherwise.
    */
-  protected function classExists($class) {
+  private function classExists($class) {
     return class_exists($class, TRUE) || trait_exists($class, TRUE) || interface_exists($class, TRUE);
   }
 
@@ -50,7 +53,7 @@ protected function classExists($class) {
    * @param \PHPUnit\Framework\TestCase $test
    *   The test to examine.
    */
-  public function checkValidCoversForTest(TestCase $test) {
+  private function checkValidCoversForTest(TestCase $test) {
     // If we're generating a coverage report already, don't do anything here.
     if ($test->getTestResultObject() && $test->getTestResultObject()->getCollectCodeCoverageInformation()) {
       return;
@@ -141,7 +144,7 @@ public function checkValidCoversForTest(TestCase $test) {
   }
 
   /**
-   * {@inheritdoc}
+   * Reacts to the end of a test.
    *
    * We must mark this method as belonging to the special legacy group because
    * it might trigger an E_USER_DEPRECATED error during coverage annotation
@@ -151,22 +154,58 @@ public function checkValidCoversForTest(TestCase $test) {
    *
    * @group legacy
    *
+   * @param \PHPUnit\Framework\Test|\PHPUnit_Framework_Test $test
+   *   The test object that has ended its test run.
+   * @param float $time
+   *   The time the test took.
+   *
    * @see http://symfony.com/doc/current/components/phpunit_bridge.html#mark-tests-as-legacy
    */
-  public function endTest(\PHPUnit_Framework_Test $test, $time) {
+  private function doEndTest($test, $time) {
     // \PHPUnit_Framework_Test does not have any useful methods of its own for
     // our purpose, so we have to distinguish between the different known
     // subclasses.
     if ($test instanceof TestCase) {
       $this->checkValidCoversForTest($test);
     }
-    elseif ($test instanceof \PHPUnit_Framework_TestSuite) {
+    elseif ($this->isTestSuite($test)) {
       foreach ($test->getGroupDetails() as $tests) {
         foreach ($tests as $test) {
-          $this->endTest($test, $time);
+          $this->doEndTest($test, $time);
         }
       }
     }
   }
 
+  /**
+   * Determine if a test object is a test suite regardless of PHPUnit version.
+   *
+   * @param \PHPUnit\Framework\Test|\PHPUnit_Framework_Test $test
+   *   The test object to test if it is a test suite.
+   *
+   * @return bool
+   *   TRUE if it is a test suite, FALSE if not.
+   */
+  private function isTestSuite($test) {
+    if (class_exists('\PHPUnit_Framework_TestSuite') && $test instanceof \PHPUnit_Framework_TestSuite) {
+      return TRUE;
+    }
+    if (class_exists('PHPUnit\Framework\TestSuite') && $test instanceof TestSuite) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Reacts to the end of a test.
+   *
+   * @param \PHPUnit\Framework\Test|\PHPUnit_Framework_Test $test
+   *   The test object that has ended its test run.
+   * @param float $time
+   *   The time the test took.
+   */
+  protected function standardsEndTest($test, $time) {
+    $this->doEndTest($test, $time);
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php
index ac22072d1636..80219898682f 100644
--- a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php
+++ b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php
@@ -2,70 +2,41 @@
 
 namespace Drupal\Tests\Listeners;
 
-/**
- * Defines a class for providing html output results for functional tests.
- */
-class HtmlOutputPrinter extends \PHPUnit_TextUI_ResultPrinter {
-
+use PHPUnit\Framework\TestResult;
+use PHPUnit\TextUI\ResultPrinter;
+
+if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) {
+  class_alias('Drupal\Tests\Listeners\Legacy\HtmlOutputPrinter', 'Drupal\Tests\Listeners\HtmlOutputPrinter');
+  // Using an early return instead of a else does not work when using the
+  // PHPUnit phar due to some weird PHP behavior (the class gets defined without
+  // executing the code before it and so the definition is not properly
+  // conditional).
+}
+else {
   /**
-   * File to write html links to.
+   * Defines a class for providing html output results for functional tests.
    *
-   * @var string
+   * @internal
    */
-  protected $browserOutputFile;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct($out, $verbose, $colors, $debug, $numberOfColumns) {
-    parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns);
-    if ($html_output_directory = getenv('BROWSERTEST_OUTPUT_DIRECTORY')) {
-      // Initialize html output debugging.
-      $html_output_directory = rtrim($html_output_directory, '/');
-
-      // Check if directory exists.
-      if (!is_dir($html_output_directory) || !is_writable($html_output_directory)) {
-        $this->writeWithColor('bg-red, fg-black', "HTML output directory $html_output_directory is not a writable directory.");
-      }
-      else {
-        // Convert to a canonicalized absolute pathname just in case the current
-        // working directory is changed.
-        $html_output_directory = realpath($html_output_directory);
-        $this->browserOutputFile = tempnam($html_output_directory, 'browser_output_');
-        if ($this->browserOutputFile) {
-          touch($this->browserOutputFile);
-        }
-        else {
-          $this->writeWithColor('bg-red, fg-black', "Unable to create a temporary file in $html_output_directory.");
-        }
-      }
-    }
-
-    if ($this->browserOutputFile) {
-      putenv('BROWSERTEST_OUTPUT_FILE=' . $this->browserOutputFile);
-    }
-    else {
-      // Remove any environment variable.
-      putenv('BROWSERTEST_OUTPUT_FILE');
+  class HtmlOutputPrinter extends ResultPrinter {
+    use HtmlOutputPrinterTrait;
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct($out = NULL, $verbose = FALSE, $colors = self::COLOR_DEFAULT, $debug = FALSE, $numberOfColumns = 80, $reverse = FALSE) {
+      parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse);
+
+      $this->setUpHtmlOutput();
     }
-  }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function printResult(\PHPUnit_Framework_TestResult $result) {
-    parent::printResult($result);
+    /**
+     * {@inheritdoc}
+     */
+    public function printResult(TestResult $result) {
+      parent::printResult($result);
 
-    if ($this->browserOutputFile) {
-      $contents = file_get_contents($this->browserOutputFile);
-      if ($contents) {
-        $this->writeNewLine();
-        $this->writeWithColor('bg-yellow, fg-black', 'HTML output was generated');
-        $this->write($contents);
-      }
-      // No need to keep the file around any more.
-      unlink($this->browserOutputFile);
+      $this->printHtmlOutput();
     }
-  }
 
+  }
 }
diff --git a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php
new file mode 100644
index 000000000000..1dd67eb9e9a3
--- /dev/null
+++ b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Drupal\Tests\Listeners;
+
+/**
+ * Defines a class for providing html output results for functional tests.
+ *
+ * @internal
+ */
+trait HtmlOutputPrinterTrait {
+
+  /**
+   * File to write html links to.
+   *
+   * @var string
+   */
+  protected $browserOutputFile;
+
+  /**
+   * Creates the file to list the HTML output created during the test.
+   *
+   * @see \Drupal\Tests\BrowserTestBase::initBrowserOutputFile()
+   */
+  protected function setUpHtmlOutput() {
+    if ($html_output_directory = getenv('BROWSERTEST_OUTPUT_DIRECTORY')) {
+      // Initialize html output debugging.
+      $html_output_directory = rtrim($html_output_directory, '/');
+
+      // Check if directory exists.
+      if (!is_dir($html_output_directory) || !is_writable($html_output_directory)) {
+        $this->writeWithColor('bg-red, fg-black', "HTML output directory $html_output_directory is not a writable directory.");
+      }
+      else {
+        // Convert to a canonicalized absolute pathname just in case the current
+        // working directory is changed.
+        $html_output_directory = realpath($html_output_directory);
+        $this->browserOutputFile = tempnam($html_output_directory, 'browser_output_');
+        if ($this->browserOutputFile) {
+          touch($this->browserOutputFile);
+        }
+        else {
+          $this->writeWithColor('bg-red, fg-black', "Unable to create a temporary file in $html_output_directory.");
+        }
+      }
+    }
+
+    if ($this->browserOutputFile) {
+      putenv('BROWSERTEST_OUTPUT_FILE=' . $this->browserOutputFile);
+    }
+    else {
+      // Remove any environment variable.
+      putenv('BROWSERTEST_OUTPUT_FILE');
+    }
+  }
+
+  /**
+   * Prints the list of HTML output generated during the test.
+   */
+  protected function printHtmlOutput() {
+    if ($this->browserOutputFile) {
+      $contents = file_get_contents($this->browserOutputFile);
+      if ($contents) {
+        $this->writeNewLine();
+        $this->writeWithColor('bg-yellow, fg-black', 'HTML output was generated');
+        $this->write($contents);
+      }
+      // No need to keep the file around any more.
+      unlink($this->browserOutputFile);
+    }
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Listeners/Legacy/DrupalListener.php b/core/tests/Drupal/Tests/Listeners/Legacy/DrupalListener.php
new file mode 100644
index 000000000000..f7c2c76668cb
--- /dev/null
+++ b/core/tests/Drupal/Tests/Listeners/Legacy/DrupalListener.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Drupal\Tests\Listeners\Legacy;
+
+use Drupal\Tests\Listeners\DeprecationListenerTrait;
+use Drupal\Tests\Listeners\DrupalComponentTestListenerTrait;
+use Drupal\Tests\Listeners\DrupalStandardsListenerTrait;
+
+/**
+ * Listens to PHPUnit test runs.
+ *
+ * @internal
+ *   This class is not public Drupal API.
+ */
+class DrupalListener extends \PHPUnit_Framework_BaseTestListener {
+  use DeprecationListenerTrait;
+  use DrupalComponentTestListenerTrait;
+  use DrupalStandardsListenerTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function endTest(\PHPUnit_Framework_Test $test, $time) {
+    $this->deprecationEndTest($test, $time);
+    $this->componentEndTest($test, $time);
+    $this->standardsEndTest($test, $time);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Listeners/Legacy/HtmlOutputPrinter.php b/core/tests/Drupal/Tests/Listeners/Legacy/HtmlOutputPrinter.php
new file mode 100644
index 000000000000..7c1f45e38f90
--- /dev/null
+++ b/core/tests/Drupal/Tests/Listeners/Legacy/HtmlOutputPrinter.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Drupal\Tests\Listeners\Legacy;
+
+use Drupal\Tests\Listeners\HtmlOutputPrinterTrait;
+
+/**
+ * Defines a class for providing html output results for functional tests.
+ *
+ * @internal
+ */
+class HtmlOutputPrinter extends \PHPUnit_TextUI_ResultPrinter {
+  use HtmlOutputPrinterTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct($out, $verbose, $colors, $debug, $numberOfColumns) {
+    parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns);
+
+    $this->setUpHtmlOutput();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function printResult(\PHPUnit_Framework_TestResult $result) {
+    parent::printResult($result);
+
+    $this->printHtmlOutput();
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/TestSuites/TestSuiteBaseTest.php b/core/tests/Drupal/Tests/TestSuites/TestSuiteBaseTest.php
index 4c289e4d78bb..41a98419ee03 100644
--- a/core/tests/Drupal/Tests/TestSuites/TestSuiteBaseTest.php
+++ b/core/tests/Drupal/Tests/TestSuites/TestSuiteBaseTest.php
@@ -34,6 +34,13 @@ protected function getFilesystem() {
             ],
             'Tests' => [
               'CoreUnitTest.php' => '<?php',
+              // Ensure that the following files are not found as tests.
+              'Listeners' => [
+                'Listener.php' => '<?php',
+                'Legacy' => [
+                  'Listener.php' => '<?php',
+                ]
+              ]
             ],
           ],
         ],
@@ -91,8 +98,7 @@ public function testAddTestsBySuiteNamespaceCore($filesystem, $suite_namespace,
     $ref_add_tests->invokeArgs($stub, [vfsStream::url('root'), $suite_namespace]);
 
     // Determine if we loaded the expected test files.
-    $this->assertNotEmpty($stub->testFiles);
-    $this->assertEmpty(array_diff_assoc($expected_tests, $stub->testFiles));
+    $this->assertEquals($expected_tests, $stub->testFiles);
   }
 
   /**
diff --git a/core/tests/TestSuites/TestSuiteBase.php b/core/tests/TestSuites/TestSuiteBase.php
index 82a13ba25574..e5925debb901 100644
--- a/core/tests/TestSuites/TestSuiteBase.php
+++ b/core/tests/TestSuites/TestSuiteBase.php
@@ -3,11 +3,12 @@
 namespace Drupal\Tests\TestSuites;
 
 use Drupal\simpletest\TestDiscovery;
+use PHPUnit\Framework\TestSuite;
 
 /**
  * Base class for Drupal test suites.
  */
-abstract class TestSuiteBase extends \PHPUnit_Framework_TestSuite {
+abstract class TestSuiteBase extends TestSuite {
 
   /**
    * Finds extensions in a Drupal installation.
@@ -40,7 +41,12 @@ protected function addTestsBySuiteNamespace($root, $suite_namespace) {
     // always inside of core/tests/Drupal/${suite_namespace}Tests. The exception
     // to this is Unit tests for historical reasons.
     if ($suite_namespace == 'Unit') {
-      $this->addTestFiles(TestDiscovery::scanDirectory("Drupal\\Tests\\", "$root/core/tests/Drupal/Tests"));
+      $tests = TestDiscovery::scanDirectory("Drupal\\Tests\\", "$root/core/tests/Drupal/Tests");
+      $tests = array_filter($tests, function ($test) use ($root) {
+        // The Listeners directory does not contain tests.
+        return !preg_match("@^$root/core/tests/Drupal/Tests/Listeners(/|$)@", dirname($test));
+      });
+      $this->addTestFiles($tests);
     }
     else {
       $this->addTestFiles(TestDiscovery::scanDirectory("Drupal\\${suite_namespace}Tests\\", "$root/core/tests/Drupal/${suite_namespace}Tests"));
-- 
GitLab