UpdatePathTestTrait.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <?php
  2. namespace Drupal\Tests;
  3. use Drupal\Core\Url;
  4. /**
  5. * Trait UpdatePathTestTrait
  6. *
  7. * For use on \Drupal\Tests\BrowserTestBase tests.
  8. */
  9. trait UpdatePathTestTrait {
  10. use RequirementsPageTrait;
  11. use SchemaCheckTestTrait;
  12. /**
  13. * Fail the test if there are failed updates.
  14. *
  15. * @var bool
  16. */
  17. protected $checkFailedUpdates = TRUE;
  18. /**
  19. * Helper function to run pending database updates.
  20. *
  21. * @param string|null $update_url
  22. * The update URL.
  23. */
  24. protected function runUpdates($update_url = NULL) {
  25. if (!$update_url) {
  26. $update_url = Url::fromRoute('system.db_update');
  27. }
  28. require_once $this->root . '/core/includes/update.inc';
  29. // The site might be broken at the time so logging in using the UI might
  30. // not work, so we use the API itself.
  31. $this->writeSettings([
  32. 'settings' => [
  33. 'update_free_access' => (object) [
  34. 'value' => TRUE,
  35. 'required' => TRUE,
  36. ],
  37. ],
  38. ]);
  39. $this->drupalGet($update_url);
  40. $this->updateRequirementsProblem();
  41. $this->clickLink(t('Continue'));
  42. $this->doSelectionTest();
  43. // Run the update hooks.
  44. $this->clickLink(t('Apply pending updates'));
  45. $this->checkForMetaRefresh();
  46. // Ensure there are no failed updates.
  47. if ($this->checkFailedUpdates) {
  48. $failure = $this->cssSelect('.failure');
  49. if ($failure) {
  50. $this->fail('The update failed with the following message: "' . reset($failure)->getText() . '"');
  51. }
  52. // Ensure that there are no pending updates. Clear the schema version
  53. // static cache first in case it was accessed before running updates.
  54. drupal_get_installed_schema_version(NULL, TRUE);
  55. foreach (['update', 'post_update'] as $update_type) {
  56. switch ($update_type) {
  57. case 'update':
  58. $all_updates = update_get_update_list();
  59. break;
  60. case 'post_update':
  61. $all_updates = \Drupal::service('update.post_update_registry')->getPendingUpdateInformation();
  62. break;
  63. }
  64. foreach ($all_updates as $module => $updates) {
  65. if (!empty($updates['pending'])) {
  66. foreach (array_keys($updates['pending']) as $update_name) {
  67. $this->fail("The $update_name() update function from the $module module did not run.");
  68. }
  69. }
  70. }
  71. }
  72. // Ensure that the container is updated if any modules are installed or
  73. // uninstalled during the update.
  74. /** @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */
  75. $module_handler = $this->container->get('module_handler');
  76. $config_module_list = $this->config('core.extension')->get('module');
  77. $module_handler_list = $module_handler->getModuleList();
  78. $modules_installed = FALSE;
  79. // Modules that are in configuration but not the module handler have been
  80. // installed.
  81. foreach (array_keys(array_diff_key($config_module_list, $module_handler_list)) as $module) {
  82. $module_handler->addModule($module, drupal_get_path('module', $module));
  83. $modules_installed = TRUE;
  84. }
  85. $modules_uninstalled = FALSE;
  86. $module_handler_list = $module_handler->getModuleList();
  87. // Modules that are in the module handler but not configuration have been
  88. // uninstalled.
  89. foreach (array_keys(array_diff_key($module_handler_list, $config_module_list)) as $module) {
  90. $modules_uninstalled = TRUE;
  91. unset($module_handler_list[$module]);
  92. }
  93. if ($modules_installed || $modules_uninstalled) {
  94. // Note that resetAll() does not reset the kernel module list so we
  95. // have to do that manually.
  96. $this->kernel->updateModules($module_handler_list, $module_handler_list);
  97. }
  98. // If we have successfully clicked 'Apply pending updates' then we need to
  99. // clear the caches in the update test runner as this has occurred as part
  100. // of the updates.
  101. $this->resetAll();
  102. // The config schema can be incorrect while the update functions are being
  103. // executed. But once the update has been completed, it needs to be valid
  104. // again. Assert the schema of all configuration objects now.
  105. $names = $this->container->get('config.storage')->listAll();
  106. // Allow tests to opt out of checking specific configuration.
  107. $exclude = $this->getConfigSchemaExclusions();
  108. /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config */
  109. $typed_config = $this->container->get('config.typed');
  110. foreach ($names as $name) {
  111. if (in_array($name, $exclude, TRUE)) {
  112. // Skip checking schema if the config is listed in the
  113. // $configSchemaCheckerExclusions property.
  114. continue;
  115. }
  116. $config = $this->config($name);
  117. $this->assertConfigSchema($typed_config, $name, $config->get());
  118. }
  119. // Ensure that the update hooks updated all entity schema.
  120. $needs_updates = \Drupal::entityDefinitionUpdateManager()->needsUpdates();
  121. if ($needs_updates) {
  122. foreach (\Drupal::entityDefinitionUpdateManager()->getChangeSummary() as $entity_type_id => $summary) {
  123. $entity_type_label = \Drupal::entityTypeManager()->getDefinition($entity_type_id)->getLabel();
  124. foreach ($summary as $message) {
  125. $this->fail("$entity_type_label: $message");
  126. }
  127. }
  128. // The above calls to `fail()` should prevent this from ever being
  129. // called, but it is here in case something goes really wrong.
  130. $this->assertFalse($needs_updates, 'After all updates ran, entity schema is up to date.');
  131. }
  132. }
  133. }
  134. /**
  135. * Tests the selection page.
  136. */
  137. protected function doSelectionTest() {
  138. // No-op. Tests wishing to do test the selection page or the general
  139. // update.php environment before running update.php can override this method
  140. // and implement their required tests.
  141. }
  142. /**
  143. * Installs the update_script_test module and makes an update available.
  144. */
  145. protected function ensureUpdatesToRun() {
  146. \Drupal::service('module_installer')->install(['update_script_test']);
  147. // Reset the schema so there is an update to run.
  148. \Drupal::database()->update('key_value')
  149. ->fields(['value' => serialize(8000)])
  150. ->condition('collection', 'system.schema')
  151. ->condition('name', 'update_script_test')
  152. ->execute();
  153. }
  154. }