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