Commit 8546e506 authored by catch's avatar catch
Browse files

fix: #3572171 Persist is_syncing across container rebuilds

By: nicxvan
By: berdir
By: phenaproxima
By: catch
By: herved
By: godotislate
(cherry picked from commit 1e00c0b7)
parent cbb01dea
Loading
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -1330,9 +1330,20 @@ public function resetContainer(): ContainerInterface {

    $all_messages = $this->container->get('messenger')->all();

    // Track the config installer syncing flag for when multiple modules are
    // installed and container is reset.
    $config_is_syncing = $this->container->get('config.installer')->isSyncing();
    $source_storage = $this->container->get('config.installer')->getSourceStorage();
    $persist = $this->getServicesToPersist($this->container);
    $this->container->reset();
    $this->persistServices($this->container, $persist);
    // Restore syncing flag to new container.
    if ($config_is_syncing) {
      $this->container->get('config.installer')->setSyncing(TRUE);
      if ($source_storage) {
        $this->container->get('config.installer')->setSourceStorage($source_storage);
      }
    }

    $this->container->set('kernel', $this);

+7 −1
Original line number Diff line number Diff line
@@ -35,12 +35,18 @@ public function help($route_name, RouteMatchInterface $route_match): ?string {
   * Implements hook_modules_installed().
   */
  #[Hook('modules_installed')]
  public function modulesInstalled($modules): void {
  public function modulesInstalled(array $modules, bool $is_syncing): void {
    if (\Drupal::state()->get('system_test.verbose_module_hooks')) {
      foreach ($modules as $module) {
        \Drupal::messenger()->addStatus($this->t('hook_modules_installed fired for @module', ['@module' => $module]));
      }
    }
    // Save the config.installer isSyncing() value to state to check that it is
    // correctly set when installing module during config import.
    \Drupal::state()->set('system_test_modules_installed_module_config_installer_syncing', \Drupal::service('config.installer')->isSyncing());
    // Save the $is_syncing parameter value to state to check that it is
    // correctly set when installing module during config import.
    \Drupal::state()->set('system_test_modules_installed_module_syncing_param', $is_syncing);
  }

  /**
+29 −2
Original line number Diff line number Diff line
@@ -834,8 +834,9 @@ public function testIsSyncingInHooks(): void {
    $this->assertTrue($state['global_state::delete'], '\Drupal::isConfigSyncing() returns TRUE');
    $this->assertTrue($state['entity_state::delete'], 'ConfigEntity::isSyncing() returns TRUE');

    // Test that isSyncing is TRUE in hook_module_preinstall() when installing
    // module via config import.
    // Test that isSyncing is TRUE in hook_module_preinstall() and
    // hook_modules_installed() when installing a single module via config
    // import.
    $extensions = $sync->read('core.extension');
    // First, install system_test so that its hook_module_preinstall() will run
    // when module_test is installed.
@@ -853,6 +854,32 @@ public function testIsSyncingInHooks(): void {
    // when module is installed via config import.
    $this->assertTrue(\Drupal::state()->get('system_test_preinstall_module_config_installer_syncing'), '\Drupal::isConfigSyncing() in system_test_module_preinstall() returns TRUE');
    $this->assertTrue(\Drupal::state()->get('system_test_preinstall_module_syncing_param'), 'system_test_module_preinstall() $is_syncing value is TRUE');
    // Syncing values stored in state by hook_modules_installed should be TRUE
    // when module is installed via config import.
    $this->assertTrue(\Drupal::state()->get('system_test_modules_installed_module_config_installer_syncing'));
    $this->assertTrue(\Drupal::state()->get('system_test_modules_installed_module_syncing_param'));

    // Reset the state values before testing multiple module install.
    \Drupal::state()->set('system_test_preinstall_module_config_installer_syncing', FALSE);
    \Drupal::state()->set('system_test_preinstall_module_syncing_param', FALSE);
    \Drupal::state()->set('system_test_modules_installed_module_config_installer_syncing', FALSE);
    \Drupal::state()->set('system_test_modules_installed_module_syncing_param', FALSE);
    // Test isSyncing is TRUE in hook_module_preinstall() and
    // hook_modules_installed() when installing multiple module via config
    // import.
    $extensions['module']['generic_module1_test'] = 0;
    $extensions['module']['generic_module2_test'] = 0;
    $sync->write('core.extension', $extensions);
    $this->configImporter()->import();

    // Syncing values stored in state by hook_module_preinstall should be TRUE
    // when multiple modules are installed via config import.
    $this->assertTrue(\Drupal::state()->get('system_test_preinstall_module_config_installer_syncing'), '\Drupal::isConfigSyncing() in system_test_module_preinstall() returns TRUE');
    $this->assertTrue(\Drupal::state()->get('system_test_preinstall_module_syncing_param'), 'system_test_module_preinstall() $is_syncing value is TRUE');
    // Syncing values stored in state by hook_modules_installed should be TRUE
    // when multiple modules are installed via config import.
    $this->assertTrue(\Drupal::state()->get('system_test_modules_installed_module_config_installer_syncing'));
    $this->assertTrue(\Drupal::state()->get('system_test_modules_installed_module_syncing_param'));

    // Syncing value stored in state by uninstall hooks should be FALSE
    // when uninstalling outside of config import.
+13 −1
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
namespace Drupal\KernelTests\Core\DrupalKernel;

use Composer\Autoload\ClassLoader;
use Drupal\Core\Config\ConfigInstallerInterface;
use Drupal\Core\DrupalKernel;
use Drupal\Core\DrupalKernelInterface;
use Drupal\KernelTests\KernelTestBase;
@@ -15,6 +16,7 @@
use PHPUnit\Framework\Attributes\PreserveGlobalState;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use PHPUnit\Framework\Attributes\TestWith;
use Prophecy\Argument;
use Symfony\Component\HttpFoundation\Request;

@@ -277,7 +279,9 @@ public function testClassLoaderAutoDetect($value): void {
  /**
   * Tests reset container.
   */
  public function testResetContainer(): void {
  #[TestWith([TRUE])]
  #[TestWith([FALSE])]
  public function testResetContainer(bool $config_installer_syncing): void {
    $modules_enabled = [
      'system' => 'system',
      'user' => 'user',
@@ -301,6 +305,10 @@ public function testResetContainer(): void {
    $container->get('messenger')->addMessage('Test reset', 'Container reset');
    $this->assertSame(['Test reset'], $container->get('messenger')->messagesByType('Container reset'));

    // Ensure config installer isSyncing status is maintained through a
    // container reset.
    \Drupal::service(ConfigInstallerInterface::class)->setSyncing($config_installer_syncing);

    // Ensure persisted services are persisted.
    $request_stack = $container->get('request_stack');

@@ -318,6 +326,10 @@ public function testResetContainer(): void {
    // Ensure messages are maintained through a container reset.
    $this->assertSame(['Test reset'], $container->get('messenger')->messagesByType('Container reset'));

    // Ensure config installer isSyncing status is maintained through a
    // container reset.
    $this->assertSame($config_installer_syncing, \Drupal::service(ConfigInstallerInterface::class)->isSyncing());

    // Ensure persisted services are persisted.
    $this->assertSame($request_stack, $container->get('request_stack'));
  }