bootstrap.php 7.5 KB

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