diff --git a/package_manager/src/Stage.php b/package_manager/src/Stage.php index 9ae7ebfe4b08ce53bc33b0c649150464ce6e3605..71c4ceac261b57f039870f75e7a8e81b4236b41f 100644 --- a/package_manager/src/Stage.php +++ b/package_manager/src/Stage.php @@ -320,17 +320,18 @@ class Stage { $active_dir = $this->pathLocator->getProjectRoot(); $stage_dir = $this->getStageDirectory(); - $event = new PreApplyEvent($this); - $this->tempStore->set(self::TEMPSTORE_APPLY_TIME_KEY, $this->time->getRequestTime()); - // If an error occurs while dispatching the event, ensure that ::destroy() + // If an error occurs while dispatching the events, ensure that ::destroy() // doesn't think we're in the middle of applying the staged changes to the // active directory. - $this->dispatch($event, function (): void { + $release_apply = function (): void { $this->tempStore->delete(self::TEMPSTORE_APPLY_TIME_KEY); - }); + }; + + $event = new PreApplyEvent($this); + $this->tempStore->set(self::TEMPSTORE_APPLY_TIME_KEY, $this->time->getRequestTime()); + $this->dispatch($event, $release_apply); $this->committer->commit($stage_dir, $active_dir, $event->getExcludedPaths()); - $this->tempStore->delete(self::TEMPSTORE_APPLY_TIME_KEY); // Rebuild the container and clear all caches, to ensure that new services // are picked up. @@ -340,7 +341,8 @@ class Stage { // unlikely to call newly added code during the current request. $this->eventDispatcher = \Drupal::service('event_dispatcher'); - $this->dispatch(new PostApplyEvent($this)); + $this->dispatch(new PostApplyEvent($this), $release_apply); + $release_apply(); } /** diff --git a/package_manager/tests/src/Kernel/StageTest.php b/package_manager/tests/src/Kernel/StageTest.php index b08b47d8c7422c945292a4207b258500a4bdfd42..90ac26e4d9f26251fbce4b5ae45a4f3642049370 100644 --- a/package_manager/tests/src/Kernel/StageTest.php +++ b/package_manager/tests/src/Kernel/StageTest.php @@ -4,7 +4,9 @@ namespace Drupal\Tests\package_manager\Kernel; use Drupal\Component\Datetime\Time; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\package_manager\Event\PostApplyEvent; use Drupal\package_manager\Event\PreApplyEvent; +use Drupal\package_manager\Event\StageEvent; use Drupal\package_manager\Exception\StageException; /** @@ -14,6 +16,24 @@ use Drupal\package_manager\Exception\StageException; */ class StageTest extends PackageManagerKernelTestBase { + /** + * {@inheritdoc} + */ + protected static $modules = ['system']; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + $this->installConfig('system'); + $this->config('system.site')->set('uuid', $this->randomMachineName())->save(); + // Ensure that the core update system thinks that System's post-update + // functions have run. + $this->registerPostUpdateFunctions(); + } + /** * {@inheritdoc} */ @@ -22,6 +42,11 @@ class StageTest extends PackageManagerKernelTestBase { $container->getDefinition('datetime.time') ->setClass(TestTime::class); + + // Since this test adds arbitrary event listeners that aren't services, we + // need to ensure they will persist even if the container is rebuilt when + // staged changes are applied. + $container->getDefinition('event_dispatcher')->addTag('persist'); } /** @@ -29,12 +54,7 @@ class StageTest extends PackageManagerKernelTestBase { * @covers ::getStagingRoot */ public function testGetStageDirectory(): void { - $this->container->get('module_installer')->install(['system']); - // Ensure we have an up-to-date-container. - $this->container = $this->container->get('kernel')->getContainer(); - - // Ensure that a site ID was generated. - // @see system_install() + // Ensure that a site ID was generated in ::setUp(). $site_id = $this->config('system.site')->get('uuid'); $this->assertNotEmpty($site_id); @@ -68,16 +88,62 @@ class StageTest extends PackageManagerKernelTestBase { */ public function providerDestroyDuringApply(): array { return [ - 'force destroy, not stale' => [TRUE, 1, TRUE], - 'regular destroy, not stale' => [FALSE, 1, TRUE], - 'force destroy, stale' => [TRUE, 7200, FALSE], - 'regular destroy, stale' => [FALSE, 7200, FALSE], + 'force destroy on pre-apply, fresh' => [ + PreApplyEvent::class, + TRUE, + 1, + TRUE, + ], + 'destroy on pre-apply, fresh' => [ + PreApplyEvent::class, + FALSE, + 1, + TRUE, + ], + 'force destroy on pre-apply, stale' => [ + PreApplyEvent::class, + TRUE, + 7200, + FALSE, + ], + 'destroy on pre-apply, stale' => [ + PreApplyEvent::class, + FALSE, + 7200, + FALSE, + ], + 'force destroy on post-apply, fresh' => [ + PostApplyEvent::class, + TRUE, + 1, + TRUE, + ], + 'destroy on post-apply, fresh' => [ + PostApplyEvent::class, + FALSE, + 1, + TRUE, + ], + 'force destroy on post-apply, stale' => [ + PostApplyEvent::class, + TRUE, + 7200, + FALSE, + ], + 'destroy on post-apply, stale' => [ + PostApplyEvent::class, + FALSE, + 7200, + FALSE, + ], ]; } /** * Tests destroying a stage while applying it. * + * @param string $event_class + * The event class for which to attempt to destroy the stage. * @param bool $force * Whether or not the stage should be force destroyed. * @param int $time_offset @@ -88,8 +154,8 @@ class StageTest extends PackageManagerKernelTestBase { * * @dataProvider providerDestroyDuringApply */ - public function testDestroyDuringApply(bool $force, int $time_offset, bool $expect_exception): void { - $listener = function (PreApplyEvent $event) use ($force, $time_offset): void { + public function testDestroyDuringApply(string $event_class, bool $force, int $time_offset, bool $expect_exception): void { + $listener = function (StageEvent $event) use ($force, $time_offset): void { // Simulate that a certain amount of time has passed since we started // applying staged changes. After a point, it should be possible to // destroy the stage even if it hasn't finished. @@ -102,7 +168,7 @@ class StageTest extends PackageManagerKernelTestBase { $event->getStage()->destroy($force); }; $this->container->get('event_dispatcher') - ->addListener(PreApplyEvent::class, $listener); + ->addListener($event_class, $listener); $stage = $this->createStage(); $stage->create(); diff --git a/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php b/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php index 277027b7cb81b5aaf26106c7ba012d95d3046052..be9866e59914e882f963b9462a761f5ab7b9644f 100644 --- a/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php +++ b/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php @@ -55,15 +55,8 @@ abstract class AutomaticUpdatesKernelTestBase extends PackageManagerKernelTestBa $this->installConfig('update'); // Make the update system think that all of System's post-update functions - // have run. Since kernel tests don't normally install modules and register - // their updates, we need to do this so that all validators are tested from - // a clean, fully up-to-date state. - $updates = $this->container->get('update.post_update_registry') - ->getPendingUpdateFunctions(); - - $this->container->get('keyvalue') - ->get('post_update') - ->set('existing_updates', $updates); + // have run. + $this->registerPostUpdateFunctions(); // By default, pretend we're running Drupal core 9.8.0 and a non-security // update to 9.8.1 is available.