EntityDefinitionUpdateTest.php 47.6 KB
Newer Older
1 2
<?php

3
namespace Drupal\KernelTests\Core\Entity;
4

5
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
6
use Drupal\Core\Database\Database;
7
use Drupal\Core\Database\DatabaseExceptionWrapper;
8
use Drupal\Core\Database\IntegrityConstraintViolationException;
9
use Drupal\Core\Entity\ContentEntityType;
10
use Drupal\Core\Entity\EntityStorageException;
11
use Drupal\Core\Entity\EntityTypeEvents;
12 13
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
use Drupal\Core\Field\BaseFieldDefinition;
14
use Drupal\Core\Field\FieldException;
15
use Drupal\Core\Field\FieldStorageDefinitionEvents;
16
use Drupal\Core\Language\LanguageInterface;
17
use Drupal\entity_test_update\Entity\EntityTestUpdate;
18
use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
19 20 21 22 23 24

/**
 * Tests EntityDefinitionUpdateManager functionality.
 *
 * @group Entity
 */
25
class EntityDefinitionUpdateTest extends EntityKernelTestBase {
26

27 28
  use EntityDefinitionTestTrait;

29 30 31 32 33 34 35 36 37 38 39 40 41 42
  /**
   * The entity definition update manager.
   *
   * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
   */
  protected $entityDefinitionUpdateManager;

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

43 44 45 46 47 48 49
  /**
   * Modules to enable.
   *
   * @var array
   */
  public static $modules = ['entity_test_update'];

50 51 52 53 54 55 56
  /**
   * {@inheritdoc}
   */
  protected function setUp() {
    parent::setUp();
    $this->entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager');
    $this->database = $this->container->get('database');
57 58 59

    // Install every entity type's schema that wasn't installed in the parent
    // method.
60
    foreach (array_diff_key($this->entityManager->getDefinitions(), array_flip(['user', 'entity_test'])) as $entity_type_id => $entity_type) {
61 62
      $this->installEntitySchema($entity_type_id);
    }
63 64
  }

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
  /**
   * Tests that new entity type definitions are correctly handled.
   */
  public function testNewEntityType() {
    $entity_type_id = 'entity_test_new';
    $schema = $this->database->schema();

    // Check that the "entity_test_new" is not defined.
    $entity_types = $this->entityManager->getDefinitions();
    $this->assertFalse(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type does not exist.');
    $this->assertFalse($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type does not exist.');

    // Check that the "entity_test_new" is now defined and the related schema
    // has been created.
    $this->enableNewEntityType();
    $entity_types = $this->entityManager->getDefinitions();
    $this->assertTrue(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type exists.');
    $this->assertTrue($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type has been created.');
  }

85 86 87 88 89 90
  /**
   * Tests when no definition update is needed.
   */
  public function testNoUpdates() {
    // Ensure that the definition update manager reports no updates.
    $this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that no updates are needed.');
91
    $this->assertIdentical($this->entityDefinitionUpdateManager->getChangeSummary(), [], 'EntityDefinitionUpdateManager reports an empty change summary.');
92 93 94 95 96 97 98 99 100

    // Ensure that applyUpdates() runs without error (it's not expected to do
    // anything when there aren't updates).
    $this->entityDefinitionUpdateManager->applyUpdates();
  }

  /**
   * Tests updating entity schema when there are no existing entities.
   */
101 102 103 104
  public function testEntityTypeUpdateWithoutData() {
    // The 'entity_test_update' entity type starts out non-revisionable, so
    // ensure the revision table hasn't been created during setUp().
    $this->assertFalse($this->database->schema()->tableExists('entity_test_update_revision'), 'Revision table not created for entity_test_update.');
105

106 107 108
    // Update it to be revisionable and ensure the definition update manager
    // reports that an update is needed.
    $this->updateEntityTypeToRevisionable();
109
    $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
110 111
    $expected = [
      'entity_test_update' => [
112
        t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]),
113 114 115
        // 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']),
116 117
      ],
    ];
118
    $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected); // , 'EntityDefinitionUpdateManager reports the expected change summary.');
119 120 121

    // Run the update and ensure the revision table is created.
    $this->entityDefinitionUpdateManager->applyUpdates();
122
    $this->assertTrue($this->database->schema()->tableExists('entity_test_update_revision'), 'Revision table created for entity_test_update.');
123 124 125 126 127
  }

  /**
   * Tests updating entity schema when there are existing entities.
   */
128 129 130 131 132 133 134 135 136 137
  public function testEntityTypeUpdateWithData() {
    // Save an entity.
    $this->entityManager->getStorage('entity_test_update')->create()->save();

    // Update the entity type to be revisionable and try to apply the update.
    // It's expected to throw an exception.
    $this->updateEntityTypeToRevisionable();
    try {
      $this->entityDefinitionUpdateManager->applyUpdates();
      $this->fail('EntityStorageException thrown when trying to apply an update that requires data migration.');
138
    }
139 140 141 142
    catch (EntityStorageException $e) {
      $this->pass('EntityStorageException thrown when trying to apply an update that requires data migration.');
    }
  }
143

144 145 146 147 148 149 150 151
  /**
   * Tests creating, updating, and deleting a base field if no entities exist.
   */
  public function testBaseFieldCreateUpdateDeleteWithoutData() {
    // Add a base field, ensure the update manager reports it, and the update
    // creates its schema.
    $this->addBaseField();
    $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
152 153
    $expected = [
      'entity_test_update' => [
154
        t('The %field_name field needs to be installed.', ['%field_name' => t('A new base field')]),
155 156
      ],
    ];
157
    $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
158 159 160 161 162 163 164
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.');

    // Add an index on the base field, ensure the update manager reports it,
    // and the update creates it.
    $this->addBaseFieldIndex();
    $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
165 166
    $expected = [
      'entity_test_update' => [
167
        t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]),
168 169
      ],
    ];
170
    $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
171 172 173 174 175 176 177
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), 'Index created.');

    // Remove the above index, ensure the update manager reports it, and the
    // update deletes it.
    $this->removeBaseFieldIndex();
    $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
178 179
    $expected = [
      'entity_test_update' => [
180
        t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]),
181 182
      ],
    ];
183
    $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
184 185 186 187 188 189 190 191
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), 'Index deleted.');

    // Update the type of the base field from 'string' to 'text', ensure the
    // update manager reports it, and the update adjusts the schema
    // accordingly.
    $this->modifyBaseField();
    $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
192 193
    $expected = [
      'entity_test_update' => [
194
        t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]),
195 196
      ],
    ];
197
    $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
198 199 200 201 202 203 204 205 206
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Original column deleted in shared table for new_base_field.');
    $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field__value'), 'Value column created in shared table for new_base_field.');
    $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field__format'), 'Format column created in shared table for new_base_field.');

    // Remove the base field, ensure the update manager reports it, and the
    // update deletes the schema.
    $this->removeBaseField();
    $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
207 208
    $expected = [
      'entity_test_update' => [
209
        t('The %field_name field needs to be uninstalled.', ['%field_name' => t('A new base field')]),
210 211
      ],
    ];
212
    $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
213 214 215 216 217 218 219 220 221 222 223 224 225
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field_value'), 'Value column deleted from shared table for new_base_field.');
    $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field_format'), 'Format column deleted from shared table for new_base_field.');
  }

  /**
   * Tests creating, updating, and deleting a bundle field if no entities exist.
   */
  public function testBundleFieldCreateUpdateDeleteWithoutData() {
    // Add a bundle field, ensure the update manager reports it, and the update
    // creates its schema.
    $this->addBundleField();
    $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
226 227
    $expected = [
      'entity_test_update' => [
228
        t('The %field_name field needs to be installed.', ['%field_name' => t('A new bundle field')]),
229 230
      ],
    ];
231
    $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
232 233 234 235 236 237 238 239
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertTrue($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.');

    // Update the type of the base field from 'string' to 'text', ensure the
    // update manager reports it, and the update adjusts the schema
    // accordingly.
    $this->modifyBundleField();
    $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
240
    $expected = [
241 242 243
      'entity_test_update' => [
        t('The %field_name field needs to be updated.', ['%field_name' => t('A new bundle field')]),
      ],
244
    ];
245
    $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
246 247 248 249 250 251 252
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertTrue($this->database->schema()->fieldExists('entity_test_update__new_bundle_field', 'new_bundle_field_format'), 'Format column created in dedicated table for new_base_field.');

    // Remove the bundle field, ensure the update manager reports it, and the
    // update deletes the schema.
    $this->removeBundleField();
    $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
253 254
    $expected = [
      'entity_test_update' => [
255
        t('The %field_name field needs to be uninstalled.', ['%field_name' => t('A new bundle field')]),
256 257
      ],
    ];
258
    $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertFalse($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.');
  }

  /**
   * Tests creating and deleting a base field if entities exist.
   *
   * This tests deletion when there are existing entities, but not existing data
   * for the field being deleted.
   *
   * @see testBaseFieldDeleteWithExistingData()
   */
  public function testBaseFieldCreateDeleteWithExistingEntities() {
    // Save an entity.
    $name = $this->randomString();
274
    $storage = $this->entityManager->getStorage('entity_test_update');
275
    $entity = $storage->create(['name' => $name]);
276 277 278 279 280 281
    $entity->save();

    // Add a base field and run the update. Ensure the base field's column is
    // created and the prior saved entity data is still there.
    $this->addBaseField();
    $this->entityDefinitionUpdateManager->applyUpdates();
282 283
    $schema_handler = $this->database->schema();
    $this->assertTrue($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.');
284 285 286 287 288 289 290
    $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
    $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.');

    // Remove the base field and run the update. Ensure the base field's column
    // is deleted and the prior saved entity data is still there.
    $this->removeBaseField();
    $this->entityDefinitionUpdateManager->applyUpdates();
291
    $this->assertFalse($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column deleted from shared table for new_base_field.');
292 293
    $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
    $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.');
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314

    // Add a base field with a required property and run the update. Ensure
    // 'not null' is not applied and thus no exception is thrown.
    $this->addBaseField('shape_required');
    $this->entityDefinitionUpdateManager->applyUpdates();
    $assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
    $this->assertTrue($assert, 'Columns created in shared table for new_base_field.');

    // Recreate the field after emptying the base table and check that its
    // columns are not 'not null'.
    // @todo Revisit this test when allowing for required storage field
    //   definitions. See https://www.drupal.org/node/2390495.
    $entity->delete();
    $this->removeBaseField();
    $this->entityDefinitionUpdateManager->applyUpdates();
    $assert = !$schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && !$schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
    $this->assert($assert, 'Columns removed from the shared table for new_base_field.');
    $this->addBaseField('shape_required');
    $this->entityDefinitionUpdateManager->applyUpdates();
    $assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
    $this->assertTrue($assert, 'Columns created again in shared table for new_base_field.');
315
    $entity = $storage->create(['name' => $name]);
316 317
    $entity->save();
    $this->pass('The new_base_field columns are still nullable');
318 319 320 321 322 323 324 325 326 327 328
  }

  /**
   * Tests creating and deleting a bundle field if entities exist.
   *
   * This tests deletion when there are existing entities, but not existing data
   * for the field being deleted.
   *
   * @see testBundleFieldDeleteWithExistingData()
   */
  public function testBundleFieldCreateDeleteWithExistingEntities() {
329
    // Save an entity.
330
    $name = $this->randomString();
331
    $storage = $this->entityManager->getStorage('entity_test_update');
332
    $entity = $storage->create(['name' => $name]);
333 334 335 336 337 338
    $entity->save();

    // Add a bundle field and run the update. Ensure the bundle field's table
    // is created and the prior saved entity data is still there.
    $this->addBundleField();
    $this->entityDefinitionUpdateManager->applyUpdates();
339 340
    $schema_handler = $this->database->schema();
    $this->assertTrue($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.');
341 342 343 344 345 346 347
    $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
    $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.');

    // Remove the base field and run the update. Ensure the bundle field's
    // table is deleted and the prior saved entity data is still there.
    $this->removeBundleField();
    $this->entityDefinitionUpdateManager->applyUpdates();
348
    $this->assertFalse($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.');
349 350
    $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
    $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.');
351 352 353 354 355

    // Test that required columns are created as 'not null'.
    $this->addBundleField('shape_required');
    $this->entityDefinitionUpdateManager->applyUpdates();
    $message = 'The new_bundle_field_shape column is not nullable.';
356
    $values = [
357
      'bundle' => $entity->bundle(),
358
      'deleted' => 0,
359 360 361 362 363
      'entity_id' => $entity->id(),
      'revision_id' => $entity->id(),
      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
      'delta' => 0,
      'new_bundle_field_color' => $this->randomString(),
364
    ];
365 366 367 368 369 370 371 372
    try {
      // Try to insert a record without providing a value for the 'not null'
      // column. This should fail.
      $this->database->insert('entity_test_update__new_bundle_field')
        ->fields($values)
        ->execute();
      $this->fail($message);
    }
373 374 375 376 377 378 379 380 381
    catch (\RuntimeException $e) {
      if ($e instanceof DatabaseExceptionWrapper || $e instanceof IntegrityConstraintViolationException) {
        // Now provide a value for the 'not null' column. This is expected to
        // succeed.
        $values['new_bundle_field_shape'] = $this->randomString();
        $this->database->insert('entity_test_update__new_bundle_field')
          ->fields($values)
          ->execute();
        $this->pass($message);
382 383
      }
      else {
384 385 386
        // Keep throwing it.
        throw $e;
      }
387
    }
388 389 390 391 392 393 394 395 396 397 398
  }

  /**
   * Tests deleting a base field when it has existing data.
   */
  public function testBaseFieldDeleteWithExistingData() {
    // Add the base field and run the update.
    $this->addBaseField();
    $this->entityDefinitionUpdateManager->applyUpdates();

    // Save an entity with the base field populated.
399
    $this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => 'foo'])->save();
400

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
    // 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.');
    }
    catch (FieldStorageDefinitionUpdateForbiddenException $e) {
      $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
    }
  }

  /**
   * Tests deleting a bundle field when it has existing data.
   */
  public function testBundleFieldDeleteWithExistingData() {
    // Add the bundle field and run the update.
    $this->addBundleField();
    $this->entityDefinitionUpdateManager->applyUpdates();

    // Save an entity with the bundle field populated.
    entity_test_create_bundle('custom');
425
    $this->entityManager->getStorage('entity_test_update')->create(['type' => 'test_bundle', 'new_bundle_field' => 'foo'])->save();
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449

    // 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.
    $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.');
    }
  }

  /**
   * Tests updating a base field when it has existing data.
   */
  public function testBaseFieldUpdateWithExistingData() {
    // Add the base field and run the update.
    $this->addBaseField();
    $this->entityDefinitionUpdateManager->applyUpdates();

    // Save an entity with the base field populated.
450
    $this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => 'foo'])->save();
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473

    // Change the field's field type and apply updates. It's expected to
    // throw an exception.
    $this->modifyBaseField();
    try {
      $this->entityDefinitionUpdateManager->applyUpdates();
      $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
    }
    catch (FieldStorageDefinitionUpdateForbiddenException $e) {
      $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
    }
  }

  /**
   * Tests updating a bundle field when it has existing data.
   */
  public function testBundleFieldUpdateWithExistingData() {
    // Add the bundle field and run the update.
    $this->addBundleField();
    $this->entityDefinitionUpdateManager->applyUpdates();

    // Save an entity with the bundle field populated.
    entity_test_create_bundle('custom');
474
    $this->entityManager->getStorage('entity_test_update')->create(['type' => 'test_bundle', 'new_bundle_field' => 'foo'])->save();
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495

    // Change the field's field type and apply updates. It's expected to
    // throw an exception.
    $this->modifyBundleField();
    try {
      $this->entityDefinitionUpdateManager->applyUpdates();
      $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
    }
    catch (FieldStorageDefinitionUpdateForbiddenException $e) {
      $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
    }
  }

  /**
   * Tests creating and deleting a multi-field index when there are no existing entities.
   */
  public function testEntityIndexCreateDeleteWithoutData() {
    // Add an entity index and ensure the update manager reports that as an
    // update to the entity type.
    $this->addEntityIndex();
    $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
496 497
    $expected = [
      'entity_test_update' => [
498
        t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]),
499 500
      ],
    ];
501
    $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
502 503 504 505 506 507 508 509 510

    // Run the update and ensure the new index is created.
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.');

    // Remove the index and ensure the update manager reports that as an
    // update to the entity type.
    $this->removeEntityIndex();
    $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
511 512
    $expected = [
      'entity_test_update' => [
513
        t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]),
514 515
      ],
    ];
516
    $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
517 518 519 520

    // Run the update and ensure the index is deleted.
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.');
521 522 523 524 525 526 527 528 529 530 531 532

    // Test that composite indexes are handled correctly when dropping and
    // re-creating one of their columns.
    $this->addEntityIndex();
    $this->entityDefinitionUpdateManager->applyUpdates();
    $storage_definition = $this->entityDefinitionUpdateManager->getFieldStorageDefinition('name', 'entity_test_update');
    $this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition);
    $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.');
    $this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition);
    $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.');
    $this->entityDefinitionUpdateManager->installFieldStorageDefinition('name', 'entity_test_update', 'entity_test', $storage_definition);
    $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created again.');
533 534 535 536 537 538 539 540
  }

  /**
   * Tests creating a multi-field index when there are existing entities.
   */
  public function testEntityIndexCreateWithData() {
    // Save an entity.
    $name = $this->randomString();
541
    $entity = $this->entityManager->getStorage('entity_test_update')->create(['name' => $name]);
542 543
    $entity->save();

alexpott's avatar
alexpott committed
544 545
    // Add an entity index, run the update. Ensure that the index is created
    // despite having data.
546
    $this->addEntityIndex();
alexpott's avatar
alexpott committed
547 548
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index added.');
549 550
  }

551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
  /**
   * Tests entity type and field storage definition events.
   */
  public function testDefinitionEvents() {
    /** @var \Drupal\entity_test\EntityTestDefinitionSubscriber $event_subscriber */
    $event_subscriber = $this->container->get('entity_test.definition.subscriber');
    $event_subscriber->enableEventTracking();

    // Test field storage definition events.
    $storage_definition = current($this->entityManager->getFieldStorageDefinitions('entity_test_rev'));
    $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete was not dispatched yet.');
    $this->entityManager->onFieldStorageDefinitionDelete($storage_definition);
    $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete event successfully dispatched.');
    $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create was not dispatched yet.');
    $this->entityManager->onFieldStorageDefinitionCreate($storage_definition);
    $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create event successfully dispatched.');
    $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::UPDATE), 'Entity type update was not dispatched yet.');
    $this->entityManager->onFieldStorageDefinitionUpdate($storage_definition, $storage_definition);
    $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::UPDATE), 'Entity type update event successfully dispatched.');

    // Test entity type events.
    $entity_type = $this->entityManager->getDefinition('entity_test_rev');
    $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::CREATE), 'Entity type create was not dispatched yet.');
    $this->entityManager->onEntityTypeCreate($entity_type);
    $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::CREATE), 'Entity type create event successfully dispatched.');
    $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::UPDATE), 'Entity type update was not dispatched yet.');
    $this->entityManager->onEntityTypeUpdate($entity_type, $entity_type);
    $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::UPDATE), 'Entity type update event successfully dispatched.');
    $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::DELETE), 'Entity type delete was not dispatched yet.');
    $this->entityManager->onEntityTypeDelete($entity_type);
    $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::DELETE), 'Entity type delete event successfully dispatched.');
  }

584
  /**
585 586 587 588
   * Tests updating entity schema and creating a base field.
   *
   * This tests updating entity schema and creating a base field at the same
   * time when there are no existing entities.
589 590 591 592
   */
  public function testEntityTypeSchemaUpdateAndBaseFieldCreateWithoutData() {
    $this->updateEntityTypeToRevisionable();
    $this->addBaseField();
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
    $message = 'Successfully updated entity schema and created base field at the same time.';
    // Entity type updates create base fields as well, thus make sure doing both
    // at the same time does not lead to errors due to the base field being
    // created twice.
    try {
      $this->entityDefinitionUpdateManager->applyUpdates();
      $this->pass($message);
    }
    catch (\Exception $e) {
      $this->fail($message);
      throw $e;
    }
  }

  /**
   * Tests updating entity schema and creating a revisionable base field.
   *
   * This tests updating entity schema and creating a revisionable base field
   * at the same time when there are no existing entities.
   */
  public function testEntityTypeSchemaUpdateAndRevisionableBaseFieldCreateWithoutData() {
    $this->updateEntityTypeToRevisionable();
    $this->addRevisionableBaseField();
    $message = 'Successfully updated entity schema and created revisionable base field at the same time.';
617 618 619 620 621
    // Entity type updates create base fields as well, thus make sure doing both
    // at the same time does not lead to errors due to the base field being
    // created twice.
    try {
      $this->entityDefinitionUpdateManager->applyUpdates();
622
      $this->pass($message);
623 624
    }
    catch (\Exception $e) {
625
      $this->fail($message);
626 627 628 629
      throw $e;
    }
  }

630
  /**
631
   * Tests applying single updates.
632 633
   */
  public function testSingleActionCalls() {
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
    $db_schema = $this->database->schema();

    // Ensure that a non-existing entity type cannot be installed.
    $message = 'A non-existing entity type cannot be installed';
    try {
      $this->entityDefinitionUpdateManager->installEntityType(new ContentEntityType(['id' => 'foo']));
      $this->fail($message);
    }
    catch (PluginNotFoundException $e) {
      $this->pass($message);
    }

    // Ensure that a field cannot be installed on non-existing entity type.
    $message = 'A field cannot be installed on a non-existing entity type';
    try {
      $storage_definition = BaseFieldDefinition::create('string')
        ->setLabel(t('A new revisionable base field'))
        ->setRevisionable(TRUE);
      $this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'foo', 'entity_test', $storage_definition);
      $this->fail($message);
    }
    catch (PluginNotFoundException $e) {
      $this->pass($message);
    }

    // Ensure that a non-existing field cannot be installed.
    $storage_definition = BaseFieldDefinition::create('string')
      ->setLabel(t('A new revisionable base field'))
      ->setRevisionable(TRUE);
    $this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'entity_test_update', 'entity_test', $storage_definition);
    $this->assertFalse($db_schema->fieldExists('entity_test_update', 'bar'), "A non-existing field cannot be installed.");

    // Ensure that installing an existing entity type is a no-op.
    $entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update');
    $this->entityDefinitionUpdateManager->installEntityType($entity_type);
    $this->assertTrue($db_schema->tableExists('entity_test_update'), 'Installing an existing entity type is a no-op');
670 671 672

    // Create a new base field.
    $this->addRevisionableBaseField();
673 674 675 676 677 678 679
    $storage_definition = BaseFieldDefinition::create('string')
      ->setLabel(t('A new revisionable base field'))
      ->setRevisionable(TRUE);
    $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.");

680
    // Ensure that installing an existing field is a no-op.
681
    $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
682
    $this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), 'Installing an existing field is a no-op');
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704

    // Update an existing field schema.
    $this->modifyBaseField();
    $storage_definition = BaseFieldDefinition::create('text')
      ->setName('new_base_field')
      ->setTargetEntityTypeId('entity_test_update')
      ->setLabel(t('A new revisionable base field'))
      ->setRevisionable(TRUE);
    $this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition);
    $this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "Previous schema for 'new_base_field' no longer exists.");
    $this->assertTrue(
      $db_schema->fieldExists('entity_test_update', 'new_base_field__value') && $db_schema->fieldExists('entity_test_update', 'new_base_field__format'),
      "New schema for 'new_base_field' has been created."
    );

    // Drop an existing field schema.
    $this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition);
    $this->assertFalse(
      $db_schema->fieldExists('entity_test_update', 'new_base_field__value') || $db_schema->fieldExists('entity_test_update', 'new_base_field__format'),
      "The schema for 'new_base_field' has been dropped."
    );

705 706
    // Make the entity type revisionable.
    $this->updateEntityTypeToRevisionable();
707 708 709 710 711 712 713
    $this->assertFalse($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' does not exist before applying the update.");
    $entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update');
    $keys = $entity_type->getKeys();
    $keys['revision'] = 'revision_id';
    $entity_type->set('entity_keys', $keys);
    $this->entityDefinitionUpdateManager->updateEntityType($entity_type);
    $this->assertTrue($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' table has been created.");
714 715
  }

716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
  /**
   * Ensures that a new field and index on a shared table are created.
   *
   * @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::createSharedTableSchema
   */
  public function testCreateFieldAndIndexOnSharedTable() {
    $this->addBaseField();
    $this->addBaseFieldIndex();
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
    $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), "New index 'entity_test_update_field__new_base_field' has been created on the 'entity_test_update' table.");
    // Check index size in for MySQL.
    if (Database::getConnection()->driver() == 'mysql') {
      $result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update_field__new_base_field\' and column_name = \'new_base_field\'')->fetchObject();
      $this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.');
    }
  }

  /**
   * Ensures that a new entity level index is created when data exists.
   *
   * @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::onEntityTypeUpdate
   */
  public function testCreateIndexUsingEntityStorageSchemaWithData() {
    // Save an entity.
    $name = $this->randomString();
    $storage = $this->entityManager->getStorage('entity_test_update');
743
    $entity = $storage->create(['name' => $name]);
744 745 746
    $entity->save();

    // Create an index.
747 748 749
    $indexes = [
      'entity_test_update__type_index' => ['type'],
    ];
750 751 752 753 754 755 756 757 758 759
    $this->state->set('entity_test_update.additional_entity_indexes', $indexes);
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__type_index'), "New index 'entity_test_update__type_index' has been created on the 'entity_test_update' table.");
    // Check index size in for MySQL.
    if (Database::getConnection()->driver() == 'mysql') {
      $result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update__type_index\' and column_name = \'type\'')->fetchObject();
      $this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.');
    }
  }

760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
  /**
   * Tests updating a base field when it has existing data.
   */
  public function testBaseFieldEntityKeyUpdateWithExistingData() {
    // Add the base field and run the update.
    $this->addBaseField();
    $this->entityDefinitionUpdateManager->applyUpdates();

    // Save an entity with the base field populated.
    $this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => $this->randomString()])->save();

    // Save an entity with the base field not populated.
    /** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */
    $entity = $this->entityManager->getStorage('entity_test_update')->create();
    $entity->save();

    // Promote the base field to an entity key. This will trigger the addition
    // of a NOT NULL constraint.
    $this->makeBaseFieldEntityKey();

780 781 782 783 784
    // 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'));

785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
    // 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 {
      $this->entityDefinitionUpdateManager->applyUpdates();
      $this->fail($message);
    }
    catch (EntityStorageException $e) {
      $this->pass($message);
    }

    // Check that the update is correctly applied when no NULL data is left.
    $entity->set('new_base_field', $this->randomString());
    $entity->save();
    $this->entityDefinitionUpdateManager->applyUpdates();
    $this->pass('The update is correctly performed when no NULL data exists.');

    // Check that the update actually applied a NOT NULL constraint.
    $entity->set('new_base_field', NULL);
    $message = 'The NOT NULL constraint was correctly applied.';
    try {
      $entity->save();
      $this->fail($message);
    }
    catch (EntityStorageException $e) {
      $this->pass($message);
    }
  }

813 814 815
  /**
   * Check that field schema is correctly handled with long-named fields.
   */
816
  public function testLongNameFieldIndexes() {
817 818 819 820 821 822 823 824 825
    $this->addLongNameBaseField();
    $entity_type_id = 'entity_test_update';
    $entity_type = $this->entityManager->getDefinition($entity_type_id);
    $definitions = EntityTestUpdate::baseFieldDefinitions($entity_type);
    $name = 'new_long_named_entity_reference_base_field';
    $this->entityDefinitionUpdateManager->installFieldStorageDefinition($name, $entity_type_id, 'entity_test', $definitions[$name]);
    $this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'Entity and field schema data are correctly detected.');
  }

826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
  /**
   * 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.
   */
  public function testInitialValueFromField() {
    $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(['name' => 'First entity'])->save();
    $storage->create(['name' => 'Second entity'])->save();

    // Add a base field with an initial value inherited from another field.
    $this->addBaseField();
    $storage_definition = BaseFieldDefinition::create('string')
      ->setLabel(t('A new base field'))
      ->setInitialValueFromField('name');

    $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('First entity', $entities[1]->get('new_base_field')->value);
    $this->assertEquals('Second entity', $entities[2]->get('new_base_field')->value);
  }

  /**
   * 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.');
    }
  }

941
}