diff --git a/core/modules/serialization/tests/src/EntityResolver/UuidResolverTest.php b/core/modules/serialization/tests/src/EntityResolver/UuidResolverTest.php index c41eea8..9d49dce 100644 --- a/core/modules/serialization/tests/src/EntityResolver/UuidResolverTest.php +++ b/core/modules/serialization/tests/src/EntityResolver/UuidResolverTest.php @@ -2,10 +2,10 @@ /** * @file - * Contains \Drupal\serialization\Tests\Normalizer\ListNormalizerTest. + * Contains \Drupal\serialization\Tests\EntityResolver\UuidResolverTest. */ -namespace Drupal\serialization\Tests\Normalizer; +namespace Drupal\serialization\Tests\EntityResolver; use Drupal\Tests\UnitTestCase; use Drupal\serialization\EntityResolver\UuidResolver; diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index 447fefc..1d70f35 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -435,14 +435,14 @@ function simpletest_log_read($test_id, $database_prefix, $test_class) { * @endcode */ function simpletest_test_get_all($module = NULL) { - return \Drupal::service('test_discovery')->find(); + return \Drupal::service('test_discovery')->getTestClasses(); } /** * Registers namespaces for disabled modules. */ function simpletest_classloader_register() { - \Drupal::service('test_discovery')->registerTestClasses(); + \Drupal::service('test_discovery')->registerTestNamespaces(); } /** diff --git a/core/modules/simpletest/src/TestDiscovery.php b/core/modules/simpletest/src/TestDiscovery.php index f1b8ebc..f3f7fa9 100644 --- a/core/modules/simpletest/src/TestDiscovery.php +++ b/core/modules/simpletest/src/TestDiscovery.php @@ -17,18 +17,18 @@ class TestDiscovery { /** - * Cached class map of all test classes discovered in extensions. + * Cached map of all test namespaces to respective directories. * * @var array */ - protected static $classmap; + protected $testNamespaces; /** - * Cached list of all discovered tests. + * Cached list of all discovered test classes. * * @var array */ - protected $allTests; + protected $testClasses; /** * Cached list of available extension names, keyed by extension type. @@ -38,6 +38,61 @@ class TestDiscovery { protected $availableExtensions; /** + * Registers test namespaces of all available extensions. + * + * @return array + * An associative array whose keys are PSR-4 namespace prefixes and whose + * values are directory names. + * + * @todo Inject class loader. + */ + public function registerTestNamespaces() { + if (isset($this->testNamespaces)) { + return $this->testNamespaces; + } + $this->testNamespaces = array(); + + $loader = drupal_classloader(); + $existing = $loader->getPrefixes(); + $old = array(); + $new = array(); + + // Add PHPUnit test namespace of Drupal core. + $new['Drupal\\Tests\\'] = DRUPAL_ROOT . '/core/tests/Drupal/Tests'; + + $this->availableExtensions = array(); + foreach ($this->getExtensions() as $name => $extension) { + $this->availableExtensions[$extension->getType()][$name] = $name; + + $base_namespace = "Drupal\\$name\\"; + $base_path = DRUPAL_ROOT . '/' . $extension->getPath(); + + // Add namespace of disabled/uninstalled extensions. + if (!isset($existing[$base_namespace])) { + $new[$base_namespace] = "$base_path/src"; + } + else { + $old[$base_namespace] = reset($existing[$base_namespace]); + } + // Add PHPUnit test namespace. + // @todo Move PHPUnit namespace of extensions into Drupal\Tests\$name. + // @see https://drupal.org/node/2260121 + $new[$base_namespace . 'Tests\\'] = "$base_path/tests/src"; + + // While being there, prime drupal_get_filename(). + // @todo Remove this. + drupal_get_filename($extension->getType(), $name, $extension->getPathname()); + } + + foreach ($new as $prefix => $path) { + $loader->addPsr4($prefix, $path); + } + + $this->testNamespaces = $new + $old; + return $this->testNamespaces; + } + + /** * Discovers all available tests in all extensions. * * @return array @@ -53,18 +108,12 @@ class TestDiscovery { * ); * @endcode */ - public function find() { - if (isset($this->allTests)) { - return $this->allTests; + public function getTestClasses() { + if (isset($this->testClasses)) { + return $this->testClasses; } - // Register test namespaces and test classes of all available extensions. - // (including disabled) - $classes = $this->registerTestClasses(); - - // Check that each class has a getInfo() method and store the information - // in an array keyed with the group specified in the test information. - $groups = array(); - foreach ($classes as $classname => $pathname) { + $this->testClasses = array(); + foreach ($this->findTestClasses() as $classname => $pathname) { try { $class = new \ReflectionClass($classname); } @@ -83,7 +132,7 @@ public function find() { } $info = static::getTestInfo($class); - // If this test class requires a non-existing module, skip it. + // Skip this test class if it requires unavailable modules. if (!empty($info['requires']['module'])) { if (array_diff($info['requires']['module'], $this->availableExtensions['module'])) { continue; @@ -94,20 +143,80 @@ public function find() { // throws a PHPUnit_Framework_Warning exception. // @see simpletest_phpunit_get_available_tests() - $groups[$info['group']][$classname] = $info; + $this->testClasses[$info['group']][$classname] = $info; } // Sort the groups and tests within the groups by name. - uksort($groups, 'strnatcasecmp'); - foreach ($groups as &$tests) { + uksort($this->testClasses, 'strnatcasecmp'); + foreach ($this->testClasses as &$tests) { uksort($tests, 'strnatcasecmp'); } // Allow modules extending core tests to disable originals. - \Drupal::moduleHandler()->alter('simpletest', $groups); + \Drupal::moduleHandler()->alter('simpletest', $this->testClasses); - $this->allTests = $groups; - return $groups; + return $this->testClasses; + } + + /** + * Discovers all test classes in all available extensions. + * + * @return array + * A classmap containing all discovered test classes. + */ + protected function findTestClasses() { + $classmap = array(); + foreach ($this->registerTestNamespaces() as $namespace => $path) { + if (!is_dir($path)) { + continue; + } + $classmap += static::scanDirectory($namespace, $path); + } + return $classmap; + } + + /** + * Scans a given directory for class files. + * + * @param string $namespace_prefix + * The namespace prefix to use for discovered classes. Must contain a + * trailing namespace separator (backslash). + * For example: 'Drupal\\node\\Tests\\' + * @param string $path + * The directory path to scan. + * For example: '/path/to/drupal/core/modules/node/tests/src' + * + * @return array + * An associative array whose keys are fully-qualified class names and whose + * values are corresponding filesystem pathnames. + */ + public static function scanDirectory($namespace_prefix, $path) { + if (substr($namespace_prefix, -1) !== '\\') { + throw new \InvalidArgumentException("Namespace prefix for $path must contain a trailing namespace separator."); + } + $flags = \FilesystemIterator::UNIX_PATHS; + $flags |= \FilesystemIterator::SKIP_DOTS; + $flags |= \FilesystemIterator::FOLLOW_SYMLINKS; + $flags |= \FilesystemIterator::CURRENT_AS_SELF; + + $iterator = new \RecursiveDirectoryIterator($path, $flags); + $filter = new \RecursiveCallbackFilterIterator($iterator, function ($current, $key, $iterator) { + if ($iterator->hasChildren()) { + return TRUE; + } + return $current->isFile() && $current->getExtension() === 'php'; + }); + $files = new \RecursiveIteratorIterator($filter); + $classes = array(); + foreach ($files as $fileinfo) { + $class = $namespace_prefix; + if ('' !== $subpath = $fileinfo->getSubPath()) { + $class .= strtr($subpath, '/', '\\') . '\\'; + } + $class .= $fileinfo->getBasename('.php'); + $classes[$class] = $fileinfo->getPathname(); + } + return $classes; } /** @@ -240,144 +349,6 @@ public static function parseTestClassAnnotations(\ReflectionClass $class) { } /** - * Registers test classes for all available extensions. - * - * @return array - * A classmap containing all discovered test classes, which has been added - * to the classloader already. - * - * @see \Drupal\simpletest\TestDiscovery::getClassmap() - * - * @todo Inject class loader. - */ - public function registerTestClasses() { - if (isset(static::$classmap)) { - return static::$classmap; - } - static::$classmap = array(); - $this->availableExtensions = array(); - - $loader = drupal_classloader(); - $existing = $loader->getPrefixes(); - - // Discover PHPUnit tests of core. - static::$classmap += static::scanDirectory(DRUPAL_ROOT . '/core/tests/Drupal/Tests', 'Drupal\\Tests\\'); - - // Safety net: Register PHPUnit test namespace. - // @todo Remove this; obsolete. - $loader->add('Drupal\\Tests', DRUPAL_ROOT . '/core/tests'); - - $extensions = $this->getExtensions(); - foreach ($extensions as $name => $extension) { - $this->availableExtensions[$extension->getType()][$name] = $name; - - $base_namespace = "Drupal\\$name\\"; - $base_path = DRUPAL_ROOT . '/' . $extension->getPath(); - - // Register the namespace of disabled/uninstalled extensions. - if (!isset($existing[$base_namespace])) { - $loader->addPsr4($base_namespace, array( - "$base_path/lib/Drupal/$name", - "$base_path/src", - )); - } - // While being there, prime drupal_get_filename(). - drupal_get_filename($extension->getType(), $name, $extension->getPathname()); - - // Discover all test classes and add them to the classmap. - $test_paths = array( - "$base_path/lib/Drupal/$name/Tests", - "$base_path/src/Tests", - "$base_path/tests/Drupal/$name/Tests", - "$base_path/tests/src", - ); - $test_namespace = $base_namespace . 'Tests\\'; - foreach ($test_paths as $test_path) { - if (!is_dir($test_path)) { - continue; - } - static::$classmap += static::scanDirectory($test_path, $test_namespace); - } - - // Safety net: Register PHPUnit test namespace. - // @todo Remove this; obsolete. - $loader->addPsr4($base_namespace . 'Tests\\', array( - "$base_path/tests/Drupal/$name/Tests", - "$base_path/tests/src", - )); - } - $loader->addClassMap(static::$classmap); - - return static::$classmap; - } - - /** - * Scans a given directory for class files. - * - * @param string $path - * The directory path to scan. - * For example: '/path/to/drupal/core/modules/node/tests/src' - * @param string $namespace_prefix - * The namespace prefix to use for discovered classes. Must contain a - * trailing namespace separator (backslash). - * For example: 'Drupal\\node\\Tests\\' - * - * @return array - * An associative array whose keys are fully-qualified class names and whose - * values are corresponding filesystem pathnames. - */ - public static function scanDirectory($path, $namespace_prefix) { - if (substr($namespace_prefix, -1) !== '\\') { - throw new \InvalidArgumentException("Namespace prefix for $path must contain a trailing namespace separator."); - } - $flags = \FilesystemIterator::UNIX_PATHS; - $flags |= \FilesystemIterator::SKIP_DOTS; - $flags |= \FilesystemIterator::FOLLOW_SYMLINKS; - $flags |= \FilesystemIterator::CURRENT_AS_SELF; - - $iterator = new \RecursiveDirectoryIterator($path, $flags); - $filter = new \RecursiveCallbackFilterIterator($iterator, function ($current, $key, $iterator) { - if ($iterator->hasChildren()) { - return TRUE; - } - return $current->isFile() && $current->getExtension() === 'php'; - }); - $files = new \RecursiveIteratorIterator($filter); - $classes = array(); - foreach ($files as $fileinfo) { - $class = $namespace_prefix; - if ('' !== $subpath = $fileinfo->getSubPath()) { - $class .= strtr($subpath, '/', '\\') . '\\'; - } - $class .= $fileinfo->getBasename('.php'); - $classes[$class] = $fileinfo->getPathname(); - } - return $classes; - } - - /** - * Returns the previously discovered classmap, if any. - * - * @return array - */ - public function getClassmap() { - return static::$classmap; - } - - /** - * Sets the classmap. - * - * @param array $new_classmap - * The new classmap. - * - * @return $this - */ - public function setClassmap($new_classmap) { - static::$classmap = $new_classmap; - return $this; - } - - /** * Returns all available extensions. * * @return \Drupal\Core\Extension\Extension[] diff --git a/core/modules/system/src/Tests/Page/DefaultMetatagsTest.php b/core/modules/system/src/Tests/Page/DefaultMetatagsTest.php index baae991..3b2afdf 100644 --- a/core/modules/system/src/Tests/Page/DefaultMetatagsTest.php +++ b/core/modules/system/src/Tests/Page/DefaultMetatagsTest.php @@ -9,6 +9,9 @@ use Drupal\simpletest\WebTestBase; +/** + * Tests default HTML metatags on a page. + */ class DefaultMetatagsTest extends WebTestBase { /** diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index 88e9822..b99ef44 100755 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -11,7 +11,7 @@ use Drupal\Core\Test\TestKernel; use Symfony\Component\HttpFoundation\Request; -$loader = require_once __DIR__ . '/../vendor/autoload.php'; +require_once __DIR__ . '/../vendor/autoload.php'; const SIMPLETEST_SCRIPT_COLOR_PASS = 32; // Green. const SIMPLETEST_SCRIPT_COLOR_FAIL = 31; // Red. @@ -29,33 +29,12 @@ simpletest_script_bootstrap(); if ($args['execute-test']) { - // @todo A test classmap is actually unnecessary for running a single test. - // Instead, an "extension map" would be much more effective, in order to - // prime ExtensionDiscovery's static cache ahead of time. - // Oh, and likewise InfoParser's. - $start = microtime(TRUE); - $classmap = json_decode(file_get_contents('test/test.classmap.json'), TRUE); - $loader->addClassMap($classmap); - simpletest_script_bootstrap(); -// $loader = drupal_classloader(); -// \Drupal::service('test_discovery')->setClassmap($classmap); - echo var_dump(microtime(TRUE) - $start), "\n"; - simpletest_script_setup_database(); simpletest_script_run_one_test($args['test-id'], $args['execute-test']); // Sub-process exited already; this is just for clarity. exit; } -if (!is_dir('./test')) { - mkdir('test', 0755); -} -$start = microtime(TRUE); -simpletest_classloader_register(); -$classmap = \Drupal::service('test_discovery')->getClassmap(); -file_put_contents('test/test.classmap.json', json_encode($classmap, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); -echo var_dump(microtime(TRUE) - $start), "\n"; - simpletest_script_setup_database(TRUE); if ($args['clean']) { @@ -416,6 +395,8 @@ function simpletest_script_bootstrap() { $module_handler = $container->get('module_handler'); $module_handler->loadAll(); + + simpletest_classloader_register(); } /** diff --git a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageReadOnlyTest.php b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageReadOnlyTest.php index 4d4bc0f..a907a07 100644 --- a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageReadOnlyTest.php +++ b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageReadOnlyTest.php @@ -2,7 +2,7 @@ /** * @file - * Contains \Drupal\Tests\Component\PhpStorage\FileStorageTest. + * Contains \Drupal\Tests\Component\PhpStorage\FileStorageReadOnlyTest. */ namespace Drupal\Tests\Component\PhpStorage; @@ -18,7 +18,7 @@ * * @coversDefaultClass \Drupal\Component\PhpStorage\FileReadOnlyStorage */ -class FileStorageReadyOnlyTest extends PhpStorageTestBase { +class FileStorageReadOnlyTest extends PhpStorageTestBase { /** * Standard test settings to pass to storage instances. diff --git a/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php b/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php index e30f551..2fa198e 100644 --- a/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php +++ b/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php @@ -2,7 +2,7 @@ /** * @file - * Contains \Drupal\Tests\Core\Logger\LogMessageParserTraitTest + * Contains \Drupal\Tests\Core\Logger\LogMessageParserTest. */ namespace Drupal\Tests\Core\Logger; diff --git a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php index 82e20b2..8bbe4ab 100644 --- a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php +++ b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php @@ -2,10 +2,10 @@ /** * @file - * Contains \Drupal\Tests\Core\Logger\LoggerChannelFactoryTest + * Contains \Drupal\Tests\Core\Logger\LoggerChannelFactoryTest. */ -namespace Drupal\Tests\Logger; +namespace Drupal\Tests\Core\Logger; use Drupal\Core\Logger\LoggerChannelFactory; use Drupal\Core\Session\AccountInterface; diff --git a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php index 2cc0a50..51c7e86 100644 --- a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php +++ b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php @@ -2,10 +2,10 @@ /** * @file - * Contains \Drupal\Tests\Core\Logger\LoggerChannelTest + * Contains \Drupal\Tests\Core\Logger\LoggerChannelTest. */ -namespace Drupal\Tests\Logger; +namespace Drupal\Tests\Core\Logger; use Drupal\Core\Logger\LoggerChannel; use Drupal\Core\Session\AccountInterface; diff --git a/core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php b/core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php index f8d5c0e..bffc88d 100644 --- a/core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php @@ -2,10 +2,10 @@ /** * @file - * Contains \Drupal\Tests\Core\ExternalUrlTest. + * Contains \Drupal\Tests\Core\Path\AliasManagerTest. */ -namespace Drupal\Tests\Core; +namespace Drupal\Tests\Core\Path; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Language\Language;