186 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			186 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| 
								 | 
							
								<?php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								declare(strict_types=1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace Drupal\Tests;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use Drupal\Core\Database\Database;
							 | 
						||
| 
								 | 
							
								use Drupal\Core\Url;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Trait UpdatePathTestTrait.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * For use on \Drupal\Tests\BrowserTestBase tests.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								trait UpdatePathTestTrait {
							 | 
						||
| 
								 | 
							
								  use RequirementsPageTrait;
							 | 
						||
| 
								 | 
							
								  use SchemaCheckTestTrait;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Fail the test if there are failed updates.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @var bool
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  protected $checkFailedUpdates = TRUE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Fail the test if there are entity field definition updates needed.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @var bool
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  protected $checkEntityFieldDefinitionUpdates = TRUE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Helper function to run pending database updates.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param string|null $update_url
							 | 
						||
| 
								 | 
							
								   *   The update URL.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  protected function runUpdates($update_url = NULL): void {
							 | 
						||
| 
								 | 
							
								    if (!$update_url) {
							 | 
						||
| 
								 | 
							
								      $update_url = Url::fromRoute('system.db_update');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    require_once $this->root . '/core/includes/update.inc';
							 | 
						||
| 
								 | 
							
								    // The site might be broken at the time so logging in using the UI might
							 | 
						||
| 
								 | 
							
								    // not work, so we use the API itself.
							 | 
						||
| 
								 | 
							
								    $this->writeSettings([
							 | 
						||
| 
								 | 
							
								      'settings' => [
							 | 
						||
| 
								 | 
							
								        'update_free_access' => (object) [
							 | 
						||
| 
								 | 
							
								          'value' => TRUE,
							 | 
						||
| 
								 | 
							
								          'required' => TRUE,
							 | 
						||
| 
								 | 
							
								        ],
							 | 
						||
| 
								 | 
							
								      ],
							 | 
						||
| 
								 | 
							
								    ]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    $this->drupalGet($update_url);
							 | 
						||
| 
								 | 
							
								    $this->updateRequirementsProblem();
							 | 
						||
| 
								 | 
							
								    $this->clickLink('Continue');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    $this->doSelectionTest();
							 | 
						||
| 
								 | 
							
								    // Run the update hooks.
							 | 
						||
| 
								 | 
							
								    $this->clickLink('Apply pending updates');
							 | 
						||
| 
								 | 
							
								    $this->checkForMetaRefresh();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Ensure there are no failed updates.
							 | 
						||
| 
								 | 
							
								    if ($this->checkFailedUpdates) {
							 | 
						||
| 
								 | 
							
								      $failure = $this->cssSelect('.failure');
							 | 
						||
| 
								 | 
							
								      if ($failure) {
							 | 
						||
| 
								 | 
							
								        $this->fail('The update failed with the following message: "' . reset($failure)->getText() . '"');
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // Ensure that there are no pending updates.
							 | 
						||
| 
								 | 
							
								      foreach (['update', 'post_update'] as $update_type) {
							 | 
						||
| 
								 | 
							
								        switch ($update_type) {
							 | 
						||
| 
								 | 
							
								          case 'update':
							 | 
						||
| 
								 | 
							
								            drupal_load_updates();
							 | 
						||
| 
								 | 
							
								            $all_updates = update_get_update_list();
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          case 'post_update':
							 | 
						||
| 
								 | 
							
								            $all_updates = \Drupal::service('update.post_update_registry')->getPendingUpdateInformation();
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        foreach ($all_updates as $module => $updates) {
							 | 
						||
| 
								 | 
							
								          if (!empty($updates['pending'])) {
							 | 
						||
| 
								 | 
							
								            foreach (array_keys($updates['pending']) as $update_name) {
							 | 
						||
| 
								 | 
							
								              $this->fail("The $update_name() update function from the $module module did not run.");
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // Ensure that the container is updated if any modules are installed or
							 | 
						||
| 
								 | 
							
								      // uninstalled during the update.
							 | 
						||
| 
								 | 
							
								      /** @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */
							 | 
						||
| 
								 | 
							
								      $module_handler = $this->container->get('module_handler');
							 | 
						||
| 
								 | 
							
								      $config_module_list = $this->config('core.extension')->get('module');
							 | 
						||
| 
								 | 
							
								      $module_handler_list = $module_handler->getModuleList();
							 | 
						||
| 
								 | 
							
								      $modules_installed = FALSE;
							 | 
						||
| 
								 | 
							
								      // Modules that are in configuration but not the module handler have been
							 | 
						||
| 
								 | 
							
								      // installed.
							 | 
						||
| 
								 | 
							
								      $modules_installed = !empty(array_diff_key($config_module_list, $module_handler_list));
							 | 
						||
| 
								 | 
							
								      $modules_uninstalled = FALSE;
							 | 
						||
| 
								 | 
							
								      $module_handler_list = $module_handler->getModuleList();
							 | 
						||
| 
								 | 
							
								      // Modules that are in the module handler but not configuration have been
							 | 
						||
| 
								 | 
							
								      // uninstalled.
							 | 
						||
| 
								 | 
							
								      foreach (array_keys(array_diff_key($module_handler_list, $config_module_list)) as $module) {
							 | 
						||
| 
								 | 
							
								        $modules_uninstalled = TRUE;
							 | 
						||
| 
								 | 
							
								        unset($module_handler_list[$module]);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if ($modules_installed || $modules_uninstalled) {
							 | 
						||
| 
								 | 
							
								        // Note that resetAll() does not reset the kernel module list so we
							 | 
						||
| 
								 | 
							
								        // have to do that manually.
							 | 
						||
| 
								 | 
							
								        $this->kernel->updateModules($module_handler_list, $module_handler_list);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // Close any open database connections. This allows DB drivers that store
							 | 
						||
| 
								 | 
							
								      // static information to refresh it in the update runner.
							 | 
						||
| 
								 | 
							
								      // @todo https://drupal.org/i/3222121 consider doing this in
							 | 
						||
| 
								 | 
							
								      //   \Drupal\Core\DrupalKernel::initializeContainer() for container
							 | 
						||
| 
								 | 
							
								      //   rebuilds.
							 | 
						||
| 
								 | 
							
								      foreach (Database::getAllConnectionInfo() as $key => $info) {
							 | 
						||
| 
								 | 
							
								        Database::closeConnection(NULL, $key);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // If we have successfully clicked 'Apply pending updates' then we need to
							 | 
						||
| 
								 | 
							
								      // clear the caches in the update test runner as this has occurred as part
							 | 
						||
| 
								 | 
							
								      // of the updates.
							 | 
						||
| 
								 | 
							
								      $this->resetAll();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // The config schema can be incorrect while the update functions are being
							 | 
						||
| 
								 | 
							
								      // executed. But once the update has been completed, it needs to be valid
							 | 
						||
| 
								 | 
							
								      // again. Assert the schema of all configuration objects now.
							 | 
						||
| 
								 | 
							
								      $names = $this->container->get('config.storage')->listAll();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // Allow tests to opt out of checking specific configuration.
							 | 
						||
| 
								 | 
							
								      $exclude = $this->getConfigSchemaExclusions();
							 | 
						||
| 
								 | 
							
								      /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config */
							 | 
						||
| 
								 | 
							
								      $typed_config = $this->container->get('config.typed');
							 | 
						||
| 
								 | 
							
								      foreach ($names as $name) {
							 | 
						||
| 
								 | 
							
								        if (in_array($name, $exclude, TRUE)) {
							 | 
						||
| 
								 | 
							
								          // Skip checking schema if the config is listed in the
							 | 
						||
| 
								 | 
							
								          // $configSchemaCheckerExclusions property.
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $config = $this->config($name);
							 | 
						||
| 
								 | 
							
								        $this->assertConfigSchema($typed_config, $name, $config->get());
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // Ensure that the update hooks updated all entity schema.
							 | 
						||
| 
								 | 
							
								      if ($this->checkEntityFieldDefinitionUpdates) {
							 | 
						||
| 
								 | 
							
								        $needs_updates = \Drupal::entityDefinitionUpdateManager()->needsUpdates();
							 | 
						||
| 
								 | 
							
								        if ($needs_updates) {
							 | 
						||
| 
								 | 
							
								          foreach (\Drupal::entityDefinitionUpdateManager()->getChangeSummary() as $entity_type_id => $summary) {
							 | 
						||
| 
								 | 
							
								            $entity_type_label = \Drupal::entityTypeManager()->getDefinition($entity_type_id)->getLabel();
							 | 
						||
| 
								 | 
							
								            foreach ($summary as $message) {
							 | 
						||
| 
								 | 
							
								              $this->fail("$entity_type_label: $message");
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          // The above calls to `fail()` should prevent this from ever being
							 | 
						||
| 
								 | 
							
								          // called, but it is here in case something goes really wrong.
							 | 
						||
| 
								 | 
							
								          $this->assertFalse($needs_updates, 'After all updates ran, entity schema is up to date.');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Tests the selection page.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  protected function doSelectionTest(): void {
							 | 
						||
| 
								 | 
							
								    // No-op. Tests wishing to do test the selection page or the general
							 | 
						||
| 
								 | 
							
								    // update.php environment before running update.php can override this method
							 | 
						||
| 
								 | 
							
								    // and implement their required tests.
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Installs the update_script_test module and makes an update available.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  protected function ensureUpdatesToRun(): void {
							 | 
						||
| 
								 | 
							
								    \Drupal::service('module_installer')->install(['update_script_test']);
							 | 
						||
| 
								 | 
							
								    // Reset the schema so there is an update to run.
							 | 
						||
| 
								 | 
							
								    \Drupal::service('update.update_hook_registry')->setInstalledVersion('update_script_test', 8000);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 |