updated core from 8.4 to 8.5 : bug with login_destination

This commit is contained in:
Bachir Soussi Chiadmi
2018-03-13 14:01:21 +01:00
parent 0d78da249b
commit e668535a4e
3486 changed files with 89604 additions and 33283 deletions
@@ -81,12 +81,6 @@ class DefaultConfigTest extends KernelTestBase {
/** @var \Drupal\Core\Extension\ModuleInstallerInterface $module_installer */
$module_installer = $this->container->get('module_installer');
// @todo https://www.drupal.org/node/2308745 Rest has an implicit dependency
// on the Node module remove once solved.
if (in_array($module, ['rest', 'hal'])) {
$module_installer->install(['node']);
}
// Work out any additional modules and themes that need installing to create
// an optional config.
$optional_config_storage = new FileStorage($module_path . InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION);
@@ -0,0 +1,80 @@
<?php
namespace Drupal\KernelTests\Core\Action;
use Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver;
use Drupal\entity_test\Entity\EntityTestMulRevPub;
use Drupal\KernelTests\KernelTestBase;
use Drupal\system\Entity\Action;
/**
* @group Action
*/
class PublishActionTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['system', 'entity_test', 'user'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('entity_test_mulrevpub');
}
/**
* @covers \Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver::getDerivativeDefinitions
*/
public function testGetDerivativeDefinitions() {
$deriver = new EntityPublishedActionDeriver(\Drupal::entityTypeManager());
$this->assertArraySubset([
'entity_test_mulrevpub' => [
'type' => 'entity_test_mulrevpub',
'label' => 'Save test entity - revisions, data table, and published interface',
'action_label' => 'Save',
],
], $deriver->getDerivativeDefinitions([
'action_label' => 'Save',
]));
}
/**
* @covers \Drupal\Core\Action\Plugin\Action\PublishAction::execute
*/
public function testPublishAction() {
$entity = EntityTestMulRevPub::create(['name' => 'test']);
$entity->setUnpublished()->save();
$action = Action::create([
'id' => 'entity_publish_action',
'plugin' => 'entity:publish_action:entity_test_mulrevpub',
]);
$action->save();
$this->assertFalse($entity->isPublished());
$action->execute([$entity]);
$this->assertTrue($entity->isPublished());
$this->assertArraySubset(['module' => ['entity_test']], $action->getDependencies());
}
/**
* @covers \Drupal\Core\Action\Plugin\Action\UnpublishAction::execute
*/
public function testUnpublishAction() {
$entity = EntityTestMulRevPub::create(['name' => 'test']);
$entity->setPublished()->save();
$action = Action::create([
'id' => 'entity_unpublish_action',
'plugin' => 'entity:unpublish_action:entity_test_mulrevpub',
]);
$action->save();
$this->assertTrue($entity->isPublished());
$action->execute([$entity]);
$this->assertFalse($entity->isPublished());
$this->assertArraySubset(['module' => ['entity_test']], $action->getDependencies());
}
}
@@ -0,0 +1,62 @@
<?php
namespace Drupal\KernelTests\Core\Action;
use Drupal\Core\Action\Plugin\Action\Derivative\EntityChangedActionDeriver;
use Drupal\entity_test\Entity\EntityTestMulChanged;
use Drupal\KernelTests\KernelTestBase;
use Drupal\system\Entity\Action;
/**
* @group Action
*/
class SaveActionTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['system', 'entity_test', 'user'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('entity_test_mul_changed');
}
/**
* @covers \Drupal\Core\Action\Plugin\Action\Derivative\EntityChangedActionDeriver::getDerivativeDefinitions
*/
public function testGetDerivativeDefinitions() {
$deriver = new EntityChangedActionDeriver(\Drupal::entityTypeManager());
$this->assertArraySubset([
'entity_test_mul_changed' => [
'type' => 'entity_test_mul_changed',
'label' => 'Save test entity - data table',
'action_label' => 'Save',
],
], $deriver->getDerivativeDefinitions([
'action_label' => 'Save',
]));
}
/**
* @covers \Drupal\Core\Action\Plugin\Action\SaveAction::execute
*/
public function testSaveAction() {
$entity = EntityTestMulChanged::create(['name' => 'test']);
$entity->save();
$saved_time = $entity->getChangedTime();
$action = Action::create([
'id' => 'entity_save_action',
'plugin' => 'entity:save_action:entity_test_mul_changed',
]);
$action->save();
$action->execute([$entity]);
$this->assertNotSame($saved_time, $entity->getChangedTime());
$this->assertArraySubset(['module' => ['entity_test']], $action->getDependencies());
}
}
@@ -108,6 +108,10 @@ class ResolvedLibraryDefinitionsFilesMatchTest extends KernelTestBase {
}
return TRUE;
});
// Remove demo_umami_content module as its install hook creates content
// that relies on the presence of entity tables and various other elements
// not present in a kernel test.
unset($all_modules['demo_umami_content']);
$this->allModules = array_keys($all_modules);
$this->allModules[] = 'system';
sort($this->allModules);
@@ -20,10 +20,4 @@ class DrupalSetMessageTest extends KernelTestBase {
$this->assertEquals('A message: bar', (string) $messages['status'][0]);
}
protected function tearDown() {
// Clear session to prevent global leakage.
unset($_SESSION['messages']);
parent::tearDown();
}
}
@@ -125,7 +125,7 @@ class ConfigFileContentTest extends KernelTestBase {
$this->assertIdentical($config->get('null'), NULL);
// Read false that had been nested in an array value.
$this->assertSame($config->get($casting_array_false_value_key), FALSE, "Nested boolean FALSE value returned FALSE.");
$this->assertSame(FALSE, $config->get($casting_array_false_value_key), "Nested boolean FALSE value returned FALSE.");
// Unset a top level value.
$config->clear($key);
@@ -67,9 +67,15 @@ class QueryTest extends DatabaseTestBase {
public function testConditionOperatorArgumentsSQLInjection() {
$injection = "IS NOT NULL) ;INSERT INTO {test} (name) VALUES ('test12345678'); -- ";
// Convert errors to exceptions for testing purposes below.
set_error_handler(function ($severity, $message, $filename, $lineno) {
throw new \ErrorException($message, 0, $severity, $filename, $lineno);
$previous_error_handler = set_error_handler(function ($severity, $message, $filename, $lineno, $context) use (&$previous_error_handler) {
// Normalize the filename to use UNIX directory separators.
if (preg_match('@core/lib/Drupal/Core/Database/Query/Condition.php$@', str_replace(DIRECTORY_SEPARATOR, '/', $filename))) {
// Convert errors to exceptions for testing purposes below.
throw new \ErrorException($message, 0, $severity, $filename, $lineno);
}
if ($previous_error_handler) {
return $previous_error_handler($severity, $message, $filename, $lineno, $context);
}
});
try {
$result = db_select('test', 't')
@@ -0,0 +1,28 @@
<?php
namespace Drupal\KernelTests\Core\Datetime;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests timestamp schema.
*
* @group Common
*/
class TimestampSchemaTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['entity_test', 'field', 'field_timestamp_test'];
/**
* Tests if the timestamp field schema is validated.
*/
public function testTimestampSchema() {
$this->installConfig(['field_timestamp_test']);
// Make at least an assertion.
$this->assertTrue(TRUE);
}
}
@@ -297,7 +297,7 @@ class ContentEntityCloneTest extends EntityKernelTestBase {
// Retrieve the entity properties.
$reflection = new \ReflectionClass($entity);
$properties = $reflection->getProperties(~\ReflectionProperty::IS_STATIC);
$translation_unique_properties = ['activeLangcode', 'translationInitialize', 'fieldDefinitions', 'languages', 'langcodeKey', 'defaultLangcode', 'defaultLangcodeKey', 'validated', 'validationRequired', 'entityTypeId', 'typedData', 'cacheContexts', 'cacheTags', 'cacheMaxAge', '_serviceIds'];
$translation_unique_properties = ['activeLangcode', 'translationInitialize', 'fieldDefinitions', 'languages', 'langcodeKey', 'defaultLangcode', 'defaultLangcodeKey', 'revisionTranslationAffectedKey', 'validated', 'validationRequired', 'entityTypeId', 'typedData', 'cacheContexts', 'cacheTags', 'cacheMaxAge', '_serviceIds'];
foreach ($properties as $property) {
// Modify each entity property on the clone and assert that the change is
@@ -0,0 +1,89 @@
<?php
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\KernelTests\KernelTestBase;
use Drupal\node\Entity\NodeType;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests the ContentEntityStorageBase::createWithSampleValues method.
*
* @coversDefaultClass \Drupal\Core\Entity\ContentEntityStorageBase
* @group Entity
*/
class CreateSampleEntityTest extends KernelTestBase {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* {@inheritdoc}
*/
public static $modules = ['system', 'field', 'filter', 'text', 'file', 'user', 'node', 'comment', 'taxonomy'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setup();
$this->installEntitySchema('file');
$this->installEntitySchema('user');
$this->installEntitySchema('node');
$this->installEntitySchema('node_type');
$this->installEntitySchema('file');
$this->installEntitySchema('comment');
$this->installEntitySchema('comment_type');
$this->installEntitySchema('taxonomy_vocabulary');
$this->installEntitySchema('taxonomy_term');
$this->entityTypeManager = $this->container->get('entity_type.manager');
NodeType::create(['type' => 'article', 'name' => 'Article'])->save();
NodeType::create(['type' => 'page', 'name' => 'Page'])->save();
Vocabulary::create(['name' => 'Tags', 'vid' => 'tags'])->save();
}
/**
* Tests sample value content entity creation of all types.
*
* @covers ::createWithSampleValues
*/
public function testSampleValueContentEntity() {
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $definition) {
if ($definition->entityClassImplements(FieldableEntityInterface::class)) {
$label = $definition->getKey('label');
$values = [];
if ($label) {
$title = $this->randomString();
$values[$label] = $title;
}
// Create sample entities with bundles.
if ($bundle_type = $definition->getBundleEntityType()) {
foreach ($this->entityTypeManager->getStorage($bundle_type)->loadMultiple() as $bundle) {
$entity = $this->entityTypeManager->getStorage($entity_type_id)->createWithSampleValues($bundle->id(), $values);
$violations = $entity->validate();
$this->assertCount(0, $violations);
if ($label) {
$this->assertEquals($title, $entity->label());
}
}
}
// Create sample entities without bundles.
else {
$entity = $this->entityTypeManager->getStorage($entity_type_id)->createWithSampleValues(FALSE, $values);
$violations = $entity->validate();
$this->assertCount(0, $violations);
if ($label) {
$this->assertEquals($title, $entity->label());
}
}
}
}
}
}
@@ -8,6 +8,7 @@ use Drupal\Core\Access\AccessibleInterface;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Session\AnonymousUserSession;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\entity_test\Entity\EntityTestStringId;
use Drupal\entity_test\Entity\EntityTestDefaultAccess;
use Drupal\entity_test\Entity\EntityTestNoUuid;
use Drupal\entity_test\Entity\EntityTestLabel;
@@ -18,6 +19,7 @@ use Drupal\user\Entity\User;
/**
* Tests the entity access control handler.
*
* @coversDefaultClass \Drupal\Core\Entity\EntityAccessControlHandler
* @group Entity
*/
class EntityAccessControlHandlerTest extends EntityLanguageTestBase {
@@ -30,6 +32,7 @@ class EntityAccessControlHandlerTest extends EntityLanguageTestBase {
$this->installEntitySchema('entity_test_no_uuid');
$this->installEntitySchema('entity_test_rev');
$this->installEntitySchema('entity_test_string_id');
}
/**
@@ -293,4 +296,73 @@ class EntityAccessControlHandlerTest extends EntityLanguageTestBase {
$this->assertEqual($state->get('entity_test_entity_test_access'), TRUE);
}
/**
* Tests the default access handling for the ID and UUID fields.
*
* @covers ::fieldAccess
* @dataProvider providerTestFieldAccess
*/
public function testFieldAccess($entity_class, array $entity_create_values, $expected_id_create_access) {
// Set up a non-admin user that is allowed to create and update test
// entities.
\Drupal::currentUser()->setAccount($this->createUser(['uid' => 2], ['administer entity_test content']));
// Create the entity to test field access with.
$entity = $entity_class::create($entity_create_values);
// On newly-created entities, field access must allow setting the UUID
// field.
$this->assertTrue($entity->get('uuid')->access('edit'));
$this->assertTrue($entity->get('uuid')->access('edit', NULL, TRUE)->isAllowed());
// On newly-created entities, field access will not allow setting the ID
// field if the ID is of type serial. It will allow access if it is of type
// string.
$this->assertEquals($expected_id_create_access, $entity->get('id')->access('edit'));
$this->assertEquals($expected_id_create_access, $entity->get('id')->access('edit', NULL, TRUE)->isAllowed());
// Save the entity and check that we can not update the ID or UUID fields
// anymore.
$entity->save();
// If the ID has been set as part of the create ensure it has been set
// correctly.
if (isset($entity_create_values['id'])) {
$this->assertSame($entity_create_values['id'], $entity->id());
}
// The UUID is hard-coded by the data provider.
$this->assertSame('60e3a179-79ed-4653-ad52-5e614c8e8fbe', $entity->uuid());
$this->assertFalse($entity->get('uuid')->access('edit'));
$access_result = $entity->get('uuid')->access('edit', NULL, TRUE);
$this->assertTrue($access_result->isForbidden());
$this->assertEquals('The entity UUID cannot be changed', $access_result->getReason());
// Ensure the ID is still not allowed to be edited.
$this->assertFalse($entity->get('id')->access('edit'));
$access_result = $entity->get('id')->access('edit', NULL, TRUE);
$this->assertTrue($access_result->isForbidden());
$this->assertEquals('The entity ID cannot be changed', $access_result->getReason());
}
public function providerTestFieldAccess() {
return [
'serial ID entity' => [
EntityTest::class,
[
'name' => 'A test entity',
'uuid' => '60e3a179-79ed-4653-ad52-5e614c8e8fbe',
],
FALSE
],
'string ID entity' => [
EntityTestStringId::class,
[
'id' => 'a_test_entity',
'name' => 'A test entity',
'uuid' => '60e3a179-79ed-4653-ad52-5e614c8e8fbe',
],
TRUE
],
];
}
}
@@ -93,7 +93,7 @@ class EntityBundleFieldTest extends EntityKernelTestBase {
$entity->save();
entity_test_delete_bundle('custom');
$table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_bundle_field'));
$table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_bundle_field'), TRUE);
$result = $this->database->select($table, 'f')
->condition('f.entity_id', $entity->id())
->condition('deleted', 1)
@@ -105,9 +105,10 @@ class EntityBundleFieldTest extends EntityKernelTestBase {
$field_map = \Drupal::entityManager()->getFieldMap();
$this->assertFalse(isset($field_map['entity_test']['custom_bundle_field']));
// @todo Test field purge and table deletion once supported. See
// https://www.drupal.org/node/2282119.
// $this->assertFalse($this->database->schema()->tableExists($table), 'Custom field table was deleted');
// Purge field data, and check that the storage definition has been
// completely removed once the data is purged.
field_purge_batch(10);
$this->assertFalse($this->database->schema()->tableExists($table), 'Custom field table was deleted');
}
}
@@ -0,0 +1,591 @@
<?php
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\user\Entity\User;
/**
* Test decoupled translation revisions.
*
* @group entity
*
* @coversDefaultClass \Drupal\Core\Entity\ContentEntityStorageBase
*/
class EntityDecoupledTranslationRevisionsTest extends EntityKernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'system',
'entity_test',
'language',
];
/**
* The entity type bundle info service.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $bundleInfo;
/**
* The entity storage.
*
* @var \Drupal\Core\Entity\ContentEntityStorageInterface
*/
protected $storage;
/**
* The translations of the test entity.
*
* @var \Drupal\Core\Entity\ContentEntityInterface[]
*/
protected $translations;
/**
* The previous revision identifiers for the various revision translations.
*
* @var int[]
*/
protected $previousRevisionId = [];
/**
* The previous unstranslatable field value.
*
* @var string[]
*/
protected $previousUntranslatableFieldValue;
/**
* The current edit sequence step index.
*
* @var int
*/
protected $stepIndex;
/**
* The current edit sequence step info.
*
* @var array
*/
protected $stepInfo;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$entity_type_id = 'entity_test_mulrev';
$this->installEntitySchema($entity_type_id);
$this->storage = $this->container->get('entity_type.manager')
->getStorage($entity_type_id);
$this->installConfig(['language']);
$langcodes = ['it', 'fr'];
foreach ($langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
$values = [
'name' => $this->randomString(),
'status' => 1,
];
User::create($values)->save();
// Make sure entity bundles are translatable.
$this->state->set('entity_test.translation', TRUE);
$this->bundleInfo = \Drupal::service('entity_type.bundle.info');
$this->bundleInfo->clearCachedBundles();
}
/**
* Data provider for ::testDecoupledDefaultRevisions.
*/
public function dataTestDecoupledPendingRevisions() {
$sets = [];
$sets['Intermixed languages - No initial default translation'][] = [
['en', TRUE],
['en', FALSE],
['it', FALSE],
['en', FALSE],
['it', FALSE],
['en', TRUE],
['it', TRUE],
];
$sets['Intermixed languages - With initial default translation'][] = [
['en', TRUE],
['it', TRUE],
['en', FALSE],
['it', FALSE],
['en', TRUE],
['it', TRUE],
];
$sets['Alternate languages - No initial default translation'][] = [
['en', TRUE],
['en', FALSE],
['en', FALSE],
['en', TRUE],
['it', FALSE],
['en', TRUE],
['it', FALSE],
['it', FALSE],
['it', TRUE],
];
$sets['Alternate languages - With initial default translation'][] = [
['en', TRUE],
['it', TRUE],
['en', TRUE],
['en', FALSE],
['en', FALSE],
['en', TRUE],
['it', TRUE],
['it', FALSE],
['it', FALSE],
['it', TRUE],
];
$sets['Multiple languages - No initial default translation'][] = [
['en', TRUE],
['it', FALSE],
['fr', FALSE],
['en', FALSE],
['en', TRUE],
['it', TRUE],
['fr', FALSE],
['en', FALSE],
['it', FALSE],
['en', TRUE],
['fr', TRUE],
['it', TRUE],
['fr', TRUE],
];
$sets['Multiple languages - With initial default translation'][] = [
['en', TRUE],
['it', TRUE],
['fr', TRUE],
['en', FALSE],
['it', FALSE],
['en', TRUE],
['it', TRUE],
['fr', FALSE],
['en', FALSE],
['it', FALSE],
['en', TRUE],
['fr', TRUE],
['it', TRUE],
['fr', TRUE],
];
return $sets;
}
/**
* Test decoupled default revisions.
*
* @param array[] $sequence
* An array with arrays of arguments for the ::doSaveNewRevision() method as
* values. Every child array corresponds to a method invocation.
*
* @covers ::createRevision
*
* @dataProvider dataTestDecoupledPendingRevisions
*/
public function testDecoupledPendingRevisions($sequence) {
$revision_id = $this->doTestEditSequence($sequence);
$this->assertEquals(count($sequence), $revision_id);
}
/**
* Data provider for ::testUntranslatableFields.
*/
public function dataTestUntranslatableFields() {
$sets = [];
$sets['Default behavior - Untranslatable fields affect all revisions'] = [
[
['en', TRUE, TRUE],
['it', FALSE, TRUE, FALSE],
['en', FALSE, TRUE, FALSE],
['en', TRUE, TRUE],
['it', TRUE, TRUE],
['en', FALSE],
['it', FALSE],
['en', TRUE],
['it', TRUE],
],
FALSE,
];
$sets['Alternative behavior - Untranslatable fields affect only default translation'] = [
[
['en', TRUE, TRUE],
['it', FALSE, TRUE, FALSE],
['en', FALSE, TRUE],
['it', TRUE, TRUE, FALSE],
['it', FALSE],
['it', TRUE],
['en', TRUE, TRUE],
['it', FALSE],
['en', FALSE],
['it', TRUE],
['en', TRUE, TRUE],
],
TRUE,
];
return $sets;
}
/**
* Tests that untranslatable fields are handled correctly.
*
* @param array[] $sequence
* An array with arrays of arguments for the ::doSaveNewRevision() method as
* values. Every child array corresponds to a method invocation.
*
* @param bool $default_translation_affected
* Whether untranslatable field changes affect all revisions or only the
* default revision.
*
* @covers ::createRevision
* @covers \Drupal\Core\Entity\Plugin\Validation\Constraint\EntityUntranslatableFieldsConstraintValidator::validate
*
* @dataProvider dataTestUntranslatableFields
*/
public function testUntranslatableFields($sequence, $default_translation_affected) {
// Configure the untranslatable fields edit mode.
$this->state->set('entity_test.untranslatable_fields.default_translation_affected', $default_translation_affected);
$this->bundleInfo->clearCachedBundles();
// Test that a new entity is always valid.
$entity = EntityTestMulRev::create();
$entity->set('non_mul_field', 0);
$violations = $entity->validate();
$this->assertEmpty($violations);
// Test the specified sequence.
$this->doTestEditSequence($sequence);
}
/**
* Actually tests an edit step sequence.
*
* @param array[] $sequence
* An array of sequence steps.
*
* @return int
* The latest saved revision id.
*/
protected function doTestEditSequence($sequence) {
$revision_id = NULL;
foreach ($sequence as $index => $step) {
$this->stepIndex = $index;
$revision_id = call_user_func_array([$this, 'doEditStep'], $step);
}
return $revision_id;
}
/**
* Saves a new revision of the test entity.
*
* @param string $active_langcode
* The language of the translation for which a new revision will be saved.
* @param bool $default_revision
* Whether the revision should be flagged as the default revision.
* @param bool $untranslatable_update
* (optional) Whether an untranslatable field update should be performed.
* Defaults to FALSE.
* @param bool $valid
* (optional) Whether entity validation is expected to succeed. Defaults to
* TRUE.
*
* @return int
* The new revision identifier.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
protected function doEditStep($active_langcode, $default_revision, $untranslatable_update = FALSE, $valid = TRUE) {
$this->stepInfo = [$active_langcode, $default_revision, $untranslatable_update, $valid];
// If changes to untranslatable fields affect only the default translation,
// we can different values for untranslatable fields in the various
// revision translations, so we need to track their previous value per
// language.
$all_translations_affected = !$this->state->get('entity_test.untranslatable_fields.default_translation_affected');
$previous_untranslatable_field_langcode = $all_translations_affected ? LanguageInterface::LANGCODE_DEFAULT : $active_langcode;
// Initialize previous data tracking.
if (!isset($this->translations)) {
$this->translations[$active_langcode] = EntityTestMulRev::create();
$this->previousRevisionId[$active_langcode] = 0;
$this->previousUntranslatableFieldValue[$previous_untranslatable_field_langcode] = NULL;
}
if (!isset($this->translations[$active_langcode])) {
$this->translations[$active_langcode] = reset($this->translations)->addTranslation($active_langcode);
$this->previousRevisionId[$active_langcode] = 0;
$this->previousUntranslatableFieldValue[$active_langcode] = NULL;
}
// We want to update previous data only if we expect a valid result,
// otherwise we would be just polluting it with invalid values.
if ($valid) {
$entity = &$this->translations[$active_langcode];
$previous_revision_id = &$this->previousRevisionId[$active_langcode];
$previous_untranslatable_field_value = &$this->previousUntranslatableFieldValue[$previous_untranslatable_field_langcode];
}
else {
$entity = clone $this->translations[$active_langcode];
$previous_revision_id = $this->previousRevisionId[$active_langcode];
$previous_untranslatable_field_value = $this->previousUntranslatableFieldValue[$previous_untranslatable_field_langcode];
}
// Check that after instantiating a new revision for the specified
// translation, we are resuming work from where we left the last time. If
// that is the case, the label generated for the previous revision should
// match the stored one.
if (!$entity->isNew()) {
$previous_label = NULL;
if (!$entity->isNewTranslation()) {
$previous_label = $this->generateNewEntityLabel($entity, $previous_revision_id);
$latest_affected_revision_id = $this->storage->getLatestTranslationAffectedRevisionId($entity->id(), $entity->language()->getId());
}
else {
// Normally it would make sense to load the default revision in this
// case, however that would mean simulating here the logic that we need
// to test, thus "masking" possible flaws. To avoid that, we simply
// pretend we are starting from an earlier non translated revision.
// This ensures that the we can check that the merging logic is applied
// also when adding a new translation.
$latest_affected_revision_id = 1;
}
$previous_revision_id = (int) $entity->getLoadedRevisionId();
/** @var \Drupal\Core\Entity\ContentEntityInterface $latest_affected_revision */
$latest_affected_revision = $this->storage->loadRevision($latest_affected_revision_id);
$translation = $latest_affected_revision->hasTranslation($active_langcode) ?
$latest_affected_revision->getTranslation($active_langcode) : $latest_affected_revision->addTranslation($active_langcode);
$entity = $this->storage->createRevision($translation, $default_revision);
$this->assertEquals($default_revision, $entity->isDefaultRevision());
$this->assertEquals($translation->getLoadedRevisionId(), $entity->getLoadedRevisionId());
$this->assertEquals($previous_label, $entity->label(), $this->formatMessage('Loaded translatable field value does not match the previous one.'));
}
// Check that the previous untranslatable field value is loaded in the new
// revision as expected. When we are dealing with a non default translation
// the expected value is always the one stored in the default revision, as
// untranslatable fields can only be changed in the default translation or
// in the default revision, depending on the configured mode.
$value = $entity->get('non_mul_field')->value;
if (isset($previous_untranslatable_field_value)) {
$this->assertEquals($previous_untranslatable_field_value, $value, $this->formatMessage('Loaded untranslatable field value does not match the previous one.'));
}
elseif (!$entity->isDefaultTranslation()) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $default_revision */
$default_revision = $this->storage->loadUnchanged($entity->id());
$expected_value = $default_revision->get('non_mul_field')->value;
$this->assertEquals($expected_value, $value, $this->formatMessage('Loaded untranslatable field value does not match the previous one.'));
}
// Perform a change and store it.
$label = $this->generateNewEntityLabel($entity, $previous_revision_id, TRUE);
$entity->set('name', $label);
if ($untranslatable_update) {
// Store the revision ID of the previous untranslatable fields update in
// the new value, besides the upcoming revision ID. Useful to analyze test
// failures.
$prev = 0;
if (isset($previous_untranslatable_field_value)) {
preg_match('/^\d+ -> (\d+)$/', $previous_untranslatable_field_value, $matches);
$prev = $matches[1];
}
$value = $prev . ' -> ' . ($entity->getLoadedRevisionId() + 1);
$entity->set('non_mul_field', $value);
$previous_untranslatable_field_value = $value;
}
$violations = $entity->validate();
$messages = [];
foreach ($violations as $violation) {
/** \Symfony\Component\Validator\ConstraintViolationInterface */
$messages[] = $violation->getMessage();
}
$this->assertEquals($valid, !$violations->count(), $this->formatMessage('Validation does not match the expected result: %s', implode(', ', $messages)));
if ($valid) {
$entity->save();
// Reload the current revision translation and the default revision to
// make sure data was stored correctly.
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $this->storage->loadRevision($entity->getRevisionId());
$entity = $entity->getTranslation($active_langcode);
/** @var \Drupal\Core\Entity\ContentEntityInterface $default_entity */
$default_entity = $this->storage->loadUnchanged($entity->id());
// Verify that the values for the current revision translation match the
// expected ones, while for the other translations they match the default
// revision. We also need to verify that only the current revision
// translation was marked as affected.
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
$translation = $entity->getTranslation($langcode);
$rta_expected = $langcode == $active_langcode || ($untranslatable_update && $all_translations_affected);
$this->assertEquals($rta_expected, $translation->isRevisionTranslationAffected(), $this->formatMessage("'$langcode' translation incorrectly affected"));
$label_expected = $label;
if ($langcode !== $active_langcode) {
$default_translation = $default_entity->hasTranslation($langcode) ? $default_entity->getTranslation($langcode) : $default_entity;
$label_expected = $default_translation->label();
}
$this->assertEquals($label_expected, $translation->label(), $this->formatMessage("Incorrect '$langcode' translation label"));
}
}
return $entity->getRevisionId();
}
/**
* Generates a new label for the specified revision.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $revision
* An entity object.
* @param int $previous_revision_id
* The previous revision identifier for this revision translation.
* @param bool $next
* (optional) Whether the label describes the current revision or the one
* to be created. Defaults to FALSE.
*
* @return string
* A revision label.
*/
protected function generateNewEntityLabel(ContentEntityInterface $revision, $previous_revision_id, $next = FALSE) {
$language_label = $revision->language()->getName();
$revision_type = $revision->isDefaultRevision() ? 'Default' : 'Pending';
$revision_id = $next ? $this->storage->getLatestRevisionId($revision->id()) + 1 : $revision->getLoadedRevisionId();
return sprintf('%s (%s %d -> %d)', $language_label, $revision_type, $previous_revision_id, $revision_id);
}
/**
* Formats an assertion message.
*
* @param string $message
* The human-readable message.
*
* @return string
* The formatted message.
*/
protected function formatMessage($message) {
$args = func_get_args();
array_shift($args);
$params = array_merge($args, $this->stepInfo);
array_unshift($params, $this->stepIndex + 1);
array_unshift($params, '[Step %d] ' . $message . ' (langcode: %s, default_revision: %d, untranslatable_update: %d, valid: %d)');
return call_user_func_array('sprintf', $params);
}
/**
* Checks that changes to multiple translations are handled correctly.
*
* @covers ::createRevision
* @covers \Drupal\Core\Entity\Plugin\Validation\Constraint\EntityUntranslatableFieldsConstraintValidator::validate
*/
public function testMultipleTranslationChanges() {
// Configure the untranslatable fields edit mode.
$this->state->set('entity_test.untranslatable_fields.default_translation_affected', TRUE);
$this->bundleInfo->clearCachedBundles();
$entity = EntityTestMulRev::create();
$entity->get('name')->value = 'Test 1.1 EN';
$entity->get('non_mul_field')->value = 'Test 1.1';
$this->storage->save($entity);
/** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
$revision = $this->storage->createRevision($entity->addTranslation('it'));
$revision->get('name')->value = 'Test 1.2 IT';
$this->storage->save($revision);
$revision = $this->storage->createRevision($revision->getTranslation('en'), FALSE);
$revision->get('non_mul_field')->value = 'Test 1.3';
$revision->getTranslation('it')->get('name')->value = 'Test 1.3 IT';
$violations = $revision->validate();
$this->assertCount(1, $violations);
$this->assertEquals('Non-translatable fields can only be changed when updating the original language.', $violations[0]->getMessage());
}
/**
* Tests that internal properties are preserved while creating a new revision.
*/
public function testInternalProperties() {
$entity = EntityTestMulRev::create();
$this->doTestInternalProperties($entity);
$entity = EntityTestMulRev::create();
$entity->save();
$this->doTestInternalProperties($entity);
/** @var \Drupal\entity_test\Entity\EntityTestMulRev $translation */
$translation = EntityTestMulRev::create()->addTranslation('it');
$translation->save();
$this->doTestInternalProperties($translation);
}
/**
* Checks that internal properties are preserved for the specified entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* An entity object.
*/
protected function doTestInternalProperties(ContentEntityInterface $entity) {
$this->assertFalse($entity->isValidationRequired());
$entity->setValidationRequired(TRUE);
$this->assertTrue($entity->isValidationRequired());
$new_revision = $this->storage->createRevision($entity);
$this->assertTrue($new_revision->isValidationRequired());
}
/**
* Tests that deleted translations are not accidentally restored.
*
* @covers ::createRevision
*/
public function testRemovedTranslations() {
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = EntityTestMulRev::create(['name' => 'Test 1.1 EN']);
$this->storage->save($entity);
/** @var \Drupal\Core\Entity\ContentEntityInterface $it_revision */
$it_revision = $this->storage->createRevision($entity->addTranslation('it'));
$it_revision->set('name', 'Test 1.2 IT');
$this->storage->save($it_revision);
/** @var \Drupal\Core\Entity\ContentEntityInterface $en_revision */
$en_revision = $this->storage->createRevision($it_revision->getUntranslated(), FALSE);
$en_revision->set('name', 'Test 1.3 EN');
$this->storage->save($en_revision);
/** @var \Drupal\Core\Entity\ContentEntityInterface $en_revision */
$it_revision = $this->storage->createRevision($it_revision);
$en_revision = $it_revision->getUntranslated();
$en_revision->removeTranslation('it');
$this->storage->save($en_revision);
$revision_id = $this->storage->getLatestTranslationAffectedRevisionId($entity->id(), 'en');
$en_revision = $this->storage->loadRevision($revision_id);
$en_revision = $this->storage->createRevision($en_revision);
$en_revision->set('name', 'Test 1.5 EN');
$this->storage->save($en_revision);
$en_revision = $this->storage->loadRevision($en_revision->getRevisionId());
$this->assertFalse($en_revision->hasTranslation('it'));
}
}
@@ -113,6 +113,7 @@ class EntityDefinitionUpdateTest extends EntityKernelTestBase {
// The revision key is now defined, so the revision field needs to be
// created.
t('The %field_name field needs to be installed.', ['%field_name' => 'Revision ID']),
t('The %field_name field needs to be installed.', ['%field_name' => 'Default revision']),
],
];
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
@@ -389,53 +390,247 @@ class EntityDefinitionUpdateTest extends EntityKernelTestBase {
/**
* Tests deleting a base field when it has existing data.
*
* @dataProvider baseFieldDeleteWithExistingDataTestCases
*/
public function testBaseFieldDeleteWithExistingData() {
public function testBaseFieldDeleteWithExistingData($entity_type_id, $create_entity_revision, $base_field_revisionable) {
/** @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage */
$storage = $this->entityManager->getStorage($entity_type_id);
$schema_handler = $this->database->schema();
// Create an entity without the base field, to ensure NULL values are not
// added to the dedicated table storage to be purged.
$entity = $storage->create();
$entity->save();
// Add the base field and run the update.
$this->addBaseField();
$this->addBaseField('string', $entity_type_id, $base_field_revisionable);
$this->entityDefinitionUpdateManager->applyUpdates();
// Save an entity with the base field populated.
$this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => 'foo'])->save();
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = $storage->getTableMapping();
$storage_definition = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id)['new_base_field'];
// Remove the base field and apply updates. It's expected to throw an
// exception.
// @todo Revisit that expectation once purging is implemented for
// all fields: https://www.drupal.org/node/2282119.
$this->removeBaseField();
try {
$this->entityDefinitionUpdateManager->applyUpdates();
$this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
// Save an entity with the base field populated.
$entity = $storage->create(['new_base_field' => 'foo']);
$entity->save();
if ($create_entity_revision) {
$entity->setNewRevision(TRUE);
$entity->new_base_field = 'bar';
$entity->save();
}
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
$this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
// Remove the base field and apply updates.
$this->removeBaseField($entity_type_id);
$this->entityDefinitionUpdateManager->applyUpdates();
// Check that the base field's column is deleted.
$this->assertFalse($schema_handler->fieldExists($entity_type_id, 'new_base_field'), 'Column deleted from shared table for new_base_field.');
// Check that a dedicated 'deleted' table was created for the deleted base
// field.
$dedicated_deleted_table_name = $table_mapping->getDedicatedDataTableName($storage_definition, TRUE);
$this->assertTrue($schema_handler->tableExists($dedicated_deleted_table_name), 'A dedicated table was created for the deleted new_base_field.');
// Check that the deleted field's data is preserved in the dedicated
// 'deleted' table.
$result = $this->database->select($dedicated_deleted_table_name, 't')
->fields('t')
->execute()
->fetchAll();
$this->assertCount(1, $result);
$expected = [
'bundle' => $entity->bundle(),
'deleted' => '1',
'entity_id' => $entity->id(),
'revision_id' => $create_entity_revision ? $entity->getRevisionId() : $entity->id(),
'langcode' => $entity->language()->getId(),
'delta' => '0',
'new_base_field_value' => $entity->new_base_field->value,
];
// Use assertEquals and not assertSame here to prevent that a different
// sequence of the columns in the table will affect the check.
$this->assertEquals($expected, (array) $result[0]);
if ($create_entity_revision) {
$dedicated_deleted_revision_table_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, TRUE);
$this->assertTrue($schema_handler->tableExists($dedicated_deleted_revision_table_name), 'A dedicated revision table was created for the deleted new_base_field.');
$result = $this->database->select($dedicated_deleted_revision_table_name, 't')
->fields('t')
->orderBy('revision_id', 'DESC')
->execute()
->fetchAll();
// Only one row will be created for non-revisionable base fields.
$this->assertCount($base_field_revisionable ? 2 : 1, $result);
// Use assertEquals and not assertSame here to prevent that a different
// sequence of the columns in the table will affect the check.
$this->assertEquals([
'bundle' => $entity->bundle(),
'deleted' => '1',
'entity_id' => $entity->id(),
'revision_id' => '3',
'langcode' => $entity->language()->getId(),
'delta' => '0',
'new_base_field_value' => 'bar',
], (array) $result[0]);
// Two rows only exist if the base field is revisionable.
if ($base_field_revisionable) {
// Use assertEquals and not assertSame here to prevent that a different
// sequence of the columns in the table will affect the check.
$this->assertEquals([
'bundle' => $entity->bundle(),
'deleted' => '1',
'entity_id' => $entity->id(),
'revision_id' => '2',
'langcode' => $entity->language()->getId(),
'delta' => '0',
'new_base_field_value' => 'foo',
], (array) $result[1]);
}
}
// Check that the field storage definition is marked for purging.
$deleted_storage_definitions = \Drupal::service('entity_field.deleted_fields_repository')->getFieldStorageDefinitions();
$this->assertArrayHasKey($storage_definition->getUniqueStorageIdentifier(), $deleted_storage_definitions, 'The base field is marked for purging.');
// Purge field data, and check that the storage definition has been
// completely removed once the data is purged.
field_purge_batch(10);
$deleted_storage_definitions = \Drupal::service('entity_field.deleted_fields_repository')->getFieldStorageDefinitions();
$this->assertEmpty($deleted_storage_definitions, 'The base field has been deleted.');
$this->assertFalse($schema_handler->tableExists($dedicated_deleted_table_name), 'A dedicated field table was deleted after new_base_field was purged.');
if (isset($dedicated_deleted_revision_table_name)) {
$this->assertFalse($schema_handler->tableExists($dedicated_deleted_revision_table_name), 'A dedicated field revision table was deleted after new_base_field was purged.');
}
}
/**
* Test cases for ::testBaseFieldDeleteWithExistingData.
*/
public function baseFieldDeleteWithExistingDataTestCases() {
return [
'Non-revisionable entity type' => [
'entity_test_update',
FALSE,
FALSE,
],
'Non-revisionable custom data table' => [
'entity_test_mul',
FALSE,
FALSE,
],
'Non-revisionable entity type, revisionable base field' => [
'entity_test_update',
FALSE,
TRUE,
],
'Non-revisionable custom data table, revisionable base field' => [
'entity_test_mul',
FALSE,
TRUE,
],
'Revisionable entity type, non revisionable base field' => [
'entity_test_mulrev',
TRUE,
FALSE,
],
'Revisionable entity type, revisionable base field' => [
'entity_test_mulrev',
TRUE,
TRUE,
],
'Non-translatable revisionable entity type, revisionable base field' => [
'entity_test_rev',
TRUE,
TRUE,
],
'Non-translatable revisionable entity type, non-revisionable base field' => [
'entity_test_rev',
TRUE,
FALSE,
],
];
}
/**
* Tests deleting a bundle field when it has existing data.
*/
public function testBundleFieldDeleteWithExistingData() {
/** @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage */
$storage = $this->entityManager->getStorage('entity_test_update');
$schema_handler = $this->database->schema();
// Add the bundle field and run the update.
$this->addBundleField();
$this->entityDefinitionUpdateManager->applyUpdates();
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = $storage->getTableMapping();
$storage_definition = $this->entityManager->getLastInstalledFieldStorageDefinitions('entity_test_update')['new_bundle_field'];
// Check that the bundle field has a dedicated table.
$dedicated_table_name = $table_mapping->getDedicatedDataTableName($storage_definition);
$this->assertTrue($schema_handler->tableExists($dedicated_table_name), 'The bundle field uses a dedicated table.');
// Save an entity with the bundle field populated.
entity_test_create_bundle('custom');
$this->entityManager->getStorage('entity_test_update')->create(['type' => 'test_bundle', 'new_bundle_field' => 'foo'])->save();
$entity = $storage->create(['type' => 'test_bundle', 'new_bundle_field' => 'foo']);
$entity->save();
// Remove the bundle field and apply updates. It's expected to throw an
// exception.
// @todo Revisit that expectation once purging is implemented for
// all fields: https://www.drupal.org/node/2282119.
// Remove the bundle field and apply updates.
$this->removeBundleField();
try {
$this->entityDefinitionUpdateManager->applyUpdates();
$this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
}
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
$this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
}
$this->entityDefinitionUpdateManager->applyUpdates();
// Check that the table of the bundle field has been renamed to use a
// 'deleted' table name.
$this->assertFalse($schema_handler->tableExists($dedicated_table_name), 'The dedicated table of the bundle field no longer exists.');
$dedicated_deleted_table_name = $table_mapping->getDedicatedDataTableName($storage_definition, TRUE);
$this->assertTrue($schema_handler->tableExists($dedicated_deleted_table_name), 'The dedicated table of the bundle fields has been renamed to use the "deleted" name.');
// Check that the deleted field's data is preserved in the dedicated
// 'deleted' table.
$result = $this->database->select($dedicated_deleted_table_name, 't')
->fields('t')
->execute()
->fetchAll();
$this->assertCount(1, $result);
$expected = [
'bundle' => $entity->bundle(),
'deleted' => '1',
'entity_id' => $entity->id(),
'revision_id' => $entity->id(),
'langcode' => $entity->language()->getId(),
'delta' => '0',
'new_bundle_field_value' => $entity->new_bundle_field->value,
];
// Use assertEquals and not assertSame here to prevent that a different
// sequence of the columns in the table will affect the check.
$this->assertEquals($expected, (array) $result[0]);
// Check that the field definition is marked for purging.
$deleted_field_definitions = \Drupal::service('entity_field.deleted_fields_repository')->getFieldDefinitions();
$this->assertArrayHasKey($storage_definition->getUniqueIdentifier(), $deleted_field_definitions, 'The bundle field is marked for purging.');
// Check that the field storage definition is marked for purging.
$deleted_storage_definitions = \Drupal::service('entity_field.deleted_fields_repository')->getFieldStorageDefinitions();
$this->assertArrayHasKey($storage_definition->getUniqueStorageIdentifier(), $deleted_storage_definitions, 'The bundle field storage is marked for purging.');
// Purge field data, and check that the storage definition has been
// completely removed once the data is purged.
field_purge_batch(10);
$deleted_field_definitions = \Drupal::service('entity_field.deleted_fields_repository')->getFieldDefinitions();
$this->assertEmpty($deleted_field_definitions, 'The bundle field has been deleted.');
$deleted_storage_definitions = \Drupal::service('entity_field.deleted_fields_repository')->getFieldStorageDefinitions();
$this->assertEmpty($deleted_storage_definitions, 'The bundle field storage has been deleted.');
$this->assertFalse($schema_handler->tableExists($dedicated_deleted_table_name), 'The dedicated table of the bundle field has been removed.');
}
/**
@@ -2,6 +2,7 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\language\Entity\ConfigurableLanguage;
@@ -23,9 +24,11 @@ class EntityRevisionTranslationTest extends EntityKernelTestBase {
protected function setUp() {
parent::setUp();
// Enable an additional language.
// Enable some additional languages.
ConfigurableLanguage::createFromLangcode('de')->save();
ConfigurableLanguage::createFromLangcode('it')->save();
$this->installEntitySchema('entity_test_mul');
$this->installEntitySchema('entity_test_mulrev');
}
@@ -157,4 +160,124 @@ class EntityRevisionTranslationTest extends EntityKernelTestBase {
$this->assertFalse($translation->isDefaultRevision());
}
/**
* @covers \Drupal\Core\Entity\RevisionableInterface::setNewRevision
*/
public function testSetNewRevision() {
$user = $this->createUser();
// All revisionable entity variations have to have the same results.
foreach (entity_test_entity_types(ENTITY_TEST_TYPES_REVISABLE) as $entity_type) {
$this->installEntitySchema($entity_type);
$entity = entity_create($entity_type, [
'name' => 'foo',
'user_id' => $user->id(),
]);
$entity->save();
$entity_id = $entity->id();
$entity_rev_id = $entity->getRevisionId();
$entity = entity_load($entity_type, $entity_id, TRUE);
$entity->setNewRevision(TRUE);
$entity->setNewRevision(FALSE);
$entity->save();
$entity = entity_load($entity_type, $entity_id, TRUE);
$this->assertEquals($entity_rev_id, $entity->getRevisionId(), 'A new entity revision was not created.');
}
}
/**
* Tests that revision translations are correctly detected.
*
* @covers \Drupal\Core\Entity\ContentEntityStorageBase::isAnyStoredRevisionTranslated
*/
public function testIsAnyStoredRevisionTranslated() {
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->entityManager->getStorage('entity_test_mul');
$method = new \ReflectionMethod(get_class($storage), 'isAnyStoredRevisionTranslated');
$method->setAccessible(TRUE);
// Check that a non-revisionable new entity is handled correctly.
$entity = EntityTestMul::create();
$this->assertEmpty($entity->getTranslationLanguages(FALSE));
$this->assertFalse($method->invoke($storage, $entity));
$entity->addTranslation('it');
$this->assertNotEmpty($entity->getTranslationLanguages(FALSE));
$this->assertFalse($method->invoke($storage, $entity));
// Check that not yet stored translations are handled correctly.
$entity = EntityTestMul::create();
$entity->save();
$entity->addTranslation('it');
$this->assertNotEmpty($entity->getTranslationLanguages(FALSE));
$this->assertFalse($method->invoke($storage, $entity));
// Check that removed translations are handled correctly.
$entity->save();
$entity->removeTranslation('it');
$this->assertEmpty($entity->getTranslationLanguages(FALSE));
$this->assertTrue($method->invoke($storage, $entity));
$entity->save();
$this->assertEmpty($entity->getTranslationLanguages(FALSE));
$this->assertFalse($method->invoke($storage, $entity));
$entity->addTranslation('de');
$entity->removeTranslation('de');
$this->assertEmpty($entity->getTranslationLanguages(FALSE));
$this->assertFalse($method->invoke($storage, $entity));
// Check that a non-revisionable not translated entity is handled correctly.
$entity = EntityTestMul::create();
$entity->save();
$this->assertEmpty($entity->getTranslationLanguages(FALSE));
$this->assertFalse($method->invoke($storage, $entity));
// Check that a non-revisionable translated entity is handled correctly.
$entity->addTranslation('it');
$entity->save();
$this->assertNotEmpty($entity->getTranslationLanguages(FALSE));
$this->assertTrue($method->invoke($storage, $entity));
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->entityManager->getStorage('entity_test_mulrev');
// Check that a revisionable new entity is handled correctly.
$entity = EntityTestMulRev::create();
$this->assertEmpty($entity->getTranslationLanguages(FALSE));
$this->assertFalse($method->invoke($storage, $entity));
$entity->addTranslation('it');
$this->assertNotEmpty($entity->getTranslationLanguages(FALSE));
$this->assertFalse($method->invoke($storage, $entity));
// Check that a revisionable not translated entity is handled correctly.
$entity = EntityTestMulRev::create();
$entity->save();
$this->assertEmpty($entity->getTranslationLanguages(FALSE));
$this->assertFalse($method->invoke($storage, $entity));
// Check that a revisionable translated pending revision is handled
// correctly.
/** @var \Drupal\Core\Entity\ContentEntityInterface $new_revision */
$new_revision = $storage->createRevision($entity, FALSE);
$new_revision->addTranslation('it');
$new_revision->save();
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $storage->loadUnchanged($entity->id());
$this->assertEmpty($entity->getTranslationLanguages(FALSE));
$this->assertNotEmpty($new_revision->getTranslationLanguages(FALSE));
$this->assertTrue($method->invoke($storage, $entity));
// Check that a revisionable translated default revision is handled
// correctly.
$new_revision->isDefaultRevision(TRUE);
$new_revision->save();
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $storage->loadUnchanged($entity->id());
$this->assertNotEmpty($entity->getTranslationLanguages(FALSE));
$this->assertNotEmpty($new_revision->getTranslationLanguages(FALSE));
$this->assertTrue($method->invoke($storage, $entity));
}
}
@@ -8,9 +8,11 @@ use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests the loaded Revision of an entity.
*
* @coversDefaultClass \Drupal\Core\Entity\ContentEntityBase
*
* @group entity
*/
class EntityLoadedRevisionTest extends EntityKernelTestBase {
class EntityRevisionsTest extends EntityKernelTestBase {
/**
* Modules to enable.
@@ -164,7 +166,99 @@ class EntityLoadedRevisionTest extends EntityKernelTestBase {
$loadedRevisionId = \Drupal::state()->get('entity_test.loadedRevisionId');
$this->assertEquals($entity->getLoadedRevisionId(), $loadedRevisionId);
$this->assertEquals($entity->getRevisionId(), $entity->getLoadedRevisionId());
}
/**
* Tests that latest revisions are working as expected.
*
* @covers ::isLatestRevision
*/
public function testIsLatestRevision() {
// Create a basic EntityTestMulRev entity and save it.
$entity = EntityTestMulRev::create();
$entity->save();
$this->assertTrue($entity->isLatestRevision());
// Load the created entity and create a new pending revision.
$pending_revision = EntityTestMulRev::load($entity->id());
$pending_revision->setNewRevision(TRUE);
$pending_revision->isDefaultRevision(FALSE);
// The pending revision should still be marked as the latest one before it
// is saved.
$this->assertTrue($pending_revision->isLatestRevision());
$pending_revision->save();
$this->assertTrue($pending_revision->isLatestRevision());
// Load the default revision and check that it is not marked as the latest
// revision.
$default_revision = EntityTestMulRev::load($entity->id());
$this->assertFalse($default_revision->isLatestRevision());
}
/**
* Tests that latest affected revisions are working as expected.
*
* The latest revision affecting a particular translation behaves as the
* latest revision for monolingual entities.
*
* @covers ::isLatestTranslationAffectedRevision
* @covers \Drupal\Core\Entity\ContentEntityStorageBase::getLatestRevisionId
* @covers \Drupal\Core\Entity\ContentEntityStorageBase::getLatestTranslationAffectedRevisionId
*/
public function testIsLatestAffectedRevisionTranslation() {
ConfigurableLanguage::createFromLangcode('it')->save();
// Create a basic EntityTestMulRev entity and save it.
$entity = EntityTestMulRev::create();
$entity->setName($this->randomString());
$entity->save();
$this->assertTrue($entity->isLatestTranslationAffectedRevision());
// Load the created entity and create a new pending revision.
$pending_revision = EntityTestMulRev::load($entity->id());
$pending_revision->setName($this->randomString());
$pending_revision->setNewRevision(TRUE);
$pending_revision->isDefaultRevision(FALSE);
// Check that no revision affecting Italian is available, given that no
// Italian translation has been created yet.
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->entityManager->getStorage($entity->getEntityTypeId());
$this->assertNull($storage->getLatestTranslationAffectedRevisionId($entity->id(), 'it'));
$this->assertEquals($pending_revision->getLoadedRevisionId(), $storage->getLatestRevisionId($entity->id()));
// The pending revision should still be marked as the latest affected one
// before it is saved.
$this->assertTrue($pending_revision->isLatestTranslationAffectedRevision());
$pending_revision->save();
$this->assertTrue($pending_revision->isLatestTranslationAffectedRevision());
// Load the default revision and check that it is not marked as the latest
// (translation-affected) revision.
$default_revision = EntityTestMulRev::load($entity->id());
$this->assertFalse($default_revision->isLatestRevision());
$this->assertFalse($default_revision->isLatestTranslationAffectedRevision());
// Add a translation in a new pending revision and verify that both the
// English and Italian revision translations are the latest affected
// revisions for their respective languages, while the English revision is
// not the latest revision.
/** @var \Drupal\entity_test\Entity\EntityTestMulRev $en_revision */
$en_revision = clone $pending_revision;
/** @var \Drupal\entity_test\Entity\EntityTestMulRev $it_revision */
$it_revision = $pending_revision->addTranslation('it');
$it_revision->setName($this->randomString());
$it_revision->setNewRevision(TRUE);
$it_revision->isDefaultRevision(FALSE);
// @todo Remove this once the "original" property works with revisions. See
// https://www.drupal.org/project/drupal/issues/2859042.
$it_revision->original = $storage->loadRevision($it_revision->getLoadedRevisionId());
$it_revision->save();
$this->assertTrue($it_revision->isLatestRevision());
$this->assertTrue($it_revision->isLatestTranslationAffectedRevision());
$this->assertFalse($en_revision->isLatestRevision());
$this->assertTrue($en_revision->isLatestTranslationAffectedRevision());
}
}
@@ -24,7 +24,11 @@ class EntityTypeConstraintsTest extends EntityKernelTestBase {
// Test reading the annotation. There should be two constraints, the defined
// constraint and the automatically added EntityChanged constraint.
$entity_type = $this->entityManager->getDefinition('entity_test_constraints');
$default_constraints = ['NotNull' => [], 'EntityChanged' => NULL];
$default_constraints = [
'NotNull' => [],
'EntityChanged' => NULL,
'EntityUntranslatableFields' => NULL,
];
$this->assertEqual($default_constraints, $entity_type->getConstraints());
// Enable our test module and test extending constraints.
@@ -2,6 +2,8 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\TypedData\EntityDataDefinition;
use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
use Drupal\Core\Field\BaseFieldDefinition;
@@ -11,6 +13,7 @@ use Drupal\Core\TypedData\DataReferenceDefinition;
use Drupal\Core\TypedData\DataReferenceDefinitionInterface;
use Drupal\Core\TypedData\ListDataDefinitionInterface;
use Drupal\KernelTests\KernelTestBase;
use Drupal\node\Entity\NodeType;
/**
* Tests deriving metadata of entity and field data types.
@@ -31,10 +34,16 @@ class EntityTypedDataDefinitionTest extends KernelTestBase {
*
* @var array
*/
public static $modules = ['filter', 'text', 'node', 'user'];
public static $modules = ['system', 'filter', 'text', 'node', 'user'];
protected function setUp() {
parent::setup();
NodeType::create([
'type' => 'article',
'name' => 'Article',
])->save();
$this->typedDataManager = $this->container->get('typed_data_manager');
}
@@ -82,10 +91,15 @@ class EntityTypedDataDefinitionTest extends KernelTestBase {
*/
public function testEntities() {
$entity_definition = EntityDataDefinition::create('node');
$bundle_definition = EntityDataDefinition::create('node', 'article');
// Entities are complex data.
$this->assertFalse($entity_definition instanceof ListDataDefinitionInterface);
$this->assertTrue($entity_definition instanceof ComplexDataDefinitionInterface);
// Entity definitions should inherit their labels from the entity type.
$this->assertEquals('Content', $entity_definition->getLabel());
$this->assertEquals('Article', $bundle_definition->getLabel());
$field_definitions = $entity_definition->getPropertyDefinitions();
// Comparison should ignore the internal static cache, so compare the
// serialized objects instead.
@@ -126,4 +140,36 @@ class EntityTypedDataDefinitionTest extends KernelTestBase {
$this->assertEqual(serialize($reference_definition2), serialize($reference_definition));
}
/**
* Tests that an entity annotation can mark the data definition as internal.
*
* @dataProvider entityDefinitionIsInternalProvider
*/
public function testEntityDefinitionIsInternal($internal, $expected) {
$entity_type_id = $this->randomMachineName();
$entity_type = $this->prophesize(EntityTypeInterface::class);
$entity_type->getLabel()->willReturn($this->randomString());
$entity_type->getConstraints()->willReturn([]);
$entity_type->isInternal()->willReturn($internal);
$entity_manager = $this->prophesize(EntityManagerInterface::class);
$entity_manager->getDefinitions()->willReturn([$entity_type_id => $entity_type->reveal()]);
$this->container->set('entity.manager', $entity_manager->reveal());
$entity_data_definition = EntityDataDefinition::create($entity_type_id);
$this->assertSame($expected, $entity_data_definition->isInternal());
}
/**
* Provides test cases for testEntityDefinitionIsInternal.
*/
public function entityDefinitionIsInternalProvider() {
return [
'internal' => [TRUE, TRUE],
'external' => [FALSE, FALSE],
'undefined' => [NULL, FALSE],
];
}
}
@@ -15,7 +15,7 @@ use Drupal\KernelTests\KernelTestBase;
*/
class FieldWidgetConstraintValidatorTest extends KernelTestBase {
public static $modules = ['entity_test', 'field', 'user', 'system'];
public static $modules = ['entity_test', 'field', 'field_test', 'user', 'system'];
/**
* {@inheritdoc}
@@ -54,7 +54,8 @@ class FieldWidgetConstraintValidatorTest extends KernelTestBase {
$display->validateFormValues($entity, $form, $form_state);
$errors = $form_state->getErrors();
$this->assertEqual($errors['name'], 'Widget constraint has failed.', 'Constraint violation is generated correctly');
$this->assertEqual($errors['name'], 'Widget constraint has failed.', 'Constraint violation at the field items list level is generated correctly');
$this->assertEqual($errors['test_field'], 'Widget constraint has failed.', 'Constraint violation at the field items list level is generated correctly for an advanced widget');
}
/**
@@ -5,7 +5,6 @@ namespace Drupal\KernelTests\Core\Entity;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\entity_test_revlog\Entity\EntityTestMulWithRevisionLog;
use Drupal\KernelTests\KernelTestBase;
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
@@ -13,7 +12,7 @@ use Drupal\user\UserInterface;
* @coversDefaultClass \Drupal\Core\Entity\RevisionableContentEntityBase
* @group Entity
*/
class RevisionableContentEntityBaseTest extends KernelTestBase {
class RevisionableContentEntityBaseTest extends EntityKernelTestBase {
/**
* {@inheritdoc}
@@ -25,10 +24,7 @@ class RevisionableContentEntityBaseTest extends KernelTestBase {
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('entity_test_mul_revlog');
$this->installEntitySchema('user');
$this->installSchema('system', 'sequences');
}
/**
@@ -89,6 +85,74 @@ class RevisionableContentEntityBaseTest extends KernelTestBase {
$this->assertItemsTableCount(3, $definition);
}
/**
* Tests the behavior of the "revision_default" flag.
*
* @covers \Drupal\Core\Entity\ContentEntityBase::wasDefaultRevision
*/
public function testWasDefaultRevision() {
$entity_type_id = 'entity_test_mul_revlog';
$entity = EntityTestMulWithRevisionLog::create([
'type' => $entity_type_id,
]);
// Checks that in a new entity ::wasDefaultRevision() always matches
// ::isDefaultRevision().
$this->assertEquals($entity->isDefaultRevision(), $entity->wasDefaultRevision());
$entity->isDefaultRevision(FALSE);
$this->assertEquals($entity->isDefaultRevision(), $entity->wasDefaultRevision());
// Check that a new entity is always flagged as a default revision on save,
// regardless of its default revision status.
$entity->save();
$this->assertTrue($entity->wasDefaultRevision());
// Check that a pending revision is not flagged as default.
$entity->setNewRevision();
$entity->isDefaultRevision(FALSE);
$entity->save();
$this->assertFalse($entity->wasDefaultRevision());
// Check that a default revision is flagged as such.
$entity->setNewRevision();
$entity->isDefaultRevision(TRUE);
$entity->save();
$this->assertTrue($entity->wasDefaultRevision());
// Check that a manually set value for the "revision_default" flag is
// ignored on save.
$entity->setNewRevision();
$entity->isDefaultRevision(FALSE);
$entity->set('revision_default', TRUE);
$this->assertTrue($entity->wasDefaultRevision());
$entity->save();
$this->assertFalse($entity->wasDefaultRevision());
// Check that the default revision status was stored correctly.
$storage = $this->entityManager->getStorage($entity_type_id);
foreach ([TRUE, FALSE, TRUE, FALSE] as $index => $expected) {
/** @var \Drupal\entity_test_revlog\Entity\EntityTestMulWithRevisionLog $revision */
$revision = $storage->loadRevision($index + 1);
$this->assertEquals($expected, $revision->wasDefaultRevision());
}
// Check that the default revision is flagged correctly.
/** @var \Drupal\entity_test_revlog\Entity\EntityTestMulWithRevisionLog $entity */
$entity = $storage->loadUnchanged($entity->id());
$this->assertTrue($entity->wasDefaultRevision());
// Check that the "revision_default" flag cannot be changed once set.
/** @var \Drupal\entity_test_revlog\Entity\EntityTestMulWithRevisionLog $entity2 */
$entity2 = EntityTestMulWithRevisionLog::create([
'type' => $entity_type_id,
]);
$entity2->save();
$this->assertTrue($entity2->wasDefaultRevision());
$entity2->isDefaultRevision(FALSE);
$entity2->save();
$this->assertTrue($entity2->wasDefaultRevision());
}
/**
* Asserts the ammount of items on entity related tables.
*
@@ -0,0 +1,61 @@
<?php
namespace Drupal\KernelTests\Core\Extension;
use Drupal\KernelTests\KernelTestBase;
/**
* Test whether deprecated hook invocations trigger errors.
*
* @group Extension
* @group legacy
*
* @coversDefaultClass Drupal\Core\Extension\ModuleHandler
*/
class ModuleHandlerDeprecatedHookTest extends KernelTestBase {
protected static $modules = ['deprecation_test'];
/**
* @covers ::invokeDeprecated
* @expectedDeprecation The deprecated hook hook_deprecated_hook() is implemented in these functions: deprecation_test_deprecated_hook(). Use something else.
*/
public function testInvokeDeprecated() {
/* @var $module_handler \Drupal\Core\Extension\ModuleHandlerInterface */
$module_handler = $this->container->get('module_handler');
$arg = 'an_arg';
$this->assertEqual(
$arg,
$module_handler->invokeDeprecated('Use something else.', 'deprecation_test', 'deprecated_hook', [$arg])
);
}
/**
* @covers ::invokeAllDeprecated
* @expectedDeprecation The deprecated hook hook_deprecated_hook() is implemented in these functions: deprecation_test_deprecated_hook(). Use something else.
*/
public function testInvokeAllDeprecated() {
/* @var $module_handler \Drupal\Core\Extension\ModuleHandlerInterface */
$module_handler = $this->container->get('module_handler');
$arg = 'an_arg';
$this->assertEqual(
[$arg],
$module_handler->invokeAllDeprecated('Use something else.', 'deprecated_hook', [$arg])
);
}
/**
* @covers ::alterDeprecated
* @expectedDeprecation The deprecated alter hook hook_deprecated_alter_alter() is implemented in these functions: deprecation_test_deprecated_alter_alter. Alter something else.
*/
public function testAlterDeprecated() {
/* @var $module_handler \Drupal\Core\Extension\ModuleHandlerInterface */
$module_handler = $this->container->get('module_handler');
$data = [];
$context1 = 'test1';
$context2 = 'test2';
$module_handler->alterDeprecated('Alter something else.', 'deprecated_alter', $data, $context1, $context2);
$this->assertEqual([$context1, $context2], $data);
}
}
@@ -0,0 +1,33 @@
<?php
namespace Drupal\KernelTests\Core\Extension;
use Drupal\KernelTests\KernelTestBase;
/**
* Test whether unimplemented deprecated hook invocations trigger errors.
*
* @group Extension
*
* @coversDefaultClass Drupal\Core\Extension\ModuleHandler
*/
class ModuleHandlerDeprecatedHookUnimplementedTest extends KernelTestBase {
/**
* @covers ::alterDeprecated
* @covers ::invokeAllDeprecated
* @covers ::invokeDeprecated
*/
public function testUnimplementedHooks() {
$unimplemented_hook_name = 'unimplemented_hook_name';
/* @var $module_handler \Drupal\Core\Extension\ModuleHandlerInterface */
$module_handler = $this->container->get('module_handler');
$module_handler->invokeDeprecated('Use something else.', 'deprecation_test', $unimplemented_hook_name);
$module_handler->invokeAllDeprecated('Use something else.', $unimplemented_hook_name);
$data = [];
$module_handler->alterDeprecated('Alter something else.', $unimplemented_hook_name, $data);
}
}
@@ -85,4 +85,13 @@ class ModuleInstallerTest extends KernelTestBase {
$this->assertFalse($schema->tableExists($table));
}
/**
* Ensure that rebuilding the container in hook_install() works.
*/
public function testKernelRebuildDuringHookInstall() {
\Drupal::state()->set('module_test_install:rebuild_container', TRUE);
$module_installer = $this->container->get('module_installer');
$this->assertTrue($module_installer->install(['module_test']));
}
}
@@ -1,140 +0,0 @@
<?php
namespace Drupal\KernelTests\Core\Field;
use Drupal\Core\Extension\ModuleUninstallValidatorException;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\entity_test\FieldStorageDefinition;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
/**
* Tests FieldModuleUninstallValidator functionality.
*
* @group Field
*/
class FieldModuleUninstallValidatorTest extends EntityKernelTestBase {
/**
* The entity definition update manager.
*
* @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
*/
protected $entityDefinitionUpdateManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('user', 'users_data');
$this->entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager');
// Setup some fields for entity_test_extra to create.
$definitions['extra_base_field'] = BaseFieldDefinition::create('string')
->setName('extra_base_field')
->setTargetEntityTypeId('entity_test')
->setTargetBundle('entity_test');
$this->state->set('entity_test.additional_base_field_definitions', $definitions);
$definitions['extra_bundle_field'] = FieldStorageDefinition::create('string')
->setName('extra_bundle_field')
->setTargetEntityTypeId('entity_test')
->setTargetBundle('entity_test');
$this->state->set('entity_test.additional_field_storage_definitions', $definitions);
$this->state->set('entity_test.entity_test.additional_bundle_field_definitions', $definitions);
$this->entityManager->clearCachedDefinitions();
}
/**
* Tests uninstall entity_test module with and without content for the field.
*/
public function testUninstallingModule() {
// Test uninstall works fine without content.
$this->assertModuleInstallUninstall('entity_test_extra');
// Test uninstalling works fine with content having no field values.
$entity = $this->entityManager->getStorage('entity_test')->create([
'name' => $this->randomString(),
]);
$entity->save();
$this->assertModuleInstallUninstall('entity_test_extra');
$entity->delete();
// Verify uninstall works fine without content again.
$this->assertModuleInstallUninstall('entity_test_extra');
// Verify uninstalling entity_test is not possible when there is content for
// the base field.
$this->enableModules(['entity_test_extra']);
$this->entityDefinitionUpdateManager->applyUpdates();
$entity = $this->entityManager->getStorage('entity_test')->create([
'name' => $this->randomString(),
'extra_base_field' => $this->randomString(),
]);
$entity->save();
try {
$message = 'Module uninstallation fails as the module provides a base field which has content.';
$this->getModuleInstaller()->uninstall(['entity_test_extra']);
$this->fail($message);
}
catch (ModuleUninstallValidatorException $e) {
$this->pass($message);
$this->assertEqual($e->getMessage(), 'The following reasons prevent the modules from being uninstalled: There is data for the field extra_base_field on entity type Test entity');
}
// Verify uninstalling entity_test is not possible when there is content for
// the bundle field.
$entity->delete();
$this->assertModuleInstallUninstall('entity_test_extra');
$this->enableModules(['entity_test_extra']);
$this->entityDefinitionUpdateManager->applyUpdates();
$entity = $this->entityManager->getStorage('entity_test')->create([
'name' => $this->randomString(),
'extra_bundle_field' => $this->randomString(),
]);
$entity->save();
try {
$this->getModuleInstaller()->uninstall(['entity_test_extra']);
$this->fail('Module uninstallation fails as the module provides a bundle field which has content.');
}
catch (ModuleUninstallValidatorException $e) {
$this->pass('Module uninstallation fails as the module provides a bundle field which has content.');
}
}
/**
* Asserts the given module can be installed and uninstalled.
*
* @param string $module_name
* The module to install and uninstall.
*/
protected function assertModuleInstallUninstall($module_name) {
// Install the module if it is not installed yet.
if (!\Drupal::moduleHandler()->moduleExists($module_name)) {
$this->enableModules([$module_name]);
}
$this->entityDefinitionUpdateManager->applyUpdates();
$this->assertTrue($this->getModuleHandler()->moduleExists($module_name), $module_name . ' module is enabled.');
$this->getModuleInstaller()->uninstall([$module_name]);
$this->entityDefinitionUpdateManager->applyUpdates();
$this->assertFalse($this->getModuleHandler()->moduleExists($module_name), $module_name . ' module is disabled.');
}
/**
* Returns the ModuleHandler.
*
* @return \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected function getModuleHandler() {
return $this->container->get('module_handler');
}
/**
* Returns the ModuleInstaller.
*
* @return \Drupal\Core\Extension\ModuleInstallerInterface
*/
protected function getModuleInstaller() {
return $this->container->get('module_installer');
}
}
@@ -46,6 +46,34 @@ class FieldSettingsTest extends EntityKernelTestBase {
$this->assertEqual($base_field->getSettings(), $expected_settings);
}
/**
* Tests the base field settings on a cloned base field definition object.
*/
public function testBaseFieldSettingsOnClone() {
$base_field = BaseFieldDefinition::create('test_field');
// Check that the default settings have been populated.
$expected_settings = [
'test_field_storage_setting' => 'dummy test string',
'changeable' => 'a changeable field storage setting',
'unchangeable' => 'an unchangeable field storage setting',
'translatable_storage_setting' => 'a translatable field storage setting',
'test_field_setting' => 'dummy test string',
'translatable_field_setting' => 'a translatable field setting',
];
$this->assertEquals($expected_settings, $base_field->getSettings());
// Clone the base field object and change one single setting using
// setSettings() on the cloned base field and check that it has been
// changed only on the cloned object.
$clone_base_field = clone $base_field;
$expected_settings_clone = $expected_settings;
$expected_settings_clone['changeable'] = $expected_settings['changeable'] . ' (clone)';
$clone_base_field->setSetting('changeable', $expected_settings_clone['changeable']);
$this->assertEquals($expected_settings, $base_field->getSettings());
$this->assertEquals($expected_settings_clone, $clone_base_field->getSettings());
}
/**
* @covers \Drupal\field\Entity\FieldStorageConfig::getSettings
* @covers \Drupal\field\Entity\FieldStorageConfig::setSettings
@@ -45,7 +45,7 @@ class DirectoryTest extends FileTestBase {
$this->assertDirectoryPermissions($directory, $old_mode);
// Check creating a directory using an absolute path.
$absolute_path = drupal_realpath($directory) . DIRECTORY_SEPARATOR . $this->randomMachineName() . DIRECTORY_SEPARATOR . $this->randomMachineName();
$absolute_path = \Drupal::service('file_system')->realpath($directory) . DIRECTORY_SEPARATOR . $this->randomMachineName() . DIRECTORY_SEPARATOR . $this->randomMachineName();
$this->assertTrue(drupal_mkdir($absolute_path, 0775, TRUE), 'No error reported when creating new absolute directories.', 'File');
$this->assertDirectoryPermissions($absolute_path, 0775);
}
@@ -0,0 +1,137 @@
<?php
namespace Drupal\KernelTests\Core\Layout;
use Drupal\Core\Layout\Icon\SvgIconBuilder;
use Drupal\Core\Render\RenderContext;
use Drupal\KernelTests\KernelTestBase;
/**
* @coversDefaultClass \Drupal\Core\Layout\Icon\SvgIconBuilder
* @group Layout
*/
class IconBuilderTest extends KernelTestBase {
/**
* @covers ::build
* @covers ::buildRenderArray
* @covers ::calculateSvgValues
* @covers ::getLength
* @covers ::getOffset
*
* @dataProvider providerTestBuild
*/
public function testBuild(SvgIconBuilder $icon_builder, $icon_map, $expected) {
$renderer = $this->container->get('renderer');
$build = $icon_builder->build($icon_map);
$output = (string) $renderer->executeInRenderContext(new RenderContext(), function () use ($build, $renderer) {
return $renderer->render($build);
});
$this->assertSame($expected, $output);
}
public function providerTestBuild() {
$data = [];
$data['empty'][] = (new SvgIconBuilder());
$data['empty'][] = [];
$data['empty'][] = <<<'EOD'
<svg width="125" height="150" class="layout-icon"></svg>
EOD;
$data['two_column'][] = (new SvgIconBuilder())
->setId('two_column')
->setLabel('Two Column')
->setWidth(250)
->setHeight(300)
->setStrokeWidth(2);
$data['two_column'][] = [['left', 'right']];
$data['two_column'][] = <<<'EOD'
<svg width="250" height="300" class="layout-icon layout-icon--two-column"><title>Two Column</title>
<g><title>left</title>
<rect x="1" y="1" width="121" height="298" stroke-width="2" class="layout-icon__region layout-icon__region--left" />
</g>
<g><title>right</title>
<rect x="128" y="1" width="121" height="298" stroke-width="2" class="layout-icon__region layout-icon__region--right" />
</g>
</svg>
EOD;
$data['two_column_no_stroke'][] = (new SvgIconBuilder())
->setWidth(250)
->setHeight(300)
->setStrokeWidth(NULL);
$data['two_column_no_stroke'][] = [['left', 'right']];
$data['two_column_no_stroke'][] = <<<'EOD'
<svg width="250" height="300" class="layout-icon"><g><title>left</title>
<rect x="0" y="0" width="123" height="300" class="layout-icon__region layout-icon__region--left" />
</g>
<g><title>right</title>
<rect x="127" y="0" width="123" height="300" class="layout-icon__region layout-icon__region--right" />
</g>
</svg>
EOD;
$data['two_column_border_collapse'][] = (new SvgIconBuilder())
->setWidth(250)
->setHeight(300)
->setStrokeWidth(2)
->setPadding(-2);
$data['two_column_border_collapse'][] = [['left', 'right']];
$data['two_column_border_collapse'][] = <<<'EOD'
<svg width="250" height="300" class="layout-icon"><g><title>left</title>
<rect x="1" y="1" width="124" height="298" stroke-width="2" class="layout-icon__region layout-icon__region--left" />
</g>
<g><title>right</title>
<rect x="125" y="1" width="124" height="298" stroke-width="2" class="layout-icon__region layout-icon__region--right" />
</g>
</svg>
EOD;
$data['stacked'][] = (new SvgIconBuilder())
->setStrokeWidth(2);
$data['stacked'][] = [
['sidebar', 'top', 'top'],
['sidebar', 'left', 'right'],
['sidebar', 'middle', 'middle'],
['footer_left', 'footer_right'],
['footer_full'],
];
$data['stacked'][] = <<<'EOD'
<svg width="125" height="150" class="layout-icon"><g><title>sidebar</title>
<rect x="1" y="1" width="37" height="86.4" stroke-width="2" class="layout-icon__region layout-icon__region--sidebar" />
</g>
<g><title>top</title>
<rect x="44" y="1" width="80" height="24.8" stroke-width="2" class="layout-icon__region layout-icon__region--top" />
</g>
<g><title>left</title>
<rect x="44" y="31.8" width="37" height="24.8" stroke-width="2" class="layout-icon__region layout-icon__region--left" />
</g>
<g><title>right</title>
<rect x="87" y="31.8" width="37" height="24.8" stroke-width="2" class="layout-icon__region layout-icon__region--right" />
</g>
<g><title>middle</title>
<rect x="44" y="62.6" width="80" height="24.8" stroke-width="2" class="layout-icon__region layout-icon__region--middle" />
</g>
<g><title>footer_left</title>
<rect x="1" y="93.4" width="58.5" height="24.8" stroke-width="2" class="layout-icon__region layout-icon__region--footer-left" />
</g>
<g><title>footer_right</title>
<rect x="65.5" y="93.4" width="58.5" height="24.8" stroke-width="2" class="layout-icon__region layout-icon__region--footer-right" />
</g>
<g><title>footer_full</title>
<rect x="1" y="124.2" width="123" height="24.8" stroke-width="2" class="layout-icon__region layout-icon__region--footer-full" />
</g>
</svg>
EOD;
return $data;
}
}
@@ -0,0 +1,108 @@
<?php
namespace Drupal\KernelTests\Core\Messenger;
use Drupal\Core\Messenger\LegacyMessenger;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\KernelTests\KernelTestBase;
/**
* @group Messenger
* @coversDefaultClass \Drupal\Core\Messenger\LegacyMessenger
*
* Note: The Symphony PHPUnit Bridge automatically treats any test class that
* starts with "Legacy" as a deprecation. To subvert that, reverse it here.
*
* @see http://symfony.com/blog/new-in-symfony-2-7-phpunit-bridge
* @see https://www.drupal.org/node/2931598#comment-12395743
*/
class MessengerLegacyTest extends KernelTestBase {
/**
* Retrieves the Messenger service from LegacyMessenger.
*
* @param \Drupal\Core\Messenger\LegacyMessenger $legacy_messenger
* The legacy messenger.
*
* @return \Drupal\Core\Messenger\MessengerInterface|null
* A messenger implementation.
*/
protected function getMessengerService(LegacyMessenger $legacy_messenger) {
$method = new \ReflectionMethod($legacy_messenger, 'getMessengerService');
$method->setAccessible(TRUE);
return $method->invoke($legacy_messenger);
}
/**
* @covers \Drupal::messenger
* @covers ::getMessengerService
* @covers ::all
* @covers ::addMessage
* @covers ::addError
* @covers ::addStatus
* @covers ::addWarning
*/
public function testMessages() {
// Save the current container for later use.
$container = \Drupal::getContainer();
// Unset the container to mimic not having one.
\Drupal::unsetContainer();
/** @var \Drupal\Core\Messenger\LegacyMessenger $messenger */
// Verify that the Messenger service doesn't exists.
$messenger = \Drupal::messenger();
$this->assertNull($this->getMessengerService($messenger));
// Add messages.
$messenger->addMessage('Foobar', 'custom');
$messenger->addMessage('Foobar', 'custom', TRUE);
$messenger->addError('Foo');
$messenger->addError('Foo', TRUE);
// Verify that retrieving another instance and adding more messages works.
$messenger = \Drupal::messenger();
$messenger->addStatus('Bar');
$messenger->addStatus('Bar', TRUE);
$messenger->addWarning('Fiz');
$messenger->addWarning('Fiz', TRUE);
// Restore the container.
\Drupal::setContainer($container);
// Verify that the Messenger service exists.
$messenger = \Drupal::messenger();
$this->assertInstanceOf(Messenger::class, $this->getMessengerService($messenger));
// Add more messages.
$messenger->addMessage('Platypus', 'custom');
$messenger->addMessage('Platypus', 'custom', TRUE);
$messenger->addError('Rhinoceros');
$messenger->addError('Rhinoceros', TRUE);
$messenger->addStatus('Giraffe');
$messenger->addStatus('Giraffe', TRUE);
$messenger->addWarning('Cheetah');
$messenger->addWarning('Cheetah', TRUE);
// Verify all messages added via LegacyMessenger are accounted for.
$messages = $messenger->all();
$this->assertContains('Foobar', $messages['custom']);
$this->assertContains('Foo', $messages[MessengerInterface::TYPE_ERROR]);
$this->assertContains('Bar', $messages[MessengerInterface::TYPE_STATUS]);
$this->assertContains('Fiz', $messages[MessengerInterface::TYPE_WARNING]);
// Verify all messages added via Messenger service are accounted for.
$this->assertContains('Platypus', $messages['custom']);
$this->assertContains('Rhinoceros', $messages[MessengerInterface::TYPE_ERROR]);
$this->assertContains('Giraffe', $messages[MessengerInterface::TYPE_STATUS]);
$this->assertContains('Cheetah', $messages[MessengerInterface::TYPE_WARNING]);
// Verify repeat counts.
$this->assertCount(4, $messages['custom']);
$this->assertCount(4, $messages[MessengerInterface::TYPE_STATUS]);
$this->assertCount(4, $messages[MessengerInterface::TYPE_WARNING]);
$this->assertCount(4, $messages[MessengerInterface::TYPE_ERROR]);
}
}
@@ -0,0 +1,165 @@
<?php
namespace Drupal\KernelTests\Core\Messenger;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\KernelTests\KernelTestBase;
/**
* @group Messenger
* @coversDefaultClass \Drupal\Core\Messenger\Messenger
*/
class MessengerTest extends KernelTestBase {
/**
* The messenger under test.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->messenger = \Drupal::service('messenger');
}
/**
* @covers ::addStatus
* @covers ::deleteByType
* @covers ::messagesByType
*/
public function testRemoveSingleMessage() {
// Set two messages.
$this->messenger->addStatus('First message (removed).');
$this->messenger->addStatus(t('Second message with <em>markup!</em> (not removed).'));
$messages = $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS);
// Remove the first.
unset($messages[0]);
// Re-add the second.
foreach ($messages as $message) {
$this->messenger->addStatus($message);
}
// Check we only have the second one.
$this->assertCount(1, $this->messenger->messagesByType(MessengerInterface::TYPE_STATUS));
$this->assertContains('Second message with <em>markup!</em> (not removed).', $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS));
}
/**
* Tests we don't add duplicates.
*
* @covers ::all
* @covers ::addStatus
* @covers ::addWarning
* @covers ::addError
* @covers ::deleteByType
* @covers ::deleteAll
*/
public function testAddNoDuplicates() {
$this->messenger->addStatus('Non Duplicated status message');
$this->messenger->addStatus('Non Duplicated status message');
$this->assertCount(1, $this->messenger->messagesByType(MessengerInterface::TYPE_STATUS));
$this->messenger->addWarning('Non Duplicated warning message');
$this->messenger->addWarning('Non Duplicated warning message');
$this->assertCount(1, $this->messenger->messagesByType(MessengerInterface::TYPE_WARNING));
$this->messenger->addError('Non Duplicated error message');
$this->messenger->addError('Non Duplicated error message');
$messages = $this->messenger->messagesByType(MessengerInterface::TYPE_ERROR);
$this->assertCount(1, $messages);
// Check getting all messages.
$messages = $this->messenger->all();
$this->assertCount(3, $messages);
$this->assertArrayHasKey(MessengerInterface::TYPE_STATUS, $messages);
$this->assertArrayHasKey(MessengerInterface::TYPE_WARNING, $messages);
$this->assertArrayHasKey(MessengerInterface::TYPE_ERROR, $messages);
// Check deletion.
$this->messenger->deleteAll();
$this->assertCount(0, $this->messenger->messagesByType(MessengerInterface::TYPE_STATUS));
$this->assertCount(0, $this->messenger->messagesByType(MessengerInterface::TYPE_WARNING));
$this->assertCount(0, $this->messenger->messagesByType(MessengerInterface::TYPE_ERROR));
}
/**
* Tests we do add duplicates with repeat flag.
*
* @covers ::addStatus
* @covers ::addWarning
* @covers ::addError
* @covers ::deleteByType
*/
public function testAddWithDuplicates() {
$this->messenger->addStatus('Duplicated status message', TRUE);
$this->messenger->addStatus('Duplicated status message', TRUE);
$this->assertCount(2, $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS));
$this->messenger->addWarning('Duplicated warning message', TRUE);
$this->messenger->addWarning('Duplicated warning message', TRUE);
$this->assertCount(2, $this->messenger->deleteByType(MessengerInterface::TYPE_WARNING));
$this->messenger->addError('Duplicated error message', TRUE);
$this->messenger->addError('Duplicated error message', TRUE);
$this->assertCount(2, $this->messenger->deleteByType(MessengerInterface::TYPE_ERROR));
}
/**
* Test adding markup.
*
* @covers ::addStatus
* @covers ::deleteByType
* @covers ::messagesByType
*/
public function testAddMarkup() {
// Add a Markup message.
$this->messenger->addStatus(Markup::create('Markup with <em>markup!</em>'));
// Test duplicate Markup messages.
$this->messenger->addStatus(Markup::create('Markup with <em>markup!</em>'));
$this->assertCount(1, $this->messenger->messagesByType(MessengerInterface::TYPE_STATUS));
// Ensure that multiple Markup messages work.
$this->messenger->addStatus(Markup::create('Markup2 with <em>markup!</em>'));
$this->assertCount(2, $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS));
// Test mixing of types.
$this->messenger->addStatus(Markup::create('Non duplicate Markup / string.'));
$this->messenger->addStatus('Non duplicate Markup / string.');
$this->messenger->addStatus(Markup::create('Duplicate Markup / string.'), TRUE);
$this->messenger->addStatus('Duplicate Markup / string.', TRUE);
$this->assertCount(3, $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS));
$this->messenger->deleteAll();
// Check translatable string is converted to Markup.
$this->messenger->addStatus(new TranslatableMarkup('Translatable message'));
$messages = $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS);
$this->assertInstanceOf(Markup::class, $messages[0]);
}
}
@@ -0,0 +1,188 @@
<?php
namespace Drupal\KernelTests\Core\ParamConverter;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\KernelTests\KernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests the entity converter when the "load_latest_revision" flag is set.
*
* @group ParamConverter
* @coversDefaultClass \Drupal\Core\ParamConverter\EntityConverter
*/
class EntityConverterLatestRevisionTest extends KernelTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = [
'entity_test',
'user',
'language',
'system',
];
/**
* The entity converter service.
*
* @var \Drupal\Core\ParamConverter\EntityConverter
*/
protected $converter;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('user');
$this->installEntitySchema('entity_test_mulrev');
$this->installEntitySchema('entity_test');
$this->installConfig(['system', 'language']);
$this->converter = $this->container->get('paramconverter.entity');
ConfigurableLanguage::createFromLangcode('de')->save();
}
/**
* Tests with no matching entity.
*/
public function testNoEntity() {
$converted = $this->converter->convert(1, [
'load_latest_revision' => TRUE,
'type' => 'entity:entity_test_mulrev',
], 'foo', []);
$this->assertEquals(NULL, $converted);
}
/**
* Tests with no pending revision.
*/
public function testEntityNoPendingRevision() {
$entity = EntityTestMulRev::create();
$entity->save();
$converted = $this->converter->convert(1, [
'load_latest_revision' => TRUE,
'type' => 'entity:entity_test_mulrev',
], 'foo', []);
$this->assertEquals($entity->getLoadedRevisionId(), $converted->getLoadedRevisionId());
}
/**
* Tests with a pending revision.
*/
public function testEntityWithPendingRevision() {
$entity = EntityTestMulRev::create();
$entity->save();
$entity->isDefaultRevision(FALSE);
$entity->setNewRevision(TRUE);
$entity->save();
$converted = $this->converter->convert(1, [
'load_latest_revision' => TRUE,
'type' => 'entity:entity_test_mulrev',
], 'foo', []);
$this->assertEquals($entity->getLoadedRevisionId(), $converted->getLoadedRevisionId());
}
/**
* Tests with a translated pending revision.
*/
public function testWithTranslatedPendingRevision() {
// Enable translation for test entities.
$this->container->get('state')->set('entity_test.translation', TRUE);
$this->container->get('entity_type.bundle.info')->clearCachedBundles();
// Create a new English entity.
$entity = EntityTestMulRev::create();
$entity->save();
// Create a translated pending revision.
$entity_type_id = 'entity_test_mulrev';
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->container->get('entity_type.manager')->getStorage($entity_type_id);
/** @var \Drupal\Core\Entity\ContentEntityInterface $translated_entity */
$translated_entity = $storage->createRevision($entity->addTranslation('de'), FALSE);
$translated_entity->save();
// Change the site language so the converters will attempt to load entities
// with language 'de'.
$this->config('system.site')->set('default_langcode', 'de')->save();
// The default loaded language is still 'en'.
EntityTestMulRev::load($entity->id());
$this->assertEquals('en', $entity->language()->getId());
// The converter will load the latest revision in the correct language.
$converted = $this->converter->convert(1, [
'load_latest_revision' => TRUE,
'type' => 'entity:entity_test_mulrev',
], 'foo', []);
$this->assertEquals('de', $converted->language()->getId());
$this->assertEquals($translated_entity->getLoadedRevisionId(), $converted->getLoadedRevisionId());
// Revert back to English as default language.
$this->config('system.site')->set('default_langcode', 'en')->save();
// The converter will load the latest revision in the correct language.
$converted = $this->converter->convert(1, [
'load_latest_revision' => TRUE,
'type' => 'entity:entity_test_mulrev',
], 'foo', []);
$this->assertEquals('en', $converted->language()->getId());
$this->assertEquals($entity->getLoadedRevisionId(), $converted->getLoadedRevisionId());
}
/**
* Tests that pending revisions are loaded only when needed.
*/
public function testOptimizedConvert() {
$entity = EntityTestMulRev::create();
$entity->save();
// Populate static cache for the current entity.
$entity = EntityTestMulRev::load($entity->id());
// Delete the base table entry for the current entity, however, since the
// storage will query the revision table to get the latest revision, the
// logic handling pending revisions will work correctly anyway.
/** @var \Drupal\Core\Database\Connection $database */
$database = $this->container->get('database');
$database->delete('entity_test_mulrev')
->condition('id', $entity->id())
->execute();
// If optimization works, converting a default revision should not trigger
// a storage load, thus making the following assertion pass.
$converted = $this->converter->convert(1, [
'load_latest_revision' => TRUE,
'type' => 'entity:entity_test_mulrev',
], 'foo', []);
$this->assertEquals($entity->getLoadedRevisionId(), $converted->getLoadedRevisionId());
}
/**
* Test the latest revision flag and non-revisionable entities.
*/
public function testConvertNonRevisionableEntityType() {
$entity = EntityTest::create();
$entity->save();
$converted = $this->converter->convert(1, [
'load_latest_revision' => TRUE,
'type' => 'entity:entity_test',
], 'foo', []);
$this->assertEquals($entity->id(), $converted->id());
}
}
@@ -63,7 +63,7 @@ class PathValidatorTest extends KernelTestBase {
$url = $pathValidator->getUrlIfValidWithoutAccessCheck($entity->toUrl()->toString(TRUE)->getGeneratedUrl());
$this->assertEquals($method, $requestContext->getMethod());
$this->assertInstanceOf(Url::class, $url);
$this->assertSame($url->getRouteParameters(), ['entity_test' => $entity->id()]);
$this->assertSame(['entity_test' => $entity->id()], $url->getRouteParameters());
}
}
@@ -18,6 +18,7 @@ use Drupal\Core\Routing\MatcherDumper;
use Drupal\Core\Routing\RouteProvider;
use Drupal\Core\State\State;
use Drupal\KernelTests\KernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\Core\Routing\RoutingFixtures;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
@@ -36,7 +37,7 @@ class RouteProviderTest extends KernelTestBase {
/**
* Modules to enable.
*/
public static $modules = ['url_alter_test', 'system'];
public static $modules = ['url_alter_test', 'system', 'language'];
/**
* A collection of shared fixture data for tests.
@@ -544,7 +545,8 @@ class RouteProviderTest extends KernelTestBase {
*/
public function testRouteCaching() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$language_manager = \Drupal::languageManager();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes', $language_manager);
$this->fixtures->createTables($connection);
@@ -558,7 +560,7 @@ class RouteProviderTest extends KernelTestBase {
$request = Request::create($path, 'GET');
$provider->getRouteCollectionForRequest($request);
$cache = $this->cache->get('route:/path/add/one:');
$cache = $this->cache->get('route:en:/path/add/one:');
$this->assertEqual('/path/add/one', $cache->data['path']);
$this->assertEqual([], $cache->data['query']);
$this->assertEqual(3, count($cache->data['routes']));
@@ -568,7 +570,7 @@ class RouteProviderTest extends KernelTestBase {
$request = Request::create($path, 'GET');
$provider->getRouteCollectionForRequest($request);
$cache = $this->cache->get('route:/path/add/one:foo=bar');
$cache = $this->cache->get('route:en:/path/add/one:foo=bar');
$this->assertEqual('/path/add/one', $cache->data['path']);
$this->assertEqual(['foo' => 'bar'], $cache->data['query']);
$this->assertEqual(3, count($cache->data['routes']));
@@ -578,7 +580,7 @@ class RouteProviderTest extends KernelTestBase {
$request = Request::create($path, 'GET');
$provider->getRouteCollectionForRequest($request);
$cache = $this->cache->get('route:/path/1/one:');
$cache = $this->cache->get('route:en:/path/1/one:');
$this->assertEqual('/path/1/one', $cache->data['path']);
$this->assertEqual([], $cache->data['query']);
$this->assertEqual(2, count($cache->data['routes']));
@@ -595,10 +597,25 @@ class RouteProviderTest extends KernelTestBase {
$request = Request::create($path, 'GET');
$provider->getRouteCollectionForRequest($request);
$cache = $this->cache->get('route:/path/add-one:');
$cache = $this->cache->get('route:en:/path/add-one:');
$this->assertEqual('/path/add/one', $cache->data['path']);
$this->assertEqual([], $cache->data['query']);
$this->assertEqual(3, count($cache->data['routes']));
// Test with a different current language by switching out the default
// language.
$swiss = ConfigurableLanguage::createFromLangcode('gsw-berne');
$language_manager->reset();
\Drupal::service('language.default')->set($swiss);
$path = '/path/add-one';
$request = Request::create($path, 'GET');
$provider->getRouteCollectionForRequest($request);
$cache = $this->cache->get('route:gsw-berne:/path/add-one:');
$this->assertEquals('/path/add/one', $cache->data['path']);
$this->assertEquals([], $cache->data['query']);
$this->assertEquals(3, count($cache->data['routes']));
}
/**
@@ -0,0 +1,139 @@
<?php
namespace Drupal\KernelTests\Core\TempStore;
use Drupal\Core\KeyValueStore\KeyValueExpirableFactory;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Core\TempStore\SharedTempStoreFactory;
use Drupal\Core\Lock\DatabaseLockBackend;
use Drupal\Core\Database\Database;
/**
* Tests the temporary object storage system.
*
* @group TempStore
* @see \Drupal\Core\TempStore\SharedTempStore
*/
class TempStoreDatabaseTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['system'];
/**
* A key/value store factory.
*
* @var \Drupal\Core\TempStore\SharedTempStoreFactory
*/
protected $storeFactory;
/**
* The name of the key/value collection to set and retrieve.
*
* @var string
*/
protected $collection;
/**
* An array of random stdClass objects.
*
* @var array
*/
protected $objects = [];
protected function setUp() {
parent::setUp();
// Install system tables to test the key/value storage without installing a
// full Drupal environment.
$this->installSchema('system', ['key_value_expire']);
// Create several objects for testing.
for ($i = 0; $i <= 3; $i++) {
$this->objects[$i] = $this->randomObject();
}
}
/**
* Tests the SharedTempStore API.
*/
public function testSharedTempStore() {
// Create a key/value collection.
$factory = new SharedTempStoreFactory(new KeyValueExpirableFactory(\Drupal::getContainer()), new DatabaseLockBackend(Database::getConnection()), $this->container->get('request_stack'));
$collection = $this->randomMachineName();
// Create two mock users.
for ($i = 0; $i <= 1; $i++) {
$users[$i] = mt_rand(500, 5000000);
// Storing the SharedTempStore objects in a class member variable causes a
// fatal exception, because in that situation garbage collection is not
// triggered until the test class itself is destructed, after tearDown()
// has deleted the database tables. Store the objects locally instead.
$stores[$i] = $factory->get($collection, $users[$i]);
}
$key = $this->randomMachineName();
// Test that setIfNotExists() succeeds only the first time.
for ($i = 0; $i <= 1; $i++) {
// setIfNotExists() should be TRUE the first time (when $i is 0) and
// FALSE the second time (when $i is 1).
$this->assertEqual(!$i, $stores[0]->setIfNotExists($key, $this->objects[$i]));
$metadata = $stores[0]->getMetadata($key);
$this->assertEqual($users[0], $metadata->owner);
$this->assertIdenticalObject($this->objects[0], $stores[0]->get($key));
// Another user should get the same result.
$metadata = $stores[1]->getMetadata($key);
$this->assertEqual($users[0], $metadata->owner);
$this->assertIdenticalObject($this->objects[0], $stores[1]->get($key));
}
// Remove the item and try to set it again.
$stores[0]->delete($key);
$stores[0]->setIfNotExists($key, $this->objects[1]);
// This time it should succeed.
$this->assertIdenticalObject($this->objects[1], $stores[0]->get($key));
// This user can update the object.
$stores[0]->set($key, $this->objects[2]);
$this->assertIdenticalObject($this->objects[2], $stores[0]->get($key));
// The object is the same when another user loads it.
$this->assertIdenticalObject($this->objects[2], $stores[1]->get($key));
// This user should be allowed to get, update, delete.
$this->assertTrue($stores[0]->getIfOwner($key) instanceof \stdClass);
$this->assertTrue($stores[0]->setIfOwner($key, $this->objects[1]));
$this->assertTrue($stores[0]->deleteIfOwner($key));
// Another user can update the object and become the owner.
$stores[1]->set($key, $this->objects[3]);
$this->assertIdenticalObject($this->objects[3], $stores[0]->get($key));
$this->assertIdenticalObject($this->objects[3], $stores[1]->get($key));
$metadata = $stores[1]->getMetadata($key);
$this->assertEqual($users[1], $metadata->owner);
// The first user should be informed that the second now owns the data.
$metadata = $stores[0]->getMetadata($key);
$this->assertEqual($users[1], $metadata->owner);
// The first user should no longer be allowed to get, update, delete.
$this->assertNull($stores[0]->getIfOwner($key));
$this->assertFalse($stores[0]->setIfOwner($key, $this->objects[1]));
$this->assertFalse($stores[0]->deleteIfOwner($key));
// Now manually expire the item (this is not exposed by the API) and then
// assert it is no longer accessible.
db_update('key_value_expire')
->fields(['expire' => REQUEST_TIME - 1])
->condition('collection', "tempstore.shared.$collection")
->condition('name', $key)
->execute();
$this->assertFalse($stores[0]->get($key));
$this->assertFalse($stores[1]->get($key));
}
}
@@ -0,0 +1,33 @@
<?php
namespace Drupal\KernelTests\Core\Test;
use Drupal\KernelTests\KernelTestBase;
use Drupal\deprecation_test\Deprecation\FixtureDeprecatedClass;
/**
* Test how kernel tests interact with deprecation errors.
*
* @group Test
* @group legacy
*/
class PhpUnitBridgeTest extends KernelTestBase {
public static $modules = ['deprecation_test'];
/**
* @expectedDeprecation Drupal\deprecation_test\Deprecation\FixtureDeprecatedClass is deprecated.
*/
public function testDeprecatedClass() {
$deprecated = new FixtureDeprecatedClass();
$this->assertEquals('test', $deprecated->testFunction());
}
/**
* @expectedDeprecation This is the deprecation message for deprecation_test_function().
*/
public function testDeprecatedFunction() {
$this->assertEquals('known_return_value', \deprecation_test_function());
}
}
@@ -192,4 +192,23 @@ class RegistryTest extends KernelTestBase {
], $suggestions, 'Found expected page node suggestions.');
}
/**
* Tests theme-provided templates that are registered by modules.
*/
public function testThemeTemplatesRegisteredByModules() {
$theme_handler = \Drupal::service('theme_handler');
$theme_handler->install(['test_theme']);
$registry_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_theme');
$registry_theme->setThemeManager(\Drupal::theme());
$expected = [
'template_preprocess',
'template_preprocess_container',
'template_preprocess_theme_test_registered_by_module'
];
$registry = $registry_theme->get();
$this->assertEquals($expected, array_values($registry['theme_test_registered_by_module']['preprocess functions']));
}
}
@@ -24,7 +24,6 @@ class StableTemplateOverrideTest extends KernelTestBase {
*/
protected $templatesToSkip = [
'views-form-views-form',
'entity-moderation-form'
];
/**
@@ -57,7 +57,7 @@ class TwigMarkupInterfaceTest extends KernelTestBase {
'empty GeneratedLink' => ['', new GeneratedLink()],
'non-empty GeneratedLink' => ['<span><a hef="http://www.example.com">test</a></span>', (new GeneratedLink())->setGeneratedLink('<a hef="http://www.example.com">test</a>')],
// Test objects that do not implement \Countable.
'empty SafeMarkupTestMarkup' => ['<span></span>', SafeMarkupTestMarkup::create('')],
'empty SafeMarkupTestMarkup' => ['', SafeMarkupTestMarkup::create('')],
'non-empty SafeMarkupTestMarkup' => ['<span>test</span>', SafeMarkupTestMarkup::create('test')],
];
}
@@ -22,7 +22,7 @@ class UpdaterTest extends KernelTestBase {
* @see https://drupal.org/node/2409515
*/
public function testGetProjectTitleWithChild() {
// Get the project title from it's directory. If it can't find the title
// Get the project title from its directory. If it can't find the title
// it will choose the first project title in the directory.
$directory = \Drupal::root() . '/core/modules/system/tests/modules/module_handler_test_multiple';
$title = Updater::getProjectTitle($directory);
@@ -251,7 +251,7 @@ abstract class KernelTestBase extends TestCase implements ServiceProviderInterfa
* Should not be called by tests. Only visible for DrupalKernel integration
* tests.
*
* @see \Drupal\system\Tests\DrupalKernel\DrupalKernelTest
* @see \Drupal\KernelTests\Core\DrupalKernel\DrupalKernelTest
* @internal
*/
protected function bootEnvironment() {
@@ -137,6 +137,10 @@ class KernelTestBaseTest extends KernelTestBase {
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Request', $new_request);
$this->assertSame($new_request, \Drupal::request());
$this->assertSame($request, $new_request);
// Ensure getting the router.route_provider does not trigger a deprecation
// message that errors.
$this->container->get('router.route_provider');
}
/**