Chris@0: getPathname(), '.info.yml') !== FALSE) { Chris@0: // Cut off ".info.yml" from the filename for use as the extension name. We Chris@0: // use getRealPath() so that we can scan extensions represented by Chris@0: // directory aliases. Chris@0: $extensions[substr($dir->getFilename(), 0, -9)] = $dir->getPathInfo() Chris@0: ->getRealPath(); Chris@0: } Chris@0: } Chris@0: return $extensions; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns directories under which contributed extensions may exist. Chris@0: * Chris@0: * @param string $root Chris@0: * (optional) Path to the root of the Drupal installation. Chris@0: * Chris@0: * @return array Chris@0: * An array of directories under which contributed extensions may exist. Chris@0: */ Chris@0: function drupal_phpunit_contrib_extension_directory_roots($root = NULL) { Chris@0: if ($root === NULL) { Chris@0: $root = dirname(dirname(__DIR__)); Chris@0: } Chris@0: $paths = [ Chris@0: $root . '/core/modules', Chris@0: $root . '/core/profiles', Chris@0: $root . '/modules', Chris@0: $root . '/profiles', Chris@0: $root . '/themes', Chris@0: ]; Chris@0: $sites_path = $root . '/sites'; Chris@0: // Note this also checks sites/../modules and sites/../profiles. Chris@0: foreach (scandir($sites_path) as $site) { Chris@0: if ($site[0] === '.' || $site === 'simpletest') { Chris@0: continue; Chris@0: } Chris@0: $path = "$sites_path/$site"; Chris@0: $paths[] = is_dir("$path/modules") ? realpath("$path/modules") : NULL; Chris@0: $paths[] = is_dir("$path/profiles") ? realpath("$path/profiles") : NULL; Chris@0: $paths[] = is_dir("$path/themes") ? realpath("$path/themes") : NULL; Chris@0: } Chris@16: return array_filter($paths, 'file_exists'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Registers the namespace for each extension directory with the autoloader. Chris@0: * Chris@0: * @param array $dirs Chris@0: * An associative array of extension directories, keyed by extension name. Chris@0: * Chris@0: * @return array Chris@0: * An associative array of extension directories, keyed by their namespace. Chris@0: */ Chris@0: function drupal_phpunit_get_extension_namespaces($dirs) { Chris@0: $suite_names = ['Unit', 'Kernel', 'Functional', 'FunctionalJavascript']; Chris@0: $namespaces = []; Chris@0: foreach ($dirs as $extension => $dir) { Chris@0: if (is_dir($dir . '/src')) { Chris@0: // Register the PSR-4 directory for module-provided classes. Chris@0: $namespaces['Drupal\\' . $extension . '\\'][] = $dir . '/src'; Chris@0: } Chris@0: $test_dir = $dir . '/tests/src'; Chris@0: if (is_dir($test_dir)) { Chris@0: foreach ($suite_names as $suite_name) { Chris@0: $suite_dir = $test_dir . '/' . $suite_name; Chris@0: if (is_dir($suite_dir)) { Chris@0: // Register the PSR-4 directory for PHPUnit-based suites. Chris@0: $namespaces['Drupal\\Tests\\' . $extension . '\\' . $suite_name . '\\'][] = $suite_dir; Chris@0: } Chris@0: } Chris@0: // Extensions can have a \Drupal\extension\Traits namespace for Chris@0: // cross-suite trait code. Chris@0: $trait_dir = $test_dir . '/Traits'; Chris@0: if (is_dir($trait_dir)) { Chris@0: $namespaces['Drupal\\Tests\\' . $extension . '\\Traits\\'][] = $trait_dir; Chris@0: } Chris@0: } Chris@0: } Chris@0: return $namespaces; Chris@0: } Chris@0: Chris@0: // We define the COMPOSER_INSTALL constant, so that PHPUnit knows where to Chris@0: // autoload from. This is needed for tests run in isolation mode, because Chris@0: // phpunit.xml.dist is located in a non-default directory relative to the Chris@0: // PHPUnit executable. Chris@0: if (!defined('PHPUNIT_COMPOSER_INSTALL')) { Chris@0: define('PHPUNIT_COMPOSER_INSTALL', __DIR__ . '/../../autoload.php'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Populate class loader with additional namespaces for tests. Chris@0: * Chris@0: * We run this in a function to avoid setting the class loader to a global Chris@0: * that can change. This change can cause unpredictable false positives for Chris@0: * phpunit's global state change watcher. The class loader can be retrieved from Chris@0: * composer at any time by requiring autoload.php. Chris@0: */ Chris@0: function drupal_phpunit_populate_class_loader() { Chris@0: Chris@0: /** @var \Composer\Autoload\ClassLoader $loader */ Chris@0: $loader = require __DIR__ . '/../../autoload.php'; Chris@0: Chris@0: // Start with classes in known locations. Chris@0: $loader->add('Drupal\\Tests', __DIR__); Chris@17: $loader->add('Drupal\\TestSite', __DIR__); Chris@0: $loader->add('Drupal\\KernelTests', __DIR__); Chris@0: $loader->add('Drupal\\FunctionalTests', __DIR__); Chris@0: $loader->add('Drupal\\FunctionalJavascriptTests', __DIR__); Chris@0: Chris@0: if (!isset($GLOBALS['namespaces'])) { Chris@0: // Scan for arbitrary extension namespaces from core and contrib. Chris@0: $extension_roots = drupal_phpunit_contrib_extension_directory_roots(); Chris@0: Chris@0: $dirs = array_map('drupal_phpunit_find_extension_directories', $extension_roots); Chris@0: $dirs = array_reduce($dirs, 'array_merge', []); Chris@0: $GLOBALS['namespaces'] = drupal_phpunit_get_extension_namespaces($dirs); Chris@0: } Chris@0: foreach ($GLOBALS['namespaces'] as $prefix => $paths) { Chris@0: $loader->addPsr4($prefix, $paths); Chris@0: } Chris@0: Chris@0: return $loader; Chris@0: }; Chris@0: Chris@0: // Do class loader population. Chris@0: drupal_phpunit_populate_class_loader(); Chris@0: Chris@14: // Ensure we have the correct PHPUnit version for the version of PHP. Chris@14: if (class_exists('\PHPUnit_Runner_Version')) { Chris@14: $phpunit_version = \PHPUnit_Runner_Version::id(); Chris@14: } Chris@14: else { Chris@14: $phpunit_version = Version::id(); Chris@14: } Chris@14: if (!Composer::upgradePHPUnitCheck($phpunit_version)) { Chris@17: $message = "PHPUnit testing framework version 6 or greater is required when running on PHP 7.0 or greater. Run the command 'composer run-script drupal-phpunit-upgrade' in order to fix this."; Chris@14: echo "\033[31m" . $message . "\n\033[0m"; Chris@14: exit(1); Chris@14: } Chris@14: Chris@0: // Set sane locale settings, to ensure consistent string, dates, times and Chris@0: // numbers handling. Chris@0: // @see \Drupal\Core\DrupalKernel::bootEnvironment() Chris@0: setlocale(LC_ALL, 'C'); Chris@0: Chris@17: // Set appropriate configuration for multi-byte strings. Chris@17: mb_internal_encoding('utf-8'); Chris@17: mb_language('uni'); Chris@17: Chris@0: // Set the default timezone. While this doesn't cause any tests to fail, PHP Chris@0: // complains if 'date.timezone' is not set in php.ini. The Australia/Sydney Chris@0: // timezone is chosen so all tests are run using an edge case scenario (UTC+10 Chris@0: // and DST). This choice is made to prevent timezone related regressions and Chris@0: // reduce the fragility of the testing system in general. Chris@0: date_default_timezone_set('Australia/Sydney'); Chris@0: Chris@0: // Runtime assertions. PHPUnit follows the php.ini assert.active setting for Chris@0: // runtime assertions. By default this setting is on. Here we make a call to Chris@0: // make PHP 5 and 7 handle assertion failures the same way, but this call does Chris@0: // not turn runtime assertions on if they weren't on already. Chris@0: Handle::register(); Chris@14: Chris@14: // PHPUnit 4 to PHPUnit 6 bridge. Tests written for PHPUnit 4 need to work on Chris@14: // PHPUnit 6 with a minimum of fuss. Chris@14: if (version_compare($phpunit_version, '6.1', '>=')) { Chris@14: class_alias('\PHPUnit\Framework\AssertionFailedError', '\PHPUnit_Framework_AssertionFailedError'); Chris@14: class_alias('\PHPUnit\Framework\Constraint\Count', '\PHPUnit_Framework_Constraint_Count'); Chris@14: class_alias('\PHPUnit\Framework\Error\Error', '\PHPUnit_Framework_Error'); Chris@14: class_alias('\PHPUnit\Framework\Error\Warning', '\PHPUnit_Framework_Error_Warning'); Chris@14: class_alias('\PHPUnit\Framework\ExpectationFailedException', '\PHPUnit_Framework_ExpectationFailedException'); Chris@14: class_alias('\PHPUnit\Framework\Exception', '\PHPUnit_Framework_Exception'); Chris@14: class_alias('\PHPUnit\Framework\MockObject\Matcher\InvokedRecorder', '\PHPUnit_Framework_MockObject_Matcher_InvokedRecorder'); Chris@14: class_alias('\PHPUnit\Framework\SkippedTestError', '\PHPUnit_Framework_SkippedTestError'); Chris@14: class_alias('\PHPUnit\Framework\TestCase', '\PHPUnit_Framework_TestCase'); Chris@14: class_alias('\PHPUnit\Util\Test', '\PHPUnit_Util_Test'); Chris@17: class_alias('\PHPUnit\Util\Xml', '\PHPUnit_Util_XML'); Chris@14: }