bootstrap.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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 Drupal\Core\Composer\Composer;
  10. use PHPUnit\Runner\Version;
  11. /**
  12. * Finds all valid extension directories recursively within a given directory.
  13. *
  14. * @param string $scan_directory
  15. * The directory that should be recursively scanned.
  16. * @return array
  17. * An associative array of extension directories found within the scanned
  18. * directory, keyed by extension name.
  19. */
  20. function drupal_phpunit_find_extension_directories($scan_directory) {
  21. $extensions = [];
  22. $dirs = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($scan_directory, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS));
  23. foreach ($dirs as $dir) {
  24. if (strpos($dir->getPathname(), '.info.yml') !== FALSE) {
  25. // Cut off ".info.yml" from the filename for use as the extension name. We
  26. // use getRealPath() so that we can scan extensions represented by
  27. // directory aliases.
  28. $extensions[substr($dir->getFilename(), 0, -9)] = $dir->getPathInfo()
  29. ->getRealPath();
  30. }
  31. }
  32. return $extensions;
  33. }
  34. /**
  35. * Returns directories under which contributed extensions may exist.
  36. *
  37. * @param string $root
  38. * (optional) Path to the root of the Drupal installation.
  39. *
  40. * @return array
  41. * An array of directories under which contributed extensions may exist.
  42. */
  43. function drupal_phpunit_contrib_extension_directory_roots($root = NULL) {
  44. if ($root === NULL) {
  45. $root = dirname(dirname(__DIR__));
  46. }
  47. $paths = [
  48. $root . '/core/modules',
  49. $root . '/core/profiles',
  50. $root . '/modules',
  51. $root . '/profiles',
  52. $root . '/themes',
  53. ];
  54. $sites_path = $root . '/sites';
  55. // Note this also checks sites/../modules and sites/../profiles.
  56. foreach (scandir($sites_path) as $site) {
  57. if ($site[0] === '.' || $site === 'simpletest') {
  58. continue;
  59. }
  60. $path = "$sites_path/$site";
  61. $paths[] = is_dir("$path/modules") ? realpath("$path/modules") : NULL;
  62. $paths[] = is_dir("$path/profiles") ? realpath("$path/profiles") : NULL;
  63. $paths[] = is_dir("$path/themes") ? realpath("$path/themes") : NULL;
  64. }
  65. return array_filter($paths, 'file_exists');
  66. }
  67. /**
  68. * Registers the namespace for each extension directory with the autoloader.
  69. *
  70. * @param array $dirs
  71. * An associative array of extension directories, keyed by extension name.
  72. *
  73. * @return array
  74. * An associative array of extension directories, keyed by their namespace.
  75. */
  76. function drupal_phpunit_get_extension_namespaces($dirs) {
  77. $suite_names = ['Unit', 'Kernel', 'Functional', 'FunctionalJavascript'];
  78. $namespaces = [];
  79. foreach ($dirs as $extension => $dir) {
  80. if (is_dir($dir . '/src')) {
  81. // Register the PSR-4 directory for module-provided classes.
  82. $namespaces['Drupal\\' . $extension . '\\'][] = $dir . '/src';
  83. }
  84. $test_dir = $dir . '/tests/src';
  85. if (is_dir($test_dir)) {
  86. foreach ($suite_names as $suite_name) {
  87. $suite_dir = $test_dir . '/' . $suite_name;
  88. if (is_dir($suite_dir)) {
  89. // Register the PSR-4 directory for PHPUnit-based suites.
  90. $namespaces['Drupal\\Tests\\' . $extension . '\\' . $suite_name . '\\'][] = $suite_dir;
  91. }
  92. }
  93. // Extensions can have a \Drupal\extension\Traits namespace for
  94. // cross-suite trait code.
  95. $trait_dir = $test_dir . '/Traits';
  96. if (is_dir($trait_dir)) {
  97. $namespaces['Drupal\\Tests\\' . $extension . '\\Traits\\'][] = $trait_dir;
  98. }
  99. }
  100. }
  101. return $namespaces;
  102. }
  103. // We define the COMPOSER_INSTALL constant, so that PHPUnit knows where to
  104. // autoload from. This is needed for tests run in isolation mode, because
  105. // phpunit.xml.dist is located in a non-default directory relative to the
  106. // PHPUnit executable.
  107. if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
  108. define('PHPUNIT_COMPOSER_INSTALL', __DIR__ . '/../../autoload.php');
  109. }
  110. /**
  111. * Populate class loader with additional namespaces for tests.
  112. *
  113. * We run this in a function to avoid setting the class loader to a global
  114. * that can change. This change can cause unpredictable false positives for
  115. * phpunit's global state change watcher. The class loader can be retrieved from
  116. * composer at any time by requiring autoload.php.
  117. */
  118. function drupal_phpunit_populate_class_loader() {
  119. /** @var \Composer\Autoload\ClassLoader $loader */
  120. $loader = require __DIR__ . '/../../autoload.php';
  121. // Start with classes in known locations.
  122. $loader->add('Drupal\\Tests', __DIR__);
  123. $loader->add('Drupal\\TestSite', __DIR__);
  124. $loader->add('Drupal\\KernelTests', __DIR__);
  125. $loader->add('Drupal\\FunctionalTests', __DIR__);
  126. $loader->add('Drupal\\FunctionalJavascriptTests', __DIR__);
  127. if (!isset($GLOBALS['namespaces'])) {
  128. // Scan for arbitrary extension namespaces from core and contrib.
  129. $extension_roots = drupal_phpunit_contrib_extension_directory_roots();
  130. $dirs = array_map('drupal_phpunit_find_extension_directories', $extension_roots);
  131. $dirs = array_reduce($dirs, 'array_merge', []);
  132. $GLOBALS['namespaces'] = drupal_phpunit_get_extension_namespaces($dirs);
  133. }
  134. foreach ($GLOBALS['namespaces'] as $prefix => $paths) {
  135. $loader->addPsr4($prefix, $paths);
  136. }
  137. return $loader;
  138. };
  139. // Do class loader population.
  140. drupal_phpunit_populate_class_loader();
  141. // Ensure we have the correct PHPUnit version for the version of PHP.
  142. if (class_exists('\PHPUnit_Runner_Version')) {
  143. $phpunit_version = \PHPUnit_Runner_Version::id();
  144. }
  145. else {
  146. $phpunit_version = Version::id();
  147. }
  148. if (!Composer::upgradePHPUnitCheck($phpunit_version)) {
  149. $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.";
  150. echo "\033[31m" . $message . "\n\033[0m";
  151. exit(1);
  152. }
  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. Here we make a call to
  168. // make PHP 5 and 7 handle assertion failures the same way, but this call does
  169. // not turn runtime assertions on 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. if (version_compare($phpunit_version, '6.1', '>=')) {
  174. class_alias('\PHPUnit\Framework\AssertionFailedError', '\PHPUnit_Framework_AssertionFailedError');
  175. class_alias('\PHPUnit\Framework\Constraint\Count', '\PHPUnit_Framework_Constraint_Count');
  176. class_alias('\PHPUnit\Framework\Error\Error', '\PHPUnit_Framework_Error');
  177. class_alias('\PHPUnit\Framework\Error\Warning', '\PHPUnit_Framework_Error_Warning');
  178. class_alias('\PHPUnit\Framework\ExpectationFailedException', '\PHPUnit_Framework_ExpectationFailedException');
  179. class_alias('\PHPUnit\Framework\Exception', '\PHPUnit_Framework_Exception');
  180. class_alias('\PHPUnit\Framework\MockObject\Matcher\InvokedRecorder', '\PHPUnit_Framework_MockObject_Matcher_InvokedRecorder');
  181. class_alias('\PHPUnit\Framework\SkippedTestError', '\PHPUnit_Framework_SkippedTestError');
  182. class_alias('\PHPUnit\Framework\TestCase', '\PHPUnit_Framework_TestCase');
  183. class_alias('\PHPUnit\Util\Test', '\PHPUnit_Util_Test');
  184. class_alias('\PHPUnit\Util\Xml', '\PHPUnit_Util_XML');
  185. }