EntityFieldTest.php 37.4 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Definition of Drupal\system\Tests\Entity\EntityFieldTest.
6 7 8 9 10
 */

namespace Drupal\system\Tests\Entity;

use Drupal\Core\Entity\EntityInterface;
11
use Drupal\Core\Field\BaseFieldDefinition;
12
use Drupal\Core\Field\FieldDefinitionInterface;
13 14
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FieldItemInterface;
15
use Drupal\Core\Language\LanguageInterface;
16 17
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
use Drupal\Core\TypedData\DataDefinitionInterface;
18
use Drupal\Core\TypedData\Type\StringInterface;
19
use Drupal\Core\TypedData\TypedDataInterface;
20
use Drupal\field\Entity\FieldInstanceConfig;
21
use Drupal\node\Entity\NodeType;
22 23

/**
24 25 26
 * Tests the Entity Field API.
 *
 * @group Entity
27
 */
28
class EntityFieldTest extends EntityUnitTestBase  {
29 30 31 32 33 34

  /**
   * Modules to enable.
   *
   * @var array
   */
35
  public static $modules = array('filter', 'text', 'node', 'user');
36

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
  /**
   * @var string
   */
  protected $entity_name;

  /**
   * @var \Drupal\user\Entity\User
   */
  protected $entity_user;

  /**
   * @var string
   */
  protected $entity_field_text;

52
  protected function setUp() {
53
    parent::setUp();
54 55 56 57 58 59

    $this->installEntitySchema('node');

    $this->installEntitySchema('entity_test_rev');
    $this->installEntitySchema('entity_test_mul');
    $this->installEntitySchema('entity_test_mulrev');
60 61 62 63 64 65 66 67

    // Create the test field.
    entity_test_install();

    // Install required default configuration for filter module.
    $this->installConfig(array('system', 'filter'));
  }

68 69 70 71 72
  /**
   * Creates a test entity.
   *
   * @return \Drupal\Core\Entity\EntityInterface
   */
73
  protected function createTestEntity($entity_type) {
74
    $this->entity_name = $this->randomMachineName();
75
    $this->entity_user = $this->createUser();
76
    $this->entity_field_text = $this->randomMachineName();
77 78 79

    // Pass in the value of the name field when creating. With the user
    // field we test setting a field after creation.
80
    $entity = entity_create($entity_type);
81
    $entity->user_id->target_id = $this->entity_user->id();
82 83 84 85 86 87 88 89 90 91 92 93
    $entity->name->value = $this->entity_name;

    // Set a value for the test field.
    $entity->field_test_text->value = $this->entity_field_text;

    return $entity;
  }

  /**
   * Tests reading and writing properties and field items.
   */
  public function testReadWrite() {
94 95 96 97 98 99 100 101 102 103 104 105 106 107
    // All entity variations have to have the same results.
    foreach (entity_test_entity_types() as $entity_type) {
      $this->assertReadWrite($entity_type);
    }
  }

  /**
   * Executes the read write test set for a defined entity type.
   *
   * @param string $entity_type
   *   The entity type to run the tests with.
   */
  protected function assertReadWrite($entity_type) {
    $entity = $this->createTestEntity($entity_type);
108

109 110
    $langcode = 'en';

111
    // Access the name field.
112
    $this->assertTrue($entity->name instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', array('%entity_type' => $entity_type)));
113
    $this->assertTrue($entity->name[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', array('%entity_type' => $entity_type)));
114

115 116 117
    $this->assertEqual($this->entity_name, $entity->name->value, format_string('%entity_type: Name value can be read.', array('%entity_type' => $entity_type)));
    $this->assertEqual($this->entity_name, $entity->name[0]->value, format_string('%entity_type: Name value can be read through list access.', array('%entity_type' => $entity_type)));
    $this->assertEqual($entity->name->getValue(), array(0 => array('value' => $this->entity_name)), format_string('%entity_type: Plain field value returned.', array('%entity_type' => $entity_type)));
118 119

    // Change the name.
120
    $new_name = $this->randomMachineName();
121
    $entity->name->value = $new_name;
122 123
    $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read.', array('%entity_type' => $entity_type)));
    $this->assertEqual($entity->name->getValue(), array(0 => array('value' => $new_name)), format_string('%entity_type: Plain field value reflects the update.', array('%entity_type' => $entity_type)));
124

125
    $new_name = $this->randomMachineName();
126
    $entity->name[0]->value = $new_name;
127
    $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read through list access.', array('%entity_type' => $entity_type)));
128 129

    // Access the user field.
130
    $this->assertTrue($entity->user_id instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', array('%entity_type' => $entity_type)));
131
    $this->assertTrue($entity->user_id[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', array('%entity_type' => $entity_type)));
132

133
    $this->assertEqual($this->entity_user->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type)));
134
    $this->assertEqual($this->entity_user->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type)));
135 136

    // Change the assigned user by entity.
137
    $new_user = $this->createUser();
138
    $entity->user_id->entity = $new_user;
139
    $this->assertEqual($new_user->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
140
    $this->assertEqual($new_user->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type)));
141 142

    // Change the assigned user by id.
143
    $new_user = $this->createUser();
144 145
    $entity->user_id->target_id = $new_user->id();
    $this->assertEqual($new_user->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
146
    $this->assertEqual($new_user->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type)));
147 148 149

    // Try unsetting a field.
    $entity->name->value = NULL;
150
    $entity->user_id->target_id = NULL;
151
    $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', array('%entity_type' => $entity_type)));
152
    $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', array('%entity_type' => $entity_type)));
153
    $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', array('%entity_type' => $entity_type)));
154 155 156 157

    // Test using isset(), empty() and unset().
    $entity->name->value = 'test unset';
    unset($entity->name->value);
158 159 160 161
    $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
    $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
    $this->assertTrue(empty($entity->name->value), format_string('%entity_type: Name is empty.', array('%entity_type' => $entity_type)));
    $this->assertTrue(empty($entity->name[0]->value), format_string('%entity_type: Name is empty.', array('%entity_type' => $entity_type)));
162 163

    $entity->name->value = 'a value';
164 165 166 167 168 169 170 171
    $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type)));
    $this->assertTrue(isset($entity->name[0]->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type)));
    $this->assertFalse(empty($entity->name->value), format_string('%entity_type: Name is not empty.', array('%entity_type' => $entity_type)));
    $this->assertFalse(empty($entity->name[0]->value), format_string('%entity_type: Name is not empty.', array('%entity_type' => $entity_type)));
    $this->assertTrue(isset($entity->name[0]), format_string('%entity_type: Name string item is set.', array('%entity_type' => $entity_type)));
    $this->assertFalse(isset($entity->name[1]), format_string('%entity_type: Second name string item is not set as it does not exist', array('%entity_type' => $entity_type)));
    $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type)));
    $this->assertFalse(isset($entity->nameInvalid), format_string('%entity_type: Not existing field is not set.', array('%entity_type' => $entity_type)));
172 173

    unset($entity->name[0]);
174 175 176
    $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', array('%entity_type' => $entity_type)));
    $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
    $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
177

178 179 180 181 182 183
    $entity->name = array();
    $this->assertTrue(isset($entity->name), 'Name field is set.');
    $this->assertFalse(isset($entity->name[0]), 'Name field item is not set.');
    $this->assertFalse(isset($entity->name[0]->value), 'First name item value is not set.');
    $this->assertFalse(isset($entity->name->value), 'Name value is not set.');

184 185 186 187 188 189
    $entity->name = NULL;
    $this->assertFalse(isset($entity->name), 'Name field is not set.');
    $this->assertFalse(isset($entity->name[0]), 'Name field item is not set.');
    $this->assertFalse(isset($entity->name[0]->value), 'First name item value is not set.');
    $this->assertFalse(isset($entity->name->value), 'Name value is not set.');

190
    $entity->name->value = 'a value';
191
    $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type)));
192
    unset($entity->name);
193 194 195 196
    $this->assertFalse(isset($entity->name), format_string('%entity_type: Name field is not set.', array('%entity_type' => $entity_type)));
    $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', array('%entity_type' => $entity_type)));
    $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
    $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
197 198

    // Access the language field.
199 200
    $this->assertEqual($langcode, $entity->langcode->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type)));
    $this->assertEqual(\Drupal::languageManager()->getLanguage($langcode), $entity->langcode->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type)));
201 202

    // Change the language by code.
203 204 205
    $entity->langcode->value = \Drupal::languageManager()->getDefaultLanguage()->id;
    $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->id, $entity->langcode->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type)));
    $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->langcode->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type)));
206 207

    // Revert language by code then try setting it by language object.
208
    $entity->langcode->value = $langcode;
209 210 211
    $entity->langcode->language = \Drupal::languageManager()->getDefaultLanguage();
    $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->id, $entity->langcode->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type)));
    $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->langcode->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type)));
212 213

    // Access the text field and test updating.
214
    $this->assertEqual($entity->field_test_text->value, $this->entity_field_text, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type)));
215
    $new_text = $this->randomMachineName();
216
    $entity->field_test_text->value = $new_text;
217
    $this->assertEqual($entity->field_test_text->value, $new_text, format_string('%entity_type: Updated text field can be read.', array('%entity_type' => $entity_type)));
218 219

    // Test creating the entity by passing in plain values.
220
    $this->entity_name = $this->randomMachineName();
221
    $name_item[0]['value'] = $this->entity_name;
222
    $this->entity_user = $this->createUser();
223
    $user_item[0]['target_id'] = $this->entity_user->id();
224
    $this->entity_field_text = $this->randomMachineName();
225 226
    $text_item[0]['value'] = $this->entity_field_text;

227
    $entity = entity_create($entity_type, array(
228 229 230 231
      'name' => $name_item,
      'user_id' => $user_item,
      'field_test_text' => $text_item,
    ));
232
    $this->assertEqual($this->entity_name, $entity->name->value, format_string('%entity_type: Name value can be read.', array('%entity_type' => $entity_type)));
233
    $this->assertEqual($this->entity_user->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type)));
234
    $this->assertEqual($this->entity_user->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type)));
235
    $this->assertEqual($this->entity_field_text, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type)));
236 237

    // Test copying field values.
238
    $entity2 = $this->createTestEntity($entity_type);
239 240 241 242
    $entity2->name = $entity->name;
    $entity2->user_id = $entity->user_id;
    $entity2->field_test_text = $entity->field_test_text;

243 244
    $this->assertTrue($entity->name !== $entity2->name, format_string('%entity_type: Copying properties results in a different field object.', array('%entity_type' => $entity_type)));
    $this->assertEqual($entity->name->value, $entity2->name->value, format_string('%entity_type: Name field copied.', array('%entity_type' => $entity_type)));
245
    $this->assertEqual($entity->user_id->target_id, $entity2->user_id->target_id, format_string('%entity_type: User id field copied.', array('%entity_type' => $entity_type)));
246
    $this->assertEqual($entity->field_test_text->value, $entity2->field_test_text->value, format_string('%entity_type: Text field copied.', array('%entity_type' => $entity_type)));
247 248 249

    // Tests adding a value to a field item list.
    $entity->name[] = 'Another name';
250
    $this->assertEqual($entity->name[1]->value, 'Another name', format_string('%entity_type: List item added via [].', array('%entity_type' => $entity_type)));
251
    $entity->name[2]->value = 'Third name';
252
    $this->assertEqual($entity->name[2]->value, 'Third name', format_string('%entity_type: List item added by a accessing not yet created item.', array('%entity_type' => $entity_type)));
253 254

    // Test removing and empty-ing list items.
255
    $this->assertEqual(count($entity->name), 3, format_string('%entity_type: List has 3 items.', array('%entity_type' => $entity_type)));
256
    unset($entity->name[1]);
257
    $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Second list item has been removed.', array('%entity_type' => $entity_type)));
258
    $entity->name[2] = NULL;
259 260
    $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Assigning NULL does not reduce array count.', array('%entity_type' => $entity_type)));
    $this->assertTrue($entity->name[2]->isEmpty(), format_string('%entity_type: Assigning NULL empties the item.', array('%entity_type' => $entity_type)));
261 262 263

    // Test using isEmpty().
    unset($entity->name[2]);
264
    $this->assertFalse($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is not empty.', array('%entity_type' => $entity_type)));
265
    $entity->name->value = NULL;
266 267 268 269
    $this->assertTrue($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is empty.', array('%entity_type' => $entity_type)));
    $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is empty.', array('%entity_type' => $entity_type)));
    $this->assertEqual(count($entity->name), 1, format_string('%entity_type: Empty item is considered when counting.', array('%entity_type' => $entity_type)));
    $this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), format_string('%entity_type: Count matches iterator count.', array('%entity_type' => $entity_type)));
270
    $this->assertTrue($entity->name->getValue() === array(0 => array('value' => NULL)), format_string('%entity_type: Name field value contains a NULL value.', array('%entity_type' => $entity_type)));
271

272 273
    // Test removing all list items by assigning an empty array.
    $entity->name = array();
274 275
    $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', array('%entity_type' => $entity_type)));
    $this->assertIdentical($entity->name->getValue(), array(), format_string('%entity_type: Name field value is an empty array.', array('%entity_type' => $entity_type)));
276

277
    $entity->name->value = 'foo';
278
    $this->assertEqual($entity->name->value, 'foo', format_string('%entity_type: Name field set.', array('%entity_type' => $entity_type)));
279 280
    // Test removing all list items by setting it to NULL.
    $entity->name = NULL;
281 282
    $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', array('%entity_type' => $entity_type)));
    $this->assertNull($entity->name->getValue(), format_string('%entity_type: Name field value is an empty array.', array('%entity_type' => $entity_type)));
283

284 285
    // Test get and set field values.
    $entity->name = 'foo';
286 287 288 289
    $this->assertEqual($entity->name[0]->toArray(), array('value' => 'foo'), format_string('%entity_type: Field value has been retrieved via toArray()', array('%entity_type' => $entity_type)));

    $values = $entity->toArray();
    $this->assertEqual($values['name'], array(0 => array('value' => 'foo')), format_string('%entity_type: Field value has been retrieved via toArray() from an entity.', array('%entity_type' => $entity_type)));
290 291

    // Make sure the user id can be set to zero.
292
    $user_item[0]['target_id'] = 0;
293
    $entity = entity_create($entity_type, array(
294 295 296 297
      'name' => $name_item,
      'user_id' => $user_item,
      'field_test_text' => $text_item,
    ));
298 299
    $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', array('%entity_type' => $entity_type)));
    $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', array('%entity_type' => $entity_type)));
300 301

    // Test setting the ID with the value only.
302
    $entity = entity_create($entity_type, array(
303 304 305 306
      'name' => $name_item,
      'user_id' => 0,
      'field_test_text' => $text_item,
    ));
307 308
    $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', array('%entity_type' => $entity_type)));
    $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', array('%entity_type' => $entity_type)));
309 310 311 312 313 314
  }

  /**
   * Tries to save and load an entity again.
   */
  public function testSave() {
315 316 317 318 319 320 321 322 323 324 325 326 327 328
    // All entity variations have to have the same results.
    foreach (entity_test_entity_types() as $entity_type) {
      $this->assertSave($entity_type);
    }
  }

  /**
   * Executes the save tests for the given entity type.
   *
   * @param string $entity_type
   *   The entity type to run the tests with.
   */
  protected function assertSave($entity_type) {
    $entity = $this->createTestEntity($entity_type);
329
    $entity->save();
330
    $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity has received an id.', array('%entity_type' => $entity_type)));
331

332 333
    $entity = entity_load($entity_type, $entity->id());
    $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity loaded.', array('%entity_type' => $entity_type)));
334 335

    // Access the name field.
336 337
    $this->assertEqual(1, $entity->id->value, format_string('%entity_type: ID value can be read.', array('%entity_type' => $entity_type)));
    $this->assertTrue(is_string($entity->uuid->value), format_string('%entity_type: UUID value can be read.', array('%entity_type' => $entity_type)));
338 339
    $this->assertEqual('en', $entity->langcode->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type)));
    $this->assertEqual(\Drupal::languageManager()->getLanguage('en'), $entity->langcode->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type)));
340
    $this->assertEqual($this->entity_user->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type)));
341
    $this->assertEqual($this->entity_user->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type)));
342
    $this->assertEqual($this->entity_field_text, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type)));
343 344 345 346 347 348
  }

  /**
   * Tests introspection and getting metadata upfront.
   */
  public function testIntrospection() {
349 350
    // All entity variations have to have the same results.
    foreach (entity_test_entity_types() as $entity_type) {
351
      $this->checkIntrospection($entity_type);
352 353 354 355 356 357 358 359 360
    }
  }

  /**
   * Executes the introspection tests for the given entity type.
   *
   * @param string $entity_type
   *   The entity type to run the tests with.
   */
361
  protected function checkIntrospection($entity_type) {
362 363 364
    // Test getting metadata upfront. The entity types used for this test have
    // a default bundle that is the same as the entity type.
    $definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity_type);
365 366 367
    $this->assertEqual($definitions['name']->getType(), 'string', $entity_type .': Name field found.');
    $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type .': User field found.');
    $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type .': Test-text-field field found.');
368

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
    // Test deriving further metadata.
    $this->assertTrue($definitions['name'] instanceof FieldDefinitionInterface);
    $field_item_definition = $definitions['name']->getItemDefinition();
    $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface);
    $this->assertEqual($field_item_definition->getDataType(), 'field_item:string');
    $value_definition = $field_item_definition->getPropertyDefinition('value');
    $this->assertTrue($value_definition instanceof DataDefinitionInterface);
    $this->assertEqual($value_definition->getDataType(), 'string');

    // Test deriving metadata from references.
    $entity_definition = \Drupal\Core\Entity\TypedData\EntityDataDefinition::create($entity_type);
    $reference_definition = $entity_definition->getPropertyDefinition('langcode')
      ->getPropertyDefinition('language')
      ->getTargetDefinition();
    $this->assertEqual($reference_definition->getDataType(), 'language');

    $reference_definition = $entity_definition->getPropertyDefinition('user_id')
      ->getPropertyDefinition('entity')
      ->getTargetDefinition();

    $this->assertTrue($reference_definition instanceof \Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface, 'Definition of the referenced user retrieved.');
    $this->assertEqual($reference_definition->getEntityTypeId(), 'user', 'Referenced entity is of type "user".');

    // Test propagating down.
    $name_definition = $reference_definition->getPropertyDefinition('name');
    $this->assertTrue($name_definition instanceof FieldDefinitionInterface);
    $this->assertEqual($name_definition->getPropertyDefinition('value')->getDataType(), 'string');

397 398
    // Test introspecting an entity object.
    // @todo: Add bundles and test bundles as well.
399
    $entity = entity_create($entity_type);
400

401
    $definitions = $entity->getFieldDefinitions();
402 403 404
    $this->assertEqual($definitions['name']->getType(), 'string', $entity_type .': Name field found.');
    $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type .': User field found.');
    $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type .': Test-text-field field found.');
405

406
    $name_properties = $entity->name->getFieldDefinition()->getPropertyDefinitions();
407
    $this->assertEqual($name_properties['value']->getDataType(), 'string', $entity_type .': String value property of the name found.');
408

409
    $userref_properties = $entity->user_id->getFieldDefinition()->getPropertyDefinitions();
410 411
    $this->assertEqual($userref_properties['target_id']->getDataType(), 'integer', $entity_type .': Entity id property of the user found.');
    $this->assertEqual($userref_properties['entity']->getDataType(), 'entity_reference', $entity_type .': Entity reference property of the user found.');
412

413
    $textfield_properties = $entity->field_test_text->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinitions();
414 415 416
    $this->assertEqual($textfield_properties['value']->getDataType(), 'string', $entity_type .': String value property of the test-text field found.');
    $this->assertEqual($textfield_properties['format']->getDataType(), 'filter_format', $entity_type .': String format field of the test-text field found.');
    $this->assertEqual($textfield_properties['processed']->getDataType(), 'string', $entity_type .': String processed property of the test-text field found.');
417

418 419 420 421 422 423 424 425
    // Make sure provided contextual information is right.
    $this->assertIdentical($entity->getRoot(), $entity, 'Entity is root object.');
    $this->assertEqual($entity->getPropertyPath(), '');
    $this->assertEqual($entity->getName(), '');
    $this->assertEqual($entity->getParent(), NULL);

    $field = $entity->user_id;
    $this->assertIdentical($field->getRoot(), $entity, 'Entity is root object.');
426
    $this->assertIdentical($field->getEntity(), $entity, 'getEntity() returns the entity.');
427 428 429 430 431 432
    $this->assertEqual($field->getPropertyPath(), 'user_id');
    $this->assertEqual($field->getName(), 'user_id');
    $this->assertIdentical($field->getParent(), $entity, 'Parent object matches.');

    $field_item = $field[0];
    $this->assertIdentical($field_item->getRoot(), $entity, 'Entity is root object.');
433
    $this->assertIdentical($field_item->getEntity(), $entity, 'getEntity() returns the entity.');
434 435 436 437 438 439 440 441 442
    $this->assertEqual($field_item->getPropertyPath(), 'user_id.0');
    $this->assertEqual($field_item->getName(), '0');
    $this->assertIdentical($field_item->getParent(), $field, 'Parent object matches.');

    $item_value = $field_item->get('entity');
    $this->assertIdentical($item_value->getRoot(), $entity, 'Entity is root object.');
    $this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity');
    $this->assertEqual($item_value->getName(), 'entity');
    $this->assertIdentical($item_value->getParent(), $field_item, 'Parent object matches.');
443 444 445 446 447 448
  }

  /**
   * Tests iterating over properties.
   */
  public function testIterator() {
449 450 451 452 453 454 455 456 457 458 459 460 461 462
    // All entity variations have to have the same results.
    foreach (entity_test_entity_types() as $entity_type) {
      $this->assertIterator($entity_type);
    }
  }

  /**
   * Executes the iterator tests for the given entity type.
   *
   * @param string $entity_type
   *   The entity type to run the tests with.
   */
  protected function assertIterator($entity_type) {
    $entity = $this->createTestEntity($entity_type);
463 464

    foreach ($entity as $name => $field) {
465
      $this->assertTrue($field instanceof FieldItemListInterface, $entity_type . ": Field $name implements interface.");
466 467

      foreach ($field as $delta => $item) {
468
        $this->assertTrue($field[0] instanceof FieldItemInterface, $entity_type . ": Item $delta of field $name implements interface.");
469 470

        foreach ($item as $value_name => $value_property) {
471
          $this->assertTrue($value_property instanceof TypedDataInterface, $entity_type . ": Value $value_name of item $delta of field $name implements interface.");
472 473

          $value = $value_property->getValue();
474
          $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.");
475 476 477 478 479
        }
      }
    }

    $properties = $entity->getProperties();
480
    $this->assertEqual(array_keys($properties), array_keys($entity->getDataDefinition()->getPropertyDefinitions()), format_string('%entity_type: All properties returned.', array('%entity_type' => $entity_type)));
481
    $this->assertEqual($properties, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all properties.', array('%entity_type' => $entity_type)));
482 483 484
  }

  /**
485
   * Tests working with the entity based upon the TypedData API.
486 487
   */
  public function testDataStructureInterfaces() {
488 489 490 491 492 493 494 495 496 497 498 499 500 501
    // All entity variations have to have the same results.
    foreach (entity_test_entity_types() as $entity_type) {
      $this->assertDataStructureInterfaces($entity_type);
    }
  }

  /**
   * Executes the data structure interfaces tests for the given entity type.
   *
   * @param string $entity_type
   *   The entity type to run the tests with.
   */
  protected function assertDataStructureInterfaces($entity_type) {
    $entity = $this->createTestEntity($entity_type);
502

503 504 505
    // Test using the whole tree of typed data by navigating through the tree of
    // contained properties and getting all contained strings, limited by a
    // certain depth.
506
    $strings = array();
507
    $this->getContainedStrings($entity, 0, $strings);
508 509 510 511 512

    // @todo: Once the user entity has defined properties this should contain
    // the user name and other user entity strings as well.
    $target_strings = array(
      $entity->uuid->value,
513
      'en',
514
      $this->entity_name,
515 516
      // Bundle name.
      $entity->bundle(),
517 518 519 520
      $this->entity_field_text,
      // Field format.
      NULL,
    );
521
    $this->assertEqual($strings, $target_strings, format_string('%entity_type: All contained strings found.', array('%entity_type' => $entity_type)));
522 523 524 525 526 527 528 529
  }

  /**
   * Recursive helper for getting all contained strings,
   * i.e. properties of type string.
   */
  public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &$strings) {

530
    if ($wrapper instanceof StringInterface) {
531 532 533 534 535 536 537 538 539 540 541
      $strings[] = $wrapper->getValue();
    }

    // Recurse until a certain depth is reached if possible.
    if ($depth < 7) {
      if ($wrapper instanceof \Drupal\Core\TypedData\ListInterface) {
        foreach ($wrapper as $item) {
          $this->getContainedStrings($item, $depth + 1, $strings);
        }
      }
      elseif ($wrapper instanceof \Drupal\Core\TypedData\ComplexDataInterface) {
542
        foreach ($wrapper as $property) {
543 544 545 546 547 548
          $this->getContainedStrings($property, $depth + 1, $strings);
        }
      }
    }
  }

549 550 551 552
  /**
   * Makes sure data types are correctly derived for all entity types.
   */
  public function testDataTypes() {
553
    $types = \Drupal::typedDataManager()->getDefinitions();
554 555 556 557 558
    foreach (entity_test_entity_types() as $entity_type) {
      $this->assertTrue($types['entity:' . $entity_type]['class'], 'Entity data type registed.');
    }
    // Check bundle types are provided as well.
    entity_test_create_bundle('bundle');
559
    $types = \Drupal::typedDataManager()->getDefinitions();
560 561 562
    $this->assertTrue($types['entity:entity_test:bundle']['class'], 'Entity bundle data type registed.');
  }

563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
  /**
   * Tests a base field override on a non-existing base field.
   *
   * @see entity_test_entity_base_field_info_alter()
   */
  public function testBaseFieldNonExistingBaseField() {
    $this->entityManager->getStorage('node_type')->create(array(
      'type' => 'page',
      'name' => 'page',
    ))->save();
    $this->entityManager->clearCachedFieldDefinitions();
    $fields = $this->entityManager->getFieldDefinitions('node', 'page');
    $override = $fields['status']->getConfig('page');
    $override->setLabel($this->randomString())->save();
    \Drupal::state()->set('entity_test.node_remove_status_field', TRUE);
    $this->entityManager->clearCachedFieldDefinitions();
    $fields = $this->entityManager->getFieldDefinitions('node', 'page');
    // A base field override on a non-existing base field should not cause a
    // field definition to come into existence.
    $this->assertFalse(isset($fields['status']), 'Node\'s status base field does not exist.');
  }

  /**
   * Tests creating a field override config for a bundle field.
   *
   * @see entity_test_entity_base_field_info_alter()
   */
  public function testFieldOverrideBundleField() {
    // First make sure the bundle field override in code, which is provided by
    // the test entity works.
    entity_test_create_bundle('some_test_bundle', 'Some test bundle', 'entity_test_field_override');
    $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'entity_test_field_override');
    $this->assertEqual($field_definitions['name']->getDescription(), 'The default description.');
    $this->assertNull($field_definitions['name']->getBundle());

    $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
    $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
    $this->assertEqual($field_definitions['name']->getBundle(), 'some_test_bundle');

    // Now create a config override of the bundle field.
    $field_config = $field_definitions['name']->getConfig('some_test_bundle');
    $field_config->setTranslatable(FALSE);
    $field_config->save();

    // Make sure both overrides are present.
    $this->entityManager->clearCachedFieldDefinitions();
    $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
    $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
    $this->assertEqual($field_definitions['name']->getBundle(), 'some_test_bundle');
    $this->assertFalse($field_definitions['name']->isTranslatable());
  }

615 616 617 618 619 620
  /**
   * Tests validation constraints provided by the Entity API.
   */
  public function testEntityConstraintValidation() {
    $entity = $this->createTestEntity('entity_test');
    $entity->save();
621
    // Create a reference field item and let it reference the entity.
622
    $definition = BaseFieldDefinition::create('entity_reference')
623
      ->setLabel('Test entity')
624
      ->setSetting('target_type', 'entity_test');
625 626
    $reference_field = \Drupal::typedDataManager()->create($definition);
    $reference = $reference_field->first()->get('entity');
627
    $reference->setValue($entity);
628 629

    // Test validation the typed data object.
630
    $violations = $reference->validate();
631 632 633
    $this->assertEqual($violations->count(), 0);

    // Test validating an entity of the wrong type.
634 635 636 637 638 639
    $user = $this->createUser();
    $user->save();
    $node = entity_create('node', array(
      'type' => 'page',
      'uid' => $user->id(),
    ));
640 641
    $reference->setValue($node);
    $violations = $reference->validate();
642 643 644
    $this->assertEqual($violations->count(), 1);

    // Test bundle validation.
645 646
    NodeType::create(array('type' => 'article'))
      ->save();
647
    $definition = BaseFieldDefinition::create('entity_reference')
648
      ->setLabel('Test entity')
649
      ->setSettings(array(
650 651
        'target_type' => 'node',
        'target_bundle' => 'article',
652
      ));
653 654
    $reference_field = \Drupal::TypedDataManager()->create($definition);
    $reference = $reference_field->first()->get('entity');
655 656
    $reference->setValue($node);
    $violations = $reference->validate();
657 658
    $this->assertEqual($violations->count(), 1);

659 660 661 662
    $node = entity_create('node', array(
      'type' => 'article',
      'uid' => $user->id(),
    ));
663
    $node->save();
664 665
    $reference->setValue($node);
    $violations = $reference->validate();
666 667 668
    $this->assertEqual($violations->count(), 0);
  }

669 670 671 672
  /**
   * Tests getting processed property values via a computed property.
   */
  public function testComputedProperties() {
673 674 675 676 677 678 679 680 681 682 683 684 685
    // All entity variations have to have the same results.
    foreach (entity_test_entity_types() as $entity_type) {
      $this->assertComputedProperties($entity_type);
    }
  }

  /**
   * Executes the computed properties tests for the given entity type.
   *
   * @param string $entity_type
   *   The entity type to run the tests with.
   */
  protected function assertComputedProperties($entity_type) {
686
    // Make the test text field processed.
687
    $instance = FieldInstanceConfig::loadByName($entity_type, $entity_type, 'field_test_text');
688 689
    $instance->settings['text_processing'] = 1;
    $instance->save();
690

691
    $entity = $this->createTestEntity($entity_type);
692 693 694 695
    $entity->field_test_text->value = "The <strong>text</strong> text to filter.";
    $entity->field_test_text->format = filter_default_format();

    $target = "<p>The &lt;strong&gt;text&lt;/strong&gt; text to filter.</p>\n";
696
    $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', array('%entity_type' => $entity_type)));
697 698 699

    // Save and load entity and make sure it still works.
    $entity->save();
700 701
    $entity = entity_load($entity_type, $entity->id());
    $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', array('%entity_type' => $entity_type)));
702 703
  }
}