Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@ namespace Drupal\KernelTests;
* Translates Simpletest assertion methods to PHPUnit.
*
* Protected methods are custom. Public static methods override methods of
* \PHPUnit_Framework_Assert.
* \PHPUnit\Framework\Assert.
*
* @deprecated Scheduled for removal in Drupal 9.0.0. Use PHPUnit's native
* assert methods instead.

View file

@ -2,7 +2,7 @@
namespace Drupal\KernelTests\Component\Utility;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Url;
use Drupal\KernelTests\KernelTestBase;
@ -28,7 +28,7 @@ class SafeMarkupKernelTest extends KernelTestBase {
}
/**
* Gets arguments for SafeMarkup::format() based on Url::fromUri() parameters.
* Gets arguments for FormattableMarkup based on Url::fromUri() parameters.
*
* @param string $uri
* The URI of the resource.
@ -38,6 +38,8 @@ class SafeMarkupKernelTest extends KernelTestBase {
* @return array
* Array containing:
* - ':url': A URL string.
*
* @see \Drupal\Component\Render\FormattableMarkup
*/
protected static function getSafeMarkupUriArgs($uri, $options = []) {
$args[':url'] = Url::fromUri($uri, $options)->toString();
@ -45,13 +47,13 @@ class SafeMarkupKernelTest extends KernelTestBase {
}
/**
* Tests URL ":placeholders" in SafeMarkup::format().
* Tests URL ":placeholders" in \Drupal\Component\Render\FormattableMarkup.
*
* @dataProvider providerTestSafeMarkupUri
*/
public function testSafeMarkupUri($string, $uri, $options, $expected) {
$args = self::getSafeMarkupUriArgs($uri, $options);
$this->assertEquals($expected, SafeMarkup::format($string, $args));
$this->assertEquals($expected, new FormattableMarkup($string, $args));
}
/**
@ -113,7 +115,7 @@ class SafeMarkupKernelTest extends KernelTestBase {
$this->setExpectedException(\InvalidArgumentException::class);
$args = self::getSafeMarkupUriArgs($uri);
SafeMarkup::format($string, $args);
new FormattableMarkup($string, $args);
}
/**

View file

@ -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);

View file

@ -0,0 +1,166 @@
<?php
namespace Drupal\KernelTests\Config;
use Drupal\Core\Config\Schema\SequenceDataDefinition;
use Drupal\Core\Config\Schema\TypedConfigInterface;
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\Core\TypedData\Type\IntegerInterface;
use Drupal\Core\TypedData\Type\StringInterface;
use Drupal\KernelTests\KernelTestBase;
use Symfony\Component\Validator\ConstraintViolationListInterface;
/**
* Tests config validation mechanism.
*
* @group Config
*/
class TypedConfigTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['config_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig('config_test');
}
/**
* Verifies that the Typed Data API is implemented correctly.
*/
public function testTypedDataAPI() {
/** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager */
$typed_config_manager = \Drupal::service('config.typed');
/** @var \Drupal\Core\Config\Schema\TypedConfigInterface $typed_config */
$typed_config = $typed_config_manager->get('config_test.validation');
// Test a primitive.
$string_data = $typed_config->get('llama');
$this->assertInstanceOf(StringInterface::class, $string_data);
$this->assertEquals('llama', $string_data->getValue());
// Test complex data.
$mapping = $typed_config->get('cat');
/** @var \Drupal\Core\TypedData\ComplexDataInterface $mapping */
$this->assertInstanceOf(ComplexDataInterface::class, $mapping);
$this->assertInstanceOf(StringInterface::class, $mapping->get('type'));
$this->assertEquals('kitten', $mapping->get('type')->getValue());
$this->assertInstanceOf(IntegerInterface::class, $mapping->get('count'));
$this->assertEquals(2, $mapping->get('count')->getValue());
// Verify the item metadata is available.
$this->assertInstanceOf(ComplexDataDefinitionInterface::class, $mapping->getDataDefinition());
$this->assertArrayHasKey('type', $mapping->getProperties());
$this->assertArrayHasKey('count', $mapping->getProperties());
// Test accessing sequences.
$sequence = $typed_config->get('giraffe');
/** @var \Drupal\Core\TypedData\ListInterface $sequence */
$this->assertInstanceOf(ComplexDataInterface::class, $sequence);
$this->assertInstanceOf(StringInterface::class, $sequence->get('hum1'));
$this->assertEquals('hum1', $sequence->get('hum1')->getValue());
$this->assertEquals('hum2', $sequence->get('hum2')->getValue());
$this->assertEquals(2, count($sequence->getIterator()));
// Verify the item metadata is available.
$this->assertInstanceOf(SequenceDataDefinition::class, $sequence->getDataDefinition());
// Test accessing typed config objects for simple config and config
// entities.
$typed_config_manager = \Drupal::service('config.typed');
$typed_config = $typed_config_manager->createFromNameAndData('config_test.validation', \Drupal::configFactory()->get('config_test.validation')->get());
$this->assertInstanceOf(TypedConfigInterface::class, $typed_config);
$this->assertEquals(['llama', 'cat', 'giraffe', 'uuid', '_core'], array_keys($typed_config->getElements()));
$config_test_entity = \Drupal::entityTypeManager()->getStorage('config_test')->create([
'id' => 'asterix',
'label' => 'Asterix',
'weight' => 11,
'style' => 'test_style',
]);
$typed_config = $typed_config_manager->createFromNameAndData($config_test_entity->getConfigDependencyName(), $config_test_entity->toArray());
$this->assertInstanceOf(TypedConfigInterface::class, $typed_config);
$this->assertEquals(['uuid', 'langcode', 'status', 'dependencies', 'id', 'label', 'weight', 'style', 'size', 'size_value', 'protected_property'], array_keys($typed_config->getElements()));
}
/**
* Tests config validation via the Typed Data API.
*/
public function testSimpleConfigValidation() {
$config = \Drupal::configFactory()->getEditable('config_test.validation');
/** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager */
$typed_config_manager = \Drupal::service('config.typed');
/** @var \Drupal\Core\Config\Schema\TypedConfigInterface $typed_config */
$typed_config = $typed_config_manager->get('config_test.validation');
$result = $typed_config->validate();
$this->assertInstanceOf(ConstraintViolationListInterface::class, $result);
$this->assertEmpty($result);
// Test constraints on primitive types.
$config->set('llama', 'elephant');
$config->save();
$typed_config = $typed_config_manager->get('config_test.validation');
$result = $typed_config->validate();
// Its not a valid llama anymore.
$this->assertCount(1, $result);
$this->assertEquals('no valid llama', $result->get(0)->getMessage());
// Test constraints on mapping.
$config->set('llama', 'llama');
$config->set('cat.type', 'nyans');
$config->save();
$typed_config = $typed_config_manager->get('config_test.validation');
$result = $typed_config->validate();
$this->assertEmpty($result);
// Test constrains on nested mapping.
$config->set('cat.type', 'miaus');
$config->save();
$typed_config = $typed_config_manager->get('config_test.validation');
$result = $typed_config->validate();
$this->assertCount(1, $result);
$this->assertEquals('no valid cat', $result->get(0)->getMessage());
// Test constrains on sequences elements.
$config->set('cat.type', 'nyans');
$config->set('giraffe', ['muh', 'hum2']);
$config->save();
$typed_config = $typed_config_manager->get('config_test.validation');
$result = $typed_config->validate();
$this->assertCount(1, $result);
$this->assertEquals('Giraffes just hum', $result->get(0)->getMessage());
// Test constrains on the sequence itself.
$config->set('giraffe', ['hum', 'hum2', 'invalid-key' => 'hum']);
$config->save();
$typed_config = $typed_config_manager->get('config_test.validation');
$result = $typed_config->validate();
$this->assertCount(1, $result);
$this->assertEquals('giraffe', $result->get(0)->getPropertyPath());
$this->assertEquals('Invalid giraffe key.', $result->get(0)->getMessage());
// Validates mapping.
$typed_config = $typed_config_manager->get('config_test.validation');
$value = $typed_config->getValue();
unset($value['giraffe']);
$value['elephant'] = 'foo';
$value['zebra'] = 'foo';
$typed_config->setValue($value);
$result = $typed_config->validate();
$this->assertCount(1, $result);
$this->assertEquals('', $result->get(0)->getPropertyPath());
$this->assertEquals('Unexpected keys: elephant, zebra', $result->get(0)->getMessage());
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace Drupal\KernelTests;
use Drupal\Core\Form\FormState;
/**
* Full generic test suite for any form that data with the configuration system.
*
* @see UserAdminSettingsFormTest
* For a full working implementation.
*/
abstract class ConfigFormTestBase extends KernelTestBase {
/**
* Form ID to use for testing.
*
* @var \Drupal\Core\Form\FormInterface
*/
protected $form;
/**
* Values to use for testing.
*
* Contains details for form key, configuration object name, and config key.
* Example:
* @code
* array(
* 'user_mail_cancel_confirm_body' => array(
* '#value' => $this->randomString(),
* '#config_name' => 'user.mail',
* '#config_key' => 'cancel_confirm.body',
* ),
* );
* @endcode
*
* @var array
*/
protected $values;
/**
* Submit the system_config_form ensure the configuration has expected values.
*/
public function testConfigForm() {
// Programmatically submit the given values.
$values = [];
foreach ($this->values as $form_key => $data) {
$values[$form_key] = $data['#value'];
}
$form_state = (new FormState())->setValues($values);
\Drupal::formBuilder()->submitForm($this->form, $form_state);
// Check that the form returns an error when expected, and vice versa.
$errors = $form_state->getErrors();
$valid_form = empty($errors);
$args = [
'%values' => print_r($values, TRUE),
'%errors' => $valid_form ? t('None') : implode(' ', $errors),
];
$this->assertTrue($valid_form, format_string('Input values: %values<br/>Validation handler errors: %errors', $args));
foreach ($this->values as $data) {
$this->assertEqual($data['#value'], $this->config($data['#config_name'])->get($data['#config_key']));
}
}
}

View file

@ -0,0 +1,85 @@
<?php
namespace Drupal\KernelTests\Core\Action;
use Drupal\Core\Action\Plugin\Action\Derivative\EntityDeleteActionDeriver;
use Drupal\entity_test\Entity\EntityTestMulRevPub;
use Drupal\KernelTests\KernelTestBase;
use Drupal\system\Entity\Action;
use Drupal\user\Entity\User;
/**
* @group Action
*/
class DeleteActionTest extends KernelTestBase {
protected $testUser;
/**
* {@inheritdoc}
*/
public static $modules = ['system', 'entity_test', 'user'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('entity_test_mulrevpub');
$this->installEntitySchema('user');
$this->installSchema('system', ['sequences', 'key_value_expire']);
$this->testUser = User::create([
'name' => 'foobar',
'mail' => 'foobar@example.com',
]);
$this->testUser->save();
\Drupal::service('current_user')->setAccount($this->testUser);
}
/**
* @covers \Drupal\Core\Action\Plugin\Action\Derivative\EntityDeleteActionDeriver::getDerivativeDefinitions
*/
public function testGetDerivativeDefinitions() {
$deriver = new EntityDeleteActionDeriver(\Drupal::entityTypeManager(), \Drupal::translation());
$this->assertEquals([
'entity_test_mulrevpub' => [
'type' => 'entity_test_mulrevpub',
'label' => 'Delete test entity - revisions, data table, and published interface',
'action_label' => 'Delete',
'confirm_form_route_name' => 'entity.entity_test_mulrevpub.delete_multiple_form',
],
'entity_test_rev' => [
'type' => 'entity_test_rev',
'label' => 'Delete test entity - revisions',
'action_label' => 'Delete',
'confirm_form_route_name' => 'entity.entity_test_rev.delete_multiple_form',
],
], $deriver->getDerivativeDefinitions([
'action_label' => 'Delete',
]));
}
/**
* @covers \Drupal\Core\Action\Plugin\Action\DeleteAction::execute
*/
public function testDeleteAction() {
$entity = EntityTestMulRevPub::create(['name' => 'test']);
$entity->save();
$action = Action::create([
'id' => 'entity_delete_action',
'plugin' => 'entity:delete_action:entity_test_mulrevpub',
]);
$action->save();
$action->execute([$entity]);
$this->assertArraySubset(['module' => ['entity_test']], $action->getDependencies());
/** @var \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store */
$temp_store = \Drupal::service('tempstore.private');
$store_entries = $temp_store->get('entity_delete_multiple_confirm')->get($this->testUser->id() . ':entity_test_mulrevpub');
$this->assertArraySubset([$this->testUser->id() => ['en' => 'en']], $store_entries);
}
}

View file

@ -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(), \Drupal::translation());
$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());
}
}

View file

@ -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(), \Drupal::translation());
$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());
}
}

View file

@ -0,0 +1,103 @@
<?php
namespace Drupal\KernelTests\Core\Ajax;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
use Drupal\KernelTests\KernelTestBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Performs tests on AJAX framework commands.
*
* @group Ajax
*/
class CommandsTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['system', 'node', 'ajax_test', 'ajax_forms_test'];
/**
* Regression test: Settings command exists regardless of JS aggregation.
*/
public function testAttachedSettings() {
$assert = function ($message) {
$response = new AjaxResponse();
$response->setAttachments([
'library' => ['core/drupalSettings'],
'drupalSettings' => ['foo' => 'bar'],
]);
$ajax_response_attachments_processor = \Drupal::service('ajax_response.attachments_processor');
$subscriber = new AjaxResponseSubscriber($ajax_response_attachments_processor);
$event = new FilterResponseEvent(
\Drupal::service('http_kernel'),
new Request(),
HttpKernelInterface::MASTER_REQUEST,
$response
);
$subscriber->onResponse($event);
$expected = [
'command' => 'settings',
];
$this->assertCommand($response->getCommands(), $expected, $message);
};
$config = $this->config('system.performance');
$config->set('js.preprocess', FALSE)->save();
$assert('Settings command exists when JS aggregation is disabled.');
$config->set('js.preprocess', TRUE)->save();
$assert('Settings command exists when JS aggregation is enabled.');
}
/**
* Asserts the array of Ajax commands contains the searched command.
*
* An AjaxResponse object stores an array of Ajax commands. This array
* sometimes includes commands automatically provided by the framework in
* addition to commands returned by a particular controller. During testing,
* we're usually interested that a particular command is present, and don't
* care whether other commands precede or follow the one we're interested in.
* Additionally, the command we're interested in may include additional data
* that we're not interested in. Therefore, this function simply asserts that
* one of the commands in $haystack contains all of the keys and values in
* $needle. Furthermore, if $needle contains a 'settings' key with an array
* value, we simply assert that all keys and values within that array are
* present in the command we're checking, and do not consider it a failure if
* the actual command contains additional settings that aren't part of
* $needle.
*
* @param $haystack
* An array of rendered Ajax commands returned by the server.
* @param $needle
* Array of info we're expecting in one of those commands.
* @param $message
* An assertion message.
*/
protected function assertCommand($haystack, $needle, $message) {
$found = FALSE;
foreach ($haystack as $command) {
// If the command has additional settings that we're not testing for, do
// not consider that a failure.
if (isset($command['settings']) && is_array($command['settings']) && isset($needle['settings']) && is_array($needle['settings'])) {
$command['settings'] = array_intersect_key($command['settings'], $needle['settings']);
}
// If the command has additional data that we're not testing for, do not
// consider that a failure. Also, == instead of ===, because we don't
// require the key/value pairs to be in any particular order
// (http://php.net/manual/language.operators.array.php).
if (array_intersect_key($command, $needle) == $needle) {
$found = TRUE;
break;
}
}
$this->assertTrue($found, $message);
}
}

View file

@ -3,14 +3,13 @@
namespace Drupal\KernelTests\Core\Asset;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Asset\AttachedAssets;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests #attached assets: attached asset libraries and JavaScript settings.
*
* i.e. tests:
* I.e. tests:
*
* @code
* $build['#attached']['library'] =
@ -70,7 +69,7 @@ class AttachedAssetsTest extends KernelTestBase {
$build['#attached']['library'][] = 'core/unknown';
$assets = AttachedAssets::createFromRenderArray($build);
$this->assertIdentical([], $this->assetResolver->getJsAssets($assets, FALSE)[0], 'Unknown library was not added to the page.');
$this->assertSame([], $this->assetResolver->getJsAssets($assets, FALSE)[0], 'Unknown library was not added to the page.');
}
/**
@ -205,7 +204,7 @@ class AttachedAssetsTest extends KernelTestBase {
$end = strrpos($rendered_js, $endToken);
// Convert to a string, as $renderer_js is a \Drupal\Core\Render\Markup
// object.
$json = Unicode::substr($rendered_js, $start, $end - $start + 1);
$json = mb_substr($rendered_js, $start, $end - $start + 1);
$parsed_settings = Json::decode($json);
// Test whether the settings for core/drupalSettings are available.
@ -299,7 +298,8 @@ class AttachedAssetsTest extends KernelTestBase {
"-8_2",
"-8_3",
"-8_4",
"-5_1", // The external script.
// The external script.
"-5_1",
"-3_1",
"-3_2",
"0_1",
@ -435,12 +435,12 @@ class AttachedAssetsTest extends KernelTestBase {
$dynamic_library = $library_discovery->getLibraryByName('common_test', 'dynamic_library');
$this->assertTrue(is_array($dynamic_library));
if ($this->assertTrue(isset($dynamic_library['version']))) {
$this->assertIdentical('1.0', $dynamic_library['version']);
$this->assertSame('1.0', $dynamic_library['version']);
}
// Make sure the dynamic library definition could be altered.
// @see common_test_library_info_alter()
if ($this->assertTrue(isset($dynamic_library['dependencies']))) {
$this->assertIdentical(['core/jquery'], $dynamic_library['dependencies']);
$this->assertSame(['core/jquery'], $dynamic_library['dependencies']);
}
}

View file

@ -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);
@ -145,7 +149,6 @@ class ResolvedLibraryDefinitionsFilesMatchTest extends KernelTestBase {
* so on.
*/
protected function verifyLibraryFilesExist($library_definitions) {
$root = \Drupal::root();
foreach ($library_definitions as $extension => $libraries) {
foreach ($libraries as $library_name => $library) {
if (in_array("$extension/$library_name", $this->librariesToSkip)) {
@ -156,7 +159,7 @@ class ResolvedLibraryDefinitionsFilesMatchTest extends KernelTestBase {
foreach (['css', 'js'] as $asset_type) {
foreach ($library[$asset_type] as $asset) {
$file = $asset['data'];
$path = $root . '/' . $file;
$path = $this->root . '/' . $file;
// Only check and assert each file path once.
if (!isset($this->pathsChecked[$path])) {
$this->assertTrue(is_file($path), "$file file referenced from the $extension/$library_name library exists.");
@ -188,10 +191,9 @@ class ResolvedLibraryDefinitionsFilesMatchTest extends KernelTestBase {
$libraries['core'] = $this->libraryDiscovery->getLibrariesByExtension('core');
$root = \Drupal::root();
foreach ($extensions as $extension_name => $extension) {
$library_file = $extension->getPath() . '/' . $extension_name . '.libraries.yml';
if (is_file($root . '/' . $library_file)) {
if (is_file($this->root . '/' . $library_file)) {
$libraries[$extension_name] = $this->libraryDiscovery->getLibrariesByExtension($extension_name);
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\KernelTests\Core\Batch;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests batch functionality.
*
* @group Batch
*/
class BatchKernelTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
require_once $this->root . '/core/includes/batch.inc';
}
/**
* Tests _batch_needs_update().
*/
public function testNeedsUpdate() {
// Before ever being called, the return value should be FALSE.
$this->assertEquals(FALSE, _batch_needs_update());
// Set the value to TRUE.
$this->assertEquals(TRUE, _batch_needs_update(TRUE));
// Check that without a parameter TRUE is returned.
$this->assertEquals(TRUE, _batch_needs_update());
// Set the value to FALSE.
$this->assertEquals(FALSE, _batch_needs_update(FALSE));
$this->assertEquals(FALSE, _batch_needs_update());
}
}

View file

@ -49,21 +49,24 @@ class GetFilenameTest extends KernelTestBase {
// a fixed location and naming.
$this->assertIdentical(drupal_get_filename('profile', 'testing'), 'core/profiles/testing/testing.info.yml');
// Generate a non-existing module name.
$non_existing_module = uniqid("", TRUE);
// Set a custom error handler so we can ignore the file not found error.
set_error_handler(function($severity, $message, $file, $line) {
set_error_handler(function ($severity, $message, $file, $line) {
// Skip error handling if this is a "file not found" error.
if (strstr($message, 'is missing from the file system:')) {
\Drupal::state()->set('get_filename_test_triggered_error', TRUE);
\Drupal::state()->set('get_filename_test_triggered_error', $message);
return;
}
throw new \ErrorException($message, 0, $severity, $file, $line);
});
$this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for an item that does not exist returns NULL.');
$this->assertTrue(\Drupal::state()->get('get_filename_test_triggered_error'), 'Searching for an item that does not exist triggers an error.');
$this->assertNull(drupal_get_filename('module', 'there_is_a_module_for_that'), 'Searching for an item that does not exist returns NULL.');
$this->assertEquals('The following module is missing from the file system: there_is_a_module_for_that', \Drupal::state()->get('get_filename_test_triggered_error'));
$this->assertNull(drupal_get_filename('theme', 'there_is_a_theme_for_you'), 'Searching for an item that does not exist returns NULL.');
$this->assertEquals('The following theme is missing from the file system: there_is_a_theme_for_you', \Drupal::state()->get('get_filename_test_triggered_error'));
$this->assertNull(drupal_get_filename('profile', 'there_is_an_install_profile_for_you'), 'Searching for an item that does not exist returns NULL.');
$this->assertEquals('The following profile is missing from the file system: there_is_an_install_profile_for_you', \Drupal::state()->get('get_filename_test_triggered_error'));
// Restore the original error handler.
restore_error_handler();
}

View file

@ -20,7 +20,7 @@ class ChainedFastBackendTest extends GenericCacheBackendUnitTestBase {
* A new ChainedFastBackend object.
*/
protected function createCacheBackend($bin) {
$consistent_backend = new DatabaseBackend(\Drupal::service('database'), \Drupal::service('cache_tags.invalidator.checksum'), $bin);
$consistent_backend = new DatabaseBackend(\Drupal::service('database'), \Drupal::service('cache_tags.invalidator.checksum'), $bin, 100);
$fast_backend = new PhpBackend($bin, \Drupal::service('cache_tags.invalidator.checksum'));
$backend = new ChainedFastBackend($consistent_backend, $fast_backend, $bin);
// Explicitly register the cache bin as it can not work through the

View file

@ -11,6 +11,13 @@ use Drupal\Core\Cache\DatabaseBackend;
*/
class DatabaseBackendTest extends GenericCacheBackendUnitTestBase {
/**
* The max rows to use for test bins.
*
* @var int
*/
protected static $maxRows = 100;
/**
* Modules to enable.
*
@ -25,7 +32,7 @@ class DatabaseBackendTest extends GenericCacheBackendUnitTestBase {
* A new DatabaseBackend object.
*/
protected function createCacheBackend($bin) {
return new DatabaseBackend($this->container->get('database'), $this->container->get('cache_tags.invalidator.checksum'), $bin);
return new DatabaseBackend($this->container->get('database'), $this->container->get('cache_tags.invalidator.checksum'), $bin, static::$maxRows);
}
/**
@ -40,12 +47,60 @@ class DatabaseBackendTest extends GenericCacheBackendUnitTestBase {
$cid_long = str_repeat('愛€', 500);
$cached_value_long = $this->randomMachineName();
$backend->set($cid_long, $cached_value_long);
$this->assertIdentical($cached_value_long, $backend->get($cid_long)->data, "Backend contains the correct value for long, non-ASCII cache id.");
$this->assertSame($cached_value_long, $backend->get($cid_long)->data, "Backend contains the correct value for long, non-ASCII cache id.");
$cid_short = '愛1€';
$cached_value_short = $this->randomMachineName();
$backend->set($cid_short, $cached_value_short);
$this->assertIdentical($cached_value_short, $backend->get($cid_short)->data, "Backend contains the correct value for short, non-ASCII cache id.");
$this->assertSame($cached_value_short, $backend->get($cid_short)->data, "Backend contains the correct value for short, non-ASCII cache id.");
}
/**
* Tests the row count limiting of cache bin database tables.
*/
public function testGarbageCollection() {
$backend = $this->getCacheBackend();
$max_rows = static::$maxRows;
$this->assertSame(0, (int) $this->getNumRows());
// Fill to just the limit.
for ($i = 0; $i < $max_rows; $i++) {
// Ensure that each cache item created happens in a different millisecond,
// by waiting 1 ms (1000 microseconds). The garbage collection might
// otherwise keep less than exactly 100 records (which is acceptable for
// real-world cases, but not for this test).
usleep(1000);
$backend->set("test$i", $i);
}
$this->assertSame($max_rows, $this->getNumRows());
// Garbage collection has no effect.
$backend->garbageCollection();
$this->assertSame($max_rows, $this->getNumRows());
// Go one row beyond the limit.
$backend->set('test' . ($max_rows + 1), $max_rows + 1);
$this->assertSame($max_rows + 1, $this->getNumRows());
// Garbage collection removes one row: the oldest.
$backend->garbageCollection();
$this->assertSame($max_rows, $this->getNumRows());
$this->assertFalse($backend->get('test0'));
}
/**
* Gets the number of rows in the test cache bin database table.
*
* @return int
* The number of rows in the test cache bin database table.
*/
protected function getNumRows() {
$table = 'cache_' . $this->testBin;
$connection = $this->container->get('database');
$query = $connection->select($table);
$query->addExpression('COUNT(cid)', 'cid');
return (int) $query->execute()->fetchField();
}
}

View file

@ -66,7 +66,7 @@ abstract class GenericCacheBackendUnitTestBase extends KernelTestBase {
* @return \Drupal\Core\Cache\CacheBackendInterface
* Cache backend to test.
*/
protected abstract function createCacheBackend($bin);
abstract protected function createCacheBackend($bin);
/**
* Allows specific implementation to change the environment before a test run.
@ -130,22 +130,22 @@ abstract class GenericCacheBackendUnitTestBase extends KernelTestBase {
public function testSetGet() {
$backend = $this->getCacheBackend();
$this->assertIdentical(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1.");
$this->assertSame(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1.");
$with_backslash = ['foo' => '\Drupal\foo\Bar'];
$backend->set('test1', $with_backslash);
$cached = $backend->get('test1');
$this->assert(is_object($cached), "Backend returned an object for cache id test1.");
$this->assertIdentical($with_backslash, $cached->data);
$this->assertSame($with_backslash, $cached->data);
$this->assertTrue($cached->valid, 'Item is marked as valid.');
// We need to round because microtime may be rounded up in the backend.
$this->assertTrue($cached->created >= REQUEST_TIME && $cached->created <= round(microtime(TRUE), 3), 'Created time is correct.');
$this->assertEqual($cached->expire, Cache::PERMANENT, 'Expire time is correct.');
$this->assertIdentical(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2.");
$this->assertSame(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2.");
$backend->set('test2', ['value' => 3], REQUEST_TIME + 3);
$cached = $backend->get('test2');
$this->assert(is_object($cached), "Backend returned an object for cache id test2.");
$this->assertIdentical(['value' => 3], $cached->data);
$this->assertSame(['value' => 3], $cached->data);
$this->assertTrue($cached->valid, 'Item is marked as valid.');
$this->assertTrue($cached->created >= REQUEST_TIME && $cached->created <= round(microtime(TRUE), 3), 'Created time is correct.');
$this->assertEqual($cached->expire, REQUEST_TIME + 3, 'Expire time is correct.');
@ -158,22 +158,22 @@ abstract class GenericCacheBackendUnitTestBase extends KernelTestBase {
$this->assertTrue($cached->created >= REQUEST_TIME && $cached->created <= round(microtime(TRUE), 3), 'Created time is correct.');
$this->assertEqual($cached->expire, REQUEST_TIME - 3, 'Expire time is correct.');
$this->assertIdentical(FALSE, $backend->get('test4'), "Backend does not contain data for cache id test4.");
$this->assertSame(FALSE, $backend->get('test4'), "Backend does not contain data for cache id test4.");
$with_eof = ['foo' => "\nEOF\ndata"];
$backend->set('test4', $with_eof);
$cached = $backend->get('test4');
$this->assert(is_object($cached), "Backend returned an object for cache id test4.");
$this->assertIdentical($with_eof, $cached->data);
$this->assertSame($with_eof, $cached->data);
$this->assertTrue($cached->valid, 'Item is marked as valid.');
$this->assertTrue($cached->created >= REQUEST_TIME && $cached->created <= round(microtime(TRUE), 3), 'Created time is correct.');
$this->assertEqual($cached->expire, Cache::PERMANENT, 'Expire time is correct.');
$this->assertIdentical(FALSE, $backend->get('test5'), "Backend does not contain data for cache id test5.");
$this->assertSame(FALSE, $backend->get('test5'), "Backend does not contain data for cache id test5.");
$with_eof_and_semicolon = ['foo' => "\nEOF;\ndata"];
$backend->set('test5', $with_eof_and_semicolon);
$cached = $backend->get('test5');
$this->assert(is_object($cached), "Backend returned an object for cache id test5.");
$this->assertIdentical($with_eof_and_semicolon, $cached->data);
$this->assertSame($with_eof_and_semicolon, $cached->data);
$this->assertTrue($cached->valid, 'Item is marked as valid.');
$this->assertTrue($cached->created >= REQUEST_TIME && $cached->created <= round(microtime(TRUE), 3), 'Created time is correct.');
$this->assertEqual($cached->expire, Cache::PERMANENT, 'Expire time is correct.');
@ -182,7 +182,7 @@ abstract class GenericCacheBackendUnitTestBase extends KernelTestBase {
$backend->set('test6', $with_variable);
$cached = $backend->get('test6');
$this->assert(is_object($cached), "Backend returned an object for cache id test6.");
$this->assertIdentical($with_variable, $cached->data);
$this->assertSame($with_variable, $cached->data);
// Make sure that a cached object is not affected by changing the original.
$data = new \stdClass();
@ -229,26 +229,26 @@ abstract class GenericCacheBackendUnitTestBase extends KernelTestBase {
public function testDelete() {
$backend = $this->getCacheBackend();
$this->assertIdentical(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1.");
$this->assertSame(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1.");
$backend->set('test1', 7);
$this->assert(is_object($backend->get('test1')), "Backend returned an object for cache id test1.");
$this->assertIdentical(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2.");
$this->assertSame(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2.");
$backend->set('test2', 3);
$this->assert(is_object($backend->get('test2')), "Backend returned an object for cache id %cid.");
$backend->delete('test1');
$this->assertIdentical(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1 after deletion.");
$this->assertSame(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1 after deletion.");
$this->assert(is_object($backend->get('test2')), "Backend still has an object for cache id test2.");
$backend->delete('test2');
$this->assertIdentical(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2 after deletion.");
$this->assertSame(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2 after deletion.");
$long_cid = str_repeat('a', 300);
$backend->set($long_cid, 'test');
$backend->delete($long_cid);
$this->assertIdentical(FALSE, $backend->get($long_cid), "Backend does not contain data for long cache id after deletion.");
$this->assertSame(FALSE, $backend->get($long_cid), "Backend does not contain data for long cache id after deletion.");
}
/**
@ -275,7 +275,7 @@ abstract class GenericCacheBackendUnitTestBase extends KernelTestBase {
foreach ($variables as $cid => $value) {
$object = $backend->get($cid);
$this->assert(is_object($object), sprintf("Backend returned an object for cache id %s.", $cid));
$this->assertIdentical($value, $object->data, sprintf("Data of cached id %s kept is identical in type and value", $cid));
$this->assertSame($value, $object->data, sprintf("Data of cached id %s kept is identical in type and value", $cid));
}
}
@ -300,9 +300,11 @@ abstract class GenericCacheBackendUnitTestBase extends KernelTestBase {
$reference = [
'test3',
'test7',
'test21', // Cid does not exist.
// Cid does not exist.
'test21',
'test6',
'test19', // Cid does not exist until added before second getMultiple().
// Cid does not exist until added before second getMultiple().
'test19',
'test2',
];
@ -440,20 +442,23 @@ abstract class GenericCacheBackendUnitTestBase extends KernelTestBase {
$backend->set('test7', 17);
$backend->delete('test1');
$backend->delete('test23'); // Nonexistent key should not cause an error.
// Nonexistent key should not cause an error.
$backend->delete('test23');
$backend->deleteMultiple([
'test3',
'test5',
'test7',
'test19', // Nonexistent key should not cause an error.
'test21', // Nonexistent key should not cause an error.
// Nonexistent key should not cause an error.
'test19',
// Nonexistent key should not cause an error.
'test21',
]);
// Test if expected keys have been deleted.
$this->assertIdentical(FALSE, $backend->get('test1'), "Cache id test1 deleted.");
$this->assertIdentical(FALSE, $backend->get('test3'), "Cache id test3 deleted.");
$this->assertIdentical(FALSE, $backend->get('test5'), "Cache id test5 deleted.");
$this->assertIdentical(FALSE, $backend->get('test7'), "Cache id test7 deleted.");
$this->assertSame(FALSE, $backend->get('test1'), "Cache id test1 deleted.");
$this->assertSame(FALSE, $backend->get('test3'), "Cache id test3 deleted.");
$this->assertSame(FALSE, $backend->get('test5'), "Cache id test5 deleted.");
$this->assertSame(FALSE, $backend->get('test7'), "Cache id test7 deleted.");
// Test if expected keys exist.
$this->assertNotIdentical(FALSE, $backend->get('test2'), "Cache id test2 exists.");
@ -461,8 +466,8 @@ abstract class GenericCacheBackendUnitTestBase extends KernelTestBase {
$this->assertNotIdentical(FALSE, $backend->get('test6'), "Cache id test6 exists.");
// Test if that expected keys do not exist.
$this->assertIdentical(FALSE, $backend->get('test19'), "Cache id test19 does not exist.");
$this->assertIdentical(FALSE, $backend->get('test21'), "Cache id test21 does not exist.");
$this->assertSame(FALSE, $backend->get('test19'), "Cache id test19 does not exist.");
$this->assertSame(FALSE, $backend->get('test21'), "Cache id test21 does not exist.");
// Calling deleteMultiple() with an empty array should not cause an error.
$this->assertFalse($backend->deleteMultiple([]));

View file

@ -2,7 +2,7 @@
namespace Drupal\KernelTests\Core\Command;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Command\DbDumpApplication;
use Drupal\Core\Config\DatabaseStorage;
use Drupal\Core\Database\Database;
@ -70,7 +70,8 @@ class DbDumpTest extends KernelTestBase {
parent::register($container);
$container->register('cache_factory', 'Drupal\Core\Cache\DatabaseBackendFactory')
->addArgument(new Reference('database'))
->addArgument(new Reference('cache_tags.invalidator.checksum'));
->addArgument(new Reference('cache_tags.invalidator.checksum'))
->addArgument(new Reference('settings'));
}
/**
@ -204,9 +205,9 @@ class DbDumpTest extends KernelTestBase {
foreach ($this->tables as $table) {
$this->assertTrue(Database::getConnection()
->schema()
->tableExists($table), SafeMarkup::format('Table @table created by the database script.', ['@table' => $table]));
$this->assertIdentical($this->originalTableSchemas[$table], $this->getTableSchema($table), SafeMarkup::format('The schema for @table was properly restored.', ['@table' => $table]));
$this->assertIdentical($this->originalTableIndexes[$table], $this->getTableIndexes($table), SafeMarkup::format('The indexes for @table were properly restored.', ['@table' => $table]));
->tableExists($table), new FormattableMarkup('Table @table created by the database script.', ['@table' => $table]));
$this->assertSame($this->originalTableSchemas[$table], $this->getTableSchema($table), new FormattableMarkup('The schema for @table was properly restored.', ['@table' => $table]));
$this->assertSame($this->originalTableIndexes[$table], $this->getTableIndexes($table), new FormattableMarkup('The indexes for @table were properly restored.', ['@table' => $table]));
}
// Ensure the test config has been replaced.

View file

@ -6,12 +6,16 @@ use Drupal\KernelTests\KernelTestBase;
/**
* @covers ::drupal_set_message
* @group PHPUnit
* @group Common
* @group legacy
*/
class DrupalSetMessageTest extends KernelTestBase {
/**
* The basic functionality of drupal_set_message().
*
* @expectedDeprecation drupal_set_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead. See https://www.drupal.org/node/2774931
* @expectedDeprecation drupal_get_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::all() or \Drupal\Core\Messenger\MessengerInterface::messagesByType() instead. See https://www.drupal.org/node/2774931
*/
public function testDrupalSetMessage() {
drupal_set_message(t('A message: @foo', ['@foo' => 'bar']));
@ -20,10 +24,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();
}
}

View file

@ -12,58 +12,42 @@ use Drupal\KernelTests\KernelTestBase;
* @group Common
*/
class SizeTest extends KernelTestBase {
protected $exactTestCases;
protected $roundedTestCases;
protected function setUp() {
parent::setUp();
$kb = Bytes::KILOBYTE;
$this->exactTestCases = [
'1 byte' => 1,
'1 KB' => $kb,
'1 MB' => $kb * $kb,
'1 GB' => $kb * $kb * $kb,
'1 TB' => $kb * $kb * $kb * $kb,
'1 PB' => $kb * $kb * $kb * $kb * $kb,
'1 EB' => $kb * $kb * $kb * $kb * $kb * $kb,
'1 ZB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb,
'1 YB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb * $kb,
];
$this->roundedTestCases = [
'2 bytes' => 2,
'1 MB' => ($kb * $kb) - 1, // rounded to 1 MB (not 1000 or 1024 kilobyte!)
round(3623651 / ($this->exactTestCases['1 MB']), 2) . ' MB' => 3623651, // megabytes
round(67234178751368124 / ($this->exactTestCases['1 PB']), 2) . ' PB' => 67234178751368124, // petabytes
round(235346823821125814962843827 / ($this->exactTestCases['1 YB']), 2) . ' YB' => 235346823821125814962843827, // yottabytes
];
}
/**
* Checks that format_size() returns the expected string.
*
* @dataProvider providerTestCommonFormatSize
*/
public function testCommonFormatSize() {
foreach ([$this->exactTestCases, $this->roundedTestCases] as $test_cases) {
foreach ($test_cases as $expected => $input) {
$this->assertEqual(
($result = format_size($input, NULL)),
$expected,
$expected . ' == ' . $result . ' (' . $input . ' bytes)'
);
}
}
public function testCommonFormatSize($expected, $input) {
$size = format_size($input, NULL);
$this->assertEquals($expected, $size);
}
/**
* Cross-tests Bytes::toInt() and format_size().
* Provides a list of byte size to test.
*/
public function testCommonParseSizeFormatSize() {
foreach ($this->exactTestCases as $size) {
$this->assertEqual(
$size,
($parsed_size = Bytes::toInt($string = format_size($size, NULL))),
$size . ' == ' . $parsed_size . ' (' . $string . ')'
);
}
public function providerTestCommonFormatSize() {
$kb = Bytes::KILOBYTE;
return [
['1 byte', 1],
['2 bytes', 2],
['1 KB', $kb],
['1 MB', pow($kb, 2)],
['1 GB', pow($kb, 3)],
['1 TB', pow($kb, 4)],
['1 PB', pow($kb, 5)],
['1 EB', pow($kb, 6)],
['1 ZB', pow($kb, 7)],
['1 YB', pow($kb, 8)],
// Rounded to 1 MB - not 1000 or 1024 kilobyte
['1 MB', ($kb * $kb) - 1],
// Decimal Megabytes
['3.46 MB', 3623651],
// Decimal Petabytes
['59.72 PB', 67234178751368124],
// Decimal Yottabytes
['194.67 YB', 235346823821125814962843827],
];
}
}

View file

@ -21,7 +21,7 @@ class CacheabilityMetadataConfigOverrideTest extends KernelTestBase {
'config',
'config_override_test',
'system',
'user'
'user',
];
/**

View file

@ -3,7 +3,7 @@
namespace Drupal\KernelTests\Core\Config;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Config\ConfigNameException;
use Drupal\Core\Config\ConfigValueException;
use Drupal\Core\Config\InstallStorage;
@ -278,12 +278,12 @@ class ConfigCRUDTest extends KernelTestBase {
$this->assertIdentical($storage->read($name), $data);
// Test that schema type enforcement can be overridden by trusting the data.
$this->assertIdentical(99, $config->get('int'));
$this->assertSame(99, $config->get('int'));
$config->set('int', '99')->save(TRUE);
$this->assertIdentical('99', $config->get('int'));
$this->assertSame('99', $config->get('int'));
// Test that re-saving without testing the data enforces the schema type.
$config->save();
$this->assertIdentical($data, $config->get());
$this->assertSame($data, $config->get());
// Test that setting an unsupported type for a config object with a schema
// fails.
@ -292,7 +292,7 @@ class ConfigCRUDTest extends KernelTestBase {
$this->fail('No Exception thrown upon saving invalid data type.');
}
catch (UnsupportedDataTypeConfigException $e) {
$this->pass(SafeMarkup::format('%class thrown upon saving invalid data type.', [
$this->pass(new FormattableMarkup('%class thrown upon saving invalid data type.', [
'%class' => get_class($e),
]));
}
@ -309,7 +309,7 @@ class ConfigCRUDTest extends KernelTestBase {
$this->fail('No Exception thrown upon saving invalid data type.');
}
catch (UnsupportedDataTypeConfigException $e) {
$this->pass(SafeMarkup::format('%class thrown upon saving invalid data type.', [
$this->pass(new FormattableMarkup('%class thrown upon saving invalid data type.', [
'%class' => get_class($e),
]));
}

View file

@ -50,9 +50,9 @@ class ConfigDependencyTest extends EntityKernelTestBase {
'id' => 'entity1',
'dependencies' => [
'enforced' => [
'module' => ['node']
]
]
'module' => ['node'],
],
],
]
);
$entity1->save();
@ -161,11 +161,13 @@ class ConfigDependencyTest extends EntityKernelTestBase {
$missing_dependencies = $config_manager->findMissingContentDependencies();
$this->assertEqual([], $missing_dependencies);
$expected = [$entity_test->uuid() => [
'entity_type' => 'entity_test',
'bundle' => $entity_test->bundle(),
'uuid' => $entity_test->uuid(),
]];
$expected = [
$entity_test->uuid() => [
'entity_type' => 'entity_test',
'bundle' => $entity_test->bundle(),
'uuid' => $entity_test->uuid(),
],
];
// Delete the content entity so that is it now missing.
$entity_test->delete();
$missing_dependencies = $config_manager->findMissingContentDependencies();
@ -198,7 +200,7 @@ class ConfigDependencyTest extends EntityKernelTestBase {
'id' => 'entity1',
'dependencies' => [
'enforced' => [
'module' => ['node', 'config_test']
'module' => ['node', 'config_test'],
],
],
]
@ -233,9 +235,9 @@ class ConfigDependencyTest extends EntityKernelTestBase {
// Ensure that alphabetical order has no influence on dependency fixing and
// removal.
return [
[['a', 'b', 'c', 'd']],
[['d', 'c', 'b', 'a']],
[['c', 'd', 'a', 'b']],
[['a', 'b', 'c', 'd', 'e']],
[['e', 'd', 'c', 'b', 'a']],
[['e', 'c', 'd', 'a', 'b']],
];
}
@ -262,7 +264,7 @@ class ConfigDependencyTest extends EntityKernelTestBase {
'id' => 'entity_' . $entity_id_suffixes[0],
'dependencies' => [
'enforced' => [
'module' => ['node', 'config_test']
'module' => ['node', 'config_test'],
],
],
]
@ -307,13 +309,32 @@ class ConfigDependencyTest extends EntityKernelTestBase {
'dependencies' => [
'enforced' => [
'config' => [$entity_1->getConfigDependencyName()],
'module' => ['node', 'config_test']
'module' => ['node', 'config_test'],
],
],
]
);
$entity_4->save();
// Entity 5 will be fixed because it is dependent on entity 3, which is
// unchanged, and entity 1 which will be fixed because
// \Drupal\config_test\Entity::onDependencyRemoval() will remove the
// dependency.
$entity_5 = $storage->create(
[
'id' => 'entity_' . $entity_id_suffixes[4],
'dependencies' => [
'enforced' => [
'config' => [
$entity_1->getConfigDependencyName(),
$entity_3->getConfigDependencyName(),
],
],
],
]
);
$entity_5->save();
// Set a more complicated test where dependencies will be fixed.
\Drupal::state()->set('config_test.fix_dependencies', [$entity_1->getConfigDependencyName()]);
\Drupal::state()->set('config_test.on_dependency_removal_called', []);
@ -321,14 +342,22 @@ class ConfigDependencyTest extends EntityKernelTestBase {
// Do a dry run using
// \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
$config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('module', ['node']);
// Assert that \Drupal\config_test\Entity\ConfigTest::onDependencyRemoval()
// is called as expected and with the correct dependencies.
$called = \Drupal::state()->get('config_test.on_dependency_removal_called', []);
$this->assertArrayNotHasKey($entity_3->id(), $called, 'ConfigEntityInterface::onDependencyRemoval() is not called for entity 3.');
$this->assertSame([$entity_1->id(), $entity_4->id(), $entity_2->id(), $entity_5->id()], array_keys($called), 'The most dependent entites have ConfigEntityInterface::onDependencyRemoval() called first.');
$this->assertSame(['config' => [], 'content' => [], 'module' => ['node'], 'theme' => []], $called[$entity_1->id()]);
$this->assertSame(['config' => [$entity_1->getConfigDependencyName()], 'content' => [], 'module' => [], 'theme' => []], $called[$entity_2->id()]);
$this->assertSame(['config' => [$entity_1->getConfigDependencyName()], 'content' => [], 'module' => ['node'], 'theme' => []], $called[$entity_4->id()]);
$this->assertSame(['config' => [$entity_1->getConfigDependencyName()], 'content' => [], 'module' => [], 'theme' => []], $called[$entity_5->id()]);
$this->assertEqual($entity_1->uuid(), $config_entities['delete'][1]->uuid(), 'Entity 1 will be deleted.');
$this->assertEqual($entity_2->uuid(), reset($config_entities['update'])->uuid(), 'Entity 2 will be updated.');
$this->assertEqual($entity_2->uuid(), $config_entities['update'][0]->uuid(), 'Entity 2 will be updated.');
$this->assertEqual($entity_3->uuid(), reset($config_entities['unchanged'])->uuid(), 'Entity 3 is not changed.');
$this->assertEqual($entity_4->uuid(), $config_entities['delete'][0]->uuid(), 'Entity 4 will be deleted.');
$called = \Drupal::state()->get('config_test.on_dependency_removal_called', []);
$this->assertFalse(in_array($entity_3->id(), $called), 'ConfigEntityInterface::onDependencyRemoval() is not called for entity 3.');
$this->assertIdentical([$entity_1->id(), $entity_4->id(), $entity_2->id()], $called, 'The most dependent entites have ConfigEntityInterface::onDependencyRemoval() called first.');
$this->assertEqual($entity_5->uuid(), $config_entities['update'][1]->uuid(), 'Entity 5 is updated.');
// Perform a module rebuild so we can know where the node module is located
// and uninstall it.
@ -441,8 +470,11 @@ class ConfigDependencyTest extends EntityKernelTestBase {
$this->assertSame($expected, $config_entity_ids);
$called = \Drupal::state()->get('config_test.on_dependency_removal_called', []);
$this->assertFalse(in_array($entity_3->id(), $called), 'ConfigEntityInterface::onDependencyRemoval() is not called for entity 3.');
$this->assertSame([$entity_1->id(), $entity_4->id(), $entity_2->id()], $called, 'The most dependent entities have ConfigEntityInterface::onDependencyRemoval() called first.');
$this->assertArrayNotHasKey($entity_3->id(), $called, 'ConfigEntityInterface::onDependencyRemoval() is not called for entity 3.');
$this->assertSame([$entity_1->id(), $entity_4->id(), $entity_2->id()], array_keys($called), 'The most dependent entities have ConfigEntityInterface::onDependencyRemoval() called first.');
$this->assertSame(['config' => [], 'content' => [], 'module' => ['node'], 'theme' => []], $called[$entity_1->id()]);
$this->assertSame(['config' => [], 'content' => [], 'module' => ['node'], 'theme' => []], $called[$entity_2->id()]);
$this->assertSame(['config' => [], 'content' => [], 'module' => ['node'], 'theme' => []], $called[$entity_4->id()]);
// Perform a module rebuild so we can know where the node module is located
// and uninstall it.
@ -476,7 +508,7 @@ class ConfigDependencyTest extends EntityKernelTestBase {
// Test dependencies between configuration entities.
$entity1 = $storage->create(
[
'id' => 'entity1'
'id' => 'entity1',
]
);
$entity1->save();
@ -589,7 +621,7 @@ class ConfigDependencyTest extends EntityKernelTestBase {
'id' => 'entity1',
'dependencies' => [
'enforced' => [
'content' => [$content_entity->getConfigDependencyName()]
'content' => [$content_entity->getConfigDependencyName()],
],
],
]
@ -600,7 +632,7 @@ class ConfigDependencyTest extends EntityKernelTestBase {
'id' => 'entity2',
'dependencies' => [
'enforced' => [
'config' => [$entity1->getConfigDependencyName()]
'config' => [$entity1->getConfigDependencyName()],
],
],
]

View file

@ -75,7 +75,7 @@ class ConfigDiffTest extends KernelTestBase {
// Test diffing a renamed config entity.
$test_entity_id = $this->randomMachineName();
$test_entity = entity_create('config_test', [
$test_entity = \Drupal::entityTypeManager()->getStorage('config_test')->create([
'id' => $test_entity_id,
'label' => $this->randomMachineName(),
]);

View file

@ -24,14 +24,14 @@ class ConfigEntityNormalizeTest extends KernelTestBase {
}
public function testNormalize() {
$config_entity = entity_create('config_test', ['id' => 'system', 'label' => 'foobar', 'weight' => 1]);
$config_entity = \Drupal::entityTypeManager()->getStorage('config_test')->create(['id' => 'system', 'label' => 'foobar', 'weight' => 1]);
$config_entity->save();
// Modify stored config entity, this is comparable with a schema change.
$config = $this->config('config_test.dynamic.system');
$data = [
'label' => 'foobar',
'additional_key' => TRUE
'additional_key' => TRUE,
] + $config->getRawData();
$config->setData($data)->save();
$this->assertNotIdentical($config_entity->toArray(), $config->getRawData(), 'Stored config entity is not is equivalent to config schema.');

View file

@ -57,7 +57,7 @@ class ConfigEntityStaticCacheTest extends KernelTestBase {
// config_entity_static_cache_test_config_test_load() sets _loadStamp to a
// random string. If they match, it means $entity_2 was retrieved from the
// static cache rather than going through a separate load sequence.
$this->assertIdentical($entity_1->_loadStamp, $entity_2->_loadStamp);
$this->assertSame($entity_1->_loadStamp, $entity_2->_loadStamp);
}
/**

View file

@ -22,7 +22,7 @@ class ConfigEntityStatusTest extends KernelTestBase {
* Tests the enabling/disabling of entities.
*/
public function testCRUD() {
$entity = entity_create('config_test', [
$entity = \Drupal::entityTypeManager()->getStorage('config_test')->create([
'id' => strtolower($this->randomMachineName()),
]);
$this->assertTrue($entity->status(), 'Default status is enabled.');

View file

@ -51,4 +51,18 @@ class ConfigEntityStorageTest extends KernelTestBase {
$this->assertIdentical($entity->toArray(), $original_properties);
}
/**
* Tests the hasData() method for config entity storage.
*
* @covers \Drupal\Core\Config\Entity\ConfigEntityStorage::hasData
*/
public function testHasData() {
$storage = \Drupal::entityTypeManager()->getStorage('config_test');
$this->assertFalse($storage->hasData());
// Add a test config entity and check again.
$storage->create(['id' => $this->randomMachineName()])->save();
$this->assertTrue($storage->hasData());
}
}

View file

@ -80,7 +80,7 @@ class ConfigEntityUnitTest extends KernelTestBase {
// Compare UUIDs as the objects are not identical since
// $entity->enforceIsNew is FALSE and $entity_loaded_by_uuid->enforceIsNew
// is NULL.
$this->assertIdentical($entity->uuid(), $entity_loaded_by_uuid->uuid());
$this->assertSame($entity->uuid(), $entity_loaded_by_uuid->uuid());
$entities = $this->storage->loadByProperties();
$this->assertEqual(count($entities), 3, 'Three entities are loaded when no properties are specified.');
@ -97,15 +97,15 @@ class ConfigEntityUnitTest extends KernelTestBase {
$entity = $this->storage->create([
'id' => $this->randomMachineName(),
'label' => $this->randomString(),
'style' => 999
'style' => 999,
]);
$entity->save();
$this->assertIdentical('999', $entity->style);
$this->assertSame('999', $entity->style);
$entity->style = 999;
$entity->trustData()->save();
$this->assertIdentical(999, $entity->style);
$this->assertSame(999, $entity->style);
$entity->save();
$this->assertIdentical('999', $entity->style);
$this->assertSame('999', $entity->style);
}
}

View file

@ -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);
@ -210,22 +210,22 @@ class ConfigFileContentTest extends KernelTestBase {
$config_parsed = $filestorage->read($name);
$key = 'numeric keys';
$this->assertIdentical($config_data[$key], $config_parsed[$key]);
$this->assertSame($config_data[$key], $config_parsed[$key]);
$key = 'nested keys';
$this->assertIdentical($config_data[$key], $config_parsed[$key]);
$this->assertSame($config_data[$key], $config_parsed[$key]);
$key = 'HTML';
$this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
$this->assertSame($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
$key = 'UTF-8';
$this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
$this->assertSame($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
$key = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ';
$this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
$this->assertSame($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
$key = 'invalid xml';
$this->assertIdentical($config_data[$key], $config_parsed[$key]);
$this->assertSame($config_data[$key], $config_parsed[$key]);
}
}

View file

@ -2,7 +2,6 @@
namespace Drupal\KernelTests\Core\Config;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\ConfigImporter;
use Drupal\Core\Config\StorageComparer;
use Drupal\KernelTests\KernelTestBase;
@ -33,7 +32,7 @@ class ConfigImportRecreateTest extends KernelTestBase {
parent::setUp();
$this->installEntitySchema('node');
$this->installConfig(['field', 'node']);
$this->installConfig(['system', 'field', 'node']);
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
@ -57,7 +56,7 @@ class ConfigImportRecreateTest extends KernelTestBase {
}
public function testRecreateEntity() {
$type_name = Unicode::strtolower($this->randomMachineName(16));
$type_name = mb_strtolower($this->randomMachineName(16));
$content_type = NodeType::create([
'type' => $type_name,
'name' => 'Node type one',
@ -92,7 +91,7 @@ class ConfigImportRecreateTest extends KernelTestBase {
$this->assertEqual(5, count($creates), 'There are 5 configuration items to create.');
$this->assertEqual(5, count($deletes), 'There are 5 configuration items to delete.');
$this->assertEqual(0, count($this->configImporter->getUnprocessedConfiguration('update')), 'There are no configuration items to update.');
$this->assertIdentical($creates, array_reverse($deletes), 'Deletes and creates contain the same configuration names in opposite orders due to dependencies.');
$this->assertSame($creates, array_reverse($deletes), 'Deletes and creates contain the same configuration names in opposite orders due to dependencies.');
$this->configImporter->import();

View file

@ -2,8 +2,7 @@
namespace Drupal\KernelTests\Core\Config;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Unicode;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Uuid\Php;
use Drupal\Core\Config\ConfigImporter;
use Drupal\Core\Config\ConfigImporterException;
@ -40,7 +39,7 @@ class ConfigImportRenameValidationTest extends KernelTestBase {
$this->installEntitySchema('user');
$this->installEntitySchema('node');
$this->installConfig(['field']);
$this->installConfig(['system', 'field']);
// Set up the ConfigImporter object for testing.
$storage_comparer = new StorageComparer(
@ -67,7 +66,7 @@ class ConfigImportRenameValidationTest extends KernelTestBase {
public function testRenameValidation() {
// Create a test entity.
$test_entity_id = $this->randomMachineName();
$test_entity = entity_create('config_test', [
$test_entity = \Drupal::entityTypeManager()->getStorage('config_test')->create([
'id' => $test_entity_id,
'label' => $this->randomMachineName(),
]);
@ -82,7 +81,7 @@ class ConfigImportRenameValidationTest extends KernelTestBase {
// Create a content type with a matching UUID in the active storage.
$content_type = NodeType::create([
'type' => Unicode::strtolower($this->randomMachineName(16)),
'type' => mb_strtolower($this->randomMachineName(16)),
'name' => $this->randomMachineName(),
'uuid' => $uuid,
]);
@ -95,7 +94,7 @@ class ConfigImportRenameValidationTest extends KernelTestBase {
'node.type.' . $content_type->id() . '::config_test.dynamic.' . $test_entity_id,
];
$renames = $this->configImporter->getUnprocessedConfiguration('rename');
$this->assertIdentical($expected, $renames);
$this->assertSame($expected, $renames);
// Try to import the configuration. We expect an exception to be thrown
// because the staged entity is of a different type.
@ -106,7 +105,7 @@ class ConfigImportRenameValidationTest extends KernelTestBase {
catch (ConfigImporterException $e) {
$this->pass('Expected ConfigImporterException thrown when a renamed configuration entity does not match the existing entity type.');
$expected = [
SafeMarkup::format('Entity type mismatch on rename. @old_type not equal to @new_type for existing configuration @old_name and staged configuration @new_name.', ['@old_type' => 'node_type', '@new_type' => 'config_test', '@old_name' => 'node.type.' . $content_type->id(), '@new_name' => 'config_test.dynamic.' . $test_entity_id])
new FormattableMarkup('Entity type mismatch on rename. @old_type not equal to @new_type for existing configuration @old_name and staged configuration @new_name.', ['@old_type' => 'node_type', '@new_type' => 'config_test', '@old_name' => 'node.type.' . $content_type->id(), '@new_name' => 'config_test.dynamic.' . $test_entity_id]),
];
$this->assertEqual($expected, $this->configImporter->getErrors());
}
@ -135,10 +134,10 @@ class ConfigImportRenameValidationTest extends KernelTestBase {
// UUIDs match.
$this->configImporter->reset();
$expected = [
'config_test.old::config_test.new'
'config_test.old::config_test.new',
];
$renames = $this->configImporter->getUnprocessedConfiguration('rename');
$this->assertIdentical($expected, $renames);
$this->assertSame($expected, $renames);
// Try to import the configuration. We expect an exception to be thrown
// because the rename is for simple configuration.
@ -149,7 +148,7 @@ class ConfigImportRenameValidationTest extends KernelTestBase {
catch (ConfigImporterException $e) {
$this->pass('Expected ConfigImporterException thrown when simple configuration is renamed.');
$expected = [
SafeMarkup::format('Rename operation for simple configuration. Existing configuration @old_name and staged configuration @new_name.', ['@old_name' => 'config_test.old', '@new_name' => 'config_test.new'])
new FormattableMarkup('Rename operation for simple configuration. Existing configuration @old_name and staged configuration @new_name.', ['@old_name' => 'config_test.old', '@new_name' => 'config_test.new']),
];
$this->assertEqual($expected, $this->configImporter->getErrors());
}

View file

@ -33,7 +33,7 @@ class ConfigImporterMissingContentTest extends KernelTestBase {
$this->installSchema('system', 'sequences');
$this->installEntitySchema('entity_test');
$this->installEntitySchema('user');
$this->installConfig(['config_test']);
$this->installConfig(['system', 'config_test']);
// Installing config_test's default configuration pollutes the global
// variable being used for recording hook invocations by this test already,
// so it has to be cleared out manually.

View file

@ -3,7 +3,7 @@
namespace Drupal\KernelTests\Core\Config;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Config\ConfigImporter;
use Drupal\Core\Config\ConfigImporterException;
use Drupal\Core\Config\StorageComparer;
@ -16,6 +16,11 @@ use Drupal\KernelTests\KernelTestBase;
*/
class ConfigImporterTest extends KernelTestBase {
/**
* The beginning of an import validation error.
*/
const FAIL_MESSAGE = 'There were errors validating the config synchronization.';
/**
* Config Importer object used for testing.
*
@ -33,7 +38,7 @@ class ConfigImporterTest extends KernelTestBase {
protected function setUp() {
parent::setUp();
$this->installConfig(['config_test']);
$this->installConfig(['system', 'config_test']);
// Installing config_test's default configuration pollutes the global
// variable being used for recording hook invocations by this test already,
// so it has to be cleared out manually.
@ -104,10 +109,17 @@ class ConfigImporterTest extends KernelTestBase {
$this->fail('ConfigImporterException not thrown, invalid import was not stopped due to mis-matching site UUID.');
}
catch (ConfigImporterException $e) {
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
$error_log = $this->configImporter->getErrors();
$expected = ['Site UUID in source storage does not match the target storage.'];
$this->assertEqual($expected, $error_log);
$actual_message = $e->getMessage();
$actual_error_log = $this->configImporter->getErrors();
$expected_error_log = ['Site UUID in source storage does not match the target storage.'];
$this->assertEqual($actual_error_log, $expected_error_log);
$expected = static::FAIL_MESSAGE . PHP_EOL . 'Site UUID in source storage does not match the target storage.';
$this->assertEquals($expected, $actual_message);
foreach ($expected_error_log as $log_row) {
$this->assertTrue(preg_match("/$log_row/", $actual_message));
}
}
}
@ -226,7 +238,7 @@ class ConfigImporterTest extends KernelTestBase {
// Add a dependency on primary, to ensure that is synced first.
'dependencies' => [
'config' => [$name_primary],
]
],
];
$sync->write($name_secondary, $values_secondary);
@ -245,7 +257,7 @@ class ConfigImporterTest extends KernelTestBase {
$logs = $this->configImporter->getErrors();
$this->assertEqual(count($logs), 1);
$this->assertEqual($logs[0], SafeMarkup::format('Deleted and replaced configuration entity "@name"', ['@name' => $name_secondary]));
$this->assertEqual($logs[0], new FormattableMarkup('Deleted and replaced configuration entity "@name"', ['@name' => $name_secondary]));
}
/**
@ -265,7 +277,7 @@ class ConfigImporterTest extends KernelTestBase {
// Add a dependency on secondary, so that is synced first.
'dependencies' => [
'config' => [$name_secondary],
]
],
];
$sync->write($name_primary, $values_primary);
$values_secondary = [
@ -322,7 +334,7 @@ class ConfigImporterTest extends KernelTestBase {
// Add a dependency on deleter, to make sure that is synced first.
'dependencies' => [
'config' => [$name_deleter],
]
],
];
$storage->write($name_deletee, $values_deletee);
$values_deletee['label'] = 'Updated Deletee';
@ -338,7 +350,7 @@ class ConfigImporterTest extends KernelTestBase {
// will also be synced after the deletee due to alphabetical ordering.
'dependencies' => [
'config' => [$name_deleter],
]
],
];
$storage->write($name_other, $values_other);
$values_other['label'] = 'Updated other';
@ -351,7 +363,7 @@ class ConfigImporterTest extends KernelTestBase {
$name_deletee,
$name_other,
];
$this->assertIdentical($expected, $updates);
$this->assertSame($expected, $updates);
// Import.
$this->configImporter->import();
@ -373,7 +385,7 @@ class ConfigImporterTest extends KernelTestBase {
$logs = $this->configImporter->getErrors();
$this->assertEqual(count($logs), 1);
$this->assertEqual($logs[0], SafeMarkup::format('Update target "@name" is missing.', ['@name' => $name_deletee]));
$this->assertEqual($logs[0], new FormattableMarkup('Update target "@name" is missing.', ['@name' => $name_deletee]));
}
/**
@ -580,7 +592,20 @@ class ConfigImporterTest extends KernelTestBase {
$this->fail('ConfigImporterException not thrown; an invalid import was not stopped due to missing dependencies.');
}
catch (ConfigImporterException $e) {
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
$expected = [
static::FAIL_MESSAGE,
'Unable to install the <em class="placeholder">unknown_module</em> module since it does not exist.',
'Unable to install the <em class="placeholder">Book</em> module since it requires the <em class="placeholder">Node, Text, Field, Filter, User</em> modules.',
'Unable to install the <em class="placeholder">unknown_theme</em> theme since it does not exist.',
'Unable to install the <em class="placeholder">Bartik</em> theme since it requires the <em class="placeholder">Classy</em> theme.',
'Unable to install the <em class="placeholder">Bartik</em> theme since it requires the <em class="placeholder">Stable</em> theme.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.config</em> depends on the <em class="placeholder">unknown</em> configuration that will not exist after import.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.existing</em> depends on the <em class="placeholder">config_test.dynamic.dotted.deleted</em> configuration that will not exist after import.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.module</em> depends on the <em class="placeholder">unknown</em> module that will not be installed after import.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.theme</em> depends on the <em class="placeholder">unknown</em> theme that will not be installed after import.',
'Configuration <em class="placeholder">unknown.config</em> depends on the <em class="placeholder">unknown</em> extension that will not be installed after import.',
];
$this->assertEquals(implode(PHP_EOL, $expected), $e->getMessage());
$error_log = $this->configImporter->getErrors();
$expected = [
'Unable to install the <em class="placeholder">unknown_module</em> module since it does not exist.',
@ -598,7 +623,7 @@ class ConfigImporterTest extends KernelTestBase {
}
}
// Make a config entity have mulitple unmet dependencies.
// Make a config entity have multiple unmet dependencies.
$config_entity_data = $sync->read('config_test.dynamic.dotted.default');
$config_entity_data['dependencies'] = ['module' => ['unknown', 'dblog']];
$sync->write('config_test.dynamic.dotted.module', $config_entity_data);
@ -611,7 +636,30 @@ class ConfigImporterTest extends KernelTestBase {
$this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing dependencies.');
}
catch (ConfigImporterException $e) {
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
$expected = [
static::FAIL_MESSAGE,
'Unable to install the <em class="placeholder">unknown_module</em> module since it does not exist.',
'Unable to install the <em class="placeholder">Book</em> module since it requires the <em class="placeholder">Node, Text, Field, Filter, User</em> modules.',
'Unable to install the <em class="placeholder">unknown_theme</em> theme since it does not exist.',
'Unable to install the <em class="placeholder">Bartik</em> theme since it requires the <em class="placeholder">Classy</em> theme.',
'Unable to install the <em class="placeholder">Bartik</em> theme since it requires the <em class="placeholder">Stable</em> theme.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.config</em> depends on the <em class="placeholder">unknown</em> configuration that will not exist after import.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.existing</em> depends on the <em class="placeholder">config_test.dynamic.dotted.deleted</em> configuration that will not exist after import.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.module</em> depends on the <em class="placeholder">unknown</em> module that will not be installed after import.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.theme</em> depends on the <em class="placeholder">unknown</em> theme that will not be installed after import.',
'Configuration <em class="placeholder">unknown.config</em> depends on the <em class="placeholder">unknown</em> extension that will not be installed after import.',
'Unable to install the <em class="placeholder">unknown_module</em> module since it does not exist.',
'Unable to install the <em class="placeholder">Book</em> module since it requires the <em class="placeholder">Node, Text, Field, Filter, User</em> modules.',
'Unable to install the <em class="placeholder">unknown_theme</em> theme since it does not exist.',
'Unable to install the <em class="placeholder">Bartik</em> theme since it requires the <em class="placeholder">Classy</em> theme.',
'Unable to install the <em class="placeholder">Bartik</em> theme since it requires the <em class="placeholder">Stable</em> theme.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.config</em> depends on configuration (<em class="placeholder">unknown, unknown2</em>) that will not exist after import.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.existing</em> depends on the <em class="placeholder">config_test.dynamic.dotted.deleted</em> configuration that will not exist after import.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.module</em> depends on modules (<em class="placeholder">unknown, Database Logging</em>) that will not be installed after import.',
'Configuration <em class="placeholder">config_test.dynamic.dotted.theme</em> depends on themes (<em class="placeholder">unknown, Seven</em>) that will not be installed after import.',
'Configuration <em class="placeholder">unknown.config</em> depends on the <em class="placeholder">unknown</em> extension that will not be installed after import.',
];
$this->assertEquals(implode(PHP_EOL, $expected), $e->getMessage());
$error_log = $this->configImporter->getErrors();
$expected = [
'Configuration <em class="placeholder">config_test.dynamic.dotted.config</em> depends on configuration (<em class="placeholder">unknown, unknown2</em>) that will not exist after import.',
@ -637,7 +685,8 @@ class ConfigImporterTest extends KernelTestBase {
$this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing dependencies.');
}
catch (ConfigImporterException $e) {
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
$expected = static::FAIL_MESSAGE . PHP_EOL . 'The core.extension configuration does not exist.';
$this->assertEquals($expected, $e->getMessage());
$error_log = $this->configImporter->getErrors();
$this->assertEqual(['The core.extension configuration does not exist.'], $error_log);
}
@ -661,7 +710,8 @@ class ConfigImporterTest extends KernelTestBase {
$this->fail('ConfigImporterException not thrown; an invalid import was not stopped due to missing dependencies.');
}
catch (ConfigImporterException $e) {
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
$expected = static::FAIL_MESSAGE . PHP_EOL . 'Unable to install the <em class="placeholder">standard</em> module since it does not exist.';
$this->assertEquals($expected, $e->getMessage(), 'There were errors validating the config synchronization.');
$error_log = $this->configImporter->getErrors();
// Install profiles should not even be scanned at this point.
$this->assertEqual(['Unable to install the <em class="placeholder">standard</em> module since it does not exist.'], $error_log);
@ -686,13 +736,14 @@ class ConfigImporterTest extends KernelTestBase {
$this->fail('ConfigImporterException not thrown; an invalid import was not stopped due to missing dependencies.');
}
catch (ConfigImporterException $e) {
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
$expected = static::FAIL_MESSAGE . PHP_EOL . 'Cannot change the install profile from <em class="placeholder"></em> to <em class="placeholder">this_will_not_work</em> once Drupal is installed.';
$this->assertEquals($expected, $e->getMessage(), 'There were errors validating the config synchronization.');
$error_log = $this->configImporter->getErrors();
// Install profiles can not be changed. Note that KernelTestBase currently
// does not use an install profile. This situation should be impossible
// to get in but site's can removed the install profile setting from
// settings.php so the test is valid.
$this->assertEqual(['Cannot change the install profile from <em class="placeholder">this_will_not_work</em> to <em class="placeholder"></em> once Drupal is installed.'], $error_log);
$this->assertEqual(['Cannot change the install profile from <em class="placeholder"></em> to <em class="placeholder">this_will_not_work</em> once Drupal is installed.'], $error_log);
}
}
@ -797,7 +848,7 @@ class ConfigImporterTest extends KernelTestBase {
}
/**
* Helper meothd to test custom config installer steps.
* Helper method to test custom config installer steps.
*
* @param array $context
* Batch context.

View file

@ -146,7 +146,7 @@ class ConfigInstallTest extends KernelTestBase {
$this->assertEqual($collections, $active_storage->getAllCollectionNames());
$collection_storage = $active_storage->createCollection('entity');
$data = $collection_storage->read('config_test.dynamic.dotted.default');
$this->assertIdentical(['label' => 'entity'], $data);
$this->assertSame(['label' => 'entity'], $data);
// Test that the config manager uninstalls configuration from collections
// as expected.
@ -185,7 +185,7 @@ class ConfigInstallTest extends KernelTestBase {
$data = $active_storage->read($name);
$this->assertTrue(isset($data['uuid']));
$data = $collection_storage->read($name);
$this->assertIdentical(['label' => 'entity'], $data);
$this->assertSame(['label' => 'entity'], $data);
}
/**
@ -227,7 +227,7 @@ class ConfigInstallTest extends KernelTestBase {
$this->assertTrue($entity, 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
// Ensure that dependencies can be added during module installation by
// hooks.
$this->assertIdentical('config_install_dependency_test', $entity->getDependencies()['module'][0]);
$this->assertSame('config_install_dependency_test', $entity->getDependencies()['module'][0]);
}
/**
@ -253,6 +253,14 @@ class ConfigInstallTest extends KernelTestBase {
);
}
/**
* Tests installing configuration where the filename and ID do not match.
*/
public function testIdMisMatch() {
$this->setExpectedException(\PHPUnit_Framework_Error_Warning::class, 'The configuration name "config_test.dynamic.no_id_match" does not match the ID "does_not_match"');
$this->installModules(['config_test_id_mismatch']);
}
/**
* Installs a module.
*

View file

@ -20,6 +20,7 @@ class ConfigOverrideTest extends KernelTestBase {
protected function setUp() {
parent::setUp();
$this->installConfig(['system']);
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
}

View file

@ -40,22 +40,23 @@ class ConfigSchemaTest extends KernelTestBase {
*/
public function testSchemaMapping() {
// Nonexistent configuration key will have Undefined as metadata.
$this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.no_such_key'));
$this->assertSame(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.no_such_key'));
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.no_such_key');
$expected = [];
$expected['label'] = 'Undefined';
$expected['class'] = Undefined::class;
$expected['type'] = 'undefined';
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for nonexistent configuration.');
// Configuration file without schema will return Undefined as well.
$this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.noschema'));
$this->assertSame(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.noschema'));
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.noschema');
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with no schema.');
// Configuration file with only some schema.
$this->assertIdentical(TRUE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'));
$this->assertSame(TRUE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'));
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema');
$expected = [];
$expected['label'] = 'Schema test data';
@ -67,6 +68,7 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['mapping']['testlist'] = ['label' => 'Test list'];
$expected['type'] = 'config_schema_test.someschema';
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with only some schema.');
// Check type detection on elements with undefined types.
@ -77,6 +79,7 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['class'] = Undefined::class;
$expected['type'] = 'undefined';
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$this->assertEqual($definition, $expected, 'Automatic type detected for a scalar is undefined.');
$definition = $config->get('testlist')->getDataDefinition()->toArray();
$expected = [];
@ -84,6 +87,7 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['class'] = Undefined::class;
$expected['type'] = 'undefined';
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$this->assertEqual($definition, $expected, 'Automatic type detected for a list is undefined.');
$definition = $config->get('testnoschema')->getDataDefinition()->toArray();
$expected = [];
@ -91,6 +95,7 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['class'] = Undefined::class;
$expected['type'] = 'undefined';
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$this->assertEqual($definition, $expected, 'Automatic type detected for an undefined integer is undefined.');
// Simple case, straight metadata.
@ -109,6 +114,7 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['mapping']['_core']['type'] = '_core_config_info';
$expected['type'] = 'system.maintenance';
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for system.maintenance');
// Mixed schema with ignore elements.
@ -139,6 +145,7 @@ class ConfigSchemaTest extends KernelTestBase {
'type' => 'integer',
];
$expected['type'] = 'config_schema_test.ignore';
$expected['unwrap_for_canonical_representation'] = TRUE;
$this->assertEqual($definition, $expected);
@ -149,6 +156,7 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['label'] = 'Irrelevant';
$expected['class'] = Ignore::class;
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$this->assertEqual($definition, $expected);
$definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('indescribable')->getDataDefinition()->toArray();
$expected['label'] = 'Indescribable';
@ -160,8 +168,9 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['label'] = 'Image style';
$expected['class'] = Mapping::class;
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$expected['mapping']['name']['type'] = 'string';
$expected['mapping']['uuid']['type'] = 'string';
$expected['mapping']['uuid']['type'] = 'uuid';
$expected['mapping']['uuid']['label'] = 'UUID';
$expected['mapping']['langcode']['type'] = 'string';
$expected['mapping']['langcode']['label'] = 'Language code';
@ -177,7 +186,7 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['mapping']['effects']['sequence']['mapping']['id']['type'] = 'string';
$expected['mapping']['effects']['sequence']['mapping']['data']['type'] = 'image.effect.[%parent.id]';
$expected['mapping']['effects']['sequence']['mapping']['weight']['type'] = 'integer';
$expected['mapping']['effects']['sequence']['mapping']['uuid']['type'] = 'string';
$expected['mapping']['effects']['sequence']['mapping']['uuid']['type'] = 'uuid';
$expected['mapping']['third_party_settings']['type'] = 'sequence';
$expected['mapping']['third_party_settings']['label'] = 'Third party settings';
$expected['mapping']['third_party_settings']['sequence']['type'] = '[%parent.%parent.%type].third_party.[%key]';
@ -193,6 +202,7 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['label'] = 'Image scale';
$expected['class'] = Mapping::class;
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$expected['mapping']['width']['type'] = 'integer';
$expected['mapping']['width']['label'] = 'Width';
$expected['mapping']['height']['type'] = 'integer';
@ -201,7 +211,6 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['mapping']['upscale']['label'] = 'Upscale';
$expected['type'] = 'image.effect.image_scale';
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.effect.image_scale');
// Most complex case, get metadata for actual configuration element.
@ -220,6 +229,7 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['label'] = 'Mapping';
$expected['class'] = Mapping::class;
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$expected['mapping'] = [
'integer' => ['type' => 'integer'],
'string' => ['type' => 'string'],
@ -241,6 +251,7 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['mapping']['testdescription']['label'] = 'Description';
$expected['type'] = 'config_schema_test.someschema.somemodule.*.*';
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_one.subsection');
@ -263,6 +274,7 @@ class ConfigSchemaTest extends KernelTestBase {
'label' => 'Test item nested one level',
'class' => StringData::class,
'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
'unwrap_for_canonical_representation' => TRUE,
];
$this->assertEqual($definition, $expected);
@ -274,6 +286,7 @@ class ConfigSchemaTest extends KernelTestBase {
'label' => 'Test item nested two levels',
'class' => StringData::class,
'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
'unwrap_for_canonical_representation' => TRUE,
];
$this->assertEqual($definition, $expected);
@ -285,6 +298,7 @@ class ConfigSchemaTest extends KernelTestBase {
'label' => 'Test item nested three levels',
'class' => StringData::class,
'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
'unwrap_for_canonical_representation' => TRUE,
];
$this->assertEqual($definition, $expected);
}
@ -321,7 +335,7 @@ class ConfigSchemaTest extends KernelTestBase {
$effect = $effects->get($uuid)->getElements();
$this->assertTrue(!$effect['data']->isEmpty() && $effect['id']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.');
$this->assertTrue($effect['data']->get('width') instanceof IntegerInterface, 'Got the right type for the scale effect width.');
$this->assertEqual($effect['data']->get('width')->getValue(), 480, 'Got the right value for the scale effect width.' );
$this->assertEqual($effect['data']->get('width')->getValue(), 480, 'Got the right value for the scale effect width.');
}
/**
@ -338,7 +352,7 @@ class ConfigSchemaTest extends KernelTestBase {
// If the config schema doesn't have a type it shouldn't be casted.
'no_type' => 1,
'mapping' => [
'string' => 1
'string' => 1,
],
'float' => '3.14',
'null_float' => '',
@ -361,7 +375,7 @@ class ConfigSchemaTest extends KernelTestBase {
'boolean' => TRUE,
'no_type' => 1,
'mapping' => [
'string' => '1'
'string' => '1',
],
'float' => 3.14,
'null_float' => NULL,
@ -395,6 +409,76 @@ class ConfigSchemaTest extends KernelTestBase {
$this->assertIdentical($installed_data, $original_data);
}
/**
* Tests configuration sequence sorting using schemas.
*/
public function testConfigSaveWithSequenceSorting() {
$data = [
'keyed_sort' => [
'b' => '1',
'a' => '2',
],
'no_sort' => [
'b' => '2',
'a' => '1',
],
];
// Save config which has a schema that enforces sorting.
$this->config('config_schema_test.schema_sequence_sort')
->setData($data)
->save();
$this->assertSame(['a' => '2', 'b' => '1'], $this->config('config_schema_test.schema_sequence_sort')->get('keyed_sort'));
$this->assertSame(['b' => '2', 'a' => '1'], $this->config('config_schema_test.schema_sequence_sort')->get('no_sort'));
$data = [
'value_sort' => ['b', 'a'],
'no_sort' => ['b', 'a'],
];
// Save config which has a schema that enforces sorting.
$this->config('config_schema_test.schema_sequence_sort')
->setData($data)
->save();
$this->assertSame(['a', 'b'], $this->config('config_schema_test.schema_sequence_sort')->get('value_sort'));
$this->assertSame(['b', 'a'], $this->config('config_schema_test.schema_sequence_sort')->get('no_sort'));
// Value sort does not preserve keys - this is intentional.
$data = [
'value_sort' => [1 => 'b', 2 => 'a'],
'no_sort' => [1 => 'b', 2 => 'a'],
];
// Save config which has a schema that enforces sorting.
$this->config('config_schema_test.schema_sequence_sort')
->setData($data)
->save();
$this->assertSame(['a', 'b'], $this->config('config_schema_test.schema_sequence_sort')->get('value_sort'));
$this->assertSame([1 => 'b', 2 => 'a'], $this->config('config_schema_test.schema_sequence_sort')->get('no_sort'));
// Test sorts do not destroy complex values.
$data = [
'complex_sort_value' => [['foo' => 'b', 'bar' => 'b'] , ['foo' => 'a', 'bar' => 'a']],
'complex_sort_key' => ['b' => ['foo' => '1', 'bar' => '1'] , 'a' => ['foo' => '2', 'bar' => '2']],
];
$this->config('config_schema_test.schema_sequence_sort')
->setData($data)
->save();
$this->assertSame([['foo' => 'a', 'bar' => 'a'], ['foo' => 'b', 'bar' => 'b']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_value'));
$this->assertSame(['a' => ['foo' => '2', 'bar' => '2'], 'b' => ['foo' => '1', 'bar' => '1']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_key'));
// Swap the previous test scenario around.
$data = [
'complex_sort_value' => ['b' => ['foo' => '1', 'bar' => '1'] , 'a' => ['foo' => '2', 'bar' => '2']],
'complex_sort_key' => [['foo' => 'b', 'bar' => 'b'] , ['foo' => 'a', 'bar' => 'a']],
];
$this->config('config_schema_test.schema_sequence_sort')
->setData($data)
->save();
$this->assertSame([['foo' => '1', 'bar' => '1'], ['foo' => '2', 'bar' => '2']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_value'));
$this->assertSame([['foo' => 'b', 'bar' => 'b'], ['foo' => 'a', 'bar' => 'a']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_key'));
}
/**
* Tests fallback to a greedy wildcard.
*/
@ -405,6 +489,7 @@ class ConfigSchemaTest extends KernelTestBase {
$expected['label'] = 'Schema wildcard fallback test';
$expected['class'] = Mapping::class;
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
$expected['unwrap_for_canonical_representation'] = TRUE;
$expected['mapping']['langcode']['type'] = 'string';
$expected['mapping']['langcode']['label'] = 'Language code';
$expected['mapping']['_core']['type'] = '_core_config_info';
@ -418,8 +503,8 @@ class ConfigSchemaTest extends KernelTestBase {
$definition2 = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something.something');
// This should be the schema of config_schema_test.wildcard_fallback.* as
//well.
$this->assertIdentical($definition, $definition2);
// well.
$this->assertSame($definition, $definition2);
}
/**

View file

@ -24,6 +24,7 @@ class ConfigSnapshotTest extends KernelTestBase {
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['system']);
// Update the config snapshot. This allows the parent::setUp() to write
// configuration files.
\Drupal::service('config.manager')->createSnapshot(\Drupal::service('config.storage'), \Drupal::service('config.storage.snapshot'));

View file

@ -0,0 +1,127 @@
<?php
namespace Drupal\KernelTests\Core\Config\Entity;
use Drupal\Core\Config\Entity\ConfigEntityUpdater;
use Drupal\Core\Site\Settings;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests \Drupal\Core\Config\Entity\ConfigEntityUpdater.
*
* @coversDefaultClass \Drupal\Core\Config\Entity\ConfigEntityUpdater
* @group config
*/
class ConfigEntityUpdaterTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['config_test'];
/**
* @covers ::update
*/
public function testUpdate() {
// Create some entities to update.
$storage = $this->container->get('entity_type.manager')->getStorage('config_test');
for ($i = 0; $i < 15; $i++) {
$entity_id = 'config_test_' . $i;
$storage->create(['id' => $entity_id, 'label' => $entity_id])->save();
}
// Set up the updater.
$sandbox = [];
$settings = Settings::getInstance() ? Settings::getAll() : [];
$settings['entity_update_batch_size'] = 10;
new Settings($settings);
$updater = $this->container->get('class_resolver')->getInstanceFromDefinition(ConfigEntityUpdater::class);
$callback = function ($config_entity) {
/** @var \Drupal\config_test\Entity\ConfigTest $config_entity */
$number = (int) str_replace('config_test_', '', $config_entity->id());
// Only update even numbered entities.
if ($number % 2 == 0) {
$config_entity->set('label', $config_entity->label . ' (updated)');
return TRUE;
}
return FALSE;
};
// This should run against the first 10 entities. The even numbered labels
// will have been updated.
$updater->update($sandbox, 'config_test', $callback);
$entities = $storage->loadMultiple();
$this->assertEquals('config_test_8 (updated)', $entities['config_test_8']->label());
$this->assertEquals('config_test_9', $entities['config_test_9']->label());
$this->assertEquals('config_test_10', $entities['config_test_10']->label());
$this->assertEquals('config_test_14', $entities['config_test_14']->label());
$this->assertEquals(15, $sandbox['config_entity_updater:config_test']['count']);
$this->assertCount(5, $sandbox['config_entity_updater:config_test']['entities']);
$this->assertEquals(10 / 15, $sandbox['#finished']);
// Update the rest.
$updater->update($sandbox, 'config_test', $callback);
$entities = $storage->loadMultiple();
$this->assertEquals('config_test_8 (updated)', $entities['config_test_8']->label());
$this->assertEquals('config_test_9', $entities['config_test_9']->label());
$this->assertEquals('config_test_10 (updated)', $entities['config_test_10']->label());
$this->assertEquals('config_test_14 (updated)', $entities['config_test_14']->label());
$this->assertEquals(1, $sandbox['#finished']);
$this->assertCount(0, $sandbox['config_entity_updater:config_test']['entities']);
}
/**
* @covers ::update
*/
public function testUpdateDefaultCallback() {
// Create some entities to update.
$storage = $this->container->get('entity_type.manager')->getStorage('config_test');
for ($i = 0; $i < 15; $i++) {
$entity_id = 'config_test_' . $i;
$storage->create(['id' => $entity_id, 'label' => $entity_id])->save();
}
// Set up the updater.
$sandbox = [];
$settings = Settings::getInstance() ? Settings::getAll() : [];
$settings['entity_update_batch_size'] = 9;
new Settings($settings);
$updater = $this->container->get('class_resolver')->getInstanceFromDefinition(ConfigEntityUpdater::class);
// Cause a dependency to be added during an update.
\Drupal::state()->set('config_test_new_dependency', 'added_dependency');
// This should run against the first 10 entities.
$updater->update($sandbox, 'config_test');
$entities = $storage->loadMultiple();
$this->assertEquals(['added_dependency'], $entities['config_test_7']->getDependencies()['module']);
$this->assertEquals(['added_dependency'], $entities['config_test_8']->getDependencies()['module']);
$this->assertEquals([], $entities['config_test_9']->getDependencies());
$this->assertEquals([], $entities['config_test_14']->getDependencies());
$this->assertEquals(15, $sandbox['config_entity_updater:config_test']['count']);
$this->assertCount(6, $sandbox['config_entity_updater:config_test']['entities']);
$this->assertEquals(9 / 15, $sandbox['#finished']);
// Update the rest.
$updater->update($sandbox, 'config_test');
$entities = $storage->loadMultiple();
$this->assertEquals(['added_dependency'], $entities['config_test_9']->getDependencies()['module']);
$this->assertEquals(['added_dependency'], $entities['config_test_14']->getDependencies()['module']);
$this->assertEquals(1, $sandbox['#finished']);
$this->assertCount(0, $sandbox['config_entity_updater:config_test']['entities']);
}
/**
* @covers ::update
*/
public function testUpdateException() {
$this->enableModules(['entity_test']);
$this->setExpectedException(\InvalidArgumentException::class, 'The provided entity type ID \'entity_test_mul_changed\' is not a configuration entity type');
$updater = $this->container->get('class_resolver')->getInstanceFromDefinition(ConfigEntityUpdater::class);
$sandbox = [];
$updater->update($sandbox, 'entity_test_mul_changed');
}
}

View file

@ -5,7 +5,6 @@ namespace Drupal\KernelTests\Core\Config;
use Drupal\Core\Config\Schema\SchemaCheckTrait;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests the functionality of SchemaCheckTrait.
*

View file

@ -151,6 +151,11 @@ abstract class ConfigStorageTestBase extends KernelTestBase {
// Listing on a non-existing storage bin returns an empty array.
$result = $this->invalidStorage->listAll();
$this->assertIdentical($result, []);
// Getting all collections on a non-existing storage bin return an empty
// array.
$this->assertSame([], $this->invalidStorage->getAllCollectionNames());
// Writing to a non-existing storage bin creates the bin.
$this->invalidStorage->write($name, ['foo' => 'bar']);
$result = $this->invalidStorage->read($name);
@ -189,7 +194,7 @@ abstract class ConfigStorageTestBase extends KernelTestBase {
$data = ['foo' => 'bar'];
$result = $this->storage->write($name, $data);
$this->assertIdentical($result, TRUE);
$this->assertIdentical($data, $this->storage->read($name));
$this->assertSame($data, $this->storage->read($name));
// Create configuration in a new collection.
$new_storage = $this->storage->createCollection('collection.sub.new');
@ -197,13 +202,13 @@ abstract class ConfigStorageTestBase extends KernelTestBase {
$this->assertEqual([], $new_storage->listAll());
$new_storage->write($name, $data);
$this->assertIdentical($result, TRUE);
$this->assertIdentical($data, $new_storage->read($name));
$this->assertSame($data, $new_storage->read($name));
$this->assertEqual([$name], $new_storage->listAll());
$this->assertTrue($new_storage->exists($name));
$new_data = ['foo' => 'baz'];
$new_storage->write($name, $new_data);
$this->assertIdentical($result, TRUE);
$this->assertIdentical($new_data, $new_storage->read($name));
$this->assertSame($new_data, $new_storage->read($name));
// Create configuration in another collection.
$another_storage = $this->storage->createCollection('collection.sub.another');
@ -211,7 +216,7 @@ abstract class ConfigStorageTestBase extends KernelTestBase {
$this->assertEqual([], $another_storage->listAll());
$another_storage->write($name, $new_data);
$this->assertIdentical($result, TRUE);
$this->assertIdentical($new_data, $another_storage->read($name));
$this->assertSame($new_data, $another_storage->read($name));
$this->assertEqual([$name], $another_storage->listAll());
$this->assertTrue($another_storage->exists($name));
@ -219,18 +224,18 @@ abstract class ConfigStorageTestBase extends KernelTestBase {
$alt_storage = $this->storage->createCollection('alternate');
$alt_storage->write($name, $new_data);
$this->assertIdentical($result, TRUE);
$this->assertIdentical($new_data, $alt_storage->read($name));
$this->assertSame($new_data, $alt_storage->read($name));
// Switch back to the collection-less mode and check the data still exists
// add has not been touched.
$this->assertIdentical($data, $this->storage->read($name));
$this->assertSame($data, $this->storage->read($name));
// Check that the getAllCollectionNames() method works.
$this->assertIdentical(['alternate', 'collection.sub.another', 'collection.sub.new'], $this->storage->getAllCollectionNames());
$this->assertSame(['alternate', 'collection.sub.another', 'collection.sub.new'], $this->storage->getAllCollectionNames());
// Check that the collections are removed when they are empty.
$alt_storage->delete($name);
$this->assertIdentical(['collection.sub.another', 'collection.sub.new'], $this->storage->getAllCollectionNames());
$this->assertSame(['collection.sub.another', 'collection.sub.new'], $this->storage->getAllCollectionNames());
// Create configuration in collection called 'collection'. This ensures that
// FileStorage's collection storage works regardless of its use of
@ -240,19 +245,19 @@ abstract class ConfigStorageTestBase extends KernelTestBase {
$this->assertEqual([], $parent_storage->listAll());
$parent_storage->write($name, $new_data);
$this->assertIdentical($result, TRUE);
$this->assertIdentical($new_data, $parent_storage->read($name));
$this->assertSame($new_data, $parent_storage->read($name));
$this->assertEqual([$name], $parent_storage->listAll());
$this->assertTrue($parent_storage->exists($name));
$this->assertIdentical(['collection', 'collection.sub.another', 'collection.sub.new'], $this->storage->getAllCollectionNames());
$this->assertSame(['collection', 'collection.sub.another', 'collection.sub.new'], $this->storage->getAllCollectionNames());
$parent_storage->deleteAll();
$this->assertIdentical(['collection.sub.another', 'collection.sub.new'], $this->storage->getAllCollectionNames());
$this->assertSame(['collection.sub.another', 'collection.sub.new'], $this->storage->getAllCollectionNames());
// Check that the having an empty collection-less storage does not break
// anything. Before deleting check that the previous delete did not affect
// data in another collection.
$this->assertIdentical($data, $this->storage->read($name));
$this->assertSame($data, $this->storage->read($name));
$this->storage->delete($name);
$this->assertIdentical(['collection.sub.another', 'collection.sub.new'], $this->storage->getAllCollectionNames());
$this->assertSame(['collection.sub.another', 'collection.sub.new'], $this->storage->getAllCollectionNames());
}
abstract protected function read($name);

View file

@ -68,8 +68,8 @@ class FileStorageTest extends ConfigStorageTestBase {
// @todo https://www.drupal.org/node/2666954 FileStorage::listAll() is
// case-sensitive. However, \Drupal\Core\Config\DatabaseStorage::listAll()
// is case-insensitive.
$this->assertIdentical(['system.performance'], $this->storage->listAll('system'), 'The FileStorage::listAll() with prefix works.');
$this->assertIdentical([], $this->storage->listAll('System'), 'The FileStorage::listAll() is case sensitive.');
$this->assertSame(['system.performance'], $this->storage->listAll('system'), 'The FileStorage::listAll() with prefix works.');
$this->assertSame([], $this->storage->listAll('System'), 'The FileStorage::listAll() is case sensitive.');
}
/**

View file

@ -12,6 +12,7 @@ namespace Drupal\KernelTests\Core\Database;
* @group Database
*/
class BasicSyntaxTest extends DatabaseTestBase {
/**
* Tests string concatenation.
*/
@ -28,15 +29,22 @@ class BasicSyntaxTest extends DatabaseTestBase {
/**
* Tests string concatenation with field values.
*
* We use 'job' and 'age' fields from the {test} table. Using the 'name' field
* for concatenation causes issues with custom or contrib database drivers,
* since its type 'varchar_ascii' may lead to using field-level collations not
* compatible with the other fields.
*/
public function testConcatFields() {
$result = db_query('SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', [
':a1' => 'The age of ',
':a2' => ' is ',
':a3' => '.',
':age' => 25,
]);
$this->assertIdentical($result->fetchField(), 'The age of John is 25.', 'Field CONCAT works.');
$result = $this->connection->query(
'SELECT CONCAT(:a1, CONCAT(job, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', [
':a1' => 'The age of ',
':a2' => ' is ',
':a3' => '.',
':age' => 25,
]
);
$this->assertSame('The age of Singer is 25.', $result->fetchField(), 'Field CONCAT works.');
}
/**

View file

@ -8,6 +8,7 @@ namespace Drupal\KernelTests\Core\Database;
* @group Database
*/
class CaseSensitivityTest extends DatabaseTestBase {
/**
* Tests BINARY collation in MySQL.
*/
@ -16,14 +17,15 @@ class CaseSensitivityTest extends DatabaseTestBase {
db_insert('test')
->fields([
'name' => 'john', // <- A record already exists with name 'John'.
// A record already exists with name 'John'.
'name' => 'john',
'age' => 2,
'job' => 'Baby',
])
->execute();
$num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
$this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
$this->assertSame($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', [':name' => 'john'])->fetchField();
$this->assertIdentical($saved_age, '2', 'Can retrieve after inserting.');
}

View file

@ -32,18 +32,18 @@ class ConnectionTest extends DatabaseTestBase {
// Try to open those targets another time, that should return the same objects.
$db1b = Database::getConnection('default', 'default');
$db2b = Database::getConnection('replica', 'default');
$this->assertIdentical($db1, $db1b, 'A second call to getConnection() returns the same object.');
$this->assertIdentical($db2, $db2b, 'A second call to getConnection() returns the same object.');
$this->assertSame($db1, $db1b, 'A second call to getConnection() returns the same object.');
$this->assertSame($db2, $db2b, 'A second call to getConnection() returns the same object.');
// Try to open an unknown target.
$unknown_target = $this->randomMachineName();
$db3 = Database::getConnection($unknown_target, 'default');
$this->assertNotNull($db3, 'Opening an unknown target returns a real connection object.');
$this->assertIdentical($db1, $db3, 'An unknown target opens the default connection.');
$this->assertSame($db1, $db3, 'An unknown target opens the default connection.');
// Try to open that unknown target another time, that should return the same object.
$db3b = Database::getConnection($unknown_target, 'default');
$this->assertIdentical($db3, $db3b, 'A second call to getConnection() returns the same object.');
$this->assertSame($db3, $db3b, 'A second call to getConnection() returns the same object.');
}
/**
@ -61,7 +61,7 @@ class ConnectionTest extends DatabaseTestBase {
$db1 = Database::getConnection('default', 'default');
$db2 = Database::getConnection('replica', 'default');
$this->assertIdentical($db1, $db2, 'Both targets refer to the same connection.');
$this->assertSame($db1, $db2, 'Both targets refer to the same connection.');
}
/**
@ -131,7 +131,7 @@ class ConnectionTest extends DatabaseTestBase {
try {
$db->query('SELECT * FROM {test}; SELECT * FROM {test_people}',
[],
[ 'allow_delimiter_in_query' => TRUE ]
['allow_delimiter_in_query' => TRUE]
);
$this->fail('No PDO exception thrown for multiple statements.');
}

View file

@ -63,7 +63,7 @@ class ConnectionUnitTest extends KernelTestBase {
*
* @return int
*/
protected function getConnectionID() {
protected function getConnectionId() {
return (int) Database::getConnection($this->target, $this->key)->query('SELECT CONNECTION_ID()')->fetchField();
}
@ -92,7 +92,7 @@ class ConnectionUnitTest extends KernelTestBase {
/**
* Tests Database::closeConnection() without query.
*
* @todo getConnectionID() executes a query.
* @todo getConnectionId() executes a query.
*/
public function testOpenClose() {
if ($this->skipTest) {
@ -100,7 +100,7 @@ class ConnectionUnitTest extends KernelTestBase {
}
// Add and open a new connection.
$this->addConnection();
$id = $this->getConnectionID();
$id = $this->getConnectionId();
Database::getConnection($this->target, $this->key);
// Verify that there is a new connection.
@ -124,7 +124,7 @@ class ConnectionUnitTest extends KernelTestBase {
}
// Add and open a new connection.
$this->addConnection();
$id = $this->getConnectionID();
$id = $this->getConnectionId();
Database::getConnection($this->target, $this->key);
// Verify that there is a new connection.
@ -151,7 +151,7 @@ class ConnectionUnitTest extends KernelTestBase {
}
// Add and open a new connection.
$this->addConnection();
$id = $this->getConnectionID();
$id = $this->getConnectionId();
Database::getConnection($this->target, $this->key);
// Verify that there is a new connection.
@ -178,7 +178,7 @@ class ConnectionUnitTest extends KernelTestBase {
}
// Add and open a new connection.
$this->addConnection();
$id = $this->getConnectionID();
$id = $this->getConnectionId();
Database::getConnection($this->target, $this->key);
// Verify that there is a new connection.

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\KernelTests\Core\Database;
/**
* Deprecation tests cases for the database layer.
*
* @group legacy
*/
class DatabaseLegacyTest extends DatabaseTestBase {
/**
* Tests the db_table_exists() function.
*
* @expectedDeprecation db_table_exists() is deprecated in Drupal 8.0.x and will be removed before Drupal 9.0.0. Use $injected_database->schema()->tableExists($table) instead. See https://www.drupal.org/node/2947929.
*/
public function testDbTableExists() {
$this->assertTrue(db_table_exists('test'));
}
/**
* Tests the db_set_active() function.
*
* @expectedDeprecation db_set_active() is deprecated in Drupal 8.0.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Database\Database::setActiveConnection() instead. See https://www.drupal.org/node/2944084.
*/
public function testDbSetActive() {
$get_active_db = $this->connection->getKey();
$this->assert(db_set_active($get_active_db), 'Database connection is active');
}
/**
* Tests the db_drop_table() function.
*
* @expectedDeprecation db_drop_table() is deprecated in Drupal 8.0.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Database\Database::getConnection()->schema()->dropTable() instead. See https://www.drupal.org/node/2987737
*/
public function testDbDropTable() {
$this->assertFalse(db_drop_table('temp_test_table'));
}
}

View file

@ -2,6 +2,7 @@
namespace Drupal\KernelTests\Core\Database;
use Drupal\Core\Database\Database;
use Drupal\KernelTests\KernelTestBase;
/**
@ -14,8 +15,16 @@ abstract class DatabaseTestBase extends KernelTestBase {
public static $modules = ['database_test'];
/**
* The database connection for testing.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
protected function setUp() {
parent::setUp();
$this->connection = Database::getConnection();
$this->installSchema('database_test', [
'test',
'test_people',
@ -35,7 +44,7 @@ abstract class DatabaseTestBase extends KernelTestBase {
* Sets up tables for NULL handling.
*/
public function ensureSampleDataNull() {
db_insert('test_null')
$this->connection->insert('test_null')
->fields(['name', 'age'])
->values([
'name' => 'Kermit',
@ -56,8 +65,10 @@ abstract class DatabaseTestBase extends KernelTestBase {
* Sets up our sample data.
*/
public static function addSampleData() {
$connection = Database::getConnection();
// We need the IDs, so we can't use a multi-insert here.
$john = db_insert('test')
$john = $connection->insert('test')
->fields([
'name' => 'John',
'age' => 25,
@ -65,7 +76,7 @@ abstract class DatabaseTestBase extends KernelTestBase {
])
->execute();
$george = db_insert('test')
$george = $connection->insert('test')
->fields([
'name' => 'George',
'age' => 27,
@ -73,7 +84,7 @@ abstract class DatabaseTestBase extends KernelTestBase {
])
->execute();
db_insert('test')
$connection->insert('test')
->fields([
'name' => 'Ringo',
'age' => 28,
@ -81,7 +92,7 @@ abstract class DatabaseTestBase extends KernelTestBase {
])
->execute();
$paul = db_insert('test')
$paul = $connection->insert('test')
->fields([
'name' => 'Paul',
'age' => 26,
@ -89,7 +100,7 @@ abstract class DatabaseTestBase extends KernelTestBase {
])
->execute();
db_insert('test_people')
$connection->insert('test_people')
->fields([
'name' => 'Meredith',
'age' => 30,
@ -97,7 +108,7 @@ abstract class DatabaseTestBase extends KernelTestBase {
])
->execute();
db_insert('test_task')
$connection->insert('test_task')
->fields(['pid', 'task', 'priority'])
->values([
'pid' => $john,
@ -136,10 +147,11 @@ abstract class DatabaseTestBase extends KernelTestBase {
])
->execute();
db_insert('test_special_columns')
$connection->insert('test_special_columns')
->fields([
'id' => 1,
'offset' => 'Offset value 1',
'function' => 'Function value 1',
])
->execute();
}

View file

@ -66,6 +66,84 @@ class DeleteTruncateTest extends DatabaseTestBase {
$this->assertEqual(0, $num_records_after, 'Truncate really deletes everything.');
}
/**
* Confirms that we can truncate a whole table while in transaction.
*/
public function testTruncateInTransaction() {
// This test won't work right if transactions are not supported.
if (!$this->connection->supportsTransactions()) {
$this->markTestSkipped('The database driver does not support transactions.');
}
$num_records_before = $this->connection->select('test')->countQuery()->execute()->fetchField();
$this->assertGreaterThan(0, $num_records_before, 'The table is not empty.');
$transaction = $this->connection->startTransaction('test_truncate_in_transaction');
$this->connection->insert('test')
->fields([
'name' => 'Freddie',
'age' => 45,
'job' => 'Great singer',
])
->execute();
$num_records_after_insert = $this->connection->select('test')->countQuery()->execute()->fetchField();
$this->assertEquals($num_records_before + 1, $num_records_after_insert);
$this->connection->truncate('test')->execute();
// Checks that there are no records left in the table, and transaction is
// still active.
$this->assertTrue($this->connection->inTransaction());
$num_records_after = $this->connection->select('test')->countQuery()->execute()->fetchField();
$this->assertEquals(0, $num_records_after);
// Close the transaction, and check that there are still no records in the
// table.
$transaction = NULL;
$this->assertFalse($this->connection->inTransaction());
$num_records_after = $this->connection->select('test')->countQuery()->execute()->fetchField();
$this->assertEquals(0, $num_records_after);
}
/**
* Confirms that transaction rollback voids a truncate operation.
*/
public function testTruncateTransactionRollback() {
// This test won't work right if transactions are not supported.
if (!$this->connection->supportsTransactions()) {
$this->markTestSkipped('The database driver does not support transactions.');
}
$num_records_before = $this->connection->select('test')->countQuery()->execute()->fetchField();
$this->assertGreaterThan(0, $num_records_before, 'The table is not empty.');
$transaction = $this->connection->startTransaction('test_truncate_in_transaction');
$this->connection->insert('test')
->fields([
'name' => 'Freddie',
'age' => 45,
'job' => 'Great singer',
])
->execute();
$num_records_after_insert = $this->connection->select('test')->countQuery()->execute()->fetchField();
$this->assertEquals($num_records_before + 1, $num_records_after_insert);
$this->connection->truncate('test')->execute();
// Checks that there are no records left in the table, and transaction is
// still active.
$this->assertTrue($this->connection->inTransaction());
$num_records_after = $this->connection->select('test')->countQuery()->execute()->fetchField();
$this->assertEquals(0, $num_records_after);
// Roll back the transaction, and check that we are back to status before
// insert and truncate.
$this->connection->rollBack();
$this->assertFalse($this->connection->inTransaction());
$num_records_after = $this->connection->select('test')->countQuery()->execute()->fetchField();
$this->assertEquals($num_records_before, $num_records_after);
}
/**
* Confirms that we can delete a single special column name record successfully.
*/

View file

@ -4,7 +4,7 @@ namespace Drupal\KernelTests\Core\Database;
use Drupal\Core\Database\RowCountException;
use Drupal\Core\Database\StatementInterface;
use Drupal\system\Tests\Database\FakeRecord;
use Drupal\Tests\system\Functional\Database\FakeRecord;
/**
* Tests the Database system's various fetch capabilities.
@ -69,7 +69,7 @@ class FetchTest extends DatabaseTestBase {
*/
public function testQueryFetchClass() {
$records = [];
$result = db_query('SELECT name FROM {test} WHERE age = :age', [':age' => 25], ['fetch' => 'Drupal\system\Tests\Database\FakeRecord']);
$result = db_query('SELECT name FROM {test} WHERE age = :age', [':age' => 25], ['fetch' => FakeRecord::class]);
foreach ($result as $record) {
$records[] = $record;
if ($this->assertTrue($record instanceof FakeRecord, 'Record is an object of class FakeRecord.')) {

View file

@ -40,7 +40,7 @@ class InsertDefaultsTest extends DatabaseTestBase {
}
$num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
$this->assertIdentical($num_records_before, $num_records_after, 'Do nothing as no fields are specified.');
$this->assertSame($num_records_before, $num_records_after, 'Do nothing as no fields are specified.');
}
/**

View file

@ -26,7 +26,7 @@ class InsertTest extends DatabaseTestBase {
$query->execute();
$num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
$this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
$this->assertSame($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Yoko'])->fetchField();
$this->assertIdentical($saved_age, '29', 'Can retrieve after inserting.');
}
@ -61,7 +61,7 @@ class InsertTest extends DatabaseTestBase {
$query->execute();
$num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
$this->assertIdentical($num_records_before + 3, $num_records_after, 'Record inserts correctly.');
$this->assertSame($num_records_before + 3, $num_records_after, 'Record inserts correctly.');
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Larry'])->fetchField();
$this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Curly'])->fetchField();
@ -84,7 +84,8 @@ class InsertTest extends DatabaseTestBase {
]);
// Check how many records are queued for insertion.
$this->assertIdentical($query->count(), 1, 'One record is queued for insertion.');
$query->execute(); // This should run the insert, but leave the fields intact.
// This should run the insert, but leave the fields intact.
$query->execute();
// We should be able to specify values in any order if named.
$query->values([
@ -103,7 +104,7 @@ class InsertTest extends DatabaseTestBase {
$query->execute();
$num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
$this->assertIdentical((int) $num_records_before + 3, (int) $num_records_after, 'Record inserts correctly.');
$this->assertSame((int) $num_records_before + 3, (int) $num_records_after, 'Record inserts correctly.');
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Larry'])->fetchField();
$this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Curly'])->fetchField();
@ -197,14 +198,20 @@ class InsertTest extends DatabaseTestBase {
* Tests that we can INSERT INTO a special named column.
*/
public function testSpecialColumnInsert() {
$id = db_insert('test_special_columns')
$this->connection->insert('test_special_columns')
->fields([
'id' => 2,
'offset' => 'Offset value 2',
'function' => 'foobar',
])
->execute();
$saved_value = db_query('SELECT "offset" FROM {test_special_columns} WHERE id = :id', [':id' => 2])->fetchField();
$this->assertIdentical($saved_value, 'Offset value 2', 'Can retrieve special column name value after inserting.');
$result = $this->connection->select('test_special_columns')
->fields('test_special_columns', ['offset', 'function'])
->condition('test_special_columns.function', 'foobar')
->execute();
$record = $result->fetch();
$this->assertSame('Offset value 2', $record->offset);
$this->assertSame('foobar', $record->function);
}
}

View file

@ -11,6 +11,7 @@ use Drupal\Core\Database\IntegrityConstraintViolationException;
* @group Database
*/
class InvalidDataTest extends DatabaseTestBase {
/**
* Tests aborting of traditional SQL database systems with invalid data.
*/
@ -24,7 +25,8 @@ class InvalidDataTest extends DatabaseTestBase {
'age' => 63,
'job' => 'Singer',
])->values([
'name' => 'John', // <-- Duplicate value on unique field.
// Duplicate value on unique field.
'name' => 'John',
'age' => 17,
'job' => 'Consultant',
])
@ -66,4 +68,81 @@ class InvalidDataTest extends DatabaseTestBase {
}
}
/**
* Tests inserting with invalid data from a select query.
*/
public function testInsertDuplicateDataFromSelect() {
// Insert multiple records in 'test_people' where one has bad data
// (duplicate key). A 'Meredith' record has already been inserted
// in ::setUp.
db_insert('test_people')
->fields(['name', 'age', 'job'])
->values([
'name' => 'Elvis',
'age' => 63,
'job' => 'Singer',
])->values([
// Duplicate value on unique field 'name' for later INSERT in 'test'
// table.
'name' => 'John',
'age' => 17,
'job' => 'Consultant',
])
->values([
'name' => 'Frank',
'age' => 75,
'job' => 'Bass',
])
->execute();
try {
// Define the subselect query. Add ORDER BY to ensure we have consistent
// order in results. Will return:
// 0 => [name] => Elvis, [age] => 63, [job] => Singer
// 1 => [name] => Frank, [age] => 75, [job] => Bass
// 2 => [name] => John, [age] => 17, [job] => Consultant
// 3 => [name] => Meredith, [age] => 30, [job] => Speaker
// Records 0 and 1 should pass, record 2 should lead to integrity
// constraint violation.
$query = db_select('test_people', 'tp')
->fields('tp', ['name', 'age', 'job'])
->orderBy('name');
// Try inserting from the subselect.
db_insert('test')
->from($query)
->execute();
$this->fail('Insert succeeded when it should not have.');
}
catch (IntegrityConstraintViolationException $e) {
// Check if the second record was inserted.
$name = db_query('SELECT name FROM {test} WHERE age = :age', [':age' => 75])->fetchField();
if ($name == 'Frank') {
if (!Database::getConnection()->supportsTransactions()) {
// This is an expected fail.
// Database engines that don't support transactions can leave partial
// inserts in place when an error occurs. This is the case for MySQL
// when running on a MyISAM table.
$this->pass("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions");
}
else {
$this->fail('The whole transaction is rolled back when a duplicate key insert occurs.');
}
}
else {
$this->pass('The whole transaction is rolled back when a duplicate key insert occurs.');
}
// Ensure the values for records 2 and 3 were not inserted.
$record = db_select('test')
->fields('test', ['name', 'age'])
->condition('age', [17, 30], 'IN')
->execute()->fetchObject();
$this->assertFalse($record, 'The rest of the insert aborted as expected.');
}
}
}

View file

@ -64,7 +64,7 @@ class LoggingTest extends DatabaseTestBase {
db_query('SELECT name FROM {test} WHERE age > :age', [':age' => 25])->fetchCol();
db_query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Ringo'], ['target' => 'replica']);//->fetchCol();
db_query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Ringo'], ['target' => 'replica'])->fetchCol();
$queries1 = Database::getLog('testing1');
@ -113,11 +113,11 @@ class LoggingTest extends DatabaseTestBase {
db_query('SELECT name FROM {test} WHERE age > :age', [':age' => 25])->fetchCol();
$old_key = db_set_active('test2');
$old_key = Database::setActiveConnection('test2');
db_query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Ringo'], ['target' => 'replica'])->fetchCol();
db_set_active($old_key);
Database::setActiveConnection($old_key);
$queries1 = Database::getLog('testing1');
$queries2 = Database::getLog('testing1', 'test2');

View file

@ -30,7 +30,7 @@ class PrefixInfoTest extends DatabaseTestBase {
$db1_schema = $db1_connection->schema();
$db2_connection = Database::getConnection('default', 'extra');
// Get the prefix info for the first databse.
// Get the prefix info for the first database.
$method = new \ReflectionMethod($db1_schema, 'getPrefixInfo');
$method->setAccessible(TRUE);
$db1_info = $method->invoke($db1_schema);

View file

@ -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')

View file

@ -30,31 +30,31 @@ class RegressionTest extends DatabaseTestBase {
])->execute();
$from_database = db_query('SELECT job FROM {test} WHERE job = :job', [':job' => $job])->fetchField();
$this->assertIdentical($job, $from_database, 'The database handles UTF-8 characters cleanly.');
$this->assertSame($job, $from_database, 'The database handles UTF-8 characters cleanly.');
}
/**
* Tests the db_table_exists() function.
*/
public function testDBTableExists() {
$this->assertIdentical(TRUE, db_table_exists('test'), 'Returns true for existent table.');
$this->assertIdentical(FALSE, db_table_exists('nosuchtable'), 'Returns false for nonexistent table.');
$this->assertSame(TRUE, $this->connection->schema()->tableExists('test'), 'Returns true for existent table.');
$this->assertSame(FALSE, $this->connection->schema()->tableExists('nosuchtable'), 'Returns false for nonexistent table.');
}
/**
* Tests the db_field_exists() function.
*/
public function testDBFieldExists() {
$this->assertIdentical(TRUE, db_field_exists('test', 'name'), 'Returns true for existent column.');
$this->assertIdentical(FALSE, db_field_exists('test', 'nosuchcolumn'), 'Returns false for nonexistent column.');
$this->assertSame(TRUE, db_field_exists('test', 'name'), 'Returns true for existent column.');
$this->assertSame(FALSE, db_field_exists('test', 'nosuchcolumn'), 'Returns false for nonexistent column.');
}
/**
* Tests the db_index_exists() function.
*/
public function testDBIndexExists() {
$this->assertIdentical(TRUE, db_index_exists('test', 'ages'), 'Returns true for existent index.');
$this->assertIdentical(FALSE, db_index_exists('test', 'nosuchindex'), 'Returns false for nonexistent index.');
$this->assertSame(TRUE, db_index_exists('test', 'ages'), 'Returns true for existent index.');
$this->assertSame(FALSE, db_index_exists('test', 'nosuchindex'), 'Returns false for nonexistent index.');
}
}

View file

@ -12,15 +12,42 @@ use Drupal\Component\Utility\Unicode;
/**
* Tests table creation and modification via the schema API.
*
* @coversDefaultClass \Drupal\Core\Database\Schema
*
* @group Database
*/
class SchemaTest extends KernelTestBase {
/**
* A global counter for table and field creation.
*
* @var int
*/
protected $counter;
/**
* Connection to the database.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* Database schema instance.
*
* @var \Drupal\Core\Database\Schema
*/
protected $schema;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->connection = Database::getConnection();
$this->schema = $this->connection->schema();
}
/**
* Tests database interactions.
*/
@ -52,10 +79,10 @@ class SchemaTest extends KernelTestBase {
],
],
];
db_create_table('test_table', $table_specification);
$this->schema->createTable('test_table', $table_specification);
// Assert that the table exists.
$this->assertTrue(db_table_exists('test_table'), 'The table exists.');
$this->assertTrue($this->schema->tableExists('test_table'), 'The table exists.');
// Assert that the table comment has been set.
$this->checkSchemaComment($table_specification['description'], 'test_table');
@ -63,12 +90,12 @@ class SchemaTest extends KernelTestBase {
// Assert that the column comment has been set.
$this->checkSchemaComment($table_specification['fields']['test_field']['description'], 'test_table', 'test_field');
if (Database::getConnection()->databaseType() == 'mysql') {
if ($this->connection->databaseType() === 'mysql') {
// Make sure that varchar fields have the correct collation.
$columns = db_query('SHOW FULL COLUMNS FROM {test_table}');
$columns = $this->connection->query('SHOW FULL COLUMNS FROM {test_table}');
foreach ($columns as $column) {
if ($column->Field == 'test_field_string') {
$string_check = ($column->Collation == 'utf8mb4_general_ci');
$string_check = ($column->Collation == 'utf8mb4_general_ci' || $column->Collation == 'utf8mb4_0900_ai_ci');
}
if ($column->Field == 'test_field_string_ascii') {
$string_ascii_check = ($column->Collation == 'ascii_general_ci');
@ -82,91 +109,95 @@ class SchemaTest extends KernelTestBase {
$this->assertFalse($this->tryInsert(), 'Insert without a default failed.');
// Add a default value to the column.
db_field_set_default('test_table', 'test_field', 0);
$this->schema->fieldSetDefault('test_table', 'test_field', 0);
// The insert should now succeed.
$this->assertTrue($this->tryInsert(), 'Insert with a default succeeded.');
// Remove the default.
db_field_set_no_default('test_table', 'test_field');
$this->schema->fieldSetNoDefault('test_table', 'test_field');
// The insert should fail again.
$this->assertFalse($this->tryInsert(), 'Insert without a default failed.');
// Test for fake index and test for the boolean result of indexExists().
$index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
$this->assertIdentical($index_exists, FALSE, 'Fake index does not exists');
$index_exists = $this->schema->indexExists('test_table', 'test_field');
$this->assertIdentical($index_exists, FALSE, 'Fake index does not exist');
// Add index.
db_add_index('test_table', 'test_field', ['test_field'], $table_specification);
$this->schema->addIndex('test_table', 'test_field', ['test_field'], $table_specification);
// Test for created index and test for the boolean result of indexExists().
$index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
$index_exists = $this->schema->indexExists('test_table', 'test_field');
$this->assertIdentical($index_exists, TRUE, 'Index created.');
// Rename the table.
db_rename_table('test_table', 'test_table2');
$this->schema->renameTable('test_table', 'test_table2');
// Index should be renamed.
$index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
$index_exists = $this->schema->indexExists('test_table2', 'test_field');
$this->assertTrue($index_exists, 'Index was renamed.');
// We need the default so that we can insert after the rename.
db_field_set_default('test_table2', 'test_field', 0);
$this->schema->fieldSetDefault('test_table2', 'test_field', 0);
$this->assertFalse($this->tryInsert(), 'Insert into the old table failed.');
$this->assertTrue($this->tryInsert('test_table2'), 'Insert into the new table succeeded.');
// We should have successfully inserted exactly two rows.
$count = db_query('SELECT COUNT(*) FROM {test_table2}')->fetchField();
$count = $this->connection->query('SELECT COUNT(*) FROM {test_table2}')->fetchField();
$this->assertEqual($count, 2, 'Two fields were successfully inserted.');
// Try to drop the table.
db_drop_table('test_table2');
$this->assertFalse(db_table_exists('test_table2'), 'The dropped table does not exist.');
$this->schema->dropTable('test_table2');
$this->assertFalse($this->schema->tableExists('test_table2'), 'The dropped table does not exist.');
// Recreate the table.
db_create_table('test_table', $table_specification);
db_field_set_default('test_table', 'test_field', 0);
db_add_field('test_table', 'test_serial', ['type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Added column description.']);
$this->schema->createTable('test_table', $table_specification);
$this->schema->fieldSetDefault('test_table', 'test_field', 0);
$this->schema->addField('test_table', 'test_serial', ['type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Added column description.']);
// Assert that the column comment has been set.
$this->checkSchemaComment('Added column description.', 'test_table', 'test_serial');
// Change the new field to a serial column.
db_change_field('test_table', 'test_serial', 'test_serial', ['type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'], ['primary key' => ['test_serial']]);
$this->schema->changeField('test_table', 'test_serial', 'test_serial', ['type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'], ['primary key' => ['test_serial']]);
// Assert that the column comment has been set.
$this->checkSchemaComment('Changed column description.', 'test_table', 'test_serial');
$this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
$max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
$max1 = $this->connection->query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
$this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
$max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
$max2 = $this->connection->query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
$this->assertTrue($max2 > $max1, 'The serial is monotone.');
$count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField();
$count = $this->connection->query('SELECT COUNT(*) FROM {test_table}')->fetchField();
$this->assertEqual($count, 2, 'There were two rows.');
// Test adding a serial field to an existing table.
db_drop_table('test_table');
db_create_table('test_table', $table_specification);
db_field_set_default('test_table', 'test_field', 0);
db_add_field('test_table', 'test_serial', ['type' => 'serial', 'not null' => TRUE], ['primary key' => ['test_serial']]);
$this->schema->dropTable('test_table');
$this->schema->createTable('test_table', $table_specification);
$this->schema->fieldSetDefault('test_table', 'test_field', 0);
$this->schema->addField('test_table', 'test_serial', ['type' => 'serial', 'not null' => TRUE], ['primary key' => ['test_serial']]);
$this->assertPrimaryKeyColumns('test_table', ['test_serial']);
// Test the primary key columns.
$method = new \ReflectionMethod(get_class($this->schema), 'findPrimaryKeyColumns');
$method->setAccessible(TRUE);
$this->assertSame(['test_serial'], $method->invoke($this->schema, 'test_table'));
$this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
$max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
$max1 = $this->connection->query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
$this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
$max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
$max2 = $this->connection->query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
$this->assertTrue($max2 > $max1, 'The serial is monotone.');
$count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField();
$count = $this->connection->query('SELECT COUNT(*) FROM {test_table}')->fetchField();
$this->assertEqual($count, 2, 'There were two rows.');
// Test adding a new column and form a composite primary key with it.
db_add_field('test_table', 'test_composite_primary_key', ['type' => 'int', 'not null' => TRUE, 'default' => 0], ['primary key' => ['test_serial', 'test_composite_primary_key']]);
$this->schema->addField('test_table', 'test_composite_primary_key', ['type' => 'int', 'not null' => TRUE, 'default' => 0], ['primary key' => ['test_serial', 'test_composite_primary_key']]);
$this->assertPrimaryKeyColumns('test_table', ['test_serial', 'test_composite_primary_key']);
// Test the primary key columns.
$this->assertSame(['test_serial', 'test_composite_primary_key'], $method->invoke($this->schema, 'test_table'));
// Test renaming of keys and constraints.
db_drop_table('test_table');
$this->schema->dropTable('test_table');
$table_specification = [
'fields' => [
'id' => [
@ -183,46 +214,50 @@ class SchemaTest extends KernelTestBase {
'test_field' => ['test_field'],
],
];
db_create_table('test_table', $table_specification);
$this->schema->createTable('test_table', $table_specification);
// Tests for indexes are Database specific.
$db_type = Database::getConnection()->databaseType();
$db_type = $this->connection->databaseType();
// Test for existing primary and unique keys.
switch ($db_type) {
case 'pgsql':
$primary_key_exists = Database::getConnection()->schema()->constraintExists('test_table', '__pkey');
$unique_key_exists = Database::getConnection()->schema()->constraintExists('test_table', 'test_field' . '__key');
$primary_key_exists = $this->schema->constraintExists('test_table', '__pkey');
$unique_key_exists = $this->schema->constraintExists('test_table', 'test_field' . '__key');
break;
case 'sqlite':
// SQLite does not create a standalone index for primary keys.
$primary_key_exists = TRUE;
$unique_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
$unique_key_exists = $this->schema->indexExists('test_table', 'test_field');
break;
default:
$primary_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'PRIMARY');
$unique_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
$primary_key_exists = $this->schema->indexExists('test_table', 'PRIMARY');
$unique_key_exists = $this->schema->indexExists('test_table', 'test_field');
break;
}
$this->assertIdentical($primary_key_exists, TRUE, 'Primary key created.');
$this->assertIdentical($unique_key_exists, TRUE, 'Unique key created.');
db_rename_table('test_table', 'test_table2');
$this->schema->renameTable('test_table', 'test_table2');
// Test for renamed primary and unique keys.
switch ($db_type) {
case 'pgsql':
$renamed_primary_key_exists = Database::getConnection()->schema()->constraintExists('test_table2', '__pkey');
$renamed_unique_key_exists = Database::getConnection()->schema()->constraintExists('test_table2', 'test_field' . '__key');
$renamed_primary_key_exists = $this->schema->constraintExists('test_table2', '__pkey');
$renamed_unique_key_exists = $this->schema->constraintExists('test_table2', 'test_field' . '__key');
break;
case 'sqlite':
// SQLite does not create a standalone index for primary keys.
$renamed_primary_key_exists = TRUE;
$renamed_unique_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
$renamed_unique_key_exists = $this->schema->indexExists('test_table2', 'test_field');
break;
default:
$renamed_primary_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'PRIMARY');
$renamed_unique_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
$renamed_primary_key_exists = $this->schema->indexExists('test_table2', 'PRIMARY');
$renamed_unique_key_exists = $this->schema->indexExists('test_table2', 'test_field');
break;
}
$this->assertIdentical($renamed_primary_key_exists, TRUE, 'Primary key was renamed.');
@ -231,8 +266,8 @@ class SchemaTest extends KernelTestBase {
// For PostgreSQL check in addition that sequence was renamed.
if ($db_type == 'pgsql') {
// Get information about new table.
$info = Database::getConnection()->schema()->queryTableInformation('test_table2');
$sequence_name = Database::getConnection()->schema()->prefixNonTable('test_table2', 'id', 'seq');
$info = $this->schema->queryTableInformation('test_table2');
$sequence_name = $this->schema->prefixNonTable('test_table2', 'id', 'seq');
$this->assertEqual($sequence_name, current($info->sequences), 'Sequence was renamed.');
}
@ -250,11 +285,11 @@ class SchemaTest extends KernelTestBase {
],
];
try {
db_create_table('test_timestamp', $table_specification);
$this->schema->createTable('test_timestamp', $table_specification);
}
catch (\Exception $e) {
}
$this->assertTrue(db_table_exists('test_timestamp'), 'Table with database specific datatype was created.');
$this->assertTrue($this->schema->tableExists('test_timestamp'), 'Table with database specific datatype was created.');
}
/**
@ -263,9 +298,10 @@ class SchemaTest extends KernelTestBase {
* @see \Drupal\Core\Database\Driver\mysql\Schema::getNormalizedIndexes()
*/
public function testIndexLength() {
if (Database::getConnection()->databaseType() != 'mysql') {
return;
if ($this->connection->databaseType() !== 'mysql') {
$this->markTestSkipped("The '{$this->connection->databaseType()}' database type does not support setting column length for indexes.");
}
$table_specification = [
'fields' => [
'id' => [
@ -312,16 +348,14 @@ class SchemaTest extends KernelTestBase {
],
],
];
db_create_table('test_table_index_length', $table_specification);
$schema_object = Database::getConnection()->schema();
$this->schema->createTable('test_table_index_length', $table_specification);
// Ensure expected exception thrown when adding index with missing info.
$expected_exception_message = "MySQL needs the 'test_field_text' field specification in order to normalize the 'test_regular' index";
$missing_field_spec = $table_specification;
unset($missing_field_spec['fields']['test_field_text']);
try {
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $missing_field_spec);
$this->schema->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $missing_field_spec);
$this->fail('SchemaException not thrown when adding index with missing information.');
}
catch (SchemaException $e) {
@ -329,14 +363,13 @@ class SchemaTest extends KernelTestBase {
}
// Add a separate index.
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
$this->schema->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
$table_specification_with_new_index = $table_specification;
$table_specification_with_new_index['indexes']['test_separate'] = [['test_field_text', 200]];
// Ensure that the exceptions of addIndex are thrown as expected.
try {
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
$this->schema->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
$this->fail('\Drupal\Core\Database\SchemaObjectExistsException exception missed.');
}
catch (SchemaObjectExistsException $e) {
@ -344,7 +377,7 @@ class SchemaTest extends KernelTestBase {
}
try {
$schema_object->addIndex('test_table_non_existing', 'test_separate', [['test_field_text', 200]], $table_specification);
$this->schema->addIndex('test_table_non_existing', 'test_separate', [['test_field_text', 200]], $table_specification);
$this->fail('\Drupal\Core\Database\SchemaObjectDoesNotExistException exception missed.');
}
catch (SchemaObjectDoesNotExistException $e) {
@ -352,7 +385,7 @@ class SchemaTest extends KernelTestBase {
}
// Get index information.
$results = db_query('SHOW INDEX FROM {test_table_index_length}');
$results = $this->connection->query('SHOW INDEX FROM {test_table_index_length}');
$expected_lengths = [
'test_regular' => [
'test_field_text' => 191,
@ -395,15 +428,16 @@ class SchemaTest extends KernelTestBase {
/**
* Tests inserting data into an existing table.
*
* @param $table
* @param string $table
* The database table to insert data into.
*
* @return
* @return bool
* TRUE if the insert succeeded, FALSE otherwise.
*/
public function tryInsert($table = 'test_table') {
try {
db_insert($table)
$this->connection
->insert($table)
->fields(['id' => mt_rand(10, 20)])
->execute();
return TRUE;
@ -424,10 +458,10 @@ class SchemaTest extends KernelTestBase {
* Optional column to test.
*/
public function checkSchemaComment($description, $table, $column = NULL) {
if (method_exists(Database::getConnection()->schema(), 'getComment')) {
$comment = Database::getConnection()->schema()->getComment($table, $column);
if (method_exists($this->schema, 'getComment')) {
$comment = $this->schema->getComment($table, $column);
// The schema comment truncation for mysql is different.
if (Database::getConnection()->databaseType() == 'mysql') {
if ($this->connection->databaseType() === 'mysql') {
$max_length = $column ? 255 : 60;
$description = Unicode::truncate($description, $max_length, TRUE, TRUE);
}
@ -445,7 +479,7 @@ class SchemaTest extends KernelTestBase {
'fields' => ['serial_column' => ['type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE]],
'primary key' => ['serial_column'],
];
db_create_table($table_name, $table_spec);
$this->schema->createTable($table_name, $table_spec);
// Now set up columns for the other types.
$types = ['int', 'float', 'numeric'];
@ -456,12 +490,12 @@ class SchemaTest extends KernelTestBase {
}
$column_name = $type . '_column';
$table_spec['fields'][$column_name] = $column_spec;
db_add_field($table_name, $column_name, $column_spec);
$this->schema->addField($table_name, $column_name, $column_spec);
}
// Finally, check each column and try to insert invalid values into them.
foreach ($table_spec['fields'] as $column_name => $column_spec) {
$this->assertTrue(db_field_exists($table_name, $column_name), format_string('Unsigned @type column was created.', ['@type' => $column_spec['type']]));
$this->assertTrue($this->schema->fieldExists($table_name, $column_name), format_string('Unsigned @type column was created.', ['@type' => $column_spec['type']]));
$this->assertFalse($this->tryUnsignedInsert($table_name, $column_name), format_string('Unsigned @type column rejected a negative value.', ['@type' => $column_spec['type']]));
}
}
@ -469,17 +503,18 @@ class SchemaTest extends KernelTestBase {
/**
* Tries to insert a negative value into columns defined as unsigned.
*
* @param $table_name
* @param string $table_name
* The table to insert.
* @param $column_name
* @param string $column_name
* The column to insert.
*
* @return
* @return bool
* TRUE if the insert succeeded, FALSE otherwise.
*/
public function tryUnsignedInsert($table_name, $column_name) {
try {
db_insert($table_name)
$this->connection
->insert($table_name)
->fields([$column_name => -1])
->execute();
return TRUE;
@ -490,9 +525,9 @@ class SchemaTest extends KernelTestBase {
}
/**
* Tests adding columns to an existing table.
* Tests adding columns to an existing table with default and initial value.
*/
public function testSchemaAddField() {
public function testSchemaAddFieldDefaultInitial() {
// Test varchar types.
foreach ([1, 32, 128, 256, 512] as $length) {
$base_field_spec = [
@ -528,6 +563,11 @@ class SchemaTest extends KernelTestBase {
['not null' => TRUE, 'initial' => 1],
['not null' => TRUE, 'initial' => 1, 'default' => 7],
['not null' => TRUE, 'initial_from_field' => 'serial_column'],
[
'not null' => TRUE,
'initial_from_field' => 'test_nullable_field',
'initial' => 100,
],
];
foreach ($variations as $variation) {
@ -581,51 +621,61 @@ class SchemaTest extends KernelTestBase {
$table_spec = [
'fields' => [
'serial_column' => ['type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE],
'test_nullable_field' => ['type' => 'int', 'not null' => FALSE],
'test_field' => $field_spec,
],
'primary key' => ['serial_column'],
];
db_create_table($table_name, $table_spec);
$this->schema->createTable($table_name, $table_spec);
$this->pass(format_string('Table %table created.', ['%table' => $table_name]));
// Check the characteristics of the field.
$this->assertFieldCharacteristics($table_name, 'test_field', $field_spec);
// Clean-up.
db_drop_table($table_name);
$this->schema->dropTable($table_name);
// Try adding a field to an existing table.
$table_name = 'test_table_' . ($this->counter++);
$table_spec = [
'fields' => [
'serial_column' => ['type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE],
'test_nullable_field' => ['type' => 'int', 'not null' => FALSE],
],
'primary key' => ['serial_column'],
];
db_create_table($table_name, $table_spec);
$this->schema->createTable($table_name, $table_spec);
$this->pass(format_string('Table %table created.', ['%table' => $table_name]));
// Insert some rows to the table to test the handling of initial values.
for ($i = 0; $i < 3; $i++) {
db_insert($table_name)
$this->connection
->insert($table_name)
->useDefaults(['serial_column'])
->fields(['test_nullable_field' => 100])
->execute();
}
db_add_field($table_name, 'test_field', $field_spec);
// Add another row with no value for the 'test_nullable_field' column.
$this->connection
->insert($table_name)
->useDefaults(['serial_column'])
->execute();
$this->schema->addField($table_name, 'test_field', $field_spec);
$this->pass(format_string('Column %column created.', ['%column' => 'test_field']));
// Check the characteristics of the field.
$this->assertFieldCharacteristics($table_name, 'test_field', $field_spec);
// Clean-up.
db_drop_field($table_name, 'test_field');
$this->schema->dropField($table_name, 'test_field');
// Add back the field and then try to delete a field which is also a primary
// key.
db_add_field($table_name, 'test_field', $field_spec);
db_drop_field($table_name, 'serial_column');
db_drop_table($table_name);
$this->schema->addField($table_name, 'test_field', $field_spec);
$this->schema->dropField($table_name, 'serial_column');
$this->schema->dropTable($table_name);
}
/**
@ -635,7 +685,8 @@ class SchemaTest extends KernelTestBase {
// Check that the initial value has been registered.
if (isset($field_spec['initial'])) {
// There should be no row with a value different then $field_spec['initial'].
$count = db_select($table_name)
$count = $this->connection
->select($table_name)
->fields($table_name, ['serial_column'])
->condition($field_name, $field_spec['initial'], '<>')
->countQuery()
@ -645,10 +696,11 @@ class SchemaTest extends KernelTestBase {
}
// Check that the initial value from another field has been registered.
if (isset($field_spec['initial_from_field'])) {
if (isset($field_spec['initial_from_field']) && !isset($field_spec['initial'])) {
// There should be no row with a value different than
// $field_spec['initial_from_field'].
$count = db_select($table_name)
$count = $this->connection
->select($table_name)
->fields($table_name, ['serial_column'])
->where($table_name . '.' . $field_spec['initial_from_field'] . ' <> ' . $table_name . '.' . $field_name)
->countQuery()
@ -656,14 +708,27 @@ class SchemaTest extends KernelTestBase {
->fetchField();
$this->assertEqual($count, 0, 'Initial values from another field filled out.');
}
elseif (isset($field_spec['initial_from_field']) && isset($field_spec['initial'])) {
// There should be no row with a value different than '100'.
$count = $this->connection
->select($table_name)
->fields($table_name, ['serial_column'])
->condition($field_name, 100, '<>')
->countQuery()
->execute()
->fetchField();
$this->assertEqual($count, 0, 'Initial values from another field or a default value filled out.');
}
// Check that the default value has been registered.
if (isset($field_spec['default'])) {
// Try inserting a row, and check the resulting value of the new column.
$id = db_insert($table_name)
$id = $this->connection
->insert($table_name)
->useDefaults(['serial_column'])
->execute();
$field_value = db_select($table_name)
$field_value = $this->connection
->select($table_name)
->fields($table_name, [$field_name])
->condition('serial_column', $id)
->execute()
@ -673,9 +738,171 @@ class SchemaTest extends KernelTestBase {
}
/**
* Tests changing columns between types.
* Tests various schema changes' effect on the table's primary key.
*
* @param array $initial_primary_key
* The initial primary key of the test table.
* @param array $renamed_primary_key
* The primary key of the test table after renaming the test field.
*
* @dataProvider providerTestSchemaCreateTablePrimaryKey
*
* @covers ::addField
* @covers ::changeField
* @covers ::dropField
* @covers ::findPrimaryKeyColumns
*/
public function testSchemaChangeField() {
public function testSchemaChangePrimaryKey(array $initial_primary_key, array $renamed_primary_key) {
$find_primary_key_columns = new \ReflectionMethod(get_class($this->schema), 'findPrimaryKeyColumns');
$find_primary_key_columns->setAccessible(TRUE);
// Test making the field the primary key of the table upon creation.
$table_name = 'test_table';
$table_spec = [
'fields' => [
'test_field' => ['type' => 'int', 'not null' => TRUE],
'other_test_field' => ['type' => 'int', 'not null' => TRUE],
],
'primary key' => $initial_primary_key,
];
$this->schema->createTable($table_name, $table_spec);
$this->assertTrue($this->schema->fieldExists($table_name, 'test_field'));
$this->assertEquals($initial_primary_key, $find_primary_key_columns->invoke($this->schema, $table_name));
// Change the field type and make sure the primary key stays in place.
$this->schema->changeField($table_name, 'test_field', 'test_field', ['type' => 'varchar', 'length' => 32, 'not null' => TRUE]);
$this->assertTrue($this->schema->fieldExists($table_name, 'test_field'));
$this->assertEquals($initial_primary_key, $find_primary_key_columns->invoke($this->schema, $table_name));
// Add some data and change the field type back, to make sure that changing
// the type leaves the primary key in place even with existing data.
$this->connection
->insert($table_name)
->fields(['test_field' => 1, 'other_test_field' => 2])
->execute();
$this->schema->changeField($table_name, 'test_field', 'test_field', ['type' => 'int', 'not null' => TRUE]);
$this->assertTrue($this->schema->fieldExists($table_name, 'test_field'));
$this->assertEquals($initial_primary_key, $find_primary_key_columns->invoke($this->schema, $table_name));
// Make sure that adding the primary key can be done as part of changing
// a field, as well.
$this->schema->dropPrimaryKey($table_name);
$this->assertEquals([], $find_primary_key_columns->invoke($this->schema, $table_name));
$this->schema->changeField($table_name, 'test_field', 'test_field', ['type' => 'int', 'not null' => TRUE], ['primary key' => $initial_primary_key]);
$this->assertTrue($this->schema->fieldExists($table_name, 'test_field'));
$this->assertEquals($initial_primary_key, $find_primary_key_columns->invoke($this->schema, $table_name));
// Rename the field and make sure the primary key was updated.
$this->schema->changeField($table_name, 'test_field', 'test_field_renamed', ['type' => 'int', 'not null' => TRUE]);
$this->assertTrue($this->schema->fieldExists($table_name, 'test_field_renamed'));
$this->assertEquals($renamed_primary_key, $find_primary_key_columns->invoke($this->schema, $table_name));
// Drop the field and make sure the primary key was dropped, as well.
$this->schema->dropField($table_name, 'test_field_renamed');
$this->assertFalse($this->schema->fieldExists($table_name, 'test_field_renamed'));
$this->assertEquals([], $find_primary_key_columns->invoke($this->schema, $table_name));
// Add the field again and make sure adding the primary key can be done at
// the same time.
$this->schema->addField($table_name, 'test_field', ['type' => 'int', 'default' => 0, 'not null' => TRUE], ['primary key' => $initial_primary_key]);
$this->assertTrue($this->schema->fieldExists($table_name, 'test_field'));
$this->assertEquals($initial_primary_key, $find_primary_key_columns->invoke($this->schema, $table_name));
// Drop the field again and explicitly add a primary key.
$this->schema->dropField($table_name, 'test_field');
$this->schema->addPrimaryKey($table_name, ['other_test_field']);
$this->assertFalse($this->schema->fieldExists($table_name, 'test_field'));
$this->assertEquals(['other_test_field'], $find_primary_key_columns->invoke($this->schema, $table_name));
// Test that adding a field with a primary key will work even with a
// pre-existing primary key.
$this->schema->addField($table_name, 'test_field', ['type' => 'int', 'default' => 0, 'not null' => TRUE], ['primary key' => $initial_primary_key]);
$this->assertTrue($this->schema->fieldExists($table_name, 'test_field'));
$this->assertEquals($initial_primary_key, $find_primary_key_columns->invoke($this->schema, $table_name));
}
/**
* Provides test cases for SchemaTest::testSchemaCreateTablePrimaryKey().
*
* @return array
* An array of test cases for SchemaTest::testSchemaCreateTablePrimaryKey().
*/
public function providerTestSchemaCreateTablePrimaryKey() {
$tests = [];
$tests['simple_primary_key'] = [
'initial_primary_key' => ['test_field'],
'renamed_primary_key' => ['test_field_renamed'],
];
$tests['composite_primary_key'] = [
'initial_primary_key' => ['test_field', 'other_test_field'],
'renamed_primary_key' => ['test_field_renamed', 'other_test_field'],
];
$tests['composite_primary_key_different_order'] = [
'initial_primary_key' => ['other_test_field', 'test_field'],
'renamed_primary_key' => ['other_test_field', 'test_field_renamed'],
];
return $tests;
}
/**
* Tests an invalid field specification as a primary key on table creation.
*/
public function testInvalidPrimaryKeyOnTableCreation() {
// Test making an invalid field the primary key of the table upon creation.
$table_name = 'test_table';
$table_spec = [
'fields' => [
'test_field' => ['type' => 'int'],
],
'primary key' => ['test_field'],
];
$this->setExpectedException(SchemaException::class, "The 'test_field' field specification does not define 'not null' as TRUE.");
$this->schema->createTable($table_name, $table_spec);
}
/**
* Tests adding an invalid field specification as a primary key.
*/
public function testInvalidPrimaryKeyAddition() {
// Test adding a new invalid field to the primary key.
$table_name = 'test_table';
$table_spec = [
'fields' => [
'test_field' => ['type' => 'int', 'not null' => TRUE],
],
'primary key' => ['test_field'],
];
$this->schema->createTable($table_name, $table_spec);
$this->setExpectedException(SchemaException::class, "The 'new_test_field' field specification does not define 'not null' as TRUE.");
$this->schema->addField($table_name, 'new_test_field', ['type' => 'int'], ['primary key' => ['test_field', 'new_test_field']]);
}
/**
* Tests changing the primary key with an invalid field specification.
*/
public function testInvalidPrimaryKeyChange() {
// Test adding a new invalid field to the primary key.
$table_name = 'test_table';
$table_spec = [
'fields' => [
'test_field' => ['type' => 'int', 'not null' => TRUE],
],
'primary key' => ['test_field'],
];
$this->schema->createTable($table_name, $table_spec);
$this->setExpectedException(SchemaException::class, "The 'changed_test_field' field specification does not define 'not null' as TRUE.");
$this->schema->dropPrimaryKey($table_name);
$this->schema->changeField($table_name, 'test_field', 'changed_test_field', ['type' => 'int'], ['primary key' => ['changed_test_field']]);
}
/**
* Tests changing columns between types with default and initial values.
*/
public function testSchemaChangeFieldDefaultInitial() {
$field_specs = [
['type' => 'int', 'size' => 'normal', 'not null' => FALSE],
['type' => 'int', 'size' => 'normal', 'not null' => TRUE, 'initial' => 1, 'default' => 17],
@ -734,26 +961,28 @@ class SchemaTest extends KernelTestBase {
],
'primary key' => ['serial_column'],
];
db_create_table($table_name, $table_spec);
$this->schema->createTable($table_name, $table_spec);
$this->pass(format_string('Table %table created.', ['%table' => $table_name]));
// Check the characteristics of the field.
$this->assertFieldCharacteristics($table_name, 'test_field', $old_spec);
// Remove inserted rows.
db_truncate($table_name)->execute();
$this->connection->truncate($table_name)->execute();
if ($test_data) {
$id = db_insert($table_name)
$id = $this->connection
->insert($table_name)
->fields(['test_field'], [$test_data])
->execute();
}
// Change the field.
db_change_field($table_name, 'test_field', 'test_field', $new_spec);
$this->schema->changeField($table_name, 'test_field', 'test_field', $new_spec);
if ($test_data) {
$field_value = db_select($table_name)
$field_value = $this->connection
->select($table_name)
->fields($table_name, ['test_field'])
->condition('serial_column', $id)
->execute()
@ -765,7 +994,142 @@ class SchemaTest extends KernelTestBase {
$this->assertFieldCharacteristics($table_name, 'test_field', $new_spec);
// Clean-up.
db_drop_table($table_name);
$this->schema->dropTable($table_name);
}
/**
* @covers ::findPrimaryKeyColumns
*/
public function testFindPrimaryKeyColumns() {
$method = new \ReflectionMethod(get_class($this->schema), 'findPrimaryKeyColumns');
$method->setAccessible(TRUE);
// Test with single column primary key.
$this->schema->createTable('table_with_pk_0', [
'description' => 'Table with primary key.',
'fields' => [
'id' => [
'type' => 'int',
'not null' => TRUE,
],
'test_field' => [
'type' => 'int',
'not null' => TRUE,
],
],
'primary key' => ['id'],
]);
$this->assertSame(['id'], $method->invoke($this->schema, 'table_with_pk_0'));
// Test with multiple column primary key.
$this->schema->createTable('table_with_pk_1', [
'description' => 'Table with primary key with multiple columns.',
'fields' => [
'id0' => [
'type' => 'int',
'not null' => TRUE,
],
'id1' => [
'type' => 'int',
'not null' => TRUE,
],
'test_field' => [
'type' => 'int',
'not null' => TRUE,
],
],
'primary key' => ['id0', 'id1'],
]);
$this->assertSame(['id0', 'id1'], $method->invoke($this->schema, 'table_with_pk_1'));
// Test with multiple column primary key and not being the first column of
// the table definition.
$this->schema->createTable('table_with_pk_2', [
'description' => 'Table with primary key with multiple columns at the end and in reverted sequence.',
'fields' => [
'test_field_1' => [
'type' => 'int',
'not null' => TRUE,
],
'test_field_2' => [
'type' => 'int',
'not null' => TRUE,
],
'id3' => [
'type' => 'int',
'not null' => TRUE,
],
'id4' => [
'type' => 'int',
'not null' => TRUE,
],
],
'primary key' => ['id4', 'id3'],
]);
$this->assertSame(['id4', 'id3'], $method->invoke($this->schema, 'table_with_pk_2'));
// Test with multiple column primary key in a different order. For the
// PostgreSQL and the SQLite drivers is sorting used to get the primary key
// columns in the right order.
$this->schema->createTable('table_with_pk_3', [
'description' => 'Table with primary key with multiple columns at the end and in reverted sequence.',
'fields' => [
'test_field_1' => [
'type' => 'int',
'not null' => TRUE,
],
'test_field_2' => [
'type' => 'int',
'not null' => TRUE,
],
'id3' => [
'type' => 'int',
'not null' => TRUE,
],
'id4' => [
'type' => 'int',
'not null' => TRUE,
],
],
'primary key' => ['id3', 'test_field_2', 'id4'],
]);
$this->assertSame(['id3', 'test_field_2', 'id4'], $method->invoke($this->schema, 'table_with_pk_3'));
// Test with table without a primary key.
$this->schema->createTable('table_without_pk_1', [
'description' => 'Table without primary key.',
'fields' => [
'id' => [
'type' => 'int',
'not null' => TRUE,
],
'test_field' => [
'type' => 'int',
'not null' => TRUE,
],
],
]);
$this->assertSame([], $method->invoke($this->schema, 'table_without_pk_1'));
// Test with table with an empty primary key.
$this->schema->createTable('table_without_pk_2', [
'description' => 'Table without primary key.',
'fields' => [
'id' => [
'type' => 'int',
'not null' => TRUE,
],
'test_field' => [
'type' => 'int',
'not null' => TRUE,
],
],
'primary key' => [],
]);
$this->assertSame([], $method->invoke($this->schema, 'table_without_pk_2'));
// Test with non existing table.
$this->assertFalse($method->invoke($this->schema, 'non_existing_table'));
}
/**
@ -774,7 +1138,6 @@ class SchemaTest extends KernelTestBase {
public function testFindTables() {
// We will be testing with three tables, two of them using the default
// prefix and the third one with an individually specified prefix.
// Set up a new connection with different connection info.
$connection_info = Database::getConnectionInfo();
@ -782,8 +1145,8 @@ class SchemaTest extends KernelTestBase {
$new_connection_info = $connection_info['default'];
$new_connection_info['prefix']['test_2_table'] = $new_connection_info['prefix']['default'] . '_shared_';
Database::addConnectionInfo('test', 'default', $new_connection_info);
Database::setActiveConnection('test');
$test_schema = Database::getConnection()->schema();
// Create the tables.
$table_specification = [
@ -795,12 +1158,12 @@ class SchemaTest extends KernelTestBase {
],
],
];
Database::getConnection()->schema()->createTable('test_1_table', $table_specification);
Database::getConnection()->schema()->createTable('test_2_table', $table_specification);
Database::getConnection()->schema()->createTable('the_third_table', $table_specification);
$test_schema->createTable('test_1_table', $table_specification);
$test_schema->createTable('test_2_table', $table_specification);
$test_schema->createTable('the_third_table', $table_specification);
// Check the "all tables" syntax.
$tables = Database::getConnection()->schema()->findTables('%');
$tables = $test_schema->findTables('%');
sort($tables);
$expected = [
// The 'config' table is added by
@ -814,7 +1177,7 @@ class SchemaTest extends KernelTestBase {
$this->assertEqual($tables, $expected, 'All tables were found.');
// Check the restrictive syntax.
$tables = Database::getConnection()->schema()->findTables('test_%');
$tables = $test_schema->findTables('test_%');
sort($tables);
$expected = [
'test_1_table',
@ -826,46 +1189,4 @@ class SchemaTest extends KernelTestBase {
Database::setActiveConnection('default');
}
/**
* Tests the primary keys of a table.
*
* @param string $table_name
* The name of the table to check.
* @param array $primary_key
* The expected key column specifier for a table's primary key.
*/
protected function assertPrimaryKeyColumns($table_name, array $primary_key = []) {
$db_type = Database::getConnection()->databaseType();
switch ($db_type) {
case 'mysql':
$result = Database::getConnection()->query("SHOW KEYS FROM {" . $table_name . "} WHERE Key_name = 'PRIMARY'")->fetchAllAssoc('Column_name');
$this->assertSame($primary_key, array_keys($result));
break;
case 'pgsql':
$result = Database::getConnection()->query("SELECT a.attname, format_type(a.atttypid, a.atttypmod) AS data_type
FROM pg_index i
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
WHERE i.indrelid = '{" . $table_name . "}'::regclass AND i.indisprimary")
->fetchAllAssoc('attname');
$this->assertSame($primary_key, array_keys($result));
break;
case 'sqlite':
// For SQLite we need access to the protected
// \Drupal\Core\Database\Driver\sqlite\Schema::introspectSchema() method
// because we have no other way of getting the table prefixes needed for
// running a straight PRAGMA query.
$schema_object = Database::getConnection()->schema();
$reflection = new \ReflectionMethod($schema_object, 'introspectSchema');
$reflection->setAccessible(TRUE);
$table_info = $reflection->invoke($schema_object, $table_name);
$this->assertSame($primary_key, $table_info['primary key']);
break;
}
}
}

View file

@ -22,6 +22,10 @@ class SelectCloneTest extends DatabaseTestBase {
$query->condition('id', $subquery, 'IN');
$clone = clone $query;
// Cloned query should have a different unique identifier.
$this->assertNotEquals($query->uniqueIdentifier(), $clone->uniqueIdentifier());
// Cloned query should not be altered by the following modification
// happening on original query.
$subquery->condition('age', 25, '>');
@ -34,4 +38,31 @@ class SelectCloneTest extends DatabaseTestBase {
$this->assertEqual(2, $query_result, 'The query returns the expected number of rows');
}
/**
* Tests that nested SELECT queries are cloned properly.
*/
public function testNestedQueryCloning() {
$sub_query = $this->connection->select('test', 't');
$sub_query->addField('t', 'id', 'id');
$sub_query->condition('age', 28, '<');
$query = $this->connection->select($sub_query, 't');
$clone = clone $query;
// Cloned query should have a different unique identifier.
$this->assertNotEquals($query->uniqueIdentifier(), $clone->uniqueIdentifier());
// Cloned query should not be altered by the following modification
// happening on original query.
$sub_query->condition('age', 25, '>');
$clone_result = $clone->countQuery()->execute()->fetchField();
$query_result = $query->countQuery()->execute()->fetchField();
// Make sure the cloned query has not been modified.
$this->assertEquals(3, $clone_result, 'The cloned query returns the expected number of rows');
$this->assertEquals(2, $query_result, 'The query returns the expected number of rows');
}
}

View file

@ -3,6 +3,7 @@
namespace Drupal\KernelTests\Core\Database;
use Drupal\Core\Database\Database;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Database\RowCountException;
use Drupal\user\Entity\User;
@ -244,7 +245,6 @@ class SelectComplexTest extends DatabaseTestBase {
$this->assertEqual($count, 4, 'Counted the correct number of records.');
}
/**
* Tests that countQuery properly removes fields and expressions.
*/
@ -312,7 +312,7 @@ class SelectComplexTest extends DatabaseTestBase {
$query = db_select('test');
$query->addField('test', 'job');
$query->condition('name', 'Paul');
$query->condition(db_or()->condition('age', 26)->condition('age', 27));
$query->condition((new Condition('OR'))->condition('age', 26)->condition('age', 27));
$job = $query->execute()->fetchField();
$this->assertEqual($job, 'Songwriter', 'Correct data retrieved.');
@ -395,7 +395,7 @@ class SelectComplexTest extends DatabaseTestBase {
public function testJoinConditionObject() {
// Same test as testDefaultJoin, but with a Condition object.
$query = db_select('test_task', 't');
$join_cond = db_and()->where('t.pid = p.id');
$join_cond = (new Condition('AND'))->where('t.pid = p.id');
$people_alias = $query->join('test', 'p', $join_cond);
$name_field = $query->addField($people_alias, 'name', 'name');
$query->addField('t', 'task', 'task');
@ -418,7 +418,7 @@ class SelectComplexTest extends DatabaseTestBase {
// Test a condition object that creates placeholders.
$t1_name = 'John';
$t2_name = 'George';
$join_cond = db_and()
$join_cond = (new Condition('AND'))
->condition('t1.name', $t1_name)
->condition('t2.name', $t2_name);
$query = db_select('test', 't1');

View file

@ -1,6 +1,7 @@
<?php
namespace Drupal\KernelTests\Core\Database;
use Drupal\Core\Database\InvalidQueryException;
use Drupal\Core\Database\Database;
@ -53,7 +54,7 @@ class SelectTest extends DatabaseTestBase {
$records = $result->fetchAll();
$query = (string) $query;
$expected = "/* Testing query comments * / SELECT nid FROM {node}. -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
$expected = "/* Testing query comments * / SELECT nid FROM {node}. -- */ SELECT test.name AS name, test.age AS age\nFROM\n{test} test";
$this->assertEqual(count($records), 4, 'Returned the correct number of rows.');
$this->assertNotIdentical(FALSE, strpos($query, $expected), 'The flattened query contains the sanitised comment string.');
@ -466,7 +467,6 @@ class SelectTest extends DatabaseTestBase {
],
];
$database = $this->container->get('database');
foreach ($test_groups as $test_group) {
$query = $database->select('test', 't');
@ -496,8 +496,7 @@ class SelectTest extends DatabaseTestBase {
];
$test_groups[] = [
'regex' => '#Singer',
'expected' => [
],
'expected' => [],
];
foreach ($test_groups as $test_group) {

View file

@ -8,6 +8,7 @@ namespace Drupal\KernelTests\Core\Database;
* @group Database
*/
class SerializeQueryTest extends DatabaseTestBase {
/**
* Confirms that a query can be serialized and unserialized.
*/

View file

@ -2,6 +2,8 @@
namespace Drupal\KernelTests\Core\Database;
use Drupal\Core\Database\Query\Condition;
/**
* Tests the Update query builder, complex queries.
*
@ -15,7 +17,7 @@ class UpdateComplexTest extends DatabaseTestBase {
public function testOrConditionUpdate() {
$update = db_update('test')
->fields(['job' => 'Musician'])
->condition(db_or()
->condition((new Condition('OR'))
->condition('name', 'John')
->condition('name', 'Paul')
);

View file

@ -53,4 +53,40 @@ class UpsertTest extends DatabaseTestBase {
$this->assertEqual($person->name, 'Meredith', 'Name was not changed.');
}
/**
* Tests that we can upsert records with a special named column.
*/
public function testSpecialColumnUpsert() {
$num_records_before = $this->connection->query('SELECT COUNT(*) FROM {test_special_columns}')->fetchField();
$upsert = $this->connection->upsert('test_special_columns')
->key('id')
->fields(['id', 'offset', 'function']);
// Add a new row.
$upsert->values([
'id' => 2,
'offset' => 'Offset 2',
'function' => 'Function 2',
]);
// Update an existing row.
$upsert->values([
'id' => 1,
'offset' => 'Offset 1 updated',
'function' => 'Function 1 updated',
]);
$upsert->execute();
$num_records_after = $this->connection->query('SELECT COUNT(*) FROM {test_special_columns}')->fetchField();
$this->assertEquals($num_records_before + 1, $num_records_after, 'Rows were inserted and updated properly.');
$record = $this->connection->query('SELECT * FROM {test_special_columns} WHERE id = :id', [':id' => 1])->fetch();
$this->assertEquals($record->offset, 'Offset 1 updated');
$this->assertEquals($record->function, 'Function 1 updated');
$record = $this->connection->query('SELECT * FROM {test_special_columns} WHERE id = :id', [':id' => 2])->fetch();
$this->assertEquals($record->offset, 'Offset 2');
$this->assertEquals($record->function, 'Function 2');
}
}

View file

@ -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);
}
}

View file

@ -137,7 +137,7 @@ class DrupalKernelTest extends KernelTestBase {
// Check that the container itself is not among the persist IDs because it
// does not make sense to persist the container itself.
$persist_ids = $container->getParameter('persist_ids');
$this->assertIdentical(FALSE, array_search('service_container', $persist_ids));
$this->assertSame(FALSE, array_search('service_container', $persist_ids));
}
/**
@ -184,6 +184,11 @@ class DrupalKernelTest extends KernelTestBase {
$pass = TRUE;
}
$this->assertTrue($pass, 'Throws LogicException if DrupalKernel::setSitePath() is called after boot');
// Ensure no LogicException if DrupalKernel::setSitePath() is called with
// identical path after boot.
$path = $kernel->getSitePath();
$kernel->setSitePath($path);
}
}

View file

@ -0,0 +1,176 @@
<?php
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Core\Config\Schema\Mapping;
use Drupal\Core\Entity\Plugin\DataType\ConfigEntityAdapter;
use Drupal\Core\TypedData\Plugin\DataType\BooleanData;
use Drupal\Core\TypedData\Plugin\DataType\IntegerData;
use Drupal\Core\TypedData\Plugin\DataType\StringData;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests entity adapter for configuration entities.
*
* @see \Drupal\Core\Entity\Plugin\DataType\ConfigEntityAdapter
*
* @group Entity
*
* @coversDefaultClass \Drupal\Core\Entity\Plugin\DataType\ConfigEntityAdapter
*/
class ConfigEntityAdapterTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['config_test'];
/**
* The config entity.
*
* @var \Drupal\config_test\Entity\ConfigTest
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(static::$modules);
// ConfigTest::create doesn't work with the following exception:
// "Multiple entity types found for Drupal\config_test\Entity\ConfigTest."
$this->entity = \Drupal::entityTypeManager()->getStorage('config_test')->create([
'id' => 'system',
'label' => 'foobar',
'weight' => 1,
]);
}
/**
* @covers \Drupal\Core\Entity\Plugin\DataType\Deriver\EntityDeriver::getDerivativeDefinitions
*/
public function testEntityDeriver() {
$definition = \Drupal::typedDataManager()->getDefinition('entity:config_test');
$this->assertEquals(ConfigEntityAdapter::class, $definition['class']);
}
/**
* @covers ::validate
*/
public function testValidate() {
$adapter = ConfigEntityAdapter::createFromEntity($this->entity);
$violations = $adapter->validate();
$this->assertEmpty($violations);
$this->entity = \Drupal::entityTypeManager()->getStorage('config_test')->create([
'id' => 'system',
'label' => 'foobar',
// Set weight to be a string which should not validate.
'weight' => 'very heavy',
]);
$adapter = ConfigEntityAdapter::createFromEntity($this->entity);
$violations = $adapter->validate();
$this->assertCount(1, $violations);
$violation = $violations->get(0);
$this->assertEquals('This value should be of the correct primitive type.', $violation->getMessage());
$this->assertEquals('weight', $violation->getPropertyPath());
}
/**
* @covers ::getProperties
*/
public function testGetProperties() {
$expected_properties = [
'uuid' => StringData::class,
'langcode' => StringData::class,
'status' => BooleanData::class,
'dependencies' => Mapping::class,
'id' => StringData::class,
'label' => StringData::class,
'weight' => IntegerData::class,
'style' => StringData::class,
'size' => StringData::class,
'size_value' => StringData::class,
'protected_property' => StringData::class,
];
$properties = ConfigEntityAdapter::createFromEntity($this->entity)->getProperties();
$keys = [];
foreach ($properties as $key => $property) {
$keys[] = $key;
$this->assertInstanceOf($expected_properties[$key], $property);
}
$this->assertSame(array_keys($expected_properties), $keys);
}
/**
* @covers ::getValue
*/
public function testGetValue() {
$adapter = ConfigEntityAdapter::createFromEntity($this->entity);
$this->assertEquals($this->entity->weight, $adapter->get('weight')->getValue());
$this->assertEquals($this->entity->id(), $adapter->get('id')->getValue());
$this->assertEquals($this->entity->label, $adapter->get('label')->getValue());
}
/**
* @covers ::set
*/
public function testSet() {
$adapter = ConfigEntityAdapter::createFromEntity($this->entity);
// Get the value via typed data to ensure that the typed representation is
// updated correctly when the value is set.
$this->assertEquals(1, $adapter->get('weight')->getValue());
$return = $adapter->set('weight', 2);
$this->assertSame($adapter, $return);
$this->assertEquals(2, $this->entity->weight);
// Ensure the typed data is updated via the set too.
$this->assertEquals(2, $adapter->get('weight')->getValue());
}
/**
* @covers ::getString
*/
public function testGetString() {
$adapter = ConfigEntityAdapter::createFromEntity($this->entity);
$this->assertEquals('foobar', $adapter->getString());
}
/**
* @covers ::applyDefaultValue
*/
public function testApplyDefaultValue() {
$this->setExpectedException(\BadMethodCallException::class, 'Method not supported');
$adapter = ConfigEntityAdapter::createFromEntity($this->entity);
$adapter->applyDefaultValue();
}
/**
* @covers ::getIterator
*/
public function testGetIterator() {
$adapter = ConfigEntityAdapter::createFromEntity($this->entity);
$iterator = $adapter->getIterator();
$fields = iterator_to_array($iterator);
$expected_fields = [
'uuid',
'langcode',
'status',
'dependencies',
'id',
'label',
'weight',
'style',
'size',
'size_value',
'protected_property',
];
$this->assertEquals($expected_fields, array_keys($fields));
$this->assertEquals($this->entity->id(), $fields['id']->getValue());
$adapter->setValue(NULL);
$this->assertEquals(new \ArrayIterator([]), $adapter->getIterator());
}
}

View file

@ -31,10 +31,17 @@ class ConfigEntityQueryTest extends KernelTestBase {
/**
* The query factory used to construct all queries in the test.
*
* @var \Drupal\Core\Entity\Query\QueryFactory
* @var \Drupal\Core\Config\Entity\Query\QueryFactory
*/
protected $factory;
/**
* The entity storage used for testing.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $entityStorage;
/**
* Stores all config entities created for the test.
*
@ -46,7 +53,7 @@ class ConfigEntityQueryTest extends KernelTestBase {
parent::setUp();
$this->entities = [];
$this->factory = $this->container->get('entity.query');
$this->entityStorage = $this->container->get('entity_type.manager')->getStorage('config_query_test');
// These two are here to make sure that matchArray needs to go over several
// non-matches on every levels.
@ -114,82 +121,82 @@ class ConfigEntityQueryTest extends KernelTestBase {
*/
public function testConfigEntityQuery() {
// Run a test without any condition.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->execute();
$this->assertResults(['1', '2', '3', '4', '5']);
// No conditions, OR.
$this->queryResults = $this->factory->get('config_query_test', 'OR')
$this->queryResults = $this->entityStorage->getQuery('OR')
->execute();
$this->assertResults(['1', '2', '3', '4', '5']);
// Filter by ID with equality.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', '3')
->execute();
$this->assertResults(['3']);
// Filter by label with a known prefix.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('label', 'test_prefix', 'STARTS_WITH')
->execute();
$this->assertResults(['3']);
// Filter by label with a known suffix.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('label', 'test_suffix', 'ENDS_WITH')
->execute();
$this->assertResults(['4']);
// Filter by label with a known containing word.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('label', 'test_contains', 'CONTAINS')
->execute();
$this->assertResults(['5']);
// Filter by ID with the IN operator.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', ['2', '3'], 'IN')
->execute();
$this->assertResults(['2', '3']);
// Filter by ID with the implicit IN operator.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', ['2', '3'])
->execute();
$this->assertResults(['2', '3']);
// Filter by ID with the > operator.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', '3', '>')
->execute();
$this->assertResults(['4', '5']);
// Filter by ID with the >= operator.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', '3', '>=')
->execute();
$this->assertResults(['3', '4', '5']);
// Filter by ID with the <> operator.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', '3', '<>')
->execute();
$this->assertResults(['1', '2', '4', '5']);
// Filter by ID with the < operator.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', '3', '<')
->execute();
$this->assertResults(['1', '2']);
// Filter by ID with the <= operator.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', '3', '<=')
->execute();
$this->assertResults(['1', '2', '3']);
// Filter by two conditions on the same field.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('label', 'test_pref', 'STARTS_WITH')
->condition('label', 'test_prefix', 'STARTS_WITH')
->execute();
@ -197,7 +204,7 @@ class ConfigEntityQueryTest extends KernelTestBase {
// Filter by two conditions on different fields. The first query matches for
// a different ID, so the result is empty.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('label', 'test_prefix', 'STARTS_WITH')
->condition('id', '5')
->execute();
@ -205,7 +212,7 @@ class ConfigEntityQueryTest extends KernelTestBase {
// Filter by two different conditions on different fields. This time the
// first condition matches on one item, but the second one does as well.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('label', 'test_prefix', 'STARTS_WITH')
->condition('id', '3')
->execute();
@ -214,7 +221,7 @@ class ConfigEntityQueryTest extends KernelTestBase {
// Filter by two different conditions, of which the first one matches for
// every entry, the second one as well, but just the third one filters so
// that just two are left.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', '1', '>=')
->condition('number', 10, '>=')
->condition('number', 50, '>=')
@ -222,30 +229,30 @@ class ConfigEntityQueryTest extends KernelTestBase {
$this->assertResults(['3', '5']);
// Filter with an OR condition group.
$this->queryResults = $this->factory->get('config_query_test', 'OR')
$this->queryResults = $this->entityStorage->getQuery('OR')
->condition('id', 1)
->condition('id', '2')
->execute();
$this->assertResults(['1', '2']);
// Simplify it with IN.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', ['1', '2'])
->execute();
$this->assertResults(['1', '2']);
// Try explicit IN.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', ['1', '2'], 'IN')
->execute();
$this->assertResults(['1', '2']);
// Try not IN.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', ['1', '2'], 'NOT IN')
->execute();
$this->assertResults(['3', '4', '5']);
// Filter with an OR condition group on different fields.
$this->queryResults = $this->factory->get('config_query_test', 'OR')
$this->queryResults = $this->entityStorage->getQuery('OR')
->condition('id', 1)
->condition('number', 41)
->execute();
@ -253,14 +260,14 @@ class ConfigEntityQueryTest extends KernelTestBase {
// Filter with an OR condition group on different fields but matching on the
// same entity.
$this->queryResults = $this->factory->get('config_query_test', 'OR')
$this->queryResults = $this->entityStorage->getQuery('OR')
->condition('id', 1)
->condition('number', 31)
->execute();
$this->assertResults(['1']);
// NO simple conditions, YES complex conditions, 'AND'.
$query = $this->factory->get('config_query_test', 'AND');
$query = $this->entityStorage->getQuery('AND');
$and_condition_1 = $query->orConditionGroup()
->condition('id', '2')
->condition('label', $this->entities[0]->label);
@ -274,7 +281,7 @@ class ConfigEntityQueryTest extends KernelTestBase {
$this->assertResults(['1']);
// NO simple conditions, YES complex conditions, 'OR'.
$query = $this->factory->get('config_query_test', 'OR');
$query = $this->entityStorage->getQuery('OR');
$and_condition_1 = $query->andConditionGroup()
->condition('id', 1)
->condition('label', $this->entities[0]->label);
@ -288,7 +295,7 @@ class ConfigEntityQueryTest extends KernelTestBase {
$this->assertResults(['1', '2']);
// YES simple conditions, YES complex conditions, 'AND'.
$query = $this->factory->get('config_query_test', 'AND');
$query = $this->entityStorage->getQuery('AND');
$and_condition_1 = $query->orConditionGroup()
->condition('id', '2')
->condition('label', $this->entities[0]->label);
@ -303,7 +310,7 @@ class ConfigEntityQueryTest extends KernelTestBase {
$this->assertResults(['1']);
// YES simple conditions, YES complex conditions, 'OR'.
$query = $this->factory->get('config_query_test', 'OR');
$query = $this->entityStorage->getQuery('OR');
$and_condition_1 = $query->orConditionGroup()
->condition('id', '2')
->condition('label', $this->entities[0]->label);
@ -318,22 +325,22 @@ class ConfigEntityQueryTest extends KernelTestBase {
$this->assertResults(['1', '2', '4', '5']);
// Test the exists and notExists conditions.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->exists('id')
->execute();
$this->assertResults(['1', '2', '3', '4', '5']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->exists('non-existent')
->execute();
$this->assertResults([]);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->notExists('id')
->execute();
$this->assertResults([]);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->notExists('non-existent')
->execute();
$this->assertResults(['1', '2', '3', '4', '5']);
@ -353,43 +360,43 @@ class ConfigEntityQueryTest extends KernelTestBase {
$entity->save();
// Test 'STARTS_WITH' condition.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', 'foo.bar', 'STARTS_WITH')
->execute();
$this->assertResults(['foo.bar']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', 'f', 'STARTS_WITH')
->execute();
$this->assertResults(['foo.bar']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', 'miss', 'STARTS_WITH')
->execute();
$this->assertResults([]);
// Test 'CONTAINS' condition.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', 'foo.bar', 'CONTAINS')
->execute();
$this->assertResults(['foo.bar']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', 'oo.ba', 'CONTAINS')
->execute();
$this->assertResults(['foo.bar']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', 'miss', 'CONTAINS')
->execute();
$this->assertResults([]);
// Test 'ENDS_WITH' condition.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', 'foo.bar', 'ENDS_WITH')
->execute();
$this->assertResults(['foo.bar']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', 'r', 'ENDS_WITH')
->execute();
$this->assertResults(['foo.bar']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', 'miss', 'ENDS_WITH')
->execute();
$this->assertResults([]);
@ -400,13 +407,13 @@ class ConfigEntityQueryTest extends KernelTestBase {
*/
public function testCount() {
// Test count on no conditions.
$count = $this->factory->get('config_query_test')
$count = $this->entityStorage->getQuery()
->count()
->execute();
$this->assertIdentical($count, count($this->entities));
// Test count on a complex query.
$query = $this->factory->get('config_query_test', 'OR');
$query = $this->entityStorage->getQuery('OR');
$and_condition_1 = $query->andConditionGroup()
->condition('id', 1)
->condition('label', $this->entities[0]->label);
@ -426,51 +433,51 @@ class ConfigEntityQueryTest extends KernelTestBase {
*/
public function testSortRange() {
// Sort by simple ascending/descending.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->sort('number', 'DESC')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['3', '5', '2', '1', '4']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->sort('number', 'ASC')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['4', '1', '2', '5', '3']);
// Apply some filters and sort.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', '3', '>')
->sort('number', 'DESC')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['5', '4']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('id', '3', '>')
->sort('number', 'ASC')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['4', '5']);
// Apply a pager and sort.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->sort('number', 'DESC')
->range('2', '2')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['2', '1']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->sort('number', 'ASC')
->range('2', '2')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['2', '5']);
// Add a range to a query without a start parameter.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->range(0, '3')
->sort('id', 'ASC')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['1', '2', '3']);
// Apply a pager with limit 4.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->pager('4', 0)
->sort('id', 'ASC')
->execute();
@ -488,28 +495,28 @@ class ConfigEntityQueryTest extends KernelTestBase {
// Sort key: id
// Sorting with 'DESC' upper case
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->tableSort($header)
->sort('id', 'DESC')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['5', '4', '3', '2', '1']);
// Sorting with 'ASC' upper case
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->tableSort($header)
->sort('id', 'ASC')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['1', '2', '3', '4', '5']);
// Sorting with 'desc' lower case
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->tableSort($header)
->sort('id', 'desc')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['5', '4', '3', '2', '1']);
// Sorting with 'asc' lower case
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->tableSort($header)
->sort('id', 'asc')
->execute();
@ -517,28 +524,28 @@ class ConfigEntityQueryTest extends KernelTestBase {
// Sort key: number
// Sorting with 'DeSc' mixed upper and lower case
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->tableSort($header)
->sort('number', 'DeSc')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['3', '5', '2', '1', '4']);
// Sorting with 'AsC' mixed upper and lower case
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->tableSort($header)
->sort('number', 'AsC')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['4', '1', '2', '5', '3']);
// Sorting with 'dEsC' mixed upper and lower case
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->tableSort($header)
->sort('number', 'dEsC')
->execute();
$this->assertIdentical(array_values($this->queryResults), ['3', '5', '2', '1', '4']);
// Sorting with 'aSc' mixed upper and lower case
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->tableSort($header)
->sort('number', 'aSc')
->execute();
@ -549,29 +556,56 @@ class ConfigEntityQueryTest extends KernelTestBase {
* Tests dotted path matching.
*/
public function testDotted() {
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('array.level1.*', 1)
->execute();
$this->assertResults(['1', '3']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('*.level1.level2', 2)
->execute();
$this->assertResults(['2', '4']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('array.level1.*', 3)
->execute();
$this->assertResults(['5']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('array.level1.level2', 3)
->execute();
$this->assertResults(['5']);
// Make sure that values on the wildcard level do not match if there are
// sub-keys defined. This must not find anything even if entity 2 has a
// top-level key number with value 41.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('*.level1.level2', 41)
->execute();
$this->assertResults([]);
// Make sure that "IS NULL" and "IS NOT NULL" work correctly with
// array-valued fields/keys.
$all = ['1', '2', '3', '4', '5'];
$this->queryResults = $this->entityStorage->getQuery()
->exists('array.level1.level2')
->execute();
$this->assertResults($all);
$this->queryResults = $this->entityStorage->getQuery()
->exists('array.level1')
->execute();
$this->assertResults($all);
$this->queryResults = $this->entityStorage->getQuery()
->exists('array')
->execute();
$this->assertResults($all);
$this->queryResults = $this->entityStorage->getQuery()
->notExists('array.level1.level2')
->execute();
$this->assertResults([]);
$this->queryResults = $this->entityStorage->getQuery()
->notExists('array.level1')
->execute();
$this->assertResults([]);
$this->queryResults = $this->entityStorage->getQuery()
->notExists('array')
->execute();
$this->assertResults([]);
}
/**
@ -579,12 +613,12 @@ class ConfigEntityQueryTest extends KernelTestBase {
*/
public function testCaseSensitivity() {
// Filter by label with a known containing case-sensitive word.
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('label', 'TEST', 'CONTAINS')
->execute();
$this->assertResults(['3', '4', '5']);
$this->queryResults = $this->factory->get('config_query_test')
$this->queryResults = $this->entityStorage->getQuery()
->condition('label', 'test', 'CONTAINS')
->execute();
$this->assertResults(['3', '4', '5']);
@ -599,7 +633,8 @@ class ConfigEntityQueryTest extends KernelTestBase {
$key_value = $this->container->get('keyvalue')->get(QueryFactory::CONFIG_LOOKUP_PREFIX . 'config_test');
$test_entities = [];
$entity = entity_create('config_test', [
$storage = \Drupal::entityTypeManager()->getStorage('config_test');
$entity = $storage->create([
'label' => $this->randomMachineName(),
'id' => '1',
'style' => 'test',
@ -608,11 +643,10 @@ class ConfigEntityQueryTest extends KernelTestBase {
$entity->enforceIsNew();
$entity->save();
$expected[] = $entity->getConfigDependencyName();
$this->assertEqual($expected, $key_value->get('style:test'));
$entity = entity_create('config_test', [
$entity = $storage->create([
'label' => $this->randomMachineName(),
'id' => '2',
'style' => 'test',
@ -623,7 +657,7 @@ class ConfigEntityQueryTest extends KernelTestBase {
$expected[] = $entity->getConfigDependencyName();
$this->assertEqual($expected, $key_value->get('style:test'));
$entity = entity_create('config_test', [
$entity = $storage->create([
'label' => $this->randomMachineName(),
'id' => '3',
'style' => 'blah',

View file

@ -74,7 +74,7 @@ class ContentEntityChangedTest extends EntityKernelTestBase {
// We can't assert equality here because the created time is set to the
// request time, while instances of ChangedTestItem use the current
// timestamp every time. Therefor we check if the changed timestamp is
// timestamp every time. Therefore we check if the changed timestamp is
// between the created time and now.
$this->assertTrue(
($entity->getChangedTime() >= $entity->get('created')->value) &&

View file

@ -131,7 +131,6 @@ class ContentEntityCloneTest extends EntityKernelTestBase {
}
$this->assertTrue($different_references, 'The entity object and the cloned entity object reference different field item list objects.');
// Reload the entity, initialize one translation, clone it and check that
// both entity objects reference different field instances.
$entity = $this->reloadEntity($entity);
@ -298,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', '_entityStorages'];
foreach ($properties as $property) {
// Modify each entity property on the clone and assert that the change is
@ -307,9 +306,17 @@ class ContentEntityCloneTest extends EntityKernelTestBase {
$property->setValue($entity, 'default-value');
$property->setValue($translation, 'default-value');
$property->setValue($clone, 'test-entity-cloning');
$this->assertEquals('default-value', $property->getValue($entity), (string) new FormattableMarkup('Entity property %property_name is not cloned properly.', ['%property_name' => $property->getName()]));
$this->assertEquals('default-value', $property->getValue($translation), (string) new FormattableMarkup('Entity property %property_name is not cloned properly.', ['%property_name' => $property->getName()]));
$this->assertEquals('test-entity-cloning', $property->getValue($clone), (string) new FormattableMarkup('Entity property %property_name is not cloned properly.', ['%property_name' => $property->getName()]));
// Static properties remain the same across all instances of the class.
if ($property->isStatic()) {
$this->assertEquals('test-entity-cloning', $property->getValue($entity), (string) new FormattableMarkup('Entity property %property_name is not cloned properly.', ['%property_name' => $property->getName()]));
$this->assertEquals('test-entity-cloning', $property->getValue($translation), (string) new FormattableMarkup('Entity property %property_name is not cloned properly.', ['%property_name' => $property->getName()]));
$this->assertEquals('test-entity-cloning', $property->getValue($clone), (string) new FormattableMarkup('Entity property %property_name is not cloned properly.', ['%property_name' => $property->getName()]));
}
else {
$this->assertEquals('default-value', $property->getValue($entity), (string) new FormattableMarkup('Entity property %property_name is not cloned properly.', ['%property_name' => $property->getName()]));
$this->assertEquals('default-value', $property->getValue($translation), (string) new FormattableMarkup('Entity property %property_name is not cloned properly.', ['%property_name' => $property->getName()]));
$this->assertEquals('test-entity-cloning', $property->getValue($clone), (string) new FormattableMarkup('Entity property %property_name is not cloned properly.', ['%property_name' => $property->getName()]));
}
// Modify each entity property on the translation entity object and assert
// that the change is propagated to the default translation entity object

View file

@ -30,10 +30,10 @@ class ContentEntityNullStorageTest extends KernelTestBase {
* @see \Drupal\Core\Entity\Query\Null\Query
*/
public function testEntityQuery() {
$this->assertIdentical(0, \Drupal::entityQuery('contact_message')->count()->execute(), 'Counting a null storage returns 0.');
$this->assertIdentical([], \Drupal::entityQuery('contact_message')->execute(), 'Querying a null storage returns an empty array.');
$this->assertIdentical([], \Drupal::entityQuery('contact_message')->condition('contact_form', 'test')->execute(), 'Querying a null storage returns an empty array and conditions are ignored.');
$this->assertIdentical([], \Drupal::entityQueryAggregate('contact_message')->aggregate('name', 'AVG')->execute(), 'Aggregate querying a null storage returns an empty array');
$this->assertSame(0, \Drupal::entityQuery('contact_message')->count()->execute(), 'Counting a null storage returns 0.');
$this->assertSame([], \Drupal::entityQuery('contact_message')->execute(), 'Querying a null storage returns an empty array.');
$this->assertSame([], \Drupal::entityQuery('contact_message')->condition('contact_form', 'test')->execute(), 'Querying a null storage returns an empty array and conditions are ignored.');
$this->assertSame([], \Drupal::entityQueryAggregate('contact_message')->aggregate('name', 'AVG')->execute(), 'Aggregate querying a null storage returns an empty array');
}
@ -43,6 +43,7 @@ class ContentEntityNullStorageTest extends KernelTestBase {
* @see \Drupal\Core\Entity\Event\BundleConfigImportValidate
*/
public function testDeleteThroughImport() {
$this->installConfig(['system']);
$contact_form = ContactForm::create(['id' => 'test']);
$contact_form->save();

View file

@ -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());
}
}
}
}
}
}

View file

@ -19,7 +19,7 @@ class DefaultTableMappingIntegrationTest extends EntityKernelTestBase {
/**
* The table mapping for the tested entity type.
*
* @var \Drupal\Core\Entity\Sql\TableMappingInterface
* @var \Drupal\Core\Entity\Sql\DefaultTableMapping
*/
protected $tableMapping;
@ -39,11 +39,18 @@ class DefaultTableMappingIntegrationTest extends EntityKernelTestBase {
->setName('multivalued_base_field')
->setTargetEntityTypeId('entity_test_mulrev')
->setTargetBundle('entity_test_mulrev')
->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)
// Base fields are non-translatable and non-revisionable by default, but
// we explicitly set these values here for extra clarity.
->setTranslatable(FALSE)
->setRevisionable(FALSE);
$this->state->set('entity_test_mulrev.additional_base_field_definitions', $definitions);
$this->entityManager->clearCachedDefinitions();
$this->tableMapping = $this->entityManager->getStorage('entity_test_mulrev')->getTableMapping();
// Ensure that the tables for the new field are created.
\Drupal::entityDefinitionUpdateManager()->applyUpdates();
}
/**
@ -68,4 +75,33 @@ class DefaultTableMappingIntegrationTest extends EntityKernelTestBase {
$this->assertEquals($this->tableMapping->getFieldTableName('multivalued_base_field'), $expected);
}
/**
* Tests DefaultTableMapping::getTableNames().
*
* @covers ::getTableNames
*/
public function testGetTableNames() {
$storage_definitions = $this->entityManager->getFieldStorageDefinitions('entity_test_mulrev');
$dedicated_data_table = $this->tableMapping->getDedicatedDataTableName($storage_definitions['multivalued_base_field']);
$dedicated_revision_table = $this->tableMapping->getDedicatedRevisionTableName($storage_definitions['multivalued_base_field']);
// Check that both the data and the revision tables exist for a multi-valued
// base field.
$database_schema = \Drupal::database()->schema();
$this->assertTrue($database_schema->tableExists($dedicated_data_table));
$this->assertTrue($database_schema->tableExists($dedicated_revision_table));
// Check that the table mapping contains both the data and the revision
// tables exist for a multi-valued base field.
$expected = [
'entity_test_mulrev',
'entity_test_mulrev_property_data',
'entity_test_mulrev_revision',
'entity_test_mulrev_property_revision',
$dedicated_data_table,
$dedicated_revision_table,
];
$this->assertEquals($expected, $this->tableMapping->getTableNames());
}
}

View file

@ -65,7 +65,7 @@ class EntityAutocompleteElementFormTest extends EntityKernelTestBase implements
for ($i = 1; $i < 3; $i++) {
$entity = EntityTest::create([
'name' => $this->randomMachineName()
'name' => $this->randomMachineName(),
]);
$entity->save();
$this->referencedEntities[] = $entity;
@ -179,12 +179,12 @@ class EntityAutocompleteElementFormTest extends EntityKernelTestBase implements
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) { }
public function submitForm(array &$form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) { }
public function validateForm(array &$form, FormStateInterface $form_state) {}
/**
* Tests valid entries in the EntityAutocomplete Form API element.
@ -297,7 +297,7 @@ class EntityAutocompleteElementFormTest extends EntityKernelTestBase implements
$form_state = (new FormState())
->setValues([
'single_no_validate' => 'single - non-existent label',
'single_autocreate_no_validate' => 'single - autocreate non-existent label'
'single_autocreate_no_validate' => 'single - autocreate non-existent label',
]);
$form_builder->submitForm($this, $form_state);
@ -309,7 +309,7 @@ class EntityAutocompleteElementFormTest extends EntityKernelTestBase implements
$form_state = (new FormState())
->setValues([
'single_no_validate' => 'single - non-existent label (42)',
'single_autocreate_no_validate' => 'single - autocreate non-existent label (43)'
'single_autocreate_no_validate' => 'single - autocreate non-existent label (43)',
]);
$form_builder->submitForm($this, $form_state);
@ -353,7 +353,7 @@ class EntityAutocompleteElementFormTest extends EntityKernelTestBase implements
public function testEntityAutocompleteIdInput() {
/** @var \Drupal\Core\Form\FormBuilderInterface $form_builder */
$form_builder = $this->container->get('form_builder');
//$form = $form_builder->getForm($this);
// $form = $form_builder->getForm($this);
$form_state = (new FormState())
->setMethod('GET')
->setValues([

View file

@ -8,18 +8,33 @@ 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;
use Drupal\entity_test\Entity\EntityTestRev;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\user\Entity\User;
/**
* Tests the entity access control handler.
*
* @coversDefaultClass \Drupal\Core\Entity\EntityAccessControlHandler
* @group Entity
*/
class EntityAccessControlHandlerTest extends EntityLanguageTestBase {
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->installEntitySchema('entity_test_no_uuid');
$this->installEntitySchema('entity_test_rev');
$this->installEntitySchema('entity_test_string_id');
}
/**
* Asserts entity access correctly grants or denies access.
*/
@ -199,6 +214,64 @@ class EntityAccessControlHandlerTest extends EntityLanguageTestBase {
], $translation);
}
/**
* Ensures the static access cache works correctly in the absence of an UUID.
*
* @see entity_test_entity_access()
*/
public function testEntityWithoutUuidAccessCache() {
$account = $this->createUser();
$entity1 = EntityTestNoUuid::create([
'name' => 'Accessible',
]);
$entity1->save();
$entity2 = EntityTestNoUuid::create([
'name' => 'Inaccessible',
]);
$entity2->save();
$this->assertTrue($entity1->access('delete', $account), 'Entity 1 can be deleted.');
$this->assertFalse($entity2->access('delete', $account), 'Entity 2 CANNOT be deleted.');
$entity1
->setName('Inaccessible')
->setNewRevision();
$entity1->save();
$this->assertFalse($entity1->access('delete', $account), 'Entity 1 revision 2 CANNOT be deleted.');
}
/**
* Ensures the static access cache works correctly with a UUID and revisions.
*
* @see entity_test_entity_access()
*/
public function testEntityWithUuidAccessCache() {
$account = $this->createUser();
$entity1 = EntityTestRev::create([
'name' => 'Accessible',
]);
$entity1->save();
$entity2 = EntityTestRev::create([
'name' => 'Inaccessible',
]);
$entity2->save();
$this->assertTrue($entity1->access('delete', $account), 'Entity 1 can be deleted.');
$this->assertFalse($entity2->access('delete', $account), 'Entity 2 CANNOT be deleted.');
$entity1
->setName('Inaccessible')
->setNewRevision();
$entity1->save();
$this->assertFalse($entity1->access('delete', $account), 'Entity 1 revision 2 CANNOT be deleted.');
}
/**
* Tests hook invocations.
*/
@ -223,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,
],
];
}
}

View file

@ -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');
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\KernelTests\Core\Entity;
/**
* @coversDefaultClass \Drupal\Core\Entity\EntityBundleListener
*
* @group Entity
*/
class EntityBundleListenerTest extends EntityKernelTestBase {
/**
* @covers ::onBundleCreate
*
* Note: Installing the entity_schema_test module will mask the bug this test
* was written to cover, as the field map cache is cleared manually by
* \Drupal\Core\Field\FieldDefinitionListener::onFieldDefinitionCreate().
*/
public function testOnBundleCreate() {
$field_map = $this->container->get('entity_field.manager')->getFieldMap();
$expected = [
'entity_test' => 'entity_test',
];
$this->assertEquals($expected, $field_map['entity_test']['id']['bundles']);
entity_test_create_bundle('custom');
$field_map = $this->container->get('entity_field.manager')->getFieldMap();
$expected = [
'entity_test' => 'entity_test',
'custom' => 'custom',
];
$this->assertSame($expected, $field_map['entity_test']['id']['bundles']);
}
}

View file

@ -57,7 +57,7 @@ class EntityCrudHookTest extends EntityKernelTestBase {
/**
* Checks the order of CRUD hook execution messages.
*
* entity_crud_hook_test.module implements all core entity CRUD hooks and
* Module entity_crud_hook_test implements all core entity CRUD hooks and
* stores a message for each in $GLOBALS['entity_crud_hook_test'].
*
* @param $messages

View file

@ -0,0 +1,645 @@
<?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 untranslatable 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'));
}
/**
* Checks that the revision create hook works as expected.
*
* @covers ::createRevision
*/
public function testCreateRevisionHook() {
$entity = EntityTestMulRev::create();
$entity->get('name')->value = 'revision_create_test_en';
$this->storage->save($entity);
/** @var \Drupal\Core\Entity\ContentEntityInterface $translation */
$translation = $entity->addTranslation('it');
$translation->set('name', 'revision_create_test_it');
/** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
$revision = $this->storage->createRevision($translation, FALSE, TRUE);
// Assert that the alter hook can alter the new revision.
$this->assertEquals('revision_create_test_it_altered', $revision->get('name')->value);
// Assert the data passed to the hook.
$data = $this->state->get('entity_test.hooks');
$this->assertEquals('revision_create_test_it', $data['entity_test_mulrev_revision_create']['entity']->get('name')->value);
$this->assertEquals('revision_create_test_it_altered', $data['entity_test_mulrev_revision_create']['new_revision']->get('name')->value);
$this->assertFalse($data['entity_test_mulrev_revision_create']['entity']->isNewRevision());
$this->assertTrue($data['entity_test_mulrev_revision_create']['new_revision']->isNewRevision());
$this->assertTrue($data['entity_test_mulrev_revision_create']['entity']->isDefaultRevision());
$this->assertFalse($data['entity_test_mulrev_revision_create']['new_revision']->isDefaultRevision());
$this->assertTrue($data['entity_test_mulrev_revision_create']['keep_untranslatable_fields']);
$this->assertEquals('revision_create_test_it', $data['entity_revision_create']['entity']->get('name')->value);
$this->assertEquals('revision_create_test_it_altered', $data['entity_revision_create']['new_revision']->get('name')->value);
$this->assertFalse($data['entity_revision_create']['entity']->isNewRevision());
$this->assertTrue($data['entity_revision_create']['new_revision']->isNewRevision());
$this->assertTrue($data['entity_revision_create']['entity']->isDefaultRevision());
$this->assertFalse($data['entity_revision_create']['new_revision']->isDefaultRevision());
$this->assertTrue($data['entity_revision_create']['keep_untranslatable_fields']);
// Test again with different arguments.
$translation->isDefaultRevision(FALSE);
$this->storage->createRevision($translation);
$data = $this->state->get('entity_test.hooks');
$this->assertFalse($data['entity_revision_create']['entity']->isNewRevision());
$this->assertTrue($data['entity_revision_create']['new_revision']->isNewRevision());
$this->assertFalse($data['entity_revision_create']['entity']->isDefaultRevision());
$this->assertTrue($data['entity_revision_create']['new_revision']->isDefaultRevision());
$this->assertNull($data['entity_revision_create']['keep_untranslatable_fields']);
$this->assertFalse($data['entity_test_mulrev_revision_create']['entity']->isNewRevision());
$this->assertTrue($data['entity_test_mulrev_revision_create']['new_revision']->isNewRevision());
$this->assertFalse($data['entity_test_mulrev_revision_create']['entity']->isDefaultRevision());
$this->assertTrue($data['entity_test_mulrev_revision_create']['new_revision']->isDefaultRevision());
$this->assertNull($data['entity_test_mulrev_revision_create']['keep_untranslatable_fields']);
}
}

View file

@ -11,14 +11,17 @@ use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeEvents;
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldException;
use Drupal\Core\Field\FieldStorageDefinitionEvents;
use Drupal\Core\Language\LanguageInterface;
use Drupal\entity_test_update\Entity\EntityTestUpdate;
use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
use Drupal\Tests\system\Functional\Entity\Traits\EntityDefinitionTestTrait;
/**
* Tests EntityDefinitionUpdateManager functionality.
*
* @coversDefaultClass \Drupal\Core\Entity\EntityDefinitionUpdateManager
*
* @group Entity
*/
class EntityDefinitionUpdateTest extends EntityKernelTestBase {
@ -112,9 +115,10 @@ 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.');
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
// Run the update and ensure the revision table is created.
$this->entityDefinitionUpdateManager->applyUpdates();
@ -388,53 +392,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.');
}
/**
@ -776,6 +974,11 @@ class EntityDefinitionUpdateTest extends EntityKernelTestBase {
// of a NOT NULL constraint.
$this->makeBaseFieldEntityKey();
// Field storage CRUD operations use the last installed entity type
// definition so we need to update it before doing any other field storage
// updates.
$this->entityDefinitionUpdateManager->updateEntityType($this->state->get('entity_test_update.entity_type'));
// Try to apply the update and verify they fail since we have a NULL value.
$message = 'An error occurs when trying to enabling NOT NULL constraints with NULL data.';
try {
@ -817,4 +1020,180 @@ class EntityDefinitionUpdateTest extends EntityKernelTestBase {
$this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'Entity and field schema data are correctly detected.');
}
/**
* Tests adding a base field with initial values.
*/
public function testInitialValue() {
$storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
$db_schema = $this->database->schema();
// Create two entities before adding the base field.
/** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */
$storage->create()->save();
$storage->create()->save();
// Add a base field with an initial value.
$this->addBaseField();
$storage_definition = BaseFieldDefinition::create('string')
->setLabel(t('A new base field'))
->setInitialValue('test value');
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update.");
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
$this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
// Check that the initial values have been applied.
$storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
$entities = $storage->loadMultiple();
$this->assertEquals('test value', $entities[1]->get('new_base_field')->value);
$this->assertEquals('test value', $entities[2]->get('new_base_field')->value);
}
/**
* Tests adding a base field with initial values inherited from another field.
*
* @dataProvider initialValueFromFieldTestCases
*/
public function testInitialValueFromField($default_initial_value, $expected_value) {
$storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
$db_schema = $this->database->schema();
// Create two entities before adding the base field.
/** @var \Drupal\entity_test_update\Entity\EntityTestUpdate $entity */
$storage->create([
'name' => 'First entity',
'test_single_property' => 'test existing value',
])->save();
// The second entity does not have any value for the 'test_single_property'
// field, allowing us to test the 'default_value' parameter of
// \Drupal\Core\Field\BaseFieldDefinition::setInitialValueFromField().
$storage->create([
'name' => 'Second entity',
])->save();
// Add a base field with an initial value inherited from another field.
$definitions['new_base_field'] = BaseFieldDefinition::create('string')
->setName('new_base_field')
->setLabel('A new base field')
->setInitialValueFromField('name');
$definitions['another_base_field'] = BaseFieldDefinition::create('string')
->setName('another_base_field')
->setLabel('Another base field')
->setInitialValueFromField('test_single_property', $default_initial_value);
$this->state->set('entity_test_update.additional_base_field_definitions', $definitions);
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update.");
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'another_base_field'), "New field 'another_base_field' does not exist before applying the update.");
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $definitions['new_base_field']);
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('another_base_field', 'entity_test_update', 'entity_test', $definitions['another_base_field']);
$this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
$this->assertTrue($db_schema->fieldExists('entity_test_update', 'another_base_field'), "New field 'another_base_field' has been created on the 'entity_test_update' table.");
// Check that the initial values have been applied.
$storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
$entities = $storage->loadMultiple();
$this->assertEquals('First entity', $entities[1]->get('new_base_field')->value);
$this->assertEquals('Second entity', $entities[2]->get('new_base_field')->value);
$this->assertEquals('test existing value', $entities[1]->get('another_base_field')->value);
$this->assertEquals($expected_value, $entities[2]->get('another_base_field')->value);
}
/**
* Test cases for ::testInitialValueFromField.
*/
public function initialValueFromFieldTestCases() {
return [
'literal value' => [
'test initial value',
'test initial value',
],
'indexed array' => [
['value' => 'test initial value'],
'test initial value',
],
'empty array' => [
[],
NULL,
],
'null' => [
NULL,
NULL,
],
];
}
/**
* Tests the error handling when using initial values from another field.
*/
public function testInitialValueFromFieldErrorHandling() {
// Check that setting invalid values for 'initial value from field' doesn't
// work.
try {
$this->addBaseField();
$storage_definition = BaseFieldDefinition::create('string')
->setLabel(t('A new base field'))
->setInitialValueFromField('field_that_does_not_exist');
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
$this->fail('Using a non-existent field as initial value does not work.');
}
catch (FieldException $e) {
$this->assertEquals('Illegal initial value definition on new_base_field: The field field_that_does_not_exist does not exist.', $e->getMessage());
$this->pass('Using a non-existent field as initial value does not work.');
}
try {
$this->addBaseField();
$storage_definition = BaseFieldDefinition::create('integer')
->setLabel(t('A new base field'))
->setInitialValueFromField('name');
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
$this->fail('Using a field of a different type as initial value does not work.');
}
catch (FieldException $e) {
$this->assertEquals('Illegal initial value definition on new_base_field: The field types do not match.', $e->getMessage());
$this->pass('Using a field of a different type as initial value does not work.');
}
try {
// Add a base field that will not be stored in the shared tables.
$initial_field = BaseFieldDefinition::create('string')
->setName('initial_field')
->setLabel(t('An initial field'))
->setCardinality(2);
$this->state->set('entity_test_update.additional_base_field_definitions', ['initial_field' => $initial_field]);
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('initial_field', 'entity_test_update', 'entity_test', $initial_field);
// Now add the base field which will try to use the previously added field
// as the source of its initial values.
$new_base_field = BaseFieldDefinition::create('string')
->setName('new_base_field')
->setLabel(t('A new base field'))
->setInitialValueFromField('initial_field');
$this->state->set('entity_test_update.additional_base_field_definitions', ['initial_field' => $initial_field, 'new_base_field' => $new_base_field]);
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $new_base_field);
$this->fail('Using a field that is not stored in the shared tables as initial value does not work.');
}
catch (FieldException $e) {
$this->assertEquals('Illegal initial value definition on new_base_field: Both fields have to be stored in the shared entity tables.', $e->getMessage());
$this->pass('Using a field that is not stored in the shared tables as initial value does not work.');
}
}
/**
* @covers ::getEntityTypes
*/
public function testGetEntityTypes() {
$entity_type_definitions = $this->entityDefinitionUpdateManager->getEntityTypes();
// Ensure that we have at least one entity type to check below.
$this->assertGreaterThanOrEqual(1, count($entity_type_definitions));
foreach ($entity_type_definitions as $entity_type_id => $entity_type) {
$this->assertEquals($this->entityDefinitionUpdateManager->getEntityType($entity_type_id), $entity_type);
}
}
}

View file

@ -63,7 +63,6 @@ class EntityDisplayFormBaseTest extends KernelTestBase {
])
->shouldBeCalled();
// An initially visible field, with a submitted region change.
$entity->getComponent('field_start_visible_change_region')
->willReturn([

View file

@ -3,7 +3,7 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Component\Uuid\Uuid;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
/**
* Tests default values for entity fields.
@ -47,8 +47,8 @@ class EntityFieldDefaultValueTest extends EntityKernelTestBase {
->create();
$definition = $this->entityManager->getDefinition($entity_type_id);
$langcode_key = $definition->getKey('langcode');
$this->assertEqual($entity->{$langcode_key}->value, 'en', SafeMarkup::format('%entity_type: Default language', ['%entity_type' => $entity_type_id]));
$this->assertTrue(Uuid::isValid($entity->uuid->value), SafeMarkup::format('%entity_type: Default UUID', ['%entity_type' => $entity_type_id]));
$this->assertEqual($entity->{$langcode_key}->value, 'en', new FormattableMarkup('%entity_type: Default language', ['%entity_type' => $entity_type_id]));
$this->assertTrue(Uuid::isValid($entity->uuid->value), new FormattableMarkup('%entity_type: Default UUID', ['%entity_type' => $entity_type_id]));
$this->assertEqual($entity->name->getValue(), [], 'Field has one empty value by default.');
}

View file

@ -16,6 +16,8 @@ use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\Core\TypedData\ListInterface;
use Drupal\Core\TypedData\Type\StringInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\entity_test\Entity\EntityTestComputedField;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
@ -471,30 +473,30 @@ class EntityFieldTest extends EntityKernelTestBase {
// Make sure provided contextual information is right.
$entity_adapter = $entity->getTypedData();
$this->assertIdentical($entity_adapter->getRoot(), $entity_adapter, 'Entity is root object.');
$this->assertSame($entity_adapter->getRoot(), $entity_adapter, 'Entity is root object.');
$this->assertEqual($entity_adapter->getPropertyPath(), '');
$this->assertEqual($entity_adapter->getName(), '');
$this->assertEqual($entity_adapter->getParent(), NULL);
$field = $entity->user_id;
$this->assertIdentical($field->getRoot()->getValue(), $entity, 'Entity is root object.');
$this->assertIdentical($field->getEntity(), $entity, 'getEntity() returns the entity.');
$this->assertSame($field->getRoot()->getValue(), $entity, 'Entity is root object.');
$this->assertSame($field->getEntity(), $entity, 'getEntity() returns the entity.');
$this->assertEqual($field->getPropertyPath(), 'user_id');
$this->assertEqual($field->getName(), 'user_id');
$this->assertIdentical($field->getParent()->getValue(), $entity, 'Parent object matches.');
$this->assertSame($field->getParent()->getValue(), $entity, 'Parent object matches.');
$field_item = $field[0];
$this->assertIdentical($field_item->getRoot()->getValue(), $entity, 'Entity is root object.');
$this->assertIdentical($field_item->getEntity(), $entity, 'getEntity() returns the entity.');
$this->assertSame($field_item->getRoot()->getValue(), $entity, 'Entity is root object.');
$this->assertSame($field_item->getEntity(), $entity, 'getEntity() returns the entity.');
$this->assertEqual($field_item->getPropertyPath(), 'user_id.0');
$this->assertEqual($field_item->getName(), '0');
$this->assertIdentical($field_item->getParent(), $field, 'Parent object matches.');
$this->assertSame($field_item->getParent(), $field, 'Parent object matches.');
$item_value = $field_item->get('entity');
$this->assertIdentical($item_value->getRoot()->getValue(), $entity, 'Entity is root object.');
$this->assertSame($item_value->getRoot()->getValue(), $entity, 'Entity is root object.');
$this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity');
$this->assertEqual($item_value->getName(), 'entity');
$this->assertIdentical($item_value->getParent(), $field_item, 'Parent object matches.');
$this->assertSame($item_value->getParent(), $field_item, 'Parent object matches.');
}
/**
@ -737,6 +739,164 @@ class EntityFieldTest extends EntityKernelTestBase {
}
}
/**
* Tests all the interaction points of a computed field.
*/
public function testComputedFields() {
$this->installEntitySchema('entity_test_computed_field');
\Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
// Check that the values are not computed unnecessarily during the lifecycle
// of an entity when the field is not interacted with directly.
\Drupal::state()->set('computed_test_field_execution', 0);
$entity = EntityTestComputedField::create([]);
$this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
$entity->name->value = $this->randomString();
$this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
$entity->save();
$this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
// Test \Drupal\Core\TypedData\ComputedItemListTrait::getValue().
\Drupal::state()->set('computed_test_field_execution', 0);
$entity = EntityTestComputedField::create([]);
$this->assertSame([['value' => 'foo computed']], $entity->computed_string_field->getValue());
// Check that the values are only computed once.
$this->assertSame(1, \Drupal::state()->get('computed_test_field_execution', 0));
// Test \Drupal\Core\TypedData\ComputedItemListTrait::setValue(). This also
// checks that a subsequent getter does not try to re-compute the value.
\Drupal::state()->set('computed_test_field_execution', 0);
$entity = EntityTestComputedField::create([]);
$entity->computed_string_field->setValue([
['value' => 'foo computed 1'],
['value' => 'foo computed 2'],
]);
$this->assertSame([['value' => 'foo computed 1'], ['value' => 'foo computed 2']], $entity->computed_string_field->getValue());
// Check that the values have not been computed when they were explicitly
// set.
$this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
// Test \Drupal\Core\TypedData\ComputedItemListTrait::getString().
$entity = EntityTestComputedField::create([]);
$this->assertSame('foo computed', $entity->computed_string_field->getString());
// Test \Drupal\Core\TypedData\ComputedItemListTrait::get().
$entity = EntityTestComputedField::create([]);
$this->assertSame('foo computed', $entity->computed_string_field->get(0)->value);
$this->assertEmpty($entity->computed_string_field->get(1));
// Test \Drupal\Core\TypedData\ComputedItemListTrait::set().
$entity = EntityTestComputedField::create([]);
$entity->computed_string_field->set(1, 'foo computed 1');
$this->assertSame('foo computed', $entity->computed_string_field[0]->value);
$this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
$entity->computed_string_field->set(0, 'foo computed 0');
$this->assertSame('foo computed 0', $entity->computed_string_field[0]->value);
$this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
// Test \Drupal\Core\TypedData\ComputedItemListTrait::appendItem().
$entity = EntityTestComputedField::create([]);
$entity->computed_string_field->appendItem('foo computed 1');
$this->assertSame('foo computed', $entity->computed_string_field[0]->value);
$this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
// Test \Drupal\Core\TypedData\ComputedItemListTrait::removeItem().
$entity = EntityTestComputedField::create([]);
$entity->computed_string_field->removeItem(0);
$this->assertTrue($entity->computed_string_field->isEmpty());
// Test \Drupal\Core\TypedData\ComputedItemListTrait::isEmpty().
\Drupal::state()->set('entity_test_computed_field_item_list_value', []);
$entity = EntityTestComputedField::create([]);
$this->assertTrue($entity->computed_string_field->isEmpty());
\Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
$entity = EntityTestComputedField::create([]);
$this->assertFalse($entity->computed_string_field->isEmpty());
// Test \Drupal\Core\TypedData\ComputedItemListTrait::filter().
$filter_callback = function ($item) {
return !$item->isEmpty();
};
$entity = EntityTestComputedField::create([]);
$entity->computed_string_field->filter($filter_callback);
$this->assertCount(1, $entity->computed_string_field);
// Add an empty item to the list and check that it is filtered out.
$entity->computed_string_field->appendItem();
$entity->computed_string_field->filter($filter_callback);
$this->assertCount(1, $entity->computed_string_field);
// Add a non-empty item to the list and check that it is not filtered out.
$entity->computed_string_field->appendItem('foo computed 1');
$entity->computed_string_field->filter($filter_callback);
$this->assertCount(2, $entity->computed_string_field);
// Test \Drupal\Core\TypedData\ComputedItemListTrait::offsetExists().
$entity = EntityTestComputedField::create([]);
$this->assertTrue($entity->computed_string_field->offsetExists(0));
$this->assertFalse($entity->computed_string_field->offsetExists(1));
// Test \Drupal\Core\TypedData\ComputedItemListTrait::getIterator().
$entity = EntityTestComputedField::create([]);
foreach ($entity->computed_string_field as $delta => $item) {
$this->assertSame('foo computed', $item->value);
}
// Test \Drupal\Core\TypedData\ComputedItemListTrait::count().
$entity = EntityTestComputedField::create([]);
$this->assertCount(1, $entity->computed_string_field);
// Check that computed items are not auto-created when they have no values.
\Drupal::state()->set('entity_test_computed_field_item_list_value', []);
$entity = EntityTestComputedField::create([]);
$this->assertCount(0, $entity->computed_string_field);
// Test \Drupal\Core\Field\FieldItemList::equals() for a computed field.
\Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
$entity = EntityTestComputedField::create([]);
$computed_item_list1 = $entity->computed_string_field;
$entity = EntityTestComputedField::create([]);
$computed_item_list2 = $entity->computed_string_field;
$this->assertTrue($computed_item_list1->equals($computed_item_list2));
$computed_item_list2->value = 'foo computed 2';
$this->assertFalse($computed_item_list1->equals($computed_item_list2));
}
/**
* Tests an entity reference computed field.
*/
public function testEntityReferenceComputedField() {
$this->installEntitySchema('entity_test_computed_field');
// Create 2 entities to be referenced.
$ref1 = EntityTest::create(['name' => 'foo', 'type' => 'bar']);
$ref1->save();
$ref2 = EntityTest::create(['name' => 'baz', 'type' => 'bar']);
$ref2->save();
\Drupal::state()->set('entity_test_reference_computed_target_ids', [$ref1->id(), $ref2->id()]);
$entity = EntityTestComputedField::create([]);
$entity->save();
/** @var \Drupal\entity_test\Plugin\Field\ComputedReferenceTestFieldItemList $field */
$field = $entity->get('computed_reference_field');
/** @var \Drupal\Core\Entity\EntityInterface[] $referenced_entities */
$referenced_entities = $field->referencedEntities();
// Check that ::referencedEntities() is working with computed fields.
$this->assertEquals($ref1->id(), $referenced_entities[0]->id());
$this->assertEquals($ref2->id(), $referenced_entities[1]->id());
}
/**
* Executes the computed properties tests for the given entity type.
*

View file

@ -0,0 +1,70 @@
<?php
namespace Drupal\KernelTests\Core\Entity;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* @covers \Drupal\Core\Entity\Plugin\Validation\Constraint\EntityHasFieldConstraintValidator
*
* @group Entity
*/
class EntityHasFieldConstraintValidatorTest extends EntityKernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['entity_test_constraints'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('entity_test_constraints');
$this->createUser();
}
public function testValidation() {
$this->state->set('entity_test_constraints.build', [
'EntityHasField' => 'body',
]);
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
$entity_type_manager = $this->container->get('entity_type.manager');
$entity_type_manager->clearCachedDefinitions();
// Clear the typed data cache so that the entity has the correct constraints
// during validation.
$this->container->get('typed_data_manager')->clearCachedDefinitions();
$storage = $entity_type_manager->getStorage('entity_test_constraints');
/** @var \Drupal\entity_test\Entity\EntityTestConstraints $entity */
$entity = $storage->create();
// We should get a violation if we try to validate the entity before the
// field has been created.
$violations = $entity->validate();
$this->assertCount(1, $violations);
$this->assertEquals($violations[0]->getMessage(), 'The entity must have the <em class="placeholder">body</em> field.');
$storage->save($entity);
// Create the field.
$field_storage = FieldStorageConfig::create([
'type' => 'string',
'entity_type' => $entity->getEntityTypeId(),
'field_name' => 'body',
]);
$field_storage->save();
FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $entity->bundle(),
])->save();
// Now that the field has been created, there should be no violations.
$this->assertCount(0, $storage->loadUnchanged(1)->validate());
}
}

View file

@ -0,0 +1,56 @@
<?php
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\entity_test\Entity\EntityTest;
/**
* Test the behavior of entity keys.
*
* @group entity
*/
class EntityKeysTest extends EntityKernelTestBase {
/**
* Test the cache when multiple keys reference a single field.
*
* @dataProvider multipleKeysCacheTestCases
*/
public function testMultipleKeysCache($translatable) {
$this->state->set('entity_test.additional_base_field_definitions', [
'test_field' => BaseFieldDefinition::create('string')->setTranslatable($translatable),
]);
$this->state->set('entity_test.entity_keys', [
'key_1' => 'test_field',
'key_2' => 'test_field',
]);
drupal_flush_all_caches();
$this->installEntitySchema('entity_test');
$entity = EntityTest::create([]);
$entity->set('test_field', 'foo');
$this->assertEquals('foo', $entity->getEntityKey('key_1'));
$this->assertEquals('foo', $entity->getEntityKey('key_2'));
$entity->set('test_field', 'bar');
$this->assertEquals('bar', $entity->getEntityKey('key_1'));
$this->assertEquals('bar', $entity->getEntityKey('key_2'));
}
/**
* Data provider for ::testMultipleKeysCache.
*/
public function multipleKeysCacheTestCases() {
return [
'translatable Entity Key' => [
TRUE,
],
'Non-translatable entity key' => [
FALSE,
],
];
}
}

View file

@ -2,7 +2,6 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Component\Utility\Unicode;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
@ -64,10 +63,10 @@ abstract class EntityLanguageTestBase extends EntityKernelTestBase {
$this->state->set('entity_test.translation', TRUE);
// Create a translatable test field.
$this->fieldName = Unicode::strtolower($this->randomMachineName() . '_field_name');
$this->fieldName = mb_strtolower($this->randomMachineName() . '_field_name');
// Create an untranslatable test field.
$this->untranslatableFieldName = Unicode::strtolower($this->randomMachineName() . '_field_name');
$this->untranslatableFieldName = mb_strtolower($this->randomMachineName() . '_field_name');
// Create field fields in all entity variations.
foreach (entity_test_entity_types() as $entity_type) {

View file

@ -0,0 +1,53 @@
<?php
namespace Drupal\KernelTests\Core\Entity;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests loading entities by UUID.
*
* @group entity
*/
class EntityLoadByUuidTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['entity_test', 'user'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('user');
$this->installEntitySchema('entity_test');
}
/**
* Ensures that ::loadEntityByUuid() doesn't apply access checking.
*/
public function testLoadEntityByUuidAccessChecking() {
\Drupal::state()->set('entity_test_query_access', TRUE);
// Create two test entities.
$entity_0 = EntityTest::create([
'type' => 'entity_test',
'name' => 'published entity',
]);
$entity_0->save();
$entity_1 = EntityTest::create([
'type' => 'entity_test',
'name' => 'unpublished entity',
]);
$entity_1->save();
/** @var \Drupal\Core\Entity\EntityRepositoryInterface $repository */
$repository = \Drupal::service('entity.repository');
$this->assertEquals($entity_0->id(), $repository->loadEntityByUuid('entity_test', $entity_0->uuid())->id());
$this->assertEquals($entity_1->id(), $repository->loadEntityByUuid('entity_test', $entity_1->uuid())->id());
}
}

View file

@ -1,6 +1,7 @@
<?php
namespace Drupal\KernelTests\Core\Entity;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\language\Entity\ConfigurableLanguage;

View file

@ -23,7 +23,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
/**
* The entity_test storage to create the test entities.
*
* @var \Drupal\entity_test\EntityTestStorage
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $entityStorage;
@ -34,18 +34,10 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
*/
protected $queryResult;
/**
* The query factory to create entity queries.
*
* @var \Drupal\Core\Entity\Query\QueryFactory
*/
public $factory;
protected function setUp() {
parent::setUp();
$this->entityStorage = $this->entityManager->getStorage('entity_test');
$this->factory = $this->container->get('entity.query');
// Add some fieldapi fields to be used in the test.
for ($i = 1; $i <= 2; $i++) {
@ -120,7 +112,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
*/
public function testAggregation() {
// Apply a simple groupby.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('user_id')
->execute();
@ -139,14 +131,14 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Apply a simple aggregation for different aggregation functions.
foreach ($function_expected as $aggregation_function => $expected) {
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', $aggregation_function)
->execute();
$this->assertEqual($this->queryResult, $expected);
}
// Apply aggregation and groupby on the same query.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', 'COUNT')
->groupBy('user_id')
->execute();
@ -157,7 +149,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
]);
// Apply aggregation and a condition which matches.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', 'COUNT')
->groupBy('id')
->conditionAggregate('id', 'COUNT', 8)
@ -165,14 +157,14 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
$this->assertResults([]);
// Don't call aggregate to test the implicit aggregate call.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('id')
->conditionAggregate('id', 'COUNT', 8)
->execute();
$this->assertResults([]);
// Apply aggregation and a condition which matches.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', 'count')
->groupBy('id')
->conditionAggregate('id', 'COUNT', 6)
@ -181,7 +173,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Apply aggregation, a groupby and a condition which matches partially via
// the operator '='.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', 'count')
->conditionAggregate('id', 'count', 2)
->groupBy('user_id')
@ -190,7 +182,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Apply aggregation, a groupby and a condition which matches partially via
// the operator '>'.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', 'count')
->conditionAggregate('id', 'COUNT', 1, '>')
->groupBy('user_id')
@ -202,20 +194,20 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Apply aggregation and a sort. This might not be useful, but have a proper
// test coverage.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', 'COUNT')
->sortAggregate('id', 'COUNT')
->execute();
$this->assertSortedResults([['id_count' => 6]]);
// Don't call aggregate to test the implicit aggregate call.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->sortAggregate('id', 'COUNT')
->execute();
$this->assertSortedResults([['id_count' => 6]]);
// Apply aggregation, groupby and a sort descending.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', 'COUNT')
->groupBy('user_id')
->sortAggregate('id', 'COUNT', 'DESC')
@ -227,7 +219,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
]);
// Apply aggregation, groupby and a sort ascending.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', 'COUNT')
->groupBy('user_id')
->sortAggregate('id', 'COUNT', 'ASC')
@ -240,7 +232,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Apply aggregation, groupby, an aggregation condition and a sort with the
// operator '='.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', 'COUNT')
->groupBy('user_id')
->sortAggregate('id', 'COUNT')
@ -250,7 +242,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Apply aggregation, groupby, an aggregation condition and a sort with the
// operator '<' and order ASC.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', 'COUNT')
->groupBy('user_id')
->sortAggregate('id', 'COUNT', 'ASC')
@ -263,7 +255,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Apply aggregation, groupby, an aggregation condition and a sort with the
// operator '<' and order DESC.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('id', 'COUNT')
->groupBy('user_id')
->sortAggregate('id', 'COUNT', 'DESC')
@ -277,7 +269,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Test aggregation/groupby support for fieldapi fields.
// Just group by a fieldapi field.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('field_test_1')
->execute();
$this->assertResults([
@ -287,7 +279,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
]);
// Group by a fieldapi field and aggregate a normal property.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('user_id', 'COUNT')
->groupBy('field_test_1')
->execute();
@ -299,7 +291,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
]);
// Group by a normal property and aggregate a fieldapi field.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('field_test_1', 'COUNT')
->groupBy('user_id')
->execute();
@ -310,7 +302,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
['user_id' => 3, 'field_test_1_count' => 2],
]);
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('field_test_1', 'SUM')
->groupBy('user_id')
->execute();
@ -321,7 +313,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
]);
// Aggregate by two different fieldapi fields.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('field_test_1', 'SUM')
->aggregate('field_test_2', 'SUM')
->groupBy('user_id')
@ -333,7 +325,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
]);
// This time aggregate the same field twice.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('field_test_1', 'SUM')
->aggregate('field_test_1', 'COUNT')
->groupBy('user_id')
@ -345,7 +337,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
]);
// Group by and aggregate by a fieldapi field.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('field_test_1')
->aggregate('field_test_2', 'COUNT')
->execute();
@ -357,7 +349,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Group by and aggregate by a fieldapi field and use multiple aggregate
// functions.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('field_test_1')
->aggregate('field_test_2', 'COUNT')
->aggregate('field_test_2', 'SUM')
@ -370,7 +362,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Apply an aggregate condition for a fieldapi field and group by a simple
// property.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->conditionAggregate('field_test_1', 'COUNT', 3)
->groupBy('user_id')
->execute();
@ -379,7 +371,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
['user_id' => 3, 'field_test_1_count' => 2],
]);
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('field_test_1', 'SUM')
->conditionAggregate('field_test_1', 'COUNT', 2, '>')
->groupBy('user_id')
@ -391,7 +383,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Apply an aggregate condition for a simple property and a group by a
// fieldapi field.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->conditionAggregate('user_id', 'COUNT', 2)
->groupBy('field_test_1')
->execute();
@ -399,7 +391,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
['field_test_1' => 1, 'user_id_count' => 2],
]);
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->conditionAggregate('user_id', 'COUNT', 2, '>')
->groupBy('field_test_1')
->execute();
@ -409,14 +401,14 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
]);
// Apply an aggregate condition and a group by fieldapi fields.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('field_test_1')
->conditionAggregate('field_test_2', 'COUNT', 2)
->execute();
$this->assertResults([
['field_test_1' => 1, 'field_test_2_count' => 2],
]);
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('field_test_1')
->conditionAggregate('field_test_2', 'COUNT', 2, '>')
->execute();
@ -427,7 +419,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Apply an aggregate condition and a group by fieldapi fields with multiple
// conditions via AND.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('field_test_1')
->conditionAggregate('field_test_2', 'COUNT', 2)
->conditionAggregate('field_test_2', 'SUM', 8)
@ -436,7 +428,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Apply an aggregate condition and a group by fieldapi fields with multiple
// conditions via OR.
$this->queryResult = $this->factory->getAggregate('entity_test', 'OR')
$this->queryResult = $this->entityStorage->getAggregateQuery('OR')
->groupBy('field_test_1')
->conditionAggregate('field_test_2', 'COUNT', 2)
->conditionAggregate('field_test_2', 'SUM', 8)
@ -448,7 +440,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Group by a normal property and aggregate a fieldapi field and sort by the
// groupby field.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('field_test_1', 'COUNT')
->groupBy('user_id')
->sort('user_id', 'DESC')
@ -459,7 +451,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
['user_id' => 1, 'field_test_1_count' => 1],
]);
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->aggregate('field_test_1', 'COUNT')
->groupBy('user_id')
->sort('user_id', 'ASC')
@ -470,7 +462,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
['user_id' => 3, 'field_test_1_count' => 2],
]);
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->conditionAggregate('field_test_1', 'COUNT', 2, '>')
->groupBy('user_id')
->sort('user_id', 'ASC')
@ -482,7 +474,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Group by a normal property, aggregate a fieldapi field, and sort by the
// aggregated field.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->sortAggregate('field_test_1', 'COUNT', 'DESC')
->groupBy('user_id')
->execute();
@ -492,7 +484,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
['user_id' => 1, 'field_test_1_count' => 1],
]);
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->sortAggregate('field_test_1', 'COUNT', 'ASC')
->groupBy('user_id')
->execute();
@ -503,7 +495,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
]);
// Group by and aggregate by fieldapi field, and sort by the groupby field.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('field_test_1')
->aggregate('field_test_2', 'COUNT')
->sort('field_test_1', 'ASC')
@ -514,7 +506,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
['field_test_1' => 3, 'field_test_2_count' => 1],
]);
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('field_test_1')
->aggregate('field_test_2', 'COUNT')
->sort('field_test_1', 'DESC')
@ -527,7 +519,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
// Groupby and aggregate by fieldapi field, and sort by the aggregated
// field.
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('field_test_1')
->sortAggregate('field_test_2', 'COUNT', 'DESC')
->execute();
@ -537,7 +529,7 @@ class EntityQueryAggregateTest extends EntityKernelTestBase {
['field_test_1' => 3, 'field_test_2_count' => 1],
]);
$this->queryResult = $this->factory->getAggregate('entity_test')
$this->queryResult = $this->entityStorage->getAggregateQuery()
->groupBy('field_test_1')
->sortAggregate('field_test_2', 'COUNT', 'ASC')
->execute();

View file

@ -3,11 +3,10 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Component\Utility\Unicode;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\taxonomy\Entity\Term;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
/**
* Tests the Entity Query relationship API.
@ -25,11 +24,6 @@ class EntityQueryRelationshipTest extends EntityKernelTestBase {
*/
public static $modules = ['taxonomy'];
/**
* @var \Drupal\Core\Entity\Query\QueryFactory
*/
protected $factory;
/**
* Term entities.
*
@ -73,7 +67,7 @@ class EntityQueryRelationshipTest extends EntityKernelTestBase {
// We want an entity reference field. It needs a vocabulary, terms, a field
// storage and a field. First, create the vocabulary.
$vocabulary = Vocabulary::create([
'vid' => Unicode::strtolower($this->randomMachineName()),
'vid' => mb_strtolower($this->randomMachineName()),
]);
$vocabulary->save();
@ -110,88 +104,88 @@ class EntityQueryRelationshipTest extends EntityKernelTestBase {
$entity->save();
$this->entities[] = $entity;
}
$this->factory = \Drupal::service('entity.query');
}
/**
* Tests querying.
*/
public function testQuery() {
$storage = $this->container->get('entity_type.manager')->getStorage('entity_test');
// This returns the 0th entity as that's the only one pointing to the 0th
// account.
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->condition("user_id.entity.name", $this->accounts[0]->getUsername())
->execute();
$this->assertResults([0]);
// This returns the 1st and 2nd entity as those point to the 1st account.
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->condition("user_id.entity.name", $this->accounts[0]->getUsername(), '<>')
->execute();
$this->assertResults([1, 2]);
// This returns all three entities because all of them point to an
// account.
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->exists("user_id.entity.name")
->execute();
$this->assertResults([0, 1, 2]);
// This returns no entities because all of them point to an account.
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->notExists("user_id.entity.name")
->execute();
$this->assertEqual(count($this->queryResults), 0);
// This returns the 0th entity as that's only one pointing to the 0th
// term (test without specifying the field column).
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->condition("$this->fieldName.entity.name", $this->terms[0]->name->value)
->execute();
$this->assertResults([0]);
// This returns the 0th entity as that's only one pointing to the 0th
// term (test with specifying the column name).
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->condition("$this->fieldName.target_id.entity.name", $this->terms[0]->name->value)
->execute();
$this->assertResults([0]);
// This returns the 1st and 2nd entity as those point to the 1st term.
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->condition("$this->fieldName.entity.name", $this->terms[0]->name->value, '<>')
->execute();
$this->assertResults([1, 2]);
// This returns the 0th entity as that's only one pointing to the 0th
// account.
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->condition("user_id.entity:user.name", $this->accounts[0]->getUsername())
->execute();
$this->assertResults([0]);
// This returns the 1st and 2nd entity as those point to the 1st account.
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->condition("user_id.entity:user.name", $this->accounts[0]->getUsername(), '<>')
->execute();
$this->assertResults([1, 2]);
// This returns all three entities because all of them point to an
// account.
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->exists("user_id.entity:user.name")
->execute();
$this->assertResults([0, 1, 2]);
// This returns no entities because all of them point to an account.
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->notExists("user_id.entity:user.name")
->execute();
$this->assertEqual(count($this->queryResults), 0);
// This returns the 0th entity as that's only one pointing to the 0th
// term (test without specifying the field column).
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->condition("$this->fieldName.entity:taxonomy_term.name", $this->terms[0]->name->value)
->execute();
$this->assertResults([0]);
// This returns the 0th entity as that's only one pointing to the 0th
// term (test with specifying the column name).
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->condition("$this->fieldName.target_id.entity:taxonomy_term.name", $this->terms[0]->name->value)
->execute();
$this->assertResults([0]);
// This returns the 1st and 2nd entity as those point to the 1st term.
$this->queryResults = $this->factory->get('entity_test')
$this->queryResults = $storage->getQuery()
->condition("$this->fieldName.entity:taxonomy_term.name", $this->terms[0]->name->value, '<>')
->execute();
$this->assertResults([1, 2]);
@ -202,8 +196,10 @@ class EntityQueryRelationshipTest extends EntityKernelTestBase {
*/
public function testInvalidSpecifier() {
$this->setExpectedException(PluginNotFoundException::class);
$this->factory
->get('taxonomy_term')
$this->container
->get('entity_type.manager')
->getStorage('taxonomy_term')
->getQuery()
->condition('langcode.language.foo', 'bar')
->execute();
}

View file

@ -2,7 +2,6 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Component\Utility\Unicode;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\field\Entity\FieldConfig;
@ -10,6 +9,7 @@ use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
use Symfony\Component\HttpFoundation\Request;
/**
@ -19,6 +19,8 @@ use Symfony\Component\HttpFoundation\Request;
*/
class EntityQueryTest extends EntityKernelTestBase {
use EntityReferenceTestTrait;
/**
* Modules to enable.
*
@ -31,11 +33,6 @@ class EntityQueryTest extends EntityKernelTestBase {
*/
protected $queryResults;
/**
* @var \Drupal\Core\Entity\Query\QueryFactory
*/
protected $factory;
/**
* A list of bundle machine names created for this test.
*
@ -57,6 +54,13 @@ class EntityQueryTest extends EntityKernelTestBase {
*/
public $figures;
/**
* The entity_test_mulrev entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $storage;
protected function setUp() {
parent::setUp();
@ -64,8 +68,8 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->installConfig(['language']);
$figures = Unicode::strtolower($this->randomMachineName());
$greetings = Unicode::strtolower($this->randomMachineName());
$figures = mb_strtolower($this->randomMachineName());
$greetings = mb_strtolower($this->randomMachineName());
foreach ([$figures => 'shape', $greetings => 'text'] as $field_name => $field_type) {
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
@ -94,23 +98,27 @@ class EntityQueryTest extends EntityKernelTestBase {
}
// Each unit is a list of field name, langcode and a column-value array.
$units[] = [$figures, 'en', [
'color' => 'red',
'shape' => 'triangle',
]];
'color' => 'red',
'shape' => 'triangle',
],
];
$units[] = [$figures, 'en', [
'color' => 'blue',
'shape' => 'circle',
]];
'color' => 'blue',
'shape' => 'circle',
],
];
// To make it easier to test sorting, the greetings get formats according
// to their langcode.
$units[] = [$greetings, 'tr', [
'value' => 'merhaba',
'format' => 'format-tr'
]];
'value' => 'merhaba',
'format' => 'format-tr',
],
];
$units[] = [$greetings, 'pl', [
'value' => 'siema',
'format' => 'format-pl'
]];
'value' => 'siema',
'format' => 'format-pl',
],
];
// Make these languages available to the greetings field.
ConfigurableLanguage::createFromLangcode('tr')->save();
ConfigurableLanguage::createFromLangcode('pl')->save();
@ -139,7 +147,7 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->bundles = $bundles;
$this->figures = $figures;
$this->greetings = $greetings;
$this->factory = \Drupal::service('entity.query');
$this->storage = $this->container->get('entity_type.manager')->getStorage('entity_test_mulrev');
}
/**
@ -148,7 +156,8 @@ class EntityQueryTest extends EntityKernelTestBase {
public function testEntityQuery() {
$greetings = $this->greetings;
$figures = $this->figures;
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->exists($greetings, 'tr')
->condition("$figures.color", 'red')
->sort('id')
@ -157,7 +166,8 @@ class EntityQueryTest extends EntityKernelTestBase {
// bit 0 and bit 2 needs to be set.
$this->assertResult(5, 7, 13, 15);
$query = $this->factory->get('entity_test_mulrev', 'OR')
$query = $this->storage
->getQuery('OR')
->exists($greetings, 'tr')
->condition("$figures.color", 'red')
->sort('id');
@ -169,7 +179,8 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->assertResult(1, 3, 4, 5, 6, 7, 9, 11, 12, 13, 14, 15);
// Test cloning of query conditions.
$query = $this->factory->get('entity_test_mulrev')
$query = $this->storage
->getQuery()
->condition("$figures.color", 'red')
->sort('id');
$cloned_query = clone $query;
@ -182,7 +193,7 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->queryResults = $cloned_query->execute();
$this->assertResult();
$query = $this->factory->get('entity_test_mulrev');
$query = $this->storage->getQuery();
$group = $query->orConditionGroup()
->exists($greetings, 'tr')
->condition("$figures.color", 'red');
@ -195,7 +206,8 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->assertResult(9, 11, 12, 13, 14, 15);
// No figure has both the colors blue and red at the same time.
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("$figures.color", 'blue')
->condition("$figures.color", 'red')
->sort('id')
@ -203,7 +215,7 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->assertResult();
// But an entity might have a red and a blue figure both.
$query = $this->factory->get('entity_test_mulrev');
$query = $this->storage->getQuery();
$group_blue = $query->andConditionGroup()->condition("$figures.color", 'blue');
$group_red = $query->andConditionGroup()->condition("$figures.color", 'red');
$this->queryResults = $query
@ -215,7 +227,7 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->assertResult(3, 7, 11, 15);
// Do the same test but with IN operator.
$query = $this->factory->get('entity_test_mulrev');
$query = $this->storage->getQuery();
$group_blue = $query->andConditionGroup()->condition("$figures.color", ['blue'], 'IN');
$group_red = $query->andConditionGroup()->condition("$figures.color", ['red'], 'IN');
$this->queryResults = $query
@ -227,14 +239,16 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->assertResult(3, 7, 11, 15);
// An entity might have either red or blue figure.
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("$figures.color", ['blue', 'red'], 'IN')
->sort('id')
->execute();
// Bit 0 or 1 is on.
$this->assertResult(1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15);
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->exists("$figures.color")
->notExists("$greetings.value")
->sort('id')
@ -243,7 +257,8 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->assertResult(1, 2, 3);
// Now update the 'merhaba' string to xsiemax which is not a meaningful
// word but allows us to test revisions and string operations.
$ids = $this->factory->get('entity_test_mulrev')
$ids = $this->storage
->getQuery()
->condition("$greetings.value", 'merhaba')
->sort('id')
->execute();
@ -257,30 +272,35 @@ class EntityQueryTest extends EntityKernelTestBase {
$entity->save();
}
// We changed the entity names, so the current revision should not match.
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition('name.value', $old_name)
->execute();
$this->assertResult();
// Only if all revisions are queried, we find the old revision.
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition('name.value', $old_name)
->allRevisions()
->sort('revision_id')
->execute();
$this->assertRevisionResult([$first_entity->id()], [$first_entity->id()]);
// When querying current revisions, this string is no longer found.
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("$greetings.value", 'merhaba')
->execute();
$this->assertResult();
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("$greetings.value", 'merhaba')
->allRevisions()
->sort('revision_id')
->execute();
// The query only matches the original revisions.
$this->assertRevisionResult([4, 5, 6, 7, 12, 13, 14, 15], [4, 5, 6, 7, 12, 13, 14, 15]);
$results = $this->factory->get('entity_test_mulrev')
$results = $this->storage
->getQuery()
->condition("$greetings.value", 'siema', 'CONTAINS')
->sort('id')
->execute();
@ -288,21 +308,24 @@ class EntityQueryTest extends EntityKernelTestBase {
// revisions are returned for some entities.
$assert = [16 => '4', 17 => '5', 18 => '6', 19 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 20 => '12', 21 => '13', 22 => '14', 23 => '15'];
$this->assertIdentical($results, $assert);
$results = $this->factory->get('entity_test_mulrev')
$results = $this->storage
->getQuery()
->condition("$greetings.value", 'siema', 'STARTS_WITH')
->sort('revision_id')
->execute();
// Now we only get the ones that originally were siema, entity id 8 and
// above.
$this->assertIdentical($results, array_slice($assert, 4, 8, TRUE));
$results = $this->factory->get('entity_test_mulrev')
$results = $this->storage
->getQuery()
->condition("$greetings.value", 'a', 'ENDS_WITH')
->sort('revision_id')
->execute();
// It is very important that we do not get the ones which only have
// xsiemax despite originally they were merhaba, ie. ended with a.
$this->assertIdentical($results, array_slice($assert, 4, 8, TRUE));
$results = $this->factory->get('entity_test_mulrev')
$results = $this->storage
->getQuery()
->condition("$greetings.value", 'a', 'ENDS_WITH')
->allRevisions()
->sort('id')
@ -311,6 +334,17 @@ class EntityQueryTest extends EntityKernelTestBase {
// Now we get everything.
$assert = [4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 12 => '12', 20 => '12', 13 => '13', 21 => '13', 14 => '14', 22 => '14', 15 => '15', 23 => '15'];
$this->assertIdentical($results, $assert);
// Check that a query on the latest revisions without any condition returns
// the correct results.
$results = $this->storage
->getQuery()
->latestRevision()
->sort('id')
->sort('revision_id')
->execute();
$expected = [1 => '1', 2 => '2', 3 => '3', 16 => '4', 17 => '5', 18 => '6', 19 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 20 => '12', 21 => '13', 22 => '14', 23 => '15'];
$this->assertSame($expected, $results);
}
/**
@ -322,15 +356,18 @@ class EntityQueryTest extends EntityKernelTestBase {
$greetings = $this->greetings;
$figures = $this->figures;
// Order up and down on a number.
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->sort('id')
->execute();
$this->assertResult(range(1, 15));
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->sort('id', 'DESC')
->execute();
$this->assertResult(range(15, 1));
$query = $this->factory->get('entity_test_mulrev')
$query = $this->storage
->getQuery()
->sort("$figures.color")
->sort("$greetings.format")
->sort('id');
@ -380,7 +417,8 @@ class EntityQueryTest extends EntityKernelTestBase {
'page' => '0,2',
]);
\Drupal::getContainer()->get('request_stack')->push($request);
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->sort("$figures.color")
->sort("$greetings.format")
->sort('id')
@ -389,7 +427,8 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->assertResult(15, 6, 7, 1);
// Now test the reversed order.
$query = $this->factory->get('entity_test_mulrev')
$query = $this->storage
->getQuery()
->sort("$figures.color", 'DESC')
->sort("$greetings.format", 'DESC')
->sort('id', 'DESC');
@ -418,7 +457,8 @@ class EntityQueryTest extends EntityKernelTestBase {
'type' => ['data' => 'Type', 'specifier' => 'type'],
];
$this->queryResults = array_values($this->factory->get('entity_test_mulrev')
$this->queryResults = array_values($this->storage
->getQuery()
->tableSort($header)
->execute());
$this->assertBundleOrder('asc');
@ -432,7 +472,8 @@ class EntityQueryTest extends EntityKernelTestBase {
'id' => ['data' => 'Id', 'specifier' => 'id'],
'type' => ['data' => 'Type', 'specifier' => 'type'],
];
$this->queryResults = array_values($this->factory->get('entity_test_mulrev')
$this->queryResults = array_values($this->storage
->getQuery()
->tableSort($header)
->execute());
$this->assertBundleOrder('desc');
@ -442,7 +483,8 @@ class EntityQueryTest extends EntityKernelTestBase {
'order' => 'Id',
]);
\Drupal::getContainer()->get('request_stack')->push($request);
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->tableSort($header)
->execute();
$this->assertResult(range(15, 1));
@ -477,7 +519,9 @@ class EntityQueryTest extends EntityKernelTestBase {
// As the single entity of this type we just saved does not have a value
// in the color field, the result should be 0.
$count = $this->factory->get('entity_test')
$count = $this->container->get('entity_type.manager')
->getStorage('entity_test')
->getQuery()
->exists("$field_name.color")
->count()
->execute();
@ -490,7 +534,7 @@ class EntityQueryTest extends EntityKernelTestBase {
public function testNestedConditionGroups() {
// Query for all entities of the first bundle that have either a red
// triangle as a figure or the Turkish greeting as a greeting.
$query = $this->factory->get('entity_test_mulrev');
$query = $this->storage->getQuery();
$first_and = $query->andConditionGroup()
->condition($this->figures . '.color', 'red')
@ -509,7 +553,32 @@ class EntityQueryTest extends EntityKernelTestBase {
->sort('id')
->execute();
$this->assertResult(6, 14);
$this->assertResult(4, 6, 12, 14);
}
/**
* Tests that condition count returns expected number of conditions.
*/
public function testConditionCount() {
// Query for all entities of the first bundle that
// have red as a colour AND are triangle shaped.
$query = $this->storage->getQuery();
// Add an AND condition group with 2 conditions in it.
$and_condition_group = $query->andConditionGroup()
->condition($this->figures . '.color', 'red')
->condition($this->figures . '.shape', 'triangle');
// We added 2 conditions so count should be 2.
$this->assertEqual($and_condition_group->count(), 2);
// Add an OR condition group with 2 conditions in it.
$or_condition_group = $query->orConditionGroup()
->condition($this->figures . '.color', 'red')
->condition($this->figures . '.shape', 'triangle');
// We added 2 conditions so count should be 2.
$this->assertEqual($or_condition_group->count(), 2);
}
/**
@ -518,14 +587,16 @@ class EntityQueryTest extends EntityKernelTestBase {
public function testDelta() {
$figures = $this->figures;
// Test numeric delta value in field condition.
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("$figures.0.color", 'red')
->sort('id')
->execute();
// As unit 0 at delta 0 was the red triangle bit 0 needs to be set.
$this->assertResult(1, 3, 5, 7, 9, 11, 13, 15);
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("$figures.1.color", 'red')
->sort('id')
->execute();
@ -533,7 +604,7 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->assertResult();
// Test on two different deltas.
$query = $this->factory->get('entity_test_mulrev');
$query = $this->storage->getQuery();
$or = $query->andConditionGroup()
->condition("$figures.0.color", 'red')
->condition("$figures.1.color", 'blue');
@ -544,7 +615,8 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->assertResult(3, 7, 11, 15);
// Test the delta range condition.
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("$figures.%delta.color", ['blue', 'red'], 'IN')
->condition("$figures.%delta", [0, 1], 'IN')
->sort('id')
@ -553,21 +625,24 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->assertResult(1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15);
// Test the delta range condition without conditions on the value.
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("$figures.%delta", 1)
->sort('id')
->execute();
// Entity needs to have atleast two figures.
// Entity needs to have at least two figures.
$this->assertResult(3, 7, 11, 15);
// Numeric delta on single value base field should return results only if
// the first item is being targeted.
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("id.0.value", [1, 3, 5], 'IN')
->sort('id')
->execute();
$this->assertResult(1, 3, 5);
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("id.1.value", [1, 3, 5], 'IN')
->sort('id')
->execute();
@ -575,18 +650,21 @@ class EntityQueryTest extends EntityKernelTestBase {
// Delta range condition on single value base field should return results
// only if just the field value is targeted.
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("id.%delta.value", [1, 3, 5], 'IN')
->sort('id')
->execute();
$this->assertResult(1, 3, 5);
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("id.%delta.value", [1, 3, 5], 'IN')
->condition("id.%delta", 0, '=')
->sort('id')
->execute();
$this->assertResult(1, 3, 5);
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition("id.%delta.value", [1, 3, 5], 'IN')
->condition("id.%delta", 1, '=')
->sort('id')
@ -643,7 +721,7 @@ class EntityQueryTest extends EntityKernelTestBase {
* The tags and metadata should propagate to the SQL query object.
*/
public function testMetaData() {
$query = \Drupal::entityQuery('entity_test_mulrev');
$query = $this->storage->getQuery();
$query
->addTag('efq_metadata_test')
->addMetaData('foo', 'bar')
@ -667,7 +745,7 @@ class EntityQueryTest extends EntityKernelTestBase {
'translatable' => FALSE,
'settings' => [
'case_sensitive' => FALSE,
]
],
]);
$field_storage->save();
@ -703,8 +781,8 @@ class EntityQueryTest extends EntityKernelTestBase {
$string = $this->randomMachineName(7) . 'a';
$fixtures[] = [
'original' => $string,
'uppercase' => Unicode::strtoupper($string),
'lowercase' => Unicode::strtolower($string),
'uppercase' => mb_strtoupper($string),
'lowercase' => mb_strtolower($string),
];
}
@ -713,140 +791,161 @@ class EntityQueryTest extends EntityKernelTestBase {
'name' => $this->randomMachineName(),
'langcode' => 'en',
'field_ci' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'],
'field_cs' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
'field_cs' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'],
])->save();
// Check the case insensitive field, = operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase']
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase'])
->execute();
$this->assertIdentical(count($result), 1, 'Case insensitive, lowercase');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase']
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase'])
->execute();
$this->assertIdentical(count($result), 1, 'Case insensitive, uppercase');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'])
->execute();
$this->assertIdentical(count($result), 1, 'Case insensitive, mixed.');
// Check the case sensitive field, = operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase']
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase'])
->execute();
$this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase']
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase'])
->execute();
$this->assertIdentical(count($result), 0, 'Case sensitive, uppercase.');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'])
->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
// Check the case insensitive field, IN operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', [$fixtures[0]['lowercase'] . $fixtures[1]['lowercase']], 'IN'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', [$fixtures[0]['lowercase'] . $fixtures[1]['lowercase']], 'IN')
->execute();
$this->assertIdentical(count($result), 1, 'Case insensitive, lowercase');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', [$fixtures[0]['uppercase'] . $fixtures[1]['uppercase']], 'IN'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', [$fixtures[0]['uppercase'] . $fixtures[1]['uppercase']], 'IN')->execute();
$this->assertIdentical(count($result), 1, 'Case insensitive, uppercase');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', [$fixtures[0]['uppercase'] . $fixtures[1]['lowercase']], 'IN'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', [$fixtures[0]['uppercase'] . $fixtures[1]['lowercase']], 'IN')
->execute();
$this->assertIdentical(count($result), 1, 'Case insensitive, mixed');
// Check the case sensitive field, IN operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', [$fixtures[0]['lowercase'] . $fixtures[1]['lowercase']], 'IN'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', [$fixtures[0]['lowercase'] . $fixtures[1]['lowercase']], 'IN')
->execute();
$this->assertIdentical(count($result), 0, 'Case sensitive, lowercase');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', [$fixtures[0]['uppercase'] . $fixtures[1]['uppercase']], 'IN'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', [$fixtures[0]['uppercase'] . $fixtures[1]['uppercase']], 'IN')
->execute();
$this->assertIdentical(count($result), 0, 'Case sensitive, uppercase');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', [$fixtures[0]['uppercase'] . $fixtures[1]['lowercase']], 'IN'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', [$fixtures[0]['uppercase'] . $fixtures[1]['lowercase']], 'IN')
->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, mixed');
// Check the case insensitive field, STARTS_WITH operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', $fixtures[0]['lowercase'], 'STARTS_WITH'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', $fixtures[0]['lowercase'], 'STARTS_WITH')
->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', $fixtures[0]['uppercase'], 'STARTS_WITH'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', $fixtures[0]['uppercase'], 'STARTS_WITH')
->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
// Check the case sensitive field, STARTS_WITH operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', $fixtures[0]['lowercase'], 'STARTS_WITH'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', $fixtures[0]['lowercase'], 'STARTS_WITH')
->execute();
$this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', $fixtures[0]['uppercase'], 'STARTS_WITH'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', $fixtures[0]['uppercase'], 'STARTS_WITH')
->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
// Check the case insensitive field, ENDS_WITH operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', $fixtures[1]['lowercase'], 'ENDS_WITH'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', $fixtures[1]['lowercase'], 'ENDS_WITH')
->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', $fixtures[1]['uppercase'], 'ENDS_WITH'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', $fixtures[1]['uppercase'], 'ENDS_WITH')
->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
// Check the case sensitive field, ENDS_WITH operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', $fixtures[1]['lowercase'], 'ENDS_WITH'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', $fixtures[1]['lowercase'], 'ENDS_WITH')
->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', $fixtures[1]['uppercase'], 'ENDS_WITH'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', $fixtures[1]['uppercase'], 'ENDS_WITH')
->execute();
$this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
// Check the case insensitive field, CONTAINS operator, use the inner 8
// characters of the uppercase and lowercase strings.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS')
->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_ci', mb_strtolower(mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS')
->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
// Check the case sensitive field, CONTAINS operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS')
->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS'
)->execute();
$result = $this->storage
->getQuery()
->condition('field_cs', mb_strtolower(mb_substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS')
->execute();
$this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
}
@ -866,7 +965,8 @@ class EntityQueryTest extends EntityKernelTestBase {
'description' => [
'value' => $this->randomString(),
'format' => 'format1',
]]);
],
]);
$term1->save();
$term2 = Term::create([
@ -875,10 +975,13 @@ class EntityQueryTest extends EntityKernelTestBase {
'description' => [
'value' => $this->randomString(),
'format' => 'format2',
]]);
],
]);
$term2->save();
$ids = \Drupal::entityQuery('taxonomy_term')
$ids = $this->container->get('entity_type.manager')
->getStorage('taxonomy_term')
->getQuery()
->condition('description.format', 'format1')
->execute();
@ -887,11 +990,12 @@ class EntityQueryTest extends EntityKernelTestBase {
}
/**
* Test forward-revisions.
* Test pending revisions.
*/
public function testForwardRevisions() {
public function testPendingRevisions() {
// Ensure entity 14 is returned.
$result = \Drupal::entityQuery('entity_test_mulrev')
$result = $this->storage
->getQuery()
->condition('id', [14], 'IN')
->execute();
$this->assertEqual(count($result), 1);
@ -904,29 +1008,85 @@ class EntityQueryTest extends EntityKernelTestBase {
$entity->isDefaultRevision(FALSE);
$entity->{$this->figures}->setValue([
'color' => 'red',
'shape' => 'square'
'shape' => 'square',
]);
$entity->save();
// Entity query should still return entity 14.
$result = \Drupal::entityQuery('entity_test_mulrev')
$result = $this->storage
->getQuery()
->condition('id', [14], 'IN')
->execute();
$this->assertEqual(count($result), 1);
// Verify that field conditions on the default and forward revision are
// Verify that field conditions on the default and pending revision are
// work as expected.
$result = \Drupal::entityQuery('entity_test_mulrev')
$result = $this->storage
->getQuery()
->condition('id', [14], 'IN')
->condition("$this->figures.color", $current_values[0]['color'])
->execute();
$this->assertEqual($result, [14 => '14']);
$result = $this->factory->get('entity_test_mulrev')
$result = $this->storage
->getQuery()
->condition('id', [14], 'IN')
->condition("$this->figures.color", 'red')
->allRevisions()
->execute();
$this->assertEqual($result, [16 => '14']);
// Add another pending revision on the same entity and repeat the checks.
$entity->setNewRevision(TRUE);
$entity->isDefaultRevision(FALSE);
$entity->{$this->figures}->setValue([
'color' => 'red',
'shape' => 'square',
]);
$entity->save();
// A non-revisioned entity query should still return entity 14.
$result = $this->storage
->getQuery()
->condition('id', [14], 'IN')
->execute();
$this->assertCount(1, $result);
$this->assertSame([14 => '14'], $result);
// Now check an entity query on the latest revision.
$result = $this->storage
->getQuery()
->condition('id', [14], 'IN')
->latestRevision()
->execute();
$this->assertCount(1, $result);
$this->assertSame([17 => '14'], $result);
// Verify that field conditions on the default and pending revision still
// work as expected.
$result = $this->storage
->getQuery()
->condition('id', [14], 'IN')
->condition("$this->figures.color", $current_values[0]['color'])
->execute();
$this->assertSame([14 => '14'], $result);
// Now there are two revisions with same value for the figure color.
$result = $this->storage
->getQuery()
->condition('id', [14], 'IN')
->condition("$this->figures.color", 'red')
->allRevisions()
->execute();
$this->assertSame([16 => '14', 17 => '14'], $result);
// Check that querying for the latest revision returns the correct one.
$result = $this->storage
->getQuery()
->condition('id', [14], 'IN')
->condition("$this->figures.color", 'red')
->latestRevision()
->execute();
$this->assertSame([17 => '14'], $result);
}
/**
@ -935,7 +1095,8 @@ class EntityQueryTest extends EntityKernelTestBase {
*/
public function testInjectionInCondition() {
try {
$this->queryResults = $this->factory->get('entity_test_mulrev')
$this->queryResults = $this->storage
->getQuery()
->condition('1 ; -- ', [0, 1], 'IN')
->sort('id')
->execute();
@ -946,4 +1107,57 @@ class EntityQueryTest extends EntityKernelTestBase {
}
}
/**
* Tests that EntityQuery works when querying the same entity from two fields.
*/
public function testWithTwoEntityReferenceFieldsToSameEntityType() {
// Create two entity reference fields referring 'entity_test' entities.
$this->createEntityReferenceField('entity_test', 'entity_test', 'ref1', $this->randomMachineName(), 'entity_test');
$this->createEntityReferenceField('entity_test', 'entity_test', 'ref2', $this->randomMachineName(), 'entity_test');
$storage = $this->container->get('entity_type.manager')
->getStorage('entity_test');
// Create two entities to be referred.
$ref1 = EntityTest::create(['type' => 'entity_test']);
$ref1->save();
$ref2 = EntityTest::create(['type' => 'entity_test']);
$ref2->save();
// Create a main entity referring the previous created entities.
$entity = EntityTest::create([
'type' => 'entity_test',
'ref1' => $ref1->id(),
'ref2' => $ref2->id(),
]);
$entity->save();
// Check that works when referring with "{$field_name}".
$result = $storage->getQuery()
->condition('type', 'entity_test')
->condition('ref1', $ref1->id())
->condition('ref2', $ref2->id())
->execute();
$this->assertCount(1, $result);
$this->assertEquals($entity->id(), reset($result));
// Check that works when referring with "{$field_name}.target_id".
$result = $storage->getQuery()
->condition('type', 'entity_test')
->condition('ref1.target_id', $ref1->id())
->condition('ref2.target_id', $ref2->id())
->execute();
$this->assertCount(1, $result);
$this->assertEquals($entity->id(), reset($result));
// Check that works when referring with "{$field_name}.entity.id".
$result = $storage->getQuery()
->condition('type', 'entity_test')
->condition('ref1.entity.id', $ref1->id())
->condition('ref2.entity.id', $ref2->id())
->execute();
$this->assertCount(1, $result);
$this->assertEquals($entity->id(), reset($result));
}
}

View file

@ -7,9 +7,9 @@ use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
@ -212,7 +212,7 @@ class EntityReferenceFieldTest extends EntityKernelTestBase {
// Create the default target entity.
$target_entity = EntityTestStringId::create([
'id' => $this->randomString(),
'type' => $this->bundle
'type' => $this->bundle,
]);
$target_entity->save();
@ -247,24 +247,24 @@ class EntityReferenceFieldTest extends EntityKernelTestBase {
->create(['name' => $this->randomString()]);
// Test content entity autocreation.
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
$this->assertUserAutocreate($entity, function (EntityInterface $entity, UserInterface $user) {
$entity->set('user_id', $user);
});
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
$this->assertUserAutocreate($entity, function (EntityInterface $entity, UserInterface $user) {
$entity->set('user_id', $user, FALSE);
});
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
$this->assertUserAutocreate($entity, function (EntityInterface $entity, UserInterface $user) {
$entity->user_id->setValue($user);
});
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
$this->assertUserAutocreate($entity, function (EntityInterface $entity, UserInterface $user) {
$entity->user_id[0]->get('entity')->setValue($user);
});
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
$this->assertUserAutocreate($entity, function (EntityInterface $entity, UserInterface $user) {
$entity->user_id->setValue(['entity' => $user, 'target_id' => NULL]);
});
try {
$message = 'Setting both the entity and an invalid target_id property fails.';
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
$this->assertUserAutocreate($entity, function (EntityInterface $entity, UserInterface $user) {
$user->save();
$entity->user_id->setValue(['entity' => $user, 'target_id' => $this->generateRandomEntityId()]);
});
@ -273,32 +273,32 @@ class EntityReferenceFieldTest extends EntityKernelTestBase {
catch (\InvalidArgumentException $e) {
$this->pass($message);
}
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
$this->assertUserAutocreate($entity, function (EntityInterface $entity, UserInterface $user) {
$entity->user_id = $user;
});
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
$this->assertUserAutocreate($entity, function (EntityInterface $entity, UserInterface $user) {
$entity->user_id->entity = $user;
});
// Test config entity autocreation.
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
$this->assertUserRoleAutocreate($entity, function (EntityInterface $entity, RoleInterface $role) {
$entity->set('user_role', $role);
});
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
$this->assertUserRoleAutocreate($entity, function (EntityInterface $entity, RoleInterface $role) {
$entity->set('user_role', $role, FALSE);
});
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
$this->assertUserRoleAutocreate($entity, function (EntityInterface $entity, RoleInterface $role) {
$entity->user_role->setValue($role);
});
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
$this->assertUserRoleAutocreate($entity, function (EntityInterface $entity, RoleInterface $role) {
$entity->user_role[0]->get('entity')->setValue($role);
});
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
$this->assertUserRoleAutocreate($entity, function (EntityInterface $entity, RoleInterface $role) {
$entity->user_role->setValue(['entity' => $role, 'target_id' => NULL]);
});
try {
$message = 'Setting both the entity and an invalid target_id property fails.';
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
$this->assertUserRoleAutocreate($entity, function (EntityInterface $entity, RoleInterface $role) {
$role->save();
$entity->user_role->setValue(['entity' => $role, 'target_id' => $this->generateRandomEntityId(TRUE)]);
});
@ -307,10 +307,10 @@ class EntityReferenceFieldTest extends EntityKernelTestBase {
catch (\InvalidArgumentException $e) {
$this->pass($message);
}
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
$this->assertUserRoleAutocreate($entity, function (EntityInterface $entity, RoleInterface $role) {
$entity->user_role = $role;
});
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
$this->assertUserRoleAutocreate($entity, function (EntityInterface $entity, RoleInterface $role) {
$entity->user_role->entity = $role;
});
@ -381,7 +381,7 @@ class EntityReferenceFieldTest extends EntityKernelTestBase {
$definitions = [
'target_reference' => BaseFieldDefinition::create('entity_reference')
->setSetting('target_type', $entity_type->id())
->setSetting('handler', 'default')
->setSetting('handler', 'default'),
];
$this->state->set('entity_test_update.additional_base_field_definitions', $definitions);
$this->entityManager->clearCachedDefinitions();

View file

@ -96,13 +96,11 @@ class EntityReferenceSelectionSortTest extends EntityKernelTestBase {
$selection_options = [
'target_type' => 'node',
'handler' => 'default',
'handler_settings' => [
'target_bundles' => NULL,
// Add sorting.
'sort' => [
'field' => 'field_text.value',
'direction' => 'DESC',
],
'target_bundles' => NULL,
// Add sorting.
'sort' => [
'field' => 'field_text.value',
'direction' => 'DESC',
],
];
$handler = $this->container->get('plugin.manager.entity_reference_selection')->getInstance($selection_options);
@ -117,7 +115,7 @@ class EntityReferenceSelectionSortTest extends EntityKernelTestBase {
$this->assertIdentical($result['article'], $expected_result, 'Query sorted by field returned expected values.');
// Assert sort by base field.
$selection_options['handler_settings']['sort'] = [
$selection_options['sort'] = [
'field' => 'nid',
'direction' => 'ASC',
];

View file

@ -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');
}
@ -88,9 +91,9 @@ class EntityRevisionTranslationTest extends EntityKernelTestBase {
}
/**
* Tests the translation values when saving a forward revision.
* Tests the translation values when saving a pending revision.
*/
public function testTranslationValuesWhenSavingForwardRevisions() {
public function testTranslationValuesWhenSavingPendingRevisions() {
$user = $this->createUser();
$storage = $this->entityManager->getStorage('entity_test_mulrev');
@ -103,33 +106,33 @@ class EntityRevisionTranslationTest extends EntityKernelTestBase {
$entity->addTranslation('de', ['name' => 'default revision - de']);
$entity->save();
// Create a forward revision for the entity and change a field value for
// Create a pending revision for the entity and change a field value for
// both languages.
$forward_revision = $this->reloadEntity($entity);
$pending_revision = $this->reloadEntity($entity);
$forward_revision->setNewRevision();
$forward_revision->isDefaultRevision(FALSE);
$pending_revision->setNewRevision();
$pending_revision->isDefaultRevision(FALSE);
$forward_revision->name = 'forward revision - en';
$forward_revision->save();
$pending_revision->name = 'pending revision - en';
$pending_revision->save();
$forward_revision_translation = $forward_revision->getTranslation('de');
$forward_revision_translation->name = 'forward revision - de';
$forward_revision_translation->save();
$pending_revision_translation = $pending_revision->getTranslation('de');
$pending_revision_translation->name = 'pending revision - de';
$pending_revision_translation->save();
$forward_revision_id = $forward_revision->getRevisionId();
$forward_revision = $storage->loadRevision($forward_revision_id);
$pending_revision_id = $pending_revision->getRevisionId();
$pending_revision = $storage->loadRevision($pending_revision_id);
// Change the value of the field in the default language, save the forward
// Change the value of the field in the default language, save the pending
// revision and check that the value of the field in the second language is
// also taken from the forward revision, *not* from the default revision.
$forward_revision->name = 'updated forward revision - en';
$forward_revision->save();
// also taken from the pending revision, *not* from the default revision.
$pending_revision->name = 'updated pending revision - en';
$pending_revision->save();
$forward_revision = $storage->loadRevision($forward_revision_id);
$pending_revision = $storage->loadRevision($pending_revision_id);
$this->assertEquals($forward_revision->name->value, 'updated forward revision - en');
$this->assertEquals($forward_revision->getTranslation('de')->name->value, 'forward revision - de');
$this->assertEquals($pending_revision->name->value, 'updated pending revision - en');
$this->assertEquals($pending_revision->getTranslation('de')->name->value, 'pending revision - de');
}
/**
@ -157,4 +160,124 @@ class EntityRevisionTranslationTest extends EntityKernelTestBase {
$this->assertFalse($translation->isDefaultRevision());
}
/**
* @covers \Drupal\Core\Entity\ContentEntityBase::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));
}
}

View file

@ -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());
}
}

View file

@ -2,12 +2,14 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
/**
* Tests adding a custom bundle field.
* Tests the default entity storage schema handler.
*
* @group system
* @group Entity
*/
class EntitySchemaTest extends EntityKernelTestBase {
@ -86,27 +88,199 @@ class EntitySchemaTest extends EntityKernelTestBase {
// Initially only the base table and the dedicated field data table should
// exist.
foreach ($tables as $index => $table) {
$this->assertEqual($schema_handler->tableExists($table), !$index, SafeMarkup::format('Entity schema correct for the @table table.', ['@table' => $table]));
$this->assertEqual($schema_handler->tableExists($table), !$index, new FormattableMarkup('Entity schema correct for the @table table.', ['@table' => $table]));
}
$this->assertTrue($schema_handler->tableExists($dedicated_tables[0]), SafeMarkup::format('Field schema correct for the @table table.', ['@table' => $table]));
$this->assertTrue($schema_handler->tableExists($dedicated_tables[0]), new FormattableMarkup('Field schema correct for the @table table.', ['@table' => $table]));
// Update the entity type definition and check that the entity schema now
// supports translations and revisions.
$this->updateEntityType(TRUE);
foreach ($tables as $table) {
$this->assertTrue($schema_handler->tableExists($table), SafeMarkup::format('Entity schema correct for the @table table.', ['@table' => $table]));
$this->assertTrue($schema_handler->tableExists($table), new FormattableMarkup('Entity schema correct for the @table table.', ['@table' => $table]));
}
foreach ($dedicated_tables as $table) {
$this->assertTrue($schema_handler->tableExists($table), SafeMarkup::format('Field schema correct for the @table table.', ['@table' => $table]));
$this->assertTrue($schema_handler->tableExists($table), new FormattableMarkup('Field schema correct for the @table table.', ['@table' => $table]));
}
// Revert changes and check that the entity schema now does not support
// neither translations nor revisions.
$this->updateEntityType(FALSE);
foreach ($tables as $index => $table) {
$this->assertEqual($schema_handler->tableExists($table), !$index, SafeMarkup::format('Entity schema correct for the @table table.', ['@table' => $table]));
$this->assertEqual($schema_handler->tableExists($table), !$index, new FormattableMarkup('Entity schema correct for the @table table.', ['@table' => $table]));
}
$this->assertTrue($schema_handler->tableExists($dedicated_tables[0]), SafeMarkup::format('Field schema correct for the @table table.', ['@table' => $table]));
$this->assertTrue($schema_handler->tableExists($dedicated_tables[0]), new FormattableMarkup('Field schema correct for the @table table.', ['@table' => $table]));
}
/**
* Tests deleting and creating a field that is part of a primary key.
*
* @param string $entity_type_id
* The ID of the entity type whose schema is being tested.
* @param string $field_name
* The name of the field that is being re-installed.
*
* @dataProvider providerTestPrimaryKeyUpdate
*/
public function testPrimaryKeyUpdate($entity_type_id, $field_name) {
// EntityKernelTestBase::setUp() already installs the schema for the
// 'entity_test' entity type.
if ($entity_type_id !== 'entity_test') {
$this->installEntitySchema($entity_type_id);
}
/* @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface $update_manager */
$update_manager = $this->container->get('entity.definition_update_manager');
$entity_type = $update_manager->getEntityType($entity_type_id);
/* @see \Drupal\Core\Entity\ContentEntityBase::baseFieldDefinitions() */
switch ($field_name) {
case 'id':
$field = BaseFieldDefinition::create('integer')
->setLabel('ID')
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
break;
case 'revision_id':
$field = BaseFieldDefinition::create('integer')
->setLabel('Revision ID')
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
break;
case 'langcode':
$field = BaseFieldDefinition::create('language')
->setLabel('Language');
if ($entity_type->isRevisionable()) {
$field->setRevisionable(TRUE);
}
if ($entity_type->isTranslatable()) {
$field->setTranslatable(TRUE);
}
break;
}
$field
->setName($field_name)
->setTargetEntityTypeId($entity_type_id)
->setProvider($entity_type->getProvider());
// Build up a map of expected primary keys depending on the entity type
// configuration.
$id_key = $entity_type->getKey('id');
$revision_key = $entity_type->getKey('revision');
$langcode_key = $entity_type->getKey('langcode');
$expected = [];
$expected[$entity_type->getBaseTable()] = [$id_key];
if ($entity_type->isRevisionable()) {
$expected[$entity_type->getRevisionTable()] = [$revision_key];
}
if ($entity_type->isTranslatable()) {
$expected[$entity_type->getDataTable()] = [$id_key, $langcode_key];
}
if ($entity_type->isRevisionable() && $entity_type->isTranslatable()) {
$expected[$entity_type->getRevisionDataTable()] = [$revision_key, $langcode_key];
}
// First, test explicitly deleting and re-installing a field. Make sure that
// all primary keys are there to start with.
$this->assertSame($expected, $this->findPrimaryKeys($entity_type));
// Then uninstall the field and make sure all primary keys that the field
// was part of have been updated. Since this is not a valid state of the
// entity type (for example a revisionable entity type without a revision ID
// field or a translatable entity type without a language code field) the
// actual primary keys at this point are irrelevant.
$update_manager->uninstallFieldStorageDefinition($field);
$this->assertNotEquals($expected, $this->findPrimaryKeys($entity_type));
// Finally, reinstall the field and make sure the primary keys have been
// recreated.
$update_manager->installFieldStorageDefinition($field->getName(), $entity_type_id, $field->getProvider(), $field);
$this->assertSame($expected, $this->findPrimaryKeys($entity_type));
// Now test updating a field without data. This will end up deleting
// and re-creating the field, similar to the code above.
$update_manager->updateFieldStorageDefinition($field);
$this->assertSame($expected, $this->findPrimaryKeys($entity_type));
// Now test updating a field with data.
/* @var \Drupal\Core\Entity\FieldableEntityStorageInterface $storage */
$storage = $this->entityManager->getStorage($entity_type_id);
// The schema of ID fields is incorrectly recreated as 'int' instead of
// 'serial', so we manually have to specify an ID.
// @todo Remove this in https://www.drupal.org/project/drupal/issues/2928906
$storage->create(['id' => 1, 'revision_id' => 1])->save();
$this->assertTrue($storage->countFieldData($field, TRUE));
$update_manager->updateFieldStorageDefinition($field);
$this->assertSame($expected, $this->findPrimaryKeys($entity_type));
$this->assertTrue($storage->countFieldData($field, TRUE));
}
/**
* Finds the primary keys for a given entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type whose primary keys are being fetched.
*
* @return array[]
* An array where the keys are the table names of the entity type's tables
* and the values are a list of the respective primary keys.
*/
protected function findPrimaryKeys(EntityTypeInterface $entity_type) {
$base_table = $entity_type->getBaseTable();
$revision_table = $entity_type->getRevisionTable();
$data_table = $entity_type->getDataTable();
$revision_data_table = $entity_type->getRevisionDataTable();
$schema = $this->database->schema();
$find_primary_key_columns = new \ReflectionMethod(get_class($schema), 'findPrimaryKeyColumns');
$find_primary_key_columns->setAccessible(TRUE);
// Build up a map of primary keys depending on the entity type
// configuration. If the field that is being removed is part of a table's
// primary key, we skip the assertion for that table as this represents an
// intermediate and invalid state of the entity schema.
$primary_keys[$base_table] = $find_primary_key_columns->invoke($schema, $base_table);
if ($entity_type->isRevisionable()) {
$primary_keys[$revision_table] = $find_primary_key_columns->invoke($schema, $revision_table);
}
if ($entity_type->isTranslatable()) {
$primary_keys[$data_table] = $find_primary_key_columns->invoke($schema, $data_table);
}
if ($entity_type->isRevisionable() && $entity_type->isTranslatable()) {
$primary_keys[$revision_data_table] = $find_primary_key_columns->invoke($schema, $revision_data_table);
}
return $primary_keys;
}
/**
* Provides test cases for EntitySchemaTest::testPrimaryKeyUpdate()
*
* @return array
* An array of test cases consisting of an entity type ID and a field name.
*/
public function providerTestPrimaryKeyUpdate() {
// Build up test cases for all possible entity type configurations.
// For each entity type we test reinstalling each field that is part of
// any table's primary key.
$tests = [];
$tests['entity_test:id'] = ['entity_test', 'id'];
$tests['entity_test_rev:id'] = ['entity_test_rev', 'id'];
$tests['entity_test_rev:revision_id'] = ['entity_test_rev', 'revision_id'];
$tests['entity_test_mul:id'] = ['entity_test_mul', 'id'];
$tests['entity_test_mul:langcode'] = ['entity_test_mul', 'langcode'];
$tests['entity_test_mulrev:id'] = ['entity_test_mulrev', 'id'];
$tests['entity_test_mulrev:revision_id'] = ['entity_test_mulrev', 'revision_id'];
$tests['entity_test_mulrev:langcode'] = ['entity_test_mulrev', 'langcode'];
return $tests;
}
/**

View file

@ -2,9 +2,10 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\TypedData\TranslationStatusInterface;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
@ -341,12 +342,12 @@ class EntityTranslationTest extends EntityLanguageTestBase {
// retrieve a translation referring to it.
$translation = $entity->getTranslation(LanguageInterface::LANGCODE_NOT_SPECIFIED);
$this->assertFalse($translation->isNewTranslation(), 'Existing translations are not marked as new.');
$this->assertIdentical($entity, $translation, 'The translation object corresponding to a non-default language is the entity object itself when the entity is language-neutral.');
$this->assertSame($entity, $translation, 'The translation object corresponding to a non-default language is the entity object itself when the entity is language-neutral.');
$entity->{$langcode_key}->value = $default_langcode;
$translation = $entity->getTranslation($default_langcode);
$this->assertIdentical($entity, $translation, 'The translation object corresponding to the default language (explicit) is the entity object itself.');
$this->assertSame($entity, $translation, 'The translation object corresponding to the default language (explicit) is the entity object itself.');
$translation = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT);
$this->assertIdentical($entity, $translation, 'The translation object corresponding to the default language (implicit) is the entity object itself.');
$this->assertSame($entity, $translation, 'The translation object corresponding to the default language (implicit) is the entity object itself.');
$this->assertTrue($entity->{$default_langcode_key}->value, 'The translation object is the default one.');
// Verify that trying to retrieve a translation for a locked language when
@ -657,7 +658,7 @@ class EntityTranslationTest extends EntityLanguageTestBase {
$translation = $this->entityManager->getTranslationFromContext($entity2, $default_langcode);
$translation_build = $controller->view($translation);
$translation_output = (string) $renderer->renderRoot($translation_build);
$this->assertIdentical($entity2_output, $translation_output, 'When the entity has no translation no fallback is applied.');
$this->assertSame($entity2_output, $translation_output, 'When the entity has no translation no fallback is applied.');
// Checks that entity translations are rendered properly.
$controller = $this->entityManager->getViewBuilder($entity_type);
@ -806,7 +807,7 @@ class EntityTranslationTest extends EntityLanguageTestBase {
foreach ($langcodes as $langcode) {
$adapter = $entity->getTranslation($langcode)->getTypedData();
$name = $adapter->get('name')->value;
$this->assertEqual($name, $values[$langcode]['name'], SafeMarkup::format('Name correctly retrieved from "@langcode" adapter', ['@langcode' => $langcode]));
$this->assertEqual($name, $values[$langcode]['name'], new FormattableMarkup('Name correctly retrieved from "@langcode" adapter', ['@langcode' => $langcode]));
}
}
@ -1013,4 +1014,31 @@ class EntityTranslationTest extends EntityLanguageTestBase {
}
}
/**
* Tests the translation object cache.
*/
public function testTranslationObjectCache() {
$default_langcode = $this->langcodes[1];
$translation_langcode = $this->langcodes[2];
$entity = EntityTestMul::create([
'name' => 'test',
'langcode' => $default_langcode,
]);
$entity->save();
$entity->addTranslation($translation_langcode)->save();
// Test that the default translation object is put into the translation
// object cache when a new translation object is initialized.
$entity = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId())->loadUnchanged($entity->id());
$default_translation_spl_object_hash = spl_object_hash($entity);
$this->assertEquals($default_translation_spl_object_hash, spl_object_hash($entity->getTranslation($translation_langcode)->getTranslation($default_langcode)));
// Test that non-default translations are always served from the translation
// object cache.
$entity = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId())->loadUnchanged($entity->id());
$this->assertEquals(spl_object_hash($entity->getTranslation($translation_langcode)), spl_object_hash($entity->getTranslation($translation_langcode)));
$this->assertEquals(spl_object_hash($entity->getTranslation($translation_langcode)), spl_object_hash($entity->getTranslation($translation_langcode)->getTranslation($default_langcode)->getTranslation($translation_langcode)));
}
}

View file

@ -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.
@ -41,7 +45,7 @@ class EntityTypeConstraintsTest extends EntityKernelTestBase {
$this->assertEqual($default_constraints + $extra_constraints, $entity_type->getConstraints());
// Test altering constraints.
$altered_constraints = ['Test' => [ 'some_setting' => TRUE]];
$altered_constraints = ['Test' => ['some_setting' => TRUE]];
$this->state->set('entity_test_constraints.alter', $altered_constraints);
// Clear the cache in state instance in the Drupal container, so it can pick
// up the modified value.

View file

@ -2,6 +2,9 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
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 +14,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 +35,11 @@ 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();
$this->typedDataManager = $this->container->get('typed_data_manager');
}
@ -81,11 +86,21 @@ class EntityTypedDataDefinitionTest extends KernelTestBase {
* Tests deriving metadata about entities.
*/
public function testEntities() {
NodeType::create([
'type' => 'article',
'name' => 'Article',
])->save();
$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.
@ -101,8 +116,8 @@ class EntityTypedDataDefinitionTest extends KernelTestBase {
// Test that the definition factory creates the right definitions for all
// entity data types variants.
$this->assertEqual($this->typedDataManager->createDataDefinition('entity'), EntityDataDefinition::create());
$this->assertEqual($this->typedDataManager->createDataDefinition('entity:node'), EntityDataDefinition::create('node'));
$this->assertEqual(serialize($this->typedDataManager->createDataDefinition('entity')), serialize(EntityDataDefinition::create()));
$this->assertEqual(serialize($this->typedDataManager->createDataDefinition('entity:node')), serialize(EntityDataDefinition::create('node')));
// Config entities don't support typed data.
$entity_definition = EntityDataDefinition::create('node_type');
@ -123,7 +138,40 @@ class EntityTypedDataDefinitionTest extends KernelTestBase {
// Test that the definition factory creates the right definition object.
$reference_definition2 = $this->typedDataManager->createDataDefinition('entity_reference');
$this->assertTrue($reference_definition2 instanceof DataReferenceDefinitionInterface);
$this->assertEqual($reference_definition2, $reference_definition);
$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->entityClassImplements(ConfigEntityInterface::class)->willReturn(FALSE);
$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],
];
}
}

View file

@ -3,6 +3,7 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests the Entity Validation API.
@ -16,7 +17,7 @@ class EntityValidationTest extends EntityKernelTestBase {
*
* @var array
*/
public static $modules = ['filter', 'text'];
public static $modules = ['filter', 'text', 'language'];
/**
* @var string
@ -39,6 +40,10 @@ class EntityValidationTest extends EntityKernelTestBase {
protected function setUp() {
parent::setUp();
// Enable an additional language.
ConfigurableLanguage::createFromLangcode('de')
->save();
// Create the test field.
module_load_install('entity_test');
entity_test_install();
@ -196,8 +201,53 @@ class EntityValidationTest extends EntityKernelTestBase {
$this->assertTrue($constraint instanceof CompositeConstraintBase, 'Constraint is composite constraint.');
$this->assertEqual('type', $violations[0]->getPropertyPath());
/** @var CompositeConstraintBase $constraint */
/** @var \Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase $constraint */
$this->assertEqual($constraint->coversFields(), ['name', 'type'], 'Information about covered fields can be retrieved.');
}
/**
* Tests the EntityChangedConstraintValidator with multiple translations.
*/
public function testEntityChangedConstraintOnConcurrentMultilingualEditing() {
$this->installEntitySchema('entity_test_mulrev_changed');
$storage = \Drupal::entityTypeManager()
->getStorage('entity_test_mulrev_changed');
// Create a test entity.
$entity = $this->createTestEntity('entity_test_mulrev_changed');
$entity->save();
$entity->setChangedTime($entity->getChangedTime() - 1);
$violations = $entity->validate();
$this->assertEquals(1, $violations->count());
$this->assertEqual($violations[0]->getMessage(), 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
$entity = $storage->loadUnchanged($entity->id());
$translation = $entity->addTranslation('de');
$entity->save();
// Ensure that the new translation has a newer changed timestamp than the
// default translation.
$this->assertGreaterThan($entity->getChangedTime(), $translation->getChangedTime());
// Simulate concurrent form editing by saving the entity with an altered
// non-translatable field in order for the changed timestamp to be updated
// across all entity translations.
$original_entity_time = $entity->getChangedTime();
$entity->set('not_translatable', $this->randomString());
$entity->save();
// Simulate form submission of an uncached form by setting the previous
// timestamp of an entity translation on the saved entity object. This
// happens in the entity form API where we put the changed timestamp of
// the entity in a form hidden value and then set it on the entity which on
// form submit is loaded from the storage if the form is not yet cached.
$entity->setChangedTime($original_entity_time);
// Setting the changed timestamp from the user input on the entity loaded
// from the storage is used as a prevention from saving a form built with a
// previous version of the entity and thus reverting changes by other users.
$violations = $entity->validate();
$this->assertEquals(1, $violations->count());
$this->assertEqual($violations[0]->getMessage(), 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
}
}

Some files were not shown because too many files have changed in this diff Show more