TestRequirementsTrait.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. <?php
  2. namespace Drupal\Tests;
  3. use Drupal\Core\Extension\ExtensionDiscovery;
  4. use PHPUnit\Util\Test;
  5. use PHPUnit\Framework\SkippedTestError;
  6. /**
  7. * Allows test classes to require Drupal modules as dependencies.
  8. *
  9. * This trait is assumed to be on a subclass of \PHPUnit\Framework\TestCase, and
  10. * overrides \PHPUnit\Framework\TestCase::checkRequirements(). This allows the
  11. * test to be marked as skipped before any kernel boot processes have happened.
  12. */
  13. trait TestRequirementsTrait {
  14. /**
  15. * Returns the Drupal root directory.
  16. *
  17. * @return string
  18. */
  19. protected static function getDrupalRoot() {
  20. return dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
  21. }
  22. /**
  23. * Check module requirements for the Drupal use case.
  24. *
  25. * This method is assumed to override
  26. * \PHPUnit\Framework\TestCase::checkRequirements().
  27. *
  28. * @throws \PHPUnit\Framework\SkippedTestError
  29. * Thrown when the requirements are not met, and this test should be
  30. * skipped. Callers should not catch this exception.
  31. */
  32. protected function checkRequirements() {
  33. if (!$this->getName(FALSE) || !method_exists($this, $this->getName(FALSE))) {
  34. return;
  35. }
  36. $missingRequirements = Test::getMissingRequirements(
  37. get_class($this),
  38. $this->getName(FALSE)
  39. );
  40. if (!empty($missingRequirements)) {
  41. $this->markTestSkipped(implode(PHP_EOL, $missingRequirements));
  42. }
  43. $root = static::getDrupalRoot();
  44. // Check if required dependencies exist.
  45. $annotations = $this->getAnnotations();
  46. if (!empty($annotations['class']['requires'])) {
  47. $this->checkModuleRequirements($root, $annotations['class']['requires']);
  48. }
  49. if (!empty($annotations['method']['requires'])) {
  50. $this->checkModuleRequirements($root, $annotations['method']['requires']);
  51. }
  52. }
  53. /**
  54. * Checks missing module requirements.
  55. *
  56. * Iterates through a list of requires annotations and looks for missing
  57. * modules. The test will be skipped if any of the required modules is
  58. * missing.
  59. *
  60. * @param string $root
  61. * The path to the root of the Drupal installation to scan.
  62. * @param string[] $annotations
  63. * A list of requires annotations from either a method or class annotation.
  64. *
  65. * @throws \PHPUnit\Framework\SkippedTestError
  66. * Thrown when the requirements are not met, and this test should be
  67. * skipped. Callers should not catch this exception.
  68. */
  69. private function checkModuleRequirements($root, array $annotations) {
  70. // drupal_valid_ua() might not be loaded.
  71. require_once $root . '/core/includes/bootstrap.inc';
  72. // Make a list of required modules.
  73. $required_modules = [];
  74. foreach ($annotations as $requirement) {
  75. if (strpos($requirement, 'module ') === 0) {
  76. $required_modules[] = trim(str_replace('module ', '', $requirement));
  77. }
  78. }
  79. // If there are required modules, check if they're available.
  80. if (!empty($required_modules)) {
  81. // Scan for modules.
  82. $discovery = new ExtensionDiscovery($root, FALSE);
  83. $discovery->setProfileDirectories([]);
  84. $list = array_keys($discovery->scan('module'));
  85. $not_available = array_diff($required_modules, $list);
  86. if (!empty($not_available)) {
  87. throw new SkippedTestError('Required modules: ' . implode(', ', $not_available));
  88. }
  89. }
  90. }
  91. }