From 0b5234db4ddbf1d5fa12c42c99e0d2ce64c79d9e Mon Sep 17 00:00:00 2001 From: phenaproxima <phenaproxima@205645.no-reply.drupal.org> Date: Wed, 27 Oct 2021 15:58:44 +0000 Subject: [PATCH] Issue #3246203 by phenaproxima, tedbow: Replace PreCommitEvent and PostCommitEvent with PreApplyEvent and PostApplyEvent --- package_manager/src/Stage.php | 22 ++ src/Event/PostCommitEvent.php | 10 - src/Event/PreCommitEvent.php | 47 ----- src/Event/UpdateRefreshSubscriber.php | 3 +- src/Updater.php | 10 - src/Validator/StagedProjectsValidator.php | 13 +- .../src/ReadinessChecker/TestChecker1.php | 16 +- .../StagedProjectsValidatorTest.php | 148 ++++++++++++++ .../src/Unit/StagedProjectsValidatorTest.php | 191 ------------------ 9 files changed, 187 insertions(+), 273 deletions(-) delete mode 100644 src/Event/PostCommitEvent.php delete mode 100644 src/Event/PreCommitEvent.php create mode 100644 tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php delete mode 100644 tests/src/Unit/StagedProjectsValidatorTest.php diff --git a/package_manager/src/Stage.php b/package_manager/src/Stage.php index aef260e920..2a27734761 100644 --- a/package_manager/src/Stage.php +++ b/package_manager/src/Stage.php @@ -165,4 +165,26 @@ class Stage { } } + /** + * Returns a Composer utility object for the active directory. + * + * @return \Drupal\package_manager\ComposerUtility + * The Composer utility object. + */ + public function getActiveComposer(): ComposerUtility { + $dir = $this->pathLocator->getActiveDirectory(); + return ComposerUtility::createForDirectory($dir); + } + + /** + * Returns a Composer utility object for the stage directory. + * + * @return \Drupal\package_manager\ComposerUtility + * The Composer utility object. + */ + public function getStageComposer(): ComposerUtility { + $dir = $this->pathLocator->getStageDirectory(); + return ComposerUtility::createForDirectory($dir); + } + } diff --git a/src/Event/PostCommitEvent.php b/src/Event/PostCommitEvent.php deleted file mode 100644 index e4e166dff8..0000000000 --- a/src/Event/PostCommitEvent.php +++ /dev/null @@ -1,10 +0,0 @@ -<?php - -namespace Drupal\automatic_updates\Event; - -/** - * Event fired when a staged update has been committed. - */ -class PostCommitEvent extends UpdateEvent { - -} diff --git a/src/Event/PreCommitEvent.php b/src/Event/PreCommitEvent.php deleted file mode 100644 index 010faca493..0000000000 --- a/src/Event/PreCommitEvent.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -namespace Drupal\automatic_updates\Event; - -use Drupal\package_manager\ComposerUtility; -use Drupal\package_manager\Event\ExcludedPathsTrait; - -/** - * Event fired before staged changes are copied into the active site. - * - * Validation results added by subscribers are not cached. - */ -class PreCommitEvent extends UpdateEvent { - - use ExcludedPathsTrait; - - /** - * The Composer utility object for the stage directory. - * - * @var \Drupal\package_manager\ComposerUtility - */ - protected $stageComposer; - - /** - * Constructs a new PreCommitEvent object. - * - * @param \Drupal\package_manager\ComposerUtility $active_composer - * A Composer utility object for the active directory. - * @param \Drupal\package_manager\ComposerUtility $stage_composer - * A Composer utility object for the stage directory. - */ - public function __construct(ComposerUtility $active_composer, ComposerUtility $stage_composer) { - parent::__construct($active_composer); - $this->stageComposer = $stage_composer; - } - - /** - * Returns a Composer utility object for the stage directory. - * - * @return \Drupal\package_manager\ComposerUtility - * The Composer utility object for the stage directory. - */ - public function getStageComposer(): ComposerUtility { - return $this->stageComposer; - } - -} diff --git a/src/Event/UpdateRefreshSubscriber.php b/src/Event/UpdateRefreshSubscriber.php index fd68151a59..a336783a2d 100644 --- a/src/Event/UpdateRefreshSubscriber.php +++ b/src/Event/UpdateRefreshSubscriber.php @@ -2,6 +2,7 @@ namespace Drupal\automatic_updates\Event; +use Drupal\package_manager\Event\PostApplyEvent; use Drupal\update\UpdateManagerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -40,7 +41,7 @@ class UpdateRefreshSubscriber implements EventSubscriberInterface { */ public static function getSubscribedEvents() { return [ - PostCommitEvent::class => ['clearData', 1000], + PostApplyEvent::class => ['clearData', 1000], ]; } diff --git a/src/Updater.php b/src/Updater.php index 3ca468e408..b5d01335a3 100644 --- a/src/Updater.php +++ b/src/Updater.php @@ -2,8 +2,6 @@ namespace Drupal\automatic_updates; -use Drupal\automatic_updates\Event\PostCommitEvent; -use Drupal\automatic_updates\Event\PreCommitEvent; use Drupal\automatic_updates\Event\PreStartEvent; use Drupal\automatic_updates\Event\UpdateEvent; use Drupal\automatic_updates\Exception\UpdateException; @@ -134,15 +132,7 @@ class Updater { * Commits the current update. */ public function commit(): void { - $active_dir = $this->pathLocator->getActiveDirectory(); - $active_composer = ComposerUtility::createForDirectory($active_dir); - - $stage_dir = $this->pathLocator->getStageDirectory(); - $stage_composer = ComposerUtility::createForDirectory($stage_dir); - - $this->dispatchUpdateEvent(new PreCommitEvent($active_composer, $stage_composer)); $this->stage->apply(); - $this->dispatchUpdateEvent(new PostCommitEvent($active_composer)); } /** diff --git a/src/Validator/StagedProjectsValidator.php b/src/Validator/StagedProjectsValidator.php index 1ae41cce3c..18bccc02a5 100644 --- a/src/Validator/StagedProjectsValidator.php +++ b/src/Validator/StagedProjectsValidator.php @@ -2,7 +2,7 @@ namespace Drupal\automatic_updates\Validator; -use Drupal\automatic_updates\Event\PreCommitEvent; +use Drupal\package_manager\Event\PreApplyEvent; use Drupal\package_manager\ValidationResult; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; @@ -28,13 +28,14 @@ final class StagedProjectsValidator implements EventSubscriberInterface { /** * Validates the staged packages. * - * @param \Drupal\automatic_updates\Event\PreCommitEvent $event + * @param \Drupal\package_manager\Event\PreApplyEvent $event * The event object. */ - public function validateStagedProjects(PreCommitEvent $event): void { + public function validateStagedProjects(PreApplyEvent $event): void { + $stage = $event->getStage(); try { - $active_packages = $event->getActiveComposer()->getDrupalExtensionPackages(); - $staged_packages = $event->getStageComposer()->getDrupalExtensionPackages(); + $active_packages = $stage->getActiveComposer()->getDrupalExtensionPackages(); + $staged_packages = $stage->getStageComposer()->getDrupalExtensionPackages(); } catch (\Throwable $e) { $result = ValidationResult::createError([ @@ -123,7 +124,7 @@ final class StagedProjectsValidator implements EventSubscriberInterface { * {@inheritdoc} */ public static function getSubscribedEvents() { - $events[PreCommitEvent::class][] = ['validateStagedProjects']; + $events[PreApplyEvent::class][] = ['validateStagedProjects']; return $events; } diff --git a/tests/modules/automatic_updates_test/src/ReadinessChecker/TestChecker1.php b/tests/modules/automatic_updates_test/src/ReadinessChecker/TestChecker1.php index 3fc71760d3..3a7efd8032 100644 --- a/tests/modules/automatic_updates_test/src/ReadinessChecker/TestChecker1.php +++ b/tests/modules/automatic_updates_test/src/ReadinessChecker/TestChecker1.php @@ -2,11 +2,11 @@ namespace Drupal\automatic_updates_test\ReadinessChecker; -use Drupal\automatic_updates\Event\PreCommitEvent; use Drupal\automatic_updates\Event\PreStartEvent; use Drupal\automatic_updates\Event\ReadinessCheckEvent; use Drupal\automatic_updates\Event\UpdateEvent; use Drupal\Core\State\StateInterface; +use Drupal\package_manager\Event\PreApplyEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -63,12 +63,12 @@ class TestChecker1 implements EventSubscriberInterface { /** * Adds test result to an update event from a state setting. * - * @param \Drupal\automatic_updates\Event\UpdateEvent $event + * @param object $event * The update event. * @param string $state_key * The state key. */ - protected function addResults(UpdateEvent $event, string $state_key): void { + protected function addResults(object $event, string $state_key): void { $results = $this->state->get($state_key, []); if ($results instanceof \Throwable) { throw $results; @@ -89,13 +89,13 @@ class TestChecker1 implements EventSubscriberInterface { } /** - * Adds test results for the pre-commit event. + * Adds test results for the pre-apply event. * - * @param \Drupal\automatic_updates\Event\UpdateEvent $event + * @param \Drupal\package_manager\Event\PreApplyEvent $event * The update event. */ - public function runPreCommitChecks(UpdateEvent $event): void { - $this->addResults($event, static::STATE_KEY . "." . PreCommitEvent::class); + public function runPreApplyChecks(PreApplyEvent $event): void { + $this->addResults($event, static::STATE_KEY . "." . PreApplyEvent::class); } /** @@ -115,7 +115,7 @@ class TestChecker1 implements EventSubscriberInterface { $priority = defined('AUTOMATIC_UPDATES_TEST_SET_PRIORITY') ? AUTOMATIC_UPDATES_TEST_SET_PRIORITY : 5; $events[ReadinessCheckEvent::class][] = ['runPreChecks', $priority]; $events[PreStartEvent::class][] = ['runStartChecks', $priority]; - $events[PreCommitEvent::class][] = ['runPreCommitChecks', $priority]; + $events[PreApplyEvent::class][] = ['runPreApplyChecks', $priority]; return $events; } diff --git a/tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php b/tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php new file mode 100644 index 0000000000..b552398246 --- /dev/null +++ b/tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php @@ -0,0 +1,148 @@ +<?php + +namespace Drupal\Tests\automatic_updates\Kernel\ReadinessValidation; + +use Drupal\package_manager\Event\PreApplyEvent; +use Drupal\package_manager\PathLocator; +use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase; + +/** + * @covers \Drupal\automatic_updates\Validator\StagedProjectsValidator + * + * @group automatic_updates + */ +class StagedProjectsValidatorTest extends AutomaticUpdatesKernelTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'automatic_updates', + 'package_manager', + ]; + + /** + * Runs the validator under test against an arbitrary pair of directories. + * + * @param string $active_dir + * The active directory to validate. + * @param string $stage_dir + * The stage directory to validate. + * + * @return \Drupal\package_manager\ValidationResult[] + * The validation results. + */ + private function validate(string $active_dir, string $stage_dir): array { + $locator = $this->prophesize(PathLocator::class); + $locator->getActiveDirectory()->willReturn($active_dir); + $locator->getStageDirectory()->willReturn($stage_dir); + $this->container->set('package_manager.path_locator', $locator->reveal()); + + $event = new PreApplyEvent( + $this->container->get('package_manager.stage') + ); + + $this->container->get('event_dispatcher')->dispatch($event); + return $event->getResults(); + } + + /** + * Tests that if an exception is thrown, the event will absorb it. + */ + public function testEventConsumesExceptionResults(): void { + $results = $this->validate('/fake/active/dir', '/fake/stage/dir'); + $this->assertCount(1, $results); + $messages = reset($results)->getMessages(); + $this->assertCount(1, $messages); + $this->assertStringContainsString('Composer could not find the config file: /fake/active/dir/composer.json', (string) reset($messages)); + } + + /** + * Tests validations errors. + * + * @param string $fixtures_dir + * The fixtures directory that provides the active and staged composer.lock + * files. + * @param string $expected_summary + * The expected error summary. + * @param string[] $expected_messages + * The expected error messages. + * + * @dataProvider providerErrors + */ + public function testErrors(string $fixtures_dir, string $expected_summary, array $expected_messages): void { + $this->assertNotEmpty($fixtures_dir); + $this->assertDirectoryExists($fixtures_dir); + + $results = $this->validate("$fixtures_dir/active", "$fixtures_dir/staged"); + $this->assertCount(1, $results); + $result = array_pop($results); + $this->assertSame($expected_summary, (string) $result->getSummary()); + $actual_messages = $result->getMessages(); + $this->assertCount(count($expected_messages), $actual_messages); + foreach ($expected_messages as $message) { + $actual_message = array_shift($actual_messages); + $this->assertSame($message, (string) $actual_message); + } + } + + /** + * Data provider for testErrors(). + * + * @return \string[][] + * Test cases for testErrors(). + */ + public function providerErrors(): array { + $fixtures_folder = realpath(__DIR__ . '/../../../fixtures/project_staged_validation'); + return [ + 'new_project_added' => [ + "$fixtures_folder/new_project_added", + 'The update cannot proceed because the following Drupal projects were installed during the update.', + [ + "module 'drupal/test_module2' installed.", + "custom module 'drupal/dev-test_module2' installed.", + ], + ], + 'project_removed' => [ + "$fixtures_folder/project_removed", + 'The update cannot proceed because the following Drupal projects were removed during the update.', + [ + "theme 'drupal/test_theme' removed.", + "custom theme 'drupal/dev-test_theme' removed.", + ], + ], + 'version_changed' => [ + "$fixtures_folder/version_changed", + 'The update cannot proceed because the following Drupal projects were unexpectedly updated. Only Drupal Core updates are currently supported.', + [ + "module 'drupal/test_module' from 1.3.0 to 1.3.1.", + "module 'drupal/dev-test_module' from 1.3.0 to 1.3.1.", + ], + ], + ]; + } + + /** + * Tests validation when there are no errors. + */ + public function testNoErrors(): void { + $fixtures_dir = realpath(__DIR__ . '/../../../fixtures/project_staged_validation/no_errors'); + $results = $this->validate("$fixtures_dir/active", "$fixtures_dir/staged"); + $this->assertIsArray($results); + $this->assertEmpty($results); + } + + /** + * Tests validation when a composer.lock file is not found. + */ + public function testNoLockFile(): void { + $fixtures_dir = realpath(__DIR__ . '/../../../fixtures/project_staged_validation/no_errors'); + + $results = $this->validate("$fixtures_dir/active", $fixtures_dir); + $this->assertCount(1, $results); + $result = array_pop($results); + $this->assertSame("No lockfile found. Unable to read locked packages", (string) $result->getMessages()[0]); + $this->assertSame('', (string) $result->getSummary()); + } + +} diff --git a/tests/src/Unit/StagedProjectsValidatorTest.php b/tests/src/Unit/StagedProjectsValidatorTest.php deleted file mode 100644 index 3c26777206..0000000000 --- a/tests/src/Unit/StagedProjectsValidatorTest.php +++ /dev/null @@ -1,191 +0,0 @@ -<?php - -namespace Drupal\Tests\automatic_updates\Unit; - -use Drupal\automatic_updates\Event\PreCommitEvent; -use Drupal\automatic_updates\Validator\StagedProjectsValidator; -use Drupal\Core\StringTranslation\PluralTranslatableMarkup; -use Drupal\Core\StringTranslation\TranslatableMarkup; -use Drupal\Core\StringTranslation\TranslationInterface; -use Drupal\package_manager\ComposerUtility; -use Drupal\Tests\UnitTestCase; - -/** - * @coversDefaultClass \Drupal\automatic_updates\Validator\StagedProjectsValidator - * - * @group automatic_updates - */ -class StagedProjectsValidatorTest extends UnitTestCase { - - /** - * Creates a pre-commit event object for testing. - * - * @param string $active_dir - * The active directory. - * @param string $stage_dir - * The stage directory. - * - * @return \Drupal\automatic_updates\Event\PreCommitEvent - * The event object. - */ - private function createEvent(string $active_dir, string $stage_dir): PreCommitEvent { - return new PreCommitEvent( - ComposerUtility::createForDirectory($active_dir), - ComposerUtility::createForDirectory($stage_dir) - ); - } - - /** - * Tests that if an exception is thrown, the update event will absorb it. - */ - public function testUpdateEventConsumesExceptionResults(): void { - $message = 'An exception thrown by Composer at runtime.'; - - $composer = $this->prophesize(ComposerUtility::class); - $composer->getDrupalExtensionPackages() - ->willThrow(new \RuntimeException($message)); - $event = new PreCommitEvent($composer->reveal(), $composer->reveal()); - - $validator = new StagedProjectsValidator(new TestTranslationManager()); - $validator->validateStagedProjects($event); - $results = $event->getResults(); - $this->assertCount(1, $results); - $messages = reset($results)->getMessages(); - $this->assertCount(1, $messages); - $this->assertSame($message, (string) reset($messages)); - } - - /** - * Tests validations errors. - * - * @param string $fixtures_dir - * The fixtures directory that provides the active and staged composer.lock - * files. - * @param string $expected_summary - * The expected error summary. - * @param string[] $expected_messages - * The expected error messages. - * - * @dataProvider providerErrors - * - * @covers ::validateStagedProjects - */ - public function testErrors(string $fixtures_dir, string $expected_summary, array $expected_messages): void { - $this->assertNotEmpty($fixtures_dir); - $this->assertDirectoryExists($fixtures_dir); - - $event = $this->createEvent("$fixtures_dir/active", "$fixtures_dir/staged"); - $validator = new StagedProjectsValidator(new TestTranslationManager()); - $validator->validateStagedProjects($event); - $results = $event->getResults(); - $this->assertCount(1, $results); - $result = array_pop($results); - $this->assertSame($expected_summary, (string) $result->getSummary()); - $actual_messages = $result->getMessages(); - $this->assertCount(count($expected_messages), $actual_messages); - foreach ($expected_messages as $message) { - $actual_message = array_shift($actual_messages); - $this->assertSame($message, (string) $actual_message); - } - - } - - /** - * Data provider for testErrors(). - * - * @return \string[][] - * Test cases for testErrors(). - */ - public function providerErrors(): array { - $fixtures_folder = realpath(__DIR__ . '/../../fixtures/project_staged_validation'); - return [ - 'new_project_added' => [ - "$fixtures_folder/new_project_added", - 'The update cannot proceed because the following Drupal projects were installed during the update.', - [ - "module 'drupal/test_module2' installed.", - "custom module 'drupal/dev-test_module2' installed.", - ], - ], - 'project_removed' => [ - "$fixtures_folder/project_removed", - 'The update cannot proceed because the following Drupal projects were removed during the update.', - [ - "theme 'drupal/test_theme' removed.", - "custom theme 'drupal/dev-test_theme' removed.", - ], - ], - 'version_changed' => [ - "$fixtures_folder/version_changed", - 'The update cannot proceed because the following Drupal projects were unexpectedly updated. Only Drupal Core updates are currently supported.', - [ - "module 'drupal/test_module' from 1.3.0 to 1.3.1.", - "module 'drupal/dev-test_module' from 1.3.0 to 1.3.1.", - ], - ], - ]; - } - - /** - * Tests validation when there are no errors. - * - * @covers ::validateStagedProjects - */ - public function testNoErrors(): void { - $fixtures_dir = realpath(__DIR__ . '/../../fixtures/project_staged_validation/no_errors'); - $event = $this->createEvent("$fixtures_dir/active", "$fixtures_dir/staged"); - $validator = new StagedProjectsValidator(new TestTranslationManager()); - $validator->validateStagedProjects($event); - $results = $event->getResults(); - $this->assertIsArray($results); - $this->assertEmpty($results); - } - - /** - * Tests validation when a composer.lock file is not found. - */ - public function testNoLockFile(): void { - $fixtures_dir = realpath(__DIR__ . '/../../fixtures/project_staged_validation/no_errors'); - - $event = $this->createEvent("$fixtures_dir/active", $fixtures_dir); - $validator = new StagedProjectsValidator(new TestTranslationManager()); - $validator->validateStagedProjects($event); - $results = $event->getResults(); - $this->assertCount(1, $results); - $result = array_pop($results); - $this->assertSame("No lockfile found. Unable to read locked packages", (string) $result->getMessages()[0]); - $this->assertSame('', (string) $result->getSummary()); - } - -} - -/** - * Implements a translation manager in tests. - * - * @todo Copied from core/modules/user/tests/src/Unit/PermissionHandlerTest.php - * when moving to core open an issue consolidate this test class. - */ -class TestTranslationManager implements TranslationInterface { - - /** - * {@inheritdoc} - */ - public function translate($string, array $args = [], array $options = []) { - return new TranslatableMarkup($string, $args, $options, $this); - } - - /** - * {@inheritdoc} - */ - public function translateString(TranslatableMarkup $translated_string) { - return $translated_string->getUntranslatedString(); - } - - /** - * {@inheritdoc} - */ - public function formatPlural($count, $singular, $plural, array $args = [], array $options = []) { - return new PluralTranslatableMarkup($count, $singular, $plural, $args, $options, $this); - } - -} -- GitLab