Pull merge.
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / Entity / EntityFieldTest.php
1 <?php
2
3 namespace Drupal\KernelTests\Core\Entity;
4
5 use Drupal\Core\Entity\EntityInterface;
6 use Drupal\Core\Entity\EntityStorageException;
7 use Drupal\Core\Entity\RevisionLogInterface;
8 use Drupal\Core\Entity\TypedData\EntityDataDefinition;
9 use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
10 use Drupal\Core\Field\BaseFieldDefinition;
11 use Drupal\Core\Field\FieldDefinitionInterface;
12 use Drupal\Core\Field\FieldItemInterface;
13 use Drupal\Core\Field\FieldItemListInterface;
14 use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
15 use Drupal\Core\TypedData\ComplexDataInterface;
16 use Drupal\Core\TypedData\DataDefinitionInterface;
17 use Drupal\Core\TypedData\ListInterface;
18 use Drupal\Core\TypedData\Type\StringInterface;
19 use Drupal\Core\TypedData\TypedDataInterface;
20 use Drupal\entity_test\Entity\EntityTest;
21 use Drupal\entity_test\Entity\EntityTestComputedField;
22 use Drupal\node\Entity\Node;
23 use Drupal\node\Entity\NodeType;
24
25 /**
26  * Tests the Entity Field API.
27  *
28  * @group Entity
29  */
30 class EntityFieldTest extends EntityKernelTestBase {
31
32   /**
33    * Modules to enable.
34    *
35    * @var array
36    */
37   public static $modules = ['filter', 'text', 'node', 'user', 'field_test'];
38
39   /**
40    * @var string
41    */
42   protected $entityName;
43
44   /**
45    * @var \Drupal\user\Entity\User
46    */
47   protected $entityUser;
48
49   /**
50    * @var string
51    */
52   protected $entityFieldText;
53
54   protected function setUp() {
55     parent::setUp();
56
57     foreach (entity_test_entity_types() as $entity_type_id) {
58       // The entity_test schema is installed by the parent.
59       if ($entity_type_id != 'entity_test') {
60         $this->installEntitySchema($entity_type_id);
61       }
62     }
63
64     // Create the test field.
65     module_load_install('entity_test');
66     entity_test_install();
67
68     // Install required default configuration for filter module.
69     $this->installConfig(['system', 'filter']);
70   }
71
72   /**
73    * Creates a test entity.
74    *
75    * @return \Drupal\Core\Entity\EntityInterface
76    */
77   protected function createTestEntity($entity_type) {
78     $this->entityName = $this->randomMachineName();
79     $this->entityUser = $this->createUser();
80     $this->entityFieldText = $this->randomMachineName();
81
82     // Pass in the value of the name field when creating. With the user
83     // field we test setting a field after creation.
84     $entity = $this->container->get('entity_type.manager')
85       ->getStorage($entity_type)
86       ->create();
87     $entity->user_id->target_id = $this->entityUser->id();
88     $entity->name->value = $this->entityName;
89
90     // Set a value for the test field.
91     $entity->field_test_text->value = $this->entityFieldText;
92
93     return $entity;
94   }
95
96   /**
97    * Tests reading and writing properties and field items.
98    */
99   public function testReadWrite() {
100     // All entity variations have to have the same results.
101     foreach (entity_test_entity_types() as $entity_type) {
102       $this->doTestReadWrite($entity_type);
103     }
104   }
105
106   /**
107    * Executes the read write test set for a defined entity type.
108    *
109    * @param string $entity_type
110    *   The entity type to run the tests with.
111    */
112   protected function doTestReadWrite($entity_type) {
113     $entity = $this->createTestEntity($entity_type);
114
115     $langcode = 'en';
116
117     // Access the name field.
118     $this->assertTrue($entity->name instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', ['%entity_type' => $entity_type]));
119     $this->assertTrue($entity->name[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', ['%entity_type' => $entity_type]));
120
121     $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', ['%entity_type' => $entity_type]));
122     $this->assertEqual($this->entityName, $entity->name[0]->value, format_string('%entity_type: Name value can be read through list access.', ['%entity_type' => $entity_type]));
123     $this->assertEqual($entity->name->getValue(), [0 => ['value' => $this->entityName]], format_string('%entity_type: Plain field value returned.', ['%entity_type' => $entity_type]));
124
125     // Change the name.
126     $new_name = $this->randomMachineName();
127     $entity->name->value = $new_name;
128     $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read.', ['%entity_type' => $entity_type]));
129     $this->assertEqual($entity->name->getValue(), [0 => ['value' => $new_name]], format_string('%entity_type: Plain field value reflects the update.', ['%entity_type' => $entity_type]));
130
131     $new_name = $this->randomMachineName();
132     $entity->name[0]->value = $new_name;
133     $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read through list access.', ['%entity_type' => $entity_type]));
134
135     // Access the user field.
136     $this->assertTrue($entity->user_id instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', ['%entity_type' => $entity_type]));
137     $this->assertTrue($entity->user_id[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', ['%entity_type' => $entity_type]));
138
139     $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
140     $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
141
142     // Change the assigned user by entity.
143     $new_user1 = $this->createUser();
144     $entity->user_id->entity = $new_user1;
145     $this->assertEqual($new_user1->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
146     $this->assertEqual($new_user1->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', ['%entity_type' => $entity_type]));
147
148     // Change the assigned user by id.
149     $new_user2 = $this->createUser();
150     $entity->user_id->target_id = $new_user2->id();
151     $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
152     $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', ['%entity_type' => $entity_type]));
153
154     // Try unsetting a field property.
155     $entity->name->value = NULL;
156     $entity->user_id->target_id = NULL;
157     $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', ['%entity_type' => $entity_type]));
158     $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', ['%entity_type' => $entity_type]));
159     $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', ['%entity_type' => $entity_type]));
160
161     // Test setting the values via the typed data API works as well.
162     // Change the assigned user by entity.
163     $entity->user_id->first()->get('entity')->setValue($new_user2);
164     $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
165     $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', ['%entity_type' => $entity_type]));
166
167     // Change the assigned user by id.
168     $entity->user_id->first()->get('target_id')->setValue($new_user2->id());
169     $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
170     $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', ['%entity_type' => $entity_type]));
171
172     // Try unsetting a field.
173     $entity->name->first()->get('value')->setValue(NULL);
174     $entity->user_id->first()->get('target_id')->setValue(NULL);
175     $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', ['%entity_type' => $entity_type]));
176     $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', ['%entity_type' => $entity_type]));
177     $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', ['%entity_type' => $entity_type]));
178
179     // Create a fresh entity so target_id does not get its property object
180     // instantiated, then verify setting a new value via typed data API works.
181     $entity2 = $this->container->get('entity_type.manager')
182       ->getStorage($entity_type)
183       ->create([
184         'user_id' => ['target_id' => $new_user1->id()],
185       ]);
186     // Access the property object, and set a value.
187     $entity2->user_id->first()->get('target_id')->setValue($new_user2->id());
188     $this->assertEqual($new_user2->id(), $entity2->user_id->target_id, format_string('%entity_type: Updated user id can be read.', ['%entity_type' => $entity_type]));
189     $this->assertEqual($new_user2->name->value, $entity2->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', ['%entity_type' => $entity_type]));
190
191     // Test using isset(), empty() and unset().
192     $entity->name->value = 'test unset';
193     unset($entity->name->value);
194     $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
195     $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
196     $this->assertTrue(empty($entity->name->value), format_string('%entity_type: Name is empty.', ['%entity_type' => $entity_type]));
197     $this->assertTrue(empty($entity->name[0]->value), format_string('%entity_type: Name is empty.', ['%entity_type' => $entity_type]));
198
199     $entity->name->value = 'a value';
200     $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
201     $this->assertTrue(isset($entity->name[0]->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
202     $this->assertFalse(empty($entity->name->value), format_string('%entity_type: Name is not empty.', ['%entity_type' => $entity_type]));
203     $this->assertFalse(empty($entity->name[0]->value), format_string('%entity_type: Name is not empty.', ['%entity_type' => $entity_type]));
204     $this->assertTrue(isset($entity->name[0]), format_string('%entity_type: Name string item is set.', ['%entity_type' => $entity_type]));
205     $this->assertFalse(isset($entity->name[1]), format_string('%entity_type: Second name string item is not set as it does not exist', ['%entity_type' => $entity_type]));
206     $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
207     $this->assertFalse(isset($entity->nameInvalid), format_string('%entity_type: Not existing field is not set.', ['%entity_type' => $entity_type]));
208
209     unset($entity->name[0]);
210     $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', ['%entity_type' => $entity_type]));
211     $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
212     $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', ['%entity_type' => $entity_type]));
213
214     // Test emptying a field by assigning an empty value. NULL and array()
215     // behave the same.
216     foreach ([NULL, [], 'unset'] as $empty) {
217       // Make sure a value is present
218       $entity->name->value = 'a value';
219       $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', ['%entity_type' => $entity_type]));
220       // Now, empty the field.
221       if ($empty === 'unset') {
222         unset($entity->name);
223       }
224       else {
225         $entity->name = $empty;
226       }
227       $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
228       $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is set.', ['%entity_type' => $entity_type]));
229       $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', ['%entity_type' => $entity_type]));
230       $this->assertIdentical($entity->name->getValue(), [], format_string('%entity_type: Name field value is an empty array.', ['%entity_type' => $entity_type]));
231       $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', ['%entity_type' => $entity_type]));
232       $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: First name item value is not set.', ['%entity_type' => $entity_type]));
233       $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name value is not set.', ['%entity_type' => $entity_type]));
234     }
235
236     // Access the language field.
237     $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
238     $this->assertEqual($langcode, $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
239     $this->assertEqual(\Drupal::languageManager()->getLanguage($langcode), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
240
241     // Change the language by code.
242     $entity->{$langcode_key}->value = \Drupal::languageManager()->getDefaultLanguage()->getId();
243     $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
244     $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
245
246     // Revert language by code then try setting it by language object.
247     $entity->{$langcode_key}->value = $langcode;
248     $entity->{$langcode_key}->language = \Drupal::languageManager()->getDefaultLanguage();
249     $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
250     $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
251
252     // Access the text field and test updating.
253     $this->assertEqual($entity->field_test_text->value, $this->entityFieldText, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
254     $new_text = $this->randomMachineName();
255     $entity->field_test_text->value = $new_text;
256     $this->assertEqual($entity->field_test_text->value, $new_text, format_string('%entity_type: Updated text field can be read.', ['%entity_type' => $entity_type]));
257
258     // Test creating the entity by passing in plain values.
259     $this->entityName = $this->randomMachineName();
260     $name_item[0]['value'] = $this->entityName;
261     $this->entityUser = $this->createUser();
262     $user_item[0]['target_id'] = $this->entityUser->id();
263     $this->entityFieldText = $this->randomMachineName();
264     $text_item[0]['value'] = $this->entityFieldText;
265
266     $entity = $this->container->get('entity_type.manager')
267       ->getStorage($entity_type)
268       ->create([
269         'name' => $name_item,
270         'user_id' => $user_item,
271         'field_test_text' => $text_item,
272       ]);
273     $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', ['%entity_type' => $entity_type]));
274     $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
275     $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
276     $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
277
278     // Tests copying field values by assigning the TypedData objects.
279     $entity2 = $this->createTestEntity($entity_type);
280     $entity2->name = $entity->name;
281     $entity2->user_id = $entity->user_id;
282     $entity2->field_test_text = $entity->field_test_text;
283     $this->assertFalse($entity->name === $entity2->name, format_string('%entity_type: Copying properties results in a different field object.', ['%entity_type' => $entity_type]));
284     $this->assertEqual($entity->name->value, $entity2->name->value, format_string('%entity_type: Name field copied.', ['%entity_type' => $entity_type]));
285     $this->assertEqual($entity->user_id->target_id, $entity2->user_id->target_id, format_string('%entity_type: User id field copied.', ['%entity_type' => $entity_type]));
286     $this->assertEqual($entity->field_test_text->value, $entity2->field_test_text->value, format_string('%entity_type: Text field copied.', ['%entity_type' => $entity_type]));
287
288     // Tests that assigning TypedData objects to non-field properties keeps the
289     // assigned value as is.
290     $entity2 = $this->createTestEntity($entity_type);
291     $entity2->_not_a_field = $entity->name;
292     $this->assertTrue($entity2->_not_a_field === $entity->name, format_string('%entity_type: Typed data objects can be copied to non-field properties as is.', ['%entity_type' => $entity_type]));
293
294     // Tests adding a value to a field item list.
295     $entity->name[] = 'Another name';
296     $this->assertEqual($entity->name[1]->value, 'Another name', format_string('%entity_type: List item added via [] and the first property.', ['%entity_type' => $entity_type]));
297     $entity->name[] = ['value' => 'Third name'];
298     $this->assertEqual($entity->name[2]->value, 'Third name', format_string('%entity_type: List item added via [] and an array of properties.', ['%entity_type' => $entity_type]));
299     $entity->name[3] = ['value' => 'Fourth name'];
300     $this->assertEqual($entity->name[3]->value, 'Fourth name', format_string('%entity_type: List item added via offset and an array of properties.', ['%entity_type' => $entity_type]));
301     unset($entity->name[3]);
302
303     // Test removing and empty-ing list items.
304     $this->assertEqual(count($entity->name), 3, format_string('%entity_type: List has 3 items.', ['%entity_type' => $entity_type]));
305     unset($entity->name[1]);
306     $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Second list item has been removed.', ['%entity_type' => $entity_type]));
307     $this->assertEqual($entity->name[1]->value, 'Third name', format_string('%entity_type: The subsequent items have been shifted up.', ['%entity_type' => $entity_type]));
308     $this->assertEqual($entity->name[1]->getName(), 1, format_string('%entity_type: The items names have been updated to their new delta.', ['%entity_type' => $entity_type]));
309     $entity->name[1] = NULL;
310     $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Assigning NULL does not reduce array count.', ['%entity_type' => $entity_type]));
311     $this->assertTrue($entity->name[1]->isEmpty(), format_string('%entity_type: Assigning NULL empties the item.', ['%entity_type' => $entity_type]));
312
313     // Test using isEmpty().
314     unset($entity->name[1]);
315     $this->assertFalse($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is not empty.', ['%entity_type' => $entity_type]));
316     $entity->name->value = NULL;
317     $this->assertTrue($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is empty.', ['%entity_type' => $entity_type]));
318     $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is empty.', ['%entity_type' => $entity_type]));
319     $this->assertEqual(count($entity->name), 1, format_string('%entity_type: Empty item is considered when counting.', ['%entity_type' => $entity_type]));
320     $this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), format_string('%entity_type: Count matches iterator count.', ['%entity_type' => $entity_type]));
321     $this->assertTrue($entity->name->getValue() === [0 => ['value' => NULL]], format_string('%entity_type: Name field value contains a NULL value.', ['%entity_type' => $entity_type]));
322
323     // Test using filterEmptyItems().
324     $entity->name = [NULL, 'foo'];
325     $this->assertEqual(count($entity->name), 2, format_string('%entity_type: List has 2 items.', ['%entity_type' => $entity_type]));
326     $entity->name->filterEmptyItems();
327     $this->assertEqual(count($entity->name), 1, format_string('%entity_type: The empty item was removed.', ['%entity_type' => $entity_type]));
328     $this->assertEqual($entity->name[0]->value, 'foo', format_string('%entity_type: The items were renumbered.', ['%entity_type' => $entity_type]));
329     $this->assertEqual($entity->name[0]->getName(), 0, format_string('%entity_type: The deltas were updated in the items.', ['%entity_type' => $entity_type]));
330
331     // Test get and set field values.
332     $entity->name = 'foo';
333     $this->assertEqual($entity->name[0]->toArray(), ['value' => 'foo'], format_string('%entity_type: Field value has been retrieved via toArray()', ['%entity_type' => $entity_type]));
334
335     $values = $entity->toArray();
336     $this->assertEqual($values['name'], [0 => ['value' => 'foo']], format_string('%entity_type: Field value has been retrieved via toArray() from an entity.', ['%entity_type' => $entity_type]));
337
338     // Make sure the user id can be set to zero.
339     $user_item[0]['target_id'] = 0;
340     $entity = $this->container->get('entity_type.manager')
341       ->getStorage($entity_type)
342       ->create([
343         'name' => $name_item,
344         'user_id' => $user_item,
345         'field_test_text' => $text_item,
346       ]);
347     $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', ['%entity_type' => $entity_type]));
348     $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', ['%entity_type' => $entity_type]));
349
350     // Test setting the ID with the value only.
351     $entity = $this->container->get('entity_type.manager')
352       ->getStorage($entity_type)
353       ->create([
354         'name' => $name_item,
355         'user_id' => 0,
356         'field_test_text' => $text_item,
357       ]);
358     $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', ['%entity_type' => $entity_type]));
359     $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', ['%entity_type' => $entity_type]));
360   }
361
362   /**
363    * Tries to save and load an entity again.
364    */
365   public function testSave() {
366     // All entity variations have to have the same results.
367     foreach (entity_test_entity_types() as $entity_type) {
368       $this->doTestSave($entity_type);
369     }
370   }
371
372   /**
373    * Executes the save tests for the given entity type.
374    *
375    * @param string $entity_type
376    *   The entity type to run the tests with.
377    */
378   protected function doTestSave($entity_type) {
379     $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
380     $entity = $this->createTestEntity($entity_type);
381     $entity->save();
382     $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity has received an id.', ['%entity_type' => $entity_type]));
383
384     $entity = $this->container->get('entity_type.manager')
385       ->getStorage($entity_type)
386       ->load($entity->id());
387     $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity loaded.', ['%entity_type' => $entity_type]));
388
389     // Access the name field.
390     $this->assertEqual(1, $entity->id->value, format_string('%entity_type: ID value can be read.', ['%entity_type' => $entity_type]));
391     $this->assertTrue(is_string($entity->uuid->value), format_string('%entity_type: UUID value can be read.', ['%entity_type' => $entity_type]));
392     $this->assertEqual('en', $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', ['%entity_type' => $entity_type]));
393     $this->assertEqual(\Drupal::languageManager()->getLanguage('en'), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', ['%entity_type' => $entity_type]));
394     $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', ['%entity_type' => $entity_type]));
395     $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', ['%entity_type' => $entity_type]));
396     $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', ['%entity_type' => $entity_type]));
397   }
398
399   /**
400    * Tests introspection and getting metadata upfront.
401    */
402   public function testIntrospection() {
403     // All entity variations have to have the same results.
404     foreach (entity_test_entity_types() as $entity_type) {
405       $this->doTestIntrospection($entity_type);
406     }
407   }
408
409   /**
410    * Executes the introspection tests for the given entity type.
411    *
412    * @param string $entity_type
413    *   The entity type to run the tests with.
414    */
415   protected function doTestIntrospection($entity_type) {
416     // Test getting metadata upfront. The entity types used for this test have
417     // a default bundle that is the same as the entity type.
418     $definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity_type);
419     $this->assertEqual($definitions['name']->getType(), 'string', $entity_type . ': Name field found.');
420     $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type . ': User field found.');
421     $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type . ': Test-text-field field found.');
422
423     // Test deriving further metadata.
424     $this->assertTrue($definitions['name'] instanceof FieldDefinitionInterface);
425     $field_item_definition = $definitions['name']->getItemDefinition();
426     $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface);
427     $this->assertEqual($field_item_definition->getDataType(), 'field_item:string');
428     $value_definition = $field_item_definition->getPropertyDefinition('value');
429     $this->assertTrue($value_definition instanceof DataDefinitionInterface);
430     $this->assertEqual($value_definition->getDataType(), 'string');
431
432     // Test deriving metadata from references.
433     $entity_definition = EntityDataDefinition::create($entity_type);
434     $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
435     $reference_definition = $entity_definition->getPropertyDefinition($langcode_key)
436       ->getPropertyDefinition('language')
437       ->getTargetDefinition();
438     $this->assertEqual($reference_definition->getDataType(), 'language');
439
440     $reference_definition = $entity_definition->getPropertyDefinition('user_id')
441       ->getPropertyDefinition('entity')
442       ->getTargetDefinition();
443
444     $this->assertTrue($reference_definition instanceof EntityDataDefinitionInterface, 'Definition of the referenced user retrieved.');
445     $this->assertEqual($reference_definition->getEntityTypeId(), 'user', 'Referenced entity is of type "user".');
446
447     // Test propagating down.
448     $name_definition = $reference_definition->getPropertyDefinition('name');
449     $this->assertTrue($name_definition instanceof FieldDefinitionInterface);
450     $this->assertEqual($name_definition->getPropertyDefinition('value')->getDataType(), 'string');
451
452     // Test introspecting an entity object.
453     // @todo: Add bundles and test bundles as well.
454     $entity = $this->container->get('entity_type.manager')
455       ->getStorage($entity_type)
456       ->create();
457
458     $definitions = $entity->getFieldDefinitions();
459     $this->assertEqual($definitions['name']->getType(), 'string', $entity_type . ': Name field found.');
460     $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type . ': User field found.');
461     $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type . ': Test-text-field field found.');
462
463     $name_properties = $entity->name->getFieldDefinition()->getPropertyDefinitions();
464     $this->assertEqual($name_properties['value']->getDataType(), 'string', $entity_type . ': String value property of the name found.');
465
466     $userref_properties = $entity->user_id->getFieldDefinition()->getPropertyDefinitions();
467     $this->assertEqual($userref_properties['target_id']->getDataType(), 'integer', $entity_type . ': Entity id property of the user found.');
468     $this->assertEqual($userref_properties['entity']->getDataType(), 'entity_reference', $entity_type . ': Entity reference property of the user found.');
469
470     $textfield_properties = $entity->field_test_text->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinitions();
471     $this->assertEqual($textfield_properties['value']->getDataType(), 'string', $entity_type . ': String value property of the test-text field found.');
472     $this->assertEqual($textfield_properties['format']->getDataType(), 'filter_format', $entity_type . ': String format field of the test-text field found.');
473     $this->assertEqual($textfield_properties['processed']->getDataType(), 'string', $entity_type . ': String processed property of the test-text field found.');
474
475     // Make sure provided contextual information is right.
476     $entity_adapter = $entity->getTypedData();
477     $this->assertSame($entity_adapter->getRoot(), $entity_adapter, 'Entity is root object.');
478     $this->assertEqual($entity_adapter->getPropertyPath(), '');
479     $this->assertEqual($entity_adapter->getName(), '');
480     $this->assertEqual($entity_adapter->getParent(), NULL);
481
482     $field = $entity->user_id;
483     $this->assertSame($field->getRoot()->getValue(), $entity, 'Entity is root object.');
484     $this->assertSame($field->getEntity(), $entity, 'getEntity() returns the entity.');
485     $this->assertEqual($field->getPropertyPath(), 'user_id');
486     $this->assertEqual($field->getName(), 'user_id');
487     $this->assertSame($field->getParent()->getValue(), $entity, 'Parent object matches.');
488
489     $field_item = $field[0];
490     $this->assertSame($field_item->getRoot()->getValue(), $entity, 'Entity is root object.');
491     $this->assertSame($field_item->getEntity(), $entity, 'getEntity() returns the entity.');
492     $this->assertEqual($field_item->getPropertyPath(), 'user_id.0');
493     $this->assertEqual($field_item->getName(), '0');
494     $this->assertSame($field_item->getParent(), $field, 'Parent object matches.');
495
496     $item_value = $field_item->get('entity');
497     $this->assertSame($item_value->getRoot()->getValue(), $entity, 'Entity is root object.');
498     $this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity');
499     $this->assertEqual($item_value->getName(), 'entity');
500     $this->assertSame($item_value->getParent(), $field_item, 'Parent object matches.');
501   }
502
503   /**
504    * Tests iterating over properties.
505    */
506   public function testIterator() {
507     // All entity variations have to have the same results.
508     foreach (entity_test_entity_types() as $entity_type) {
509       $this->doTestIterator($entity_type);
510     }
511   }
512
513   /**
514    * Executes the iterator tests for the given entity type.
515    *
516    * @param string $entity_type
517    *   The entity type to run the tests with.
518    */
519   protected function doTestIterator($entity_type) {
520     $entity = $this->createTestEntity($entity_type);
521
522     foreach ($entity as $name => $field) {
523       $this->assertTrue($field instanceof FieldItemListInterface, $entity_type . ": Field $name implements interface.");
524
525       foreach ($field as $delta => $item) {
526         $this->assertTrue($field[0] instanceof FieldItemInterface, $entity_type . ": Item $delta of field $name implements interface.");
527
528         foreach ($item as $value_name => $value_property) {
529           $this->assertTrue($value_property instanceof TypedDataInterface, $entity_type . ": Value $value_name of item $delta of field $name implements interface.");
530
531           $value = $value_property->getValue();
532           $this->assertTrue(!isset($value) || is_scalar($value) || $value instanceof EntityInterface, $entity_type . ": Value $value_name of item $delta of field $name is a primitive or an entity.");
533         }
534       }
535     }
536
537     $fields = $entity->getFields();
538     $this->assertEqual(array_keys($fields), array_keys($entity->getTypedData()->getDataDefinition()->getPropertyDefinitions()), format_string('%entity_type: All fields returned.', ['%entity_type' => $entity_type]));
539     $this->assertEqual($fields, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all fields.', ['%entity_type' => $entity_type]));
540   }
541
542   /**
543    * Tests working with the entity based upon the TypedData API.
544    */
545   public function testDataStructureInterfaces() {
546     // All entity variations have to have the same results.
547     foreach (entity_test_entity_types() as $entity_type) {
548       $this->doTestDataStructureInterfaces($entity_type);
549     }
550   }
551
552   /**
553    * Executes the data structure interfaces tests for the given entity type.
554    *
555    * @param string $entity_type
556    *   The entity type to run the tests with.
557    */
558   protected function doTestDataStructureInterfaces($entity_type) {
559     $entity = $this->createTestEntity($entity_type);
560
561     // Test using the whole tree of typed data by navigating through the tree of
562     // contained properties and getting all contained strings, limited by a
563     // certain depth.
564     $strings = [];
565     $this->getContainedStrings($entity->getTypedData(), 0, $strings);
566
567     // @todo: Once the user entity has defined properties this should contain
568     // the user name and other user entity strings as well.
569     $target_strings = [
570       $entity->uuid->value,
571       'en',
572       $this->entityName,
573       // Bundle name.
574       $entity->bundle(),
575       $this->entityFieldText,
576       // Field format.
577       NULL,
578     ];
579
580     if ($entity instanceof RevisionLogInterface) {
581       // Adding empty string for revision message.
582       $target_strings[] = '';
583     }
584
585     asort($strings);
586     asort($target_strings);
587     $this->assertEqual(array_values($strings), array_values($target_strings), format_string('%entity_type: All contained strings found.', ['%entity_type' => $entity_type]));
588   }
589
590   /**
591    * Recursive helper for getting all contained strings,
592    * i.e. properties of type string.
593    */
594   public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &$strings) {
595
596     if ($wrapper instanceof StringInterface) {
597       $strings[] = $wrapper->getValue();
598     }
599
600     // Recurse until a certain depth is reached if possible.
601     if ($depth < 7) {
602       if ($wrapper instanceof ListInterface) {
603         foreach ($wrapper as $item) {
604           $this->getContainedStrings($item, $depth + 1, $strings);
605         }
606       }
607       elseif ($wrapper instanceof ComplexDataInterface) {
608         foreach ($wrapper as $property) {
609           $this->getContainedStrings($property, $depth + 1, $strings);
610         }
611       }
612     }
613   }
614
615   /**
616    * Makes sure data types are correctly derived for all entity types.
617    */
618   public function testDataTypes() {
619     $types = \Drupal::typedDataManager()->getDefinitions();
620     foreach (entity_test_entity_types() as $entity_type) {
621       $this->assertTrue($types['entity:' . $entity_type]['class'], 'Entity data type registered.');
622     }
623     // Check bundle types are provided as well.
624     entity_test_create_bundle('bundle');
625     $types = \Drupal::typedDataManager()->getDefinitions();
626     $this->assertTrue($types['entity:entity_test:bundle']['class'], 'Entity bundle data type registered.');
627   }
628
629   /**
630    * Tests a base field override on a non-existing base field.
631    *
632    * @see entity_test_entity_base_field_info_alter()
633    */
634   public function testBaseFieldNonExistingBaseField() {
635     $this->entityManager->getStorage('node_type')->create([
636       'type' => 'page',
637       'name' => 'page',
638     ])->save();
639     $this->entityManager->clearCachedFieldDefinitions();
640     $fields = $this->entityManager->getFieldDefinitions('node', 'page');
641     $override = $fields['status']->getConfig('page');
642     $override->setLabel($this->randomString())->save();
643     \Drupal::state()->set('entity_test.node_remove_status_field', TRUE);
644     $this->entityManager->clearCachedFieldDefinitions();
645     $fields = $this->entityManager->getFieldDefinitions('node', 'page');
646     // A base field override on a non-existing base field should not cause a
647     // field definition to come into existence.
648     $this->assertFalse(isset($fields['status']), 'Node\'s status base field does not exist.');
649   }
650
651   /**
652    * Tests creating a field override config for a bundle field.
653    *
654    * @see entity_test_entity_base_field_info_alter()
655    */
656   public function testFieldOverrideBundleField() {
657     // First make sure the bundle field override in code, which is provided by
658     // the test entity works.
659     entity_test_create_bundle('some_test_bundle', 'Some test bundle', 'entity_test_field_override');
660     $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'entity_test_field_override');
661     $this->assertEqual($field_definitions['name']->getDescription(), 'The default description.');
662     $this->assertNull($field_definitions['name']->getTargetBundle());
663
664     $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
665     $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
666     $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
667
668     // Now create a config override of the bundle field.
669     $field_config = $field_definitions['name']->getConfig('some_test_bundle');
670     $field_config->setTranslatable(FALSE);
671     $field_config->save();
672
673     // Make sure both overrides are present.
674     $this->entityManager->clearCachedFieldDefinitions();
675     $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
676     $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
677     $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
678     $this->assertFalse($field_definitions['name']->isTranslatable());
679   }
680
681   /**
682    * Tests validation constraints provided by the Entity API.
683    */
684   public function testEntityConstraintValidation() {
685     $entity = $this->createTestEntity('entity_test');
686     $entity->save();
687     // Create a reference field item and let it reference the entity.
688     $definition = BaseFieldDefinition::create('entity_reference')
689       ->setLabel('Test entity')
690       ->setSetting('target_type', 'entity_test');
691     $reference_field = \Drupal::typedDataManager()->create($definition);
692     $reference = $reference_field->appendItem(['entity' => $entity])->get('entity');
693
694     // Test validation the typed data object.
695     $violations = $reference->validate();
696     $this->assertEqual($violations->count(), 0);
697
698     // Test validating an entity of the wrong type.
699     $user = $this->createUser();
700     $user->save();
701     $node = $node = Node::create([
702       'type' => 'page',
703       'uid' => $user->id(),
704       'title' => $this->randomString(),
705     ]);
706     $reference->setValue($node);
707     $violations = $reference->validate();
708     $this->assertEqual($violations->count(), 1);
709
710     // Test bundle validation.
711     NodeType::create(['type' => 'article'])
712       ->save();
713     $definition = BaseFieldDefinition::create('entity_reference')
714       ->setLabel('Test entity')
715       ->setSetting('target_type', 'node')
716       ->setSetting('handler_settings', ['target_bundles' => ['article' => 'article']]);
717     $reference_field = \Drupal::TypedDataManager()->create($definition);
718     $reference_field->appendItem(['entity' => $node]);
719     $violations = $reference_field->validate();
720     $this->assertEqual($violations->count(), 1);
721
722     $node = Node::create([
723       'type' => 'article',
724       'uid' => $user->id(),
725       'title' => $this->randomString(),
726     ]);
727     $node->save();
728     $reference_field->entity = $node;
729     $violations = $reference_field->validate();
730     $this->assertEqual($violations->count(), 0);
731   }
732
733   /**
734    * Tests getting processed property values via a computed property.
735    */
736   public function testComputedProperties() {
737     // All entity variations have to have the same results.
738     foreach (entity_test_entity_types() as $entity_type) {
739       $this->doTestComputedProperties($entity_type);
740     }
741   }
742
743   /**
744    * Tests all the interaction points of a computed field.
745    */
746   public function testComputedFields() {
747     $this->installEntitySchema('entity_test_computed_field');
748
749     \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
750
751     // Check that the values are not computed unnecessarily during the lifecycle
752     // of an entity when the field is not interacted with directly.
753     \Drupal::state()->set('computed_test_field_execution', 0);
754     $entity = EntityTestComputedField::create([]);
755     $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
756
757     $entity->name->value = $this->randomString();
758     $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
759
760     $entity->save();
761     $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
762
763     // Test \Drupal\Core\TypedData\ComputedItemListTrait::getValue().
764     \Drupal::state()->set('computed_test_field_execution', 0);
765     $entity = EntityTestComputedField::create([]);
766     $this->assertSame([['value' => 'foo computed']], $entity->computed_string_field->getValue());
767
768     // Check that the values are only computed once.
769     $this->assertSame(1, \Drupal::state()->get('computed_test_field_execution', 0));
770
771     // Test \Drupal\Core\TypedData\ComputedItemListTrait::setValue(). This also
772     // checks that a subsequent getter does not try to re-compute the value.
773     \Drupal::state()->set('computed_test_field_execution', 0);
774     $entity = EntityTestComputedField::create([]);
775     $entity->computed_string_field->setValue([
776       ['value' => 'foo computed 1'],
777       ['value' => 'foo computed 2'],
778     ]);
779     $this->assertSame([['value' => 'foo computed 1'], ['value' => 'foo computed 2']], $entity->computed_string_field->getValue());
780
781     // Check that the values have not been computed when they were explicitly
782     // set.
783     $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0));
784
785     // Test \Drupal\Core\TypedData\ComputedItemListTrait::getString().
786     $entity = EntityTestComputedField::create([]);
787     $this->assertSame('foo computed', $entity->computed_string_field->getString());
788
789     // Test \Drupal\Core\TypedData\ComputedItemListTrait::get().
790     $entity = EntityTestComputedField::create([]);
791     $this->assertSame('foo computed', $entity->computed_string_field->get(0)->value);
792     $this->assertEmpty($entity->computed_string_field->get(1));
793
794     // Test \Drupal\Core\TypedData\ComputedItemListTrait::set().
795     $entity = EntityTestComputedField::create([]);
796     $entity->computed_string_field->set(1, 'foo computed 1');
797     $this->assertSame('foo computed', $entity->computed_string_field[0]->value);
798     $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
799     $entity->computed_string_field->set(0, 'foo computed 0');
800     $this->assertSame('foo computed 0', $entity->computed_string_field[0]->value);
801     $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
802
803     // Test \Drupal\Core\TypedData\ComputedItemListTrait::appendItem().
804     $entity = EntityTestComputedField::create([]);
805     $entity->computed_string_field->appendItem('foo computed 1');
806     $this->assertSame('foo computed', $entity->computed_string_field[0]->value);
807     $this->assertSame('foo computed 1', $entity->computed_string_field[1]->value);
808
809     // Test \Drupal\Core\TypedData\ComputedItemListTrait::removeItem().
810     $entity = EntityTestComputedField::create([]);
811     $entity->computed_string_field->removeItem(0);
812     $this->assertTrue($entity->computed_string_field->isEmpty());
813
814     // Test \Drupal\Core\TypedData\ComputedItemListTrait::isEmpty().
815     \Drupal::state()->set('entity_test_computed_field_item_list_value', []);
816     $entity = EntityTestComputedField::create([]);
817     $this->assertTrue($entity->computed_string_field->isEmpty());
818
819     \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
820     $entity = EntityTestComputedField::create([]);
821     $this->assertFalse($entity->computed_string_field->isEmpty());
822
823     // Test \Drupal\Core\TypedData\ComputedItemListTrait::filter().
824     $filter_callback = function ($item) {
825       return !$item->isEmpty();
826     };
827     $entity = EntityTestComputedField::create([]);
828     $entity->computed_string_field->filter($filter_callback);
829     $this->assertCount(1, $entity->computed_string_field);
830
831     // Add an empty item to the list and check that it is filtered out.
832     $entity->computed_string_field->appendItem();
833     $entity->computed_string_field->filter($filter_callback);
834     $this->assertCount(1, $entity->computed_string_field);
835
836     // Add a non-empty item to the list and check that it is not filtered out.
837     $entity->computed_string_field->appendItem('foo computed 1');
838     $entity->computed_string_field->filter($filter_callback);
839     $this->assertCount(2, $entity->computed_string_field);
840
841     // Test \Drupal\Core\TypedData\ComputedItemListTrait::offsetExists().
842     $entity = EntityTestComputedField::create([]);
843     $this->assertTrue($entity->computed_string_field->offsetExists(0));
844     $this->assertFalse($entity->computed_string_field->offsetExists(1));
845
846     // Test \Drupal\Core\TypedData\ComputedItemListTrait::getIterator().
847     $entity = EntityTestComputedField::create([]);
848     foreach ($entity->computed_string_field as $delta => $item) {
849       $this->assertSame('foo computed', $item->value);
850     }
851
852     // Test \Drupal\Core\TypedData\ComputedItemListTrait::count().
853     $entity = EntityTestComputedField::create([]);
854     $this->assertCount(1, $entity->computed_string_field);
855
856     // Check that computed items are not auto-created when they have no values.
857     \Drupal::state()->set('entity_test_computed_field_item_list_value', []);
858     $entity = EntityTestComputedField::create([]);
859     $this->assertCount(0, $entity->computed_string_field);
860
861     // Test \Drupal\Core\Field\FieldItemList::equals() for a computed field.
862     \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']);
863     $entity = EntityTestComputedField::create([]);
864     $computed_item_list1 = $entity->computed_string_field;
865
866     $entity = EntityTestComputedField::create([]);
867     $computed_item_list2 = $entity->computed_string_field;
868
869     $this->assertTrue($computed_item_list1->equals($computed_item_list2));
870
871     $computed_item_list2->value = 'foo computed 2';
872     $this->assertFalse($computed_item_list1->equals($computed_item_list2));
873   }
874
875   /**
876    * Tests an entity reference computed field.
877    */
878   public function testEntityReferenceComputedField() {
879     $this->installEntitySchema('entity_test_computed_field');
880
881     // Create 2 entities to be referenced.
882     $ref1 = EntityTest::create(['name' => 'foo', 'type' => 'bar']);
883     $ref1->save();
884     $ref2 = EntityTest::create(['name' => 'baz', 'type' => 'bar']);
885     $ref2->save();
886     \Drupal::state()->set('entity_test_reference_computed_target_ids', [$ref1->id(), $ref2->id()]);
887
888     $entity = EntityTestComputedField::create([]);
889     $entity->save();
890
891     /** @var \Drupal\entity_test\Plugin\Field\ComputedReferenceTestFieldItemList $field */
892     $field = $entity->get('computed_reference_field');
893     /** @var \Drupal\Core\Entity\EntityInterface[] $referenced_entities */
894     $referenced_entities = $field->referencedEntities();
895
896     // Check that ::referencedEntities() is working with computed fields.
897     $this->assertEquals($ref1->id(), $referenced_entities[0]->id());
898     $this->assertEquals($ref2->id(), $referenced_entities[1]->id());
899   }
900
901   /**
902    * Executes the computed properties tests for the given entity type.
903    *
904    * @param string $entity_type
905    *   The entity type to run the tests with.
906    */
907   protected function doTestComputedProperties($entity_type) {
908     $entity = $this->createTestEntity($entity_type);
909     $entity->field_test_text->value = "The <strong>text</strong> text to filter.";
910     $entity->field_test_text->format = filter_default_format();
911
912     $target = "<p>The &lt;strong&gt;text&lt;/strong&gt; text to filter.</p>\n";
913     $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', ['%entity_type' => $entity_type]));
914
915     // Save and load entity and make sure it still works.
916     $entity->save();
917     $entity = $this->container->get('entity_type.manager')
918       ->getStorage($entity_type)
919       ->load($entity->id());
920     $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', ['%entity_type' => $entity_type]));
921   }
922
923   /**
924    * Tests explicit entity ID assignment.
925    */
926   public function testEntityIdAssignment() {
927     $entity_type = 'entity_test';
928     /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
929     $storage = $this->container->get('entity_type.manager')->getStorage($entity_type);
930
931     // Check that an ID can be explicitly assigned on creation.
932     /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
933     $entity = $this->createTestEntity($entity_type);
934     $entity_id = 3;
935     $entity->set('id', $entity_id);
936     $this->assertSame($entity_id, $entity->id());
937     $storage->save($entity);
938     $entity = $storage->loadUnchanged($entity->id());
939     $this->assertTrue($entity);
940
941     // Check that an explicitly-assigned ID is preserved on update.
942     $storage->save($entity);
943     $entity = $storage->loadUnchanged($entity->id());
944     $this->assertTrue($entity);
945
946     // Check that an ID cannot be explicitly assigned on update.
947     $this->setExpectedException(EntityStorageException::class);
948     $entity->set('id', $entity_id + 1);
949     $storage->save($entity);
950   }
951
952 }