bootstrap.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <?php
  2. /**
  3. * @file
  4. * Autoloader for Drupal PHPUnit testing.
  5. *
  6. * @see phpunit.xml.dist
  7. */
  8. use Drupal\Component\Assertion\Handle;
  9. /**
  10. * Finds all valid extension directories recursively within a given directory.
  11. *
  12. * @param string $scan_directory
  13. * The directory that should be recursively scanned.
  14. * @return array
  15. * An associative array of extension directories found within the scanned
  16. * directory, keyed by extension name.
  17. */
  18. function drupal_phpunit_find_extension_directories($scan_directory) {
  19. $extensions = [];
  20. $dirs = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($scan_directory, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS));
  21. foreach ($dirs as $dir) {
  22. if (strpos($dir->getPathname(), '.info.yml') !== FALSE) {
  23. // Cut off ".info.yml" from the filename for use as the extension name. We
  24. // use getRealPath() so that we can scan extensions represented by
  25. // directory aliases.
  26. $extensions[substr($dir->getFilename(), 0, -9)] = $dir->getPathInfo()
  27. ->getRealPath();
  28. }
  29. }
  30. return $extensions;
  31. }
  32. /**
  33. * Returns directories under which contributed extensions may exist.
  34. *
  35. * @param string $root
  36. * (optional) Path to the root of the Drupal installation.
  37. *
  38. * @return array
  39. * An array of directories under which contributed extensions may exist.
  40. */
  41. function drupal_phpunit_contrib_extension_directory_roots($root = NULL) {
  42. if ($root === NULL) {
  43. $root = dirname(dirname(__DIR__));
  44. }
  45. $paths = [
  46. $root . '/core/modules',
  47. $root . '/core/profiles',
  48. $root . '/modules',
  49. $root . '/profiles',
  50. $root . '/themes',
  51. ];
  52. $sites_path = $root . '/sites';
  53. // Note this also checks sites/../modules and sites/../profiles.
  54. foreach (scandir($sites_path) as $site) {
  55. if ($site[0] === '.' || $site === 'simpletest') {
  56. continue;
  57. }
  58. $path = "$sites_path/$site";
  59. $paths[] = is_dir("$path/modules") ? realpath("$path/modules") : NULL;
  60. $paths[] = is_dir("$path/profiles") ? realpath("$path/profiles") : NULL;
  61. $paths[] = is_dir("$path/themes") ? realpath("$path/themes") : NULL;
  62. }
  63. return array_filter($paths);
  64. }
  65. /**
  66. * Registers the namespace for each extension directory with the autoloader.
  67. *
  68. * @param array $dirs
  69. * An associative array of extension directories, keyed by extension name.
  70. *
  71. * @return array
  72. * An associative array of extension directories, keyed by their namespace.
  73. */
  74. function drupal_phpunit_get_extension_namespaces($dirs) {
  75. $suite_names = ['Unit', 'Kernel', 'Functional', 'FunctionalJavascript'];
  76. $namespaces = [];
  77. foreach ($dirs as $extension => $dir) {
  78. if (is_dir($dir . '/src')) {
  79. // Register the PSR-4 directory for module-provided classes.
  80. $namespaces['Drupal\\' . $extension . '\\'][] = $dir . '/src';
  81. }
  82. $test_dir = $dir . '/tests/src';
  83. if (is_dir($test_dir)) {
  84. foreach ($suite_names as $suite_name) {
  85. $suite_dir = $test_dir . '/' . $suite_name;
  86. if (is_dir($suite_dir)) {
  87. // Register the PSR-4 directory for PHPUnit-based suites.
  88. $namespaces['Drupal\\Tests\\' . $extension . '\\' . $suite_name . '\\'][] = $suite_dir;
  89. }
  90. }
  91. // Extensions can have a \Drupal\extension\Traits namespace for
  92. // cross-suite trait code.
  93. $trait_dir = $test_dir . '/Traits';
  94. if (is_dir($trait_dir)) {
  95. $namespaces['Drupal\\Tests\\' . $extension . '\\Traits\\'][] = $trait_dir;
  96. }
  97. }
  98. }
  99. return $namespaces;
  100. }
  101. // We define the COMPOSER_INSTALL constant, so that PHPUnit knows where to
  102. // autoload from. This is needed for tests run in isolation mode, because
  103. // phpunit.xml.dist is located in a non-default directory relative to the
  104. // PHPUnit executable.
  105. if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
  106. define('PHPUNIT_COMPOSER_INSTALL', __DIR__ . '/../../autoload.php');
  107. }
  108. /**
  109. * Populate class loader with additional namespaces for tests.
  110. *
  111. * We run this in a function to avoid setting the class loader to a global
  112. * that can change. This change can cause unpredictable false positives for
  113. * phpunit's global state change watcher. The class loader can be retrieved from
  114. * composer at any time by requiring autoload.php.
  115. */
  116. function drupal_phpunit_populate_class_loader() {
  117. /** @var \Composer\Autoload\ClassLoader $loader */
  118. $loader = require __DIR__ . '/../../autoload.php';
  119. // Start with classes in known locations.
  120. $loader->add('Drupal\\Tests', __DIR__);
  121. $loader->add('Drupal\\KernelTests', __DIR__);
  122. $loader->add('Drupal\\FunctionalTests', __DIR__);
  123. $loader->add('Drupal\\FunctionalJavascriptTests', __DIR__);
  124. if (!isset($GLOBALS['namespaces'])) {
  125. // Scan for arbitrary extension namespaces from core and contrib.
  126. $extension_roots = drupal_phpunit_contrib_extension_directory_roots();
  127. $dirs = array_map('drupal_phpunit_find_extension_directories', $extension_roots);
  128. $dirs = array_reduce($dirs, 'array_merge', []);
  129. $GLOBALS['namespaces'] = drupal_phpunit_get_extension_namespaces($dirs);
  130. }
  131. foreach ($GLOBALS['namespaces'] as $prefix => $paths) {
  132. $loader->addPsr4($prefix, $paths);
  133. }
  134. return $loader;
  135. };
  136. // Do class loader population.
  137. drupal_phpunit_populate_class_loader();
  138. // Set sane locale settings, to ensure consistent string, dates, times and
  139. // numbers handling.
  140. // @see \Drupal\Core\DrupalKernel::bootEnvironment()
  141. setlocale(LC_ALL, 'C');
  142. // Set the default timezone. While this doesn't cause any tests to fail, PHP
  143. // complains if 'date.timezone' is not set in php.ini. The Australia/Sydney
  144. // timezone is chosen so all tests are run using an edge case scenario (UTC+10
  145. // and DST). This choice is made to prevent timezone related regressions and
  146. // reduce the fragility of the testing system in general.
  147. date_default_timezone_set('Australia/Sydney');
  148. // Runtime assertions. PHPUnit follows the php.ini assert.active setting for
  149. // runtime assertions. By default this setting is on. Here we make a call to
  150. // make PHP 5 and 7 handle assertion failures the same way, but this call does
  151. // not turn runtime assertions on if they weren't on already.
  152. Handle::register();