bootstrap.php
Same filename in other branches
Autoloader for Drupal PHPUnit testing.
See also
phpunit.xml.dist
File
-
core/
tests/ bootstrap.php
View source
<?php
/**
* @file
* Autoloader for Drupal PHPUnit testing.
*
* @see phpunit.xml.dist
*/
use Drupal\Component\Assertion\Handle;
use Drupal\TestTools\PhpUnitCompatibility\PhpUnit8\ClassWriter;
/**
* Finds all valid extension directories recursively within a given directory.
*
* @param string $scan_directory
* The directory that should be recursively scanned.
*
* @return array
* An associative array of extension directories found within the scanned
* directory, keyed by extension name.
*/
function drupal_phpunit_find_extension_directories($scan_directory) {
$extensions = [];
$dirs = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($scan_directory, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS));
foreach ($dirs as $dir) {
if (strpos($dir->getPathname(), '.info.yml') !== FALSE) {
// Cut off ".info.yml" from the filename for use as the extension name. We
// use getRealPath() so that we can scan extensions represented by
// directory aliases.
$extensions[substr($dir->getFilename(), 0, -9)] = $dir->getPathInfo()
->getRealPath();
}
}
return $extensions;
}
/**
* Returns directories under which contributed extensions may exist.
*
* @param string $root
* (optional) Path to the root of the Drupal installation.
*
* @return array
* An array of directories under which contributed extensions may exist.
*/
function drupal_phpunit_contrib_extension_directory_roots($root = NULL) {
if ($root === NULL) {
$root = dirname(__DIR__, 2);
}
$paths = [
$root . '/core/modules',
$root . '/core/profiles',
$root . '/core/themes',
$root . '/modules',
$root . '/profiles',
$root . '/themes',
];
$sites_path = $root . '/sites';
// Note this also checks sites/../modules and sites/../profiles.
foreach (scandir($sites_path) as $site) {
if ($site[0] === '.' || $site === 'simpletest') {
continue;
}
$path = "{$sites_path}/{$site}";
$paths[] = is_dir("{$path}/modules") ? realpath("{$path}/modules") : NULL;
$paths[] = is_dir("{$path}/profiles") ? realpath("{$path}/profiles") : NULL;
$paths[] = is_dir("{$path}/themes") ? realpath("{$path}/themes") : NULL;
}
return array_filter($paths);
}
/**
* Registers the namespace for each extension directory with the autoloader.
*
* @param array $dirs
* An associative array of extension directories, keyed by extension name.
*
* @return array
* An associative array of extension directories, keyed by their namespace.
*/
function drupal_phpunit_get_extension_namespaces($dirs) {
$suite_names = [
'Unit',
'Kernel',
'Functional',
'Build',
'FunctionalJavascript',
];
$namespaces = [];
foreach ($dirs as $extension => $dir) {
if (is_dir($dir . '/src')) {
// Register the PSR-4 directory for module-provided classes.
$namespaces['Drupal\\' . $extension . '\\'][] = $dir . '/src';
}
$test_dir = $dir . '/tests/src';
if (is_dir($test_dir)) {
foreach ($suite_names as $suite_name) {
$suite_dir = $test_dir . '/' . $suite_name;
if (is_dir($suite_dir)) {
// Register the PSR-4 directory for PHPUnit-based suites.
$namespaces['Drupal\\Tests\\' . $extension . '\\' . $suite_name . '\\'][] = $suite_dir;
}
}
// Extensions can have a \Drupal\Tests\extension\Traits namespace for
// cross-suite trait code.
$trait_dir = $test_dir . '/Traits';
if (is_dir($trait_dir)) {
$namespaces['Drupal\\Tests\\' . $extension . '\\Traits\\'][] = $trait_dir;
}
}
}
return $namespaces;
}
// We define the COMPOSER_INSTALL constant, so that PHPUnit knows where to
// autoload from. This is needed for tests run in isolation mode, because
// phpunit.xml.dist is located in a non-default directory relative to the
// PHPUnit executable.
if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
define('PHPUNIT_COMPOSER_INSTALL', __DIR__ . '/../../autoload.php');
}
/**
* Populate class loader with additional namespaces for tests.
*
* We run this in a function to avoid setting the class loader to a global
* that can change. This change can cause unpredictable false positives for
* phpunit's global state change watcher. The class loader can be retrieved from
* composer at any time by requiring autoload.php.
*/
function drupal_phpunit_populate_class_loader() {
/** @var \Composer\Autoload\ClassLoader $loader */
$loader = (require __DIR__ . '/../../autoload.php');
// Start with classes in known locations.
$loader->add('Drupal\\BuildTests', __DIR__);
$loader->add('Drupal\\Tests', __DIR__);
$loader->add('Drupal\\TestSite', __DIR__);
$loader->add('Drupal\\KernelTests', __DIR__);
$loader->add('Drupal\\FunctionalTests', __DIR__);
$loader->add('Drupal\\FunctionalJavascriptTests', __DIR__);
$loader->add('Drupal\\TestTools', __DIR__);
if (!isset($GLOBALS['namespaces'])) {
// Scan for arbitrary extension namespaces from core and contrib.
$extension_roots = drupal_phpunit_contrib_extension_directory_roots();
$dirs = array_map('drupal_phpunit_find_extension_directories', $extension_roots);
$dirs = array_reduce($dirs, 'array_merge', []);
$GLOBALS['namespaces'] = drupal_phpunit_get_extension_namespaces($dirs);
}
foreach ($GLOBALS['namespaces'] as $prefix => $paths) {
$loader->addPsr4($prefix, $paths);
}
return $loader;
}
// Do class loader population.
$loader = drupal_phpunit_populate_class_loader();
class_alias('\\Drupal\\Tests\\DocumentElement', '\\Behat\\Mink\\Element\\DocumentElement', TRUE);
ClassWriter::mutateTestBase($loader);
// Set sane locale settings, to ensure consistent string, dates, times and
// numbers handling.
// @see \Drupal\Core\DrupalKernel::bootEnvironment()
setlocale(LC_ALL, 'C');
// Set appropriate configuration for multi-byte strings.
mb_internal_encoding('utf-8');
mb_language('uni');
// Set the default timezone. While this doesn't cause any tests to fail, PHP
// complains if 'date.timezone' is not set in php.ini. The Australia/Sydney
// timezone is chosen so all tests are run using an edge case scenario (UTC+10
// and DST). This choice is made to prevent timezone related regressions and
// reduce the fragility of the testing system in general.
date_default_timezone_set('Australia/Sydney');
// Runtime assertions. PHPUnit follows the php.ini assert.active setting for
// runtime assertions. By default this setting is on. Ensure exceptions are
// thrown if an assert fails, but this call does not turn runtime assertions on
// if they weren't on already.
Handle::register();
// Drupal 9 uses PHP syntax that's deprecated in PHP 8.2. Therefore, for
// PHP 8.2, the error handler registered in
// \Drupal\Tests\Listeners\DeprecationListenerTrait::registerErrorHandler()
// ignores E_DEPRECATED errors. However, that error handler is not used
// during:
// - The execution of data providers.
// - The child process of tests that are executed in a separate (isolated)
// process, such as kernel tests.
//
// To silence E_DEPRECATED errors during the above, re-register the error
// handler that has already been set by symfony/phpunit-bridge/bootstrap.php,
// but constrain it to not get called for E_DEPRECATED errors. It is still
// useful for E_USER_DEPRECATED and other error types.
if (PHP_VERSION_ID >= 80200) {
// Get the current error handler without changing it.
$error_handler = set_error_handler(NULL);
restore_error_handler();
if ($error_handler) {
// Replace the current error handler with the same one, but filter out
// E_DEPRECATED errors.
restore_error_handler();
set_error_handler($error_handler, E_ALL & ~E_DEPRECATED);
// The above results in PHP's built-in error handler getting called for
// E_DEPRECATED errors. Silence those errors too. Note that
// \PHPUnit\TextUI\TestRunner::handleConfiguration() can get called twice
// (once before running data providers, then again before running tests),
// whereas tests/bootstrap.php is included via include_once(), so the 2nd
// handleConfiguration() call can reset error_reporting to the value in
// phpunit.xml. However, that's okay, because for those cases, the error
// handler registered in
// \Drupal\Tests\Listeners\DeprecationListenerTrait::registerErrorHandler()
// is in use, and it ignores E_DEPRECATED errors for PHP 8.2 regardless of
// the error_reporting value.
error_reporting(error_reporting() & ~E_DEPRECATED);
}
}
Functions
Title | Deprecated | Summary |
---|---|---|
drupal_phpunit_contrib_extension_directory_roots | Returns directories under which contributed extensions may exist. | |
drupal_phpunit_find_extension_directories | Finds all valid extension directories recursively within a given directory. | |
drupal_phpunit_get_extension_namespaces | Registers the namespace for each extension directory with the autoloader. | |
drupal_phpunit_populate_class_loader | Populate class loader with additional namespaces for tests. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.