diff --git a/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityConcurrentRevisionSaveTest.php b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityConcurrentRevisionSaveTest.php new file mode 100644 index 0000000..cbc3cc5 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityConcurrentRevisionSaveTest.php @@ -0,0 +1,120 @@ +installEntitySchema('entity_test_mulrev_changed'); + $this->storage = $this->entityManager->getStorage('entity_test_mulrev_changed'); + $this->saveCounter = 1; + } + + /** + * Data provider for testConcurrentSave(). + */ + public function dataTestConcurrentSave() { + return [ + 'pending revision - pending revision' => [[static::SAVE_PENDING_REVISION, static::SAVE_PENDING_REVISION]], + 'no revision - pending revision' => [[static::SAVE_NO_REVISION, static::SAVE_PENDING_REVISION]], + 'new revision - pending revision' => [[static::SAVE_NEW_REVISION, static::SAVE_PENDING_REVISION]], + 'pending revision - no revision' => [[static::SAVE_PENDING_REVISION, static::SAVE_NO_REVISION]], + 'no revision - no revision' => [[static::SAVE_NO_REVISION, static::SAVE_NO_REVISION]], + 'new revision - no revision' => [[static::SAVE_NEW_REVISION, static::SAVE_NO_REVISION]], + 'pending revision - new revision' => [[static::SAVE_PENDING_REVISION, static::SAVE_NEW_REVISION]], + 'no revision - new revision' => [[static::SAVE_NO_REVISION, static::SAVE_NEW_REVISION]], + 'new revision - new revision' => [[static::SAVE_NEW_REVISION, static::SAVE_NEW_REVISION]], + ]; + } + + /** + * Tests concurrent entity saves with various save types. + * + * @param int[] $type + * An array of save types as defined by the related ::SAVE_* constants. One + * for each of the two concurrent saves. + * + * @covers \Drupal\Core\Entity\Plugin\Validation\Constraint\EntityChangedConstraintValidator::validate + * + * @dataProvider dataTestConcurrentSave + */ + public function testConcurrentSave(array $type) { + $user = $this->createUser(); + + // Create a test entity. + $entity = EntityTestMulRevChanged::create([ + 'not_translatable' => $this->randomString(), + 'user_id' => $user->id(), + 'language' => 'en', + ]); + $this->doEntitySave($entity, static::SAVE_NO_REVISION, FALSE); + + // Perform two "concurrent" saves and verify the second one triggers a + // validation error. + $entity_other_instance = clone $entity; + $this->doEntitySave($entity, $type[0], FALSE); + $this->doEntitySave($entity_other_instance, $type[1], TRUE); + } + + /** + * Performs an entity save of the specified "type". + * + * @param \Drupal\Core\Entity\ContentEntityInterface $entity + * The entity to be saved. + * @param int $type + * The save type, as defined by the ::SAVE_* constants. + * @param bool $error_expected + * Flag indicating whether a validation error is expected. + */ + protected function doEntitySave(ContentEntityInterface $entity, $type, $error_expected) { + // Set a meaningful entity label, useful for debugging. + $entity->set('name', 'Title ' . ($this->saveCounter++)); + if ($type & static::SAVE_NEW_REVISION) { + $entity->setNewRevision(); + } + if ($type == static::SAVE_PENDING_REVISION) { + $entity->isDefaultRevision(FALSE); + } + $violations = $entity->validate(); + $this->assertEquals($error_expected, (bool) $violations->count()); + $entity->save(); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityLoadedRevisionTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityLoadedRevisionTest.php index a3b99fe..5659280 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityLoadedRevisionTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityLoadedRevisionTest.php @@ -167,4 +167,35 @@ public function testSaveInHookEntityInsert() { } + /** + * Tests manual revert of the revision id value. + * + * @covers \Drupal\Core\Entity\ContentEntityBase::getRevisionId + * @covers \Drupal\Core\Entity\ContentEntityBase::getLoadedRevisionId + * @covers \Drupal\Core\Entity\ContentEntityBase::setNewRevision + */ + public function testNewRevisionRevert() { + $entity = EntityTestMulRev::create(['name' => 'EntityLoadedRevisionTest']); + $entity->save(); + + // Check that revision id field is reset while the loaded revision id is + // preserved when flagging a new revision. + $revision_id = $entity->getRevisionId(); + $entity->setNewRevision(); + $this->assertNull($entity->getRevisionId()); + $this->assertEquals($revision_id, $entity->getLoadedRevisionId()); + + // Check that manually reverting the new revision state by assigning a value + // to the revision id field works as expected. + $key = $entity->getEntityType()->getKey('revision'); + $entity->set($key, $revision_id); + $this->assertEquals($revision_id, $entity->getRevisionId()); + $this->assertEquals($revision_id, $entity->getLoadedRevisionId()); + + // Check that flagging a new revision again works correctly. + $entity->setNewRevision(); + $this->assertNull($entity->getRevisionId()); + $this->assertEquals($revision_id, $entity->getLoadedRevisionId()); + } + }