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