diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
index 10a7a77..eb03a68 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
@@ -9,6 +9,7 @@
 
 use PDO;
 
+use Drupal\Core\Entity\Query\QueryInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Entity\EntityStorageException;
@@ -37,6 +38,13 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
   protected $bundleKey;
 
   /**
+   * The table that stores properties, if the entity has multilingual support.
+   *
+   * @var string
+   */
+  protected $dataTable;
+
+  /**
    * Overrides DatabaseStorageController::__construct().
    */
   public function __construct($entityType) {
@@ -44,6 +52,11 @@ public function __construct($entityType) {
     $this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE;
     $this->entityClass = $this->entityInfo['class'];
 
+    // Check if the entity type has a dedicated table for properties.
+    if (!empty($this->entityInfo['data_table'])) {
+      $this->dataTable = $this->entityInfo['data_table'];
+    }
+
     // Work-a-round to let load() get stdClass storage records without having to
     // override it. We map storage records to entities in
     // DatabaseStorageControllerNG:: mapFromStorageRecords().
@@ -98,6 +111,121 @@ public function create(array $values) {
   }
 
   /**
+   * Builds an entity query.
+   *
+   * @param \Drupal\Core\Entity\Query\QueryInterface $entity_query
+   *   EntityQuery instance.
+   * @param array $values
+   *   An associative array of properties of the entity, where the keys are the
+   *   property names and the values are the values those properties must have.
+   */
+  protected function buildPropertyQuery(QueryInterface $entity_query, array $values) {
+    if ($this->dataTable) {
+      // @todo We should not be using a condition to specify whether conditions
+      // apply to the default language or not. We need to move this to a
+      // separate parameter during the following API refactoring.
+      // Default to the original entity language if not explicitly specified
+      // otherwise.
+      if (!array_key_exists('default_langcode', $values)) {
+        $values['default_langcode'] = 1;
+      }
+      // If the 'default_langcode' flag is explicitly not set, we do not care
+      // whether the queried values are in the original entity language or not.
+      elseif ($values['default_langcode'] === NULL) {
+        unset($values['default_langcode']);
+      }
+    }
+
+    parent::buildPropertyQuery($entity_query, $values);
+  }
+
+  /**
+   * Builds the query to load the entity.
+   *
+   * This has full revision support. For entities requiring special queries,
+   * the class can be extended, and the default query can be constructed by
+   * calling parent::buildQuery(). This is usually necessary when the object
+   * being loaded needs to be augmented with additional data from another
+   * table, such as loading node type into comments or vocabulary machine name
+   * into terms, however it can also support $conditions on different tables.
+   * See Drupal\comment\CommentStorageController::buildQuery() or
+   * Drupal\taxonomy\TermStorageController::buildQuery() for examples.
+   *
+   * @param array|null $ids
+   *   An array of entity IDs, or NULL to load all entities.
+   * @param integer|FALSE $revision_id
+   *   The ID of the revision to load, or FALSE if this query is asking for the
+   *   most current revision(s).
+   *
+   * @return \Drupal\Core\Database\Query\Select
+   *   A Select query object for loading the entity.
+   */
+  protected function buildQuery($ids, $revision_id = FALSE) {
+    $query = db_select($this->entityInfo['base_table'], 'base');
+
+    $query->addTag($this->entityType . '_load_multiple');
+
+    if ($revision_id) {
+      $query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} = :revisionId", array(':revisionId' => $revision_id));
+    }
+    elseif ($this->revisionKey) {
+      $query->join($this->revisionTable, 'revision', "revision.{$this->revisionKey} = base.{$this->revisionKey}");
+    }
+
+    if (!empty($this->dataTable)) {
+      $query->join($this->dataTable, 'data', "data.{$this->idKey} = base.{$this->idKey}");
+    }
+
+    // Add fields from the {entity} table.
+    $entity_fields = $this->entityInfo['schema_fields_sql']['base_table'];
+
+    if ($this->revisionKey) {
+      // Add all fields from the {entity_revision} table.
+      $entity_revision_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['revision_table']);
+      // The id field is provided by entity, so remove it.
+      unset($entity_revision_fields[$this->idKey]);
+
+      // Remove all fields from the base table that are also fields by the same
+      // name in the revision table.
+      $entity_field_keys = array_flip($entity_fields);
+      foreach ($entity_revision_fields as $key => $name) {
+        if (isset($entity_field_keys[$name])) {
+          unset($entity_fields[$entity_field_keys[$name]]);
+        }
+      }
+      $query->fields('revision', $entity_revision_fields);
+
+      // Compare revision id of the base and revision table, if equal then this
+      // is the default revision.
+      $query->addExpression('base.' . $this->revisionKey . ' = revision.' . $this->revisionKey, 'isDefaultRevision');
+    }
+
+    // Add fields from the entity data_table.
+    if (!empty($this->dataTable)) {
+      // Add all fields from the {entity_data} table.
+      $entity_data_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['data_table']);
+      // The id field is provided by entity, so remove it.
+      unset($entity_data_fields[$this->idKey]);
+
+      if (!isset($entity_revision_fields)) {
+        $entity_revision_fields = array();
+      }
+
+      // Only add fields not covered by the base or the revision.
+      $entity_data_fields = array_diff($entity_data_fields, $entity_fields, $entity_revision_fields);
+      $query->fields('data', $entity_data_fields);
+    }
+
+    $query->fields('base', $entity_fields);
+
+    if ($ids) {
+      $query->condition("base.{$this->idKey}", $ids, 'IN');
+    }
+
+    return $query;
+  }
+
+  /**
    * Overrides DatabaseStorageController::attachLoad().
    *
    * Added mapping from storage records to entities.
@@ -158,10 +286,51 @@ protected function mapFromStorageRecords(array $records, $load_revision = FALSE)
       }
       $records[$id] = $entity;
     }
+
+    // Load data of translatable properties.
+    $this->attachPropertyData($records, $load_revision);
+
     return $records;
   }
 
   /**
+   * Attaches property data in all languages for translatable properties.
+   *
+   * @param array $queried_entities
+   *   Associative array of entities, keyed on the entity ID.
+   * @param boolean $load_revision
+   *   (optional) TRUE if the revision should be loaded, defaults to FALSE.
+   */
+  protected function attachPropertyData(&$queried_entities, $load_revision = FALSE) {
+    if ($this->dataTable) {
+      $query = db_select($this->dataTable, 'data', array('fetch' => PDO::FETCH_ASSOC))
+        ->fields('data')
+        ->condition($this->idKey, array_keys($queried_entities))
+        ->orderBy('data.' . $this->idKey);
+      if ($load_revision) {
+        // Get revision id's.
+        $revision_ids = array();
+        foreach ($queried_entities as $id => $entity) {
+          $revision_ids[] = $entity->get($this->revisionKey)->value;
+        }
+        $query->condition($this->revisionKey, $revision_ids);
+      }
+      $data = $query->execute();
+
+      foreach ($data as $values) {
+        $id = $values[$this->idKey];
+        // Field values in default language are stored with
+        // LANGUAGE_DEFAULT as key.
+        $langcode = empty($values['default_langcode']) ? $values['langcode'] : LANGUAGE_DEFAULT;
+
+        foreach ($this->entityInfo['schema_fields_sql']['data_table'] as $name) {
+          $queried_entities[$id]->{$name}[$langcode][0]['value'] = $values[$name];
+        }
+      }
+    }
+  }
+
+  /**
    * Overrides DatabaseStorageController::save().
    *
    * Added mapping from entities to storage records before saving.
@@ -197,6 +366,9 @@ public function save(EntityInterface $entity) {
         if ($this->revisionKey) {
           $record->{$this->revisionKey} = $this->saveRevision($entity);
         }
+        if ($this->dataTable) {
+          $this->saveData($entity);
+        }
         $this->resetCache(array($entity->id()));
         $this->postSave($entity, TRUE);
         $this->invokeHook('update', $entity);
@@ -207,14 +379,19 @@ public function save(EntityInterface $entity) {
           $entity->{$this->idKey}->value = $record->{$this->idKey};
           $record->{$this->revisionKey} = $this->saveRevision($entity);
         }
+        $entity->{$this->idKey}->value = $record->{$this->idKey};
+        if ($this->dataTable) {
+          $this->saveData($entity);
+        }
+
         // Reset general caches, but keep caches specific to certain entities.
         $this->resetCache(array());
 
-        $entity->{$this->idKey}->value = $record->{$this->idKey};
         $entity->enforceIsNew(FALSE);
         $this->postSave($entity, FALSE);
         $this->invokeHook('insert', $entity);
       }
+      $entity->updateOriginalValues();
 
       // Ignore slave server temporarily.
       db_ignore_slave();
@@ -268,6 +445,32 @@ protected function saveRevision(EntityInterface $entity) {
   }
 
   /**
+   * Saves the entity property data language aware.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object.
+   */
+  protected function saveData(EntityInterface $entity) {
+    // Delete and insert to handle removed values.
+    db_delete($this->dataTable)
+      ->condition($this->idKey, $entity->id())
+      ->execute();
+
+    $query = db_insert($this->dataTable);
+
+    foreach ($entity->getTranslationLanguages() as $langcode => $language) {
+      $record = $this->mapToDataStorageRecord($entity, $langcode);
+
+      $values = (array) $record;
+      $query
+        ->fields(array_keys($values))
+        ->values($values);
+    }
+
+    $query->execute();
+  }
+
+  /**
    * Overrides DatabaseStorageController::invokeHook().
    *
    * Invokes field API attachers in compatibility mode and disables it
@@ -294,6 +497,12 @@ protected function invokeHook($hook, EntityInterface $entity) {
 
   /**
    * Maps from an entity object to the storage record of the base table.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object.
+   *
+   * @return \stdClass
+   *   The record to store.
    */
   protected function mapToStorageRecord(EntityInterface $entity) {
     $record = new \stdClass();
@@ -305,6 +514,12 @@ protected function mapToStorageRecord(EntityInterface $entity) {
 
   /**
    * Maps from an entity object to the storage record of the revision table.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object.
+   *
+   * @return \stdClass
+   *   The record to store.
    */
   protected function mapToRevisionStorageRecord(EntityInterface $entity) {
     $record = new \stdClass();
@@ -314,4 +529,80 @@ protected function mapToRevisionStorageRecord(EntityInterface $entity) {
 
     return $record;
   }
+
+  /**
+   * Maps from an entity object to the storage record of the data table.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object.
+   * @param $langcode
+   *   The language code of the translation to get.
+   *
+   * @return \stdClass
+   *   The record to store.
+   */
+  protected function mapToDataStorageRecord(EntityInterface $entity, $langcode) {
+    $default_langcode = $entity->language()->langcode;
+    // Don't use strict mode, that way there's no need to do checks here.
+    $translation = $entity->getTranslation($langcode, FALSE);
+
+    $record = new \stdClass();
+    foreach ($this->entityInfo['schema_fields_sql']['data_table'] as $name) {
+      $record->$name = $translation->$name->value;
+    }
+    $record->langcode = $langcode;
+    $record->default_langcode = intval($default_langcode == $langcode);
+
+    return $record;
+  }
+
+  /**
+   * Overwrites Drupal\Core\Entity\DatabaseStorageController::delete().
+   */
+  public function delete(array $entities) {
+    if (!$entities) {
+      // If no IDs or invalid IDs were passed, do nothing.
+      return;
+    }
+    $transaction = db_transaction();
+
+    try {
+      $this->preDelete($entities);
+      foreach ($entities as $id => $entity) {
+        $this->invokeHook('predelete', $entity);
+      }
+      $ids = array_keys($entities);
+
+      db_delete($this->entityInfo['base_table'])
+        ->condition($this->idKey, $ids, 'IN')
+        ->execute();
+
+      if ($this->revisionKey) {
+        db_delete($this->revisionTable)
+          ->condition($this->idKey, $ids, 'IN')
+          ->execute();
+      }
+
+      if ($this->dataTable) {
+        db_delete($this->dataTable)
+          ->condition($this->idKey, $ids)
+          ->execute();
+      }
+
+      // Reset the cache as soon as the changes have been applied.
+      $this->resetCache($ids);
+
+      $this->postDelete($entities);
+      foreach ($entities as $id => $entity) {
+        $this->invokeHook('delete', $entity);
+      }
+      // Ignore slave server temporarily.
+      db_ignore_slave();
+    }
+    catch (Exception $e) {
+      $transaction->rollback();
+      watchdog_exception($this->entityType, $e);
+      throw new EntityStorageException($e->getMessage, $e->getCode, $e);
+    }
+  }
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 34feb10..581f511 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -295,6 +295,9 @@ public function processDefinition(&$definition, $plugin_id) {
     // Drupal\Core\Entity\DatabaseStorageControllerInterface::buildQuery().
     if (isset($definition['base_table'])) {
       $definition['schema_fields_sql']['base_table'] = drupal_schema_fields_sql($definition['base_table']);
+      if (isset($definition['data_table'])) {
+        $definition['schema_fields_sql']['data_table'] = drupal_schema_fields_sql($definition['data_table']);
+      }
       if (isset($definition['revision_table'])) {
         $definition['schema_fields_sql']['revision_table'] = drupal_schema_fields_sql($definition['revision_table']);
       }
diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php
index 35ef89d..99e66df 100644
--- a/core/lib/Drupal/Core/Entity/EntityNG.php
+++ b/core/lib/Drupal/Core/Entity/EntityNG.php
@@ -60,6 +60,28 @@ class EntityNG extends Entity {
    */
   protected $compatibilityMode = FALSE;
 
+  /**
+   * Overrides Entity::__construct().
+   */
+  public function __construct(array $values, $entity_type, $bundle = FALSE) {
+    parent::__construct($values, $entity_type, $bundle);
+    $this->init();
+  }
+
+  /**
+   * Initialize the object. Invoked upon construction and wake up.
+   */
+  protected function init() {
+    // We unset all defined properties, so magic getters apply.
+    unset($this->langcode);
+  }
+
+  /**
+   * Magic __wakeup() implementation.
+   */
+  public function __wakeup() {
+    $this->init();
+  }
 
   /**
    * Overrides Entity::id().
@@ -258,10 +280,16 @@ public function getTranslationLanguages($include_default = TRUE) {
     $translations = array();
     // Build an array with the translation langcodes set as keys.
     foreach ($this->getProperties() as $name => $property) {
-      if (isset($this->values[$name])) {
-        $translations += $this->values[$name];
+      // @todo Figure out why we get localized non-translatable properties here
+      // and thus have to filter them!
+      $definition = $property->getDefinition();
+      if (!empty($definition['translatable'])) {
+        if (isset($this->values[$name])) {
+          $translations += $this->values[$name];
+        }
+        $translations += $this->fields[$name];
       }
-      $translations += $this->fields[$name];
+
     }
     unset($translations[LANGUAGE_DEFAULT]);
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
index 7cf3403..6c3b546 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
@@ -35,40 +35,43 @@ public static function getInfo() {
   function testCRUD() {
     $user1 = $this->drupalCreateUser();
 
-    // Create some test entities.
-    $entity = entity_create('entity_test', array('name' => 'test', 'user_id' => $user1->uid));
-    $entity->save();
-    $entity = entity_create('entity_test', array('name' => 'test2', 'user_id' => $user1->uid));
-    $entity->save();
-    $entity = entity_create('entity_test', array('name' => 'test', 'user_id' => NULL));
-    $entity->save();
+    // All entity variations have to have the same results.
+    foreach (_entity_test_entity_types() as $entity_type) {
+      // Create some test entities.
+      $entity = entity_create($entity_type, array('name' => 'test', 'user_id' => $user1->uid));
+      $entity->save();
+      $entity = entity_create($entity_type, array('name' => 'test2', 'user_id' => $user1->uid));
+      $entity->save();
+      $entity = entity_create($entity_type, array('name' => 'test', 'user_id' => NULL));
+      $entity->save();
 
-    $entities = array_values(entity_load_multiple_by_properties('entity_test', array('name' => 'test')));
-    $this->assertEqual($entities[0]->name->value, 'test', 'Created and loaded entity.');
-    $this->assertEqual($entities[1]->name->value, 'test', 'Created and loaded entity.');
+      $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test')));
+      $this->assertEqual($entities[0]->name->value, 'test', $entity_type . ': Created and loaded entity.');
+      $this->assertEqual($entities[1]->name->value, 'test', $entity_type . ': Created and loaded entity.');
 
-    // Test loading a single entity.
-    $loaded_entity = entity_test_load($entity->id());
-    $this->assertEqual($loaded_entity->id(), $entity->id(), 'Loaded a single entity by id.');
+      // Test loading a single entity.
+      $loaded_entity = entity_load($entity_type, $entity->id());
+      $this->assertEqual($loaded_entity->id(), $entity->id(), $entity_type . ': Loaded a single entity by id.');
 
-    // Test deleting an entity.
-    $entities = array_values(entity_load_multiple_by_properties('entity_test', array('name' => 'test2')));
-    $entities[0]->delete();
-    $entities = array_values(entity_load_multiple_by_properties('entity_test', array('name' => 'test2')));
-    $this->assertEqual($entities, array(), 'Entity deleted.');
+      // Test deleting an entity.
+      $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test2')));
+      $entities[0]->delete();
+      $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test2')));
+      $this->assertEqual($entities, array(), $entity_type . ': Entity deleted.');
 
-    // Test updating an entity.
-    $entities = array_values(entity_load_multiple_by_properties('entity_test', array('name' => 'test')));
-    $entities[0]->name->value = 'test3';
-    $entities[0]->save();
-    $entity = entity_test_load($entities[0]->id());
-    $this->assertEqual($entity->name->value, 'test3', 'Entity updated.');
+      // Test updating an entity.
+      $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test')));
+      $entities[0]->name->value = 'test3';
+      $entities[0]->save();
+      $entity = entity_load($entity_type, $entities[0]->id());
+      $this->assertEqual($entity->name->value, 'test3', $entity_type . ': Entity updated.');
 
-    // Try deleting multiple test entities by deleting all.
-    $ids = array_keys(entity_test_load_multiple());
-    entity_test_delete_multiple($ids);
+      // Try deleting multiple test entities by deleting all.
+      $ids = array_keys(entity_load_multiple($entity_type));
+      entity_delete_multiple($entity_type, $ids);
 
-    $all = entity_test_load_multiple();
-    $this->assertTrue(empty($all), 'Deleted all entities.');
+      $all = entity_load_multiple($entity_type);
+      $this->assertTrue(empty($all), $entity_type . ': Deleted all entities.');
+    }
   }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
index a152bfa..8c6b521 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
@@ -38,14 +38,14 @@ public static function getInfo() {
    *
    * @return \Drupal\Core\Entity\EntityInterface
    */
-  protected function createTestEntity() {
+  protected function createTestEntity($entity_type) {
     $this->entity_name = $this->randomName();
     $this->entity_user = $this->drupalCreateUser();
     $this->entity_field_text = $this->randomName();
 
     // Pass in the value of the name field when creating. With the user
     // field we test setting a field after creation.
-    $entity = entity_create('entity_test', array());
+    $entity = entity_create($entity_type, array());
     $entity->user_id->value = $this->entity_user->uid;
     $entity->name->value = $this->entity_name;
 
@@ -59,286 +59,299 @@ protected function createTestEntity() {
    * Tests reading and writing properties and field items.
    */
   public function testReadWrite() {
-    $entity = $this->createTestEntity();
-
-    // Access the name field.
-    $this->assertTrue($entity->name instanceof FieldInterface, 'Field implements interface');
-    $this->assertTrue($entity->name[0] instanceof FieldItemInterface, 'Field item implements interface');
-
-    $this->assertEqual($this->entity_name, $entity->name->value, 'Name value can be read.');
-    $this->assertEqual($this->entity_name, $entity->name[0]->value, 'Name value can be read through list access.');
-    $this->assertEqual($entity->name->getValue(), array(0 => array('value' => $this->entity_name)), 'Plain field value returned.');
-
-    // Change the name.
-    $new_name = $this->randomName();
-    $entity->name->value = $new_name;
-    $this->assertEqual($new_name, $entity->name->value, 'Name can be updated and read.');
-    $this->assertEqual($entity->name->getValue(), array(0 => array('value' => $new_name)), 'Plain field value reflects the update.');
-
-    $new_name = $this->randomName();
-    $entity->name[0]->value = $new_name;
-    $this->assertEqual($new_name, $entity->name->value, 'Name can be updated and read through list access.');
-
-    // Access the user field.
-    $this->assertTrue($entity->user_id instanceof FieldInterface, 'Field implements interface');
-    $this->assertTrue($entity->user_id[0] instanceof FieldItemInterface, 'Field item implements interface');
-
-    $this->assertEqual($this->entity_user->uid, $entity->user_id->value, 'User id can be read.');
-    $this->assertEqual($this->entity_user->name, $entity->user_id->entity->name, 'User name can be read.');
-
-    // Change the assigned user by entity.
-    $new_user = $this->drupalCreateUser();
-    $entity->user_id->entity = $new_user;
-    $this->assertEqual($new_user->uid, $entity->user_id->value, 'Updated user id can be read.');
-    $this->assertEqual($new_user->name, $entity->user_id->entity->name, 'Updated user name value can be read.');
-
-    // Change the assigned user by id.
-    $new_user = $this->drupalCreateUser();
-    $entity->user_id->value = $new_user->uid;
-    $this->assertEqual($new_user->uid, $entity->user_id->value, 'Updated user id can be read.');
-    $this->assertEqual($new_user->name, $entity->user_id->entity->name, 'Updated user name value can be read.');
-
-    // Try unsetting a field.
-    $entity->name->value = NULL;
-    $entity->user_id->value = NULL;
-    $this->assertNull($entity->name->value, 'Name field is not set.');
-    $this->assertNull($entity->user_id->value, 'User ID field is not set.');
-    $this->assertNull($entity->user_id->entity, 'User entity field is not set.');
-
-    // Test using isset(), empty() and unset().
-    $entity->name->value = 'test unset';
-    unset($entity->name->value);
-    $this->assertFalse(isset($entity->name->value), 'Name is not set.');
-    $this->assertFalse(isset($entity->name[0]->value), 'Name is not set.');
-    $this->assertTrue(empty($entity->name->value), 'Name is empty.');
-    $this->assertTrue(empty($entity->name[0]->value), 'Name is empty.');
-
-    $entity->name->value = 'a value';
-    $this->assertTrue(isset($entity->name->value), 'Name is set.');
-    $this->assertTrue(isset($entity->name[0]->value), 'Name is set.');
-    $this->assertFalse(empty($entity->name->value), 'Name is not empty.');
-    $this->assertFalse(empty($entity->name[0]->value), 'Name is not empty.');
-    $this->assertTrue(isset($entity->name[0]), 'Name string item is set.');
-    $this->assertFalse(isset($entity->name[1]), 'Second name string item is not set as it does not exist');
-    $this->assertTrue(isset($entity->name), 'Name field is set.');
-    $this->assertFalse(isset($entity->nameInvalid), 'Not existing field is not set.');
-
-    unset($entity->name[0]);
-    $this->assertFalse(isset($entity->name[0]), 'Name field item is not set.');
-    $this->assertFalse(isset($entity->name[0]->value), 'Name is not set.');
-    $this->assertFalse(isset($entity->name->value), 'Name is not set.');
-
-    $entity->name->value = 'a value';
-    $this->assertTrue(isset($entity->name->value), 'Name is set.');
-    unset($entity->name);
-    $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), 'Name is not set.');
-    $this->assertFalse(isset($entity->name->value), 'Name is not set.');
-
-    // Access the language field.
-    $this->assertEqual(LANGUAGE_NOT_SPECIFIED, $entity->langcode->value, 'Language code can be read.');
-    $this->assertEqual(language_load(LANGUAGE_NOT_SPECIFIED), $entity->langcode->language, 'Language object can be read.');
-
-    // Change the language by code.
-    $entity->langcode->value = language_default()->langcode;
-    $this->assertEqual(language_default()->langcode, $entity->langcode->value, 'Language code can be read.');
-    $this->assertEqual(language_default(), $entity->langcode->language, 'Language object can be read.');
-
-    // Revert language by code then try setting it by language object.
-    $entity->langcode->value = LANGUAGE_NOT_SPECIFIED;
-    $entity->langcode->language = language_default();
-    $this->assertEqual(language_default()->langcode, $entity->langcode->value, 'Language code can be read.');
-    $this->assertEqual(language_default(), $entity->langcode->language, 'Language object can be read.');
-
-    // Access the text field and test updating.
-    $this->assertEqual($entity->field_test_text->value, $this->entity_field_text, 'Text field can be read.');
-    $new_text = $this->randomName();
-    $entity->field_test_text->value = $new_text;
-    $this->assertEqual($entity->field_test_text->value, $new_text, 'Updated text field can be read.');
-
-    // Test creating the entity by passing in plain values.
-    $this->entity_name = $this->randomName();
-    $name_item[0]['value'] = $this->entity_name;
-    $this->entity_user = $this->drupalCreateUser();
-    $user_item[0]['value'] = $this->entity_user->uid;
-    $this->entity_field_text = $this->randomName();
-    $text_item[0]['value'] = $this->entity_field_text;
-
-    $entity = entity_create('entity_test', array(
-      'name' => $name_item,
-      'user_id' => $user_item,
-      'field_test_text' => $text_item,
-    ));
-    $this->assertEqual($this->entity_name, $entity->name->value, 'Name value can be read.');
-    $this->assertEqual($this->entity_user->uid, $entity->user_id->value, 'User id can be read.');
-    $this->assertEqual($this->entity_user->name, $entity->user_id->entity->name, 'User name can be read.');
-    $this->assertEqual($this->entity_field_text, $entity->field_test_text->value, 'Text field can be read.');
-
-    // Test copying field values.
-    $entity2 = $this->createTestEntity();
-    $entity2->name = $entity->name;
-    $entity2->user_id = $entity->user_id;
-    $entity2->field_test_text = $entity->field_test_text;
-
-    $this->assertTrue($entity->name !== $entity2->name, 'Copying properties results in a different field object.');
-    $this->assertEqual($entity->name->value, $entity2->name->value, 'Name field copied.');
-    $this->assertEqual($entity->user_id->value, $entity2->user_id->value, 'User id field copied.');
-    $this->assertEqual($entity->field_test_text->value, $entity2->field_test_text->value, 'Text field copied.');
-
-    // Tests adding a value to a field item list.
-    $entity->name[] = 'Another name';
-    $this->assertEqual($entity->name[1]->value == 'Another name', 'List item added via [].');
-    $entity->name[2]->value = 'Third name';
-    $this->assertEqual($entity->name[2]->value == 'Third name', 'List item added by a accessing not yet created item.');
-
-    // Test removing and empty-ing list items.
-    $this->assertEqual(count($entity->name), 3, 'List has 3 items.');
-    unset($entity->name[1]);
-    $this->assertEqual(count($entity->name), 2, 'Second list item has been removed.');
-    $entity->name[2] = NULL;
-    $this->assertEqual(count($entity->name), 2, 'Assigning NULL does not reduce array count.');
-    $this->assertTrue($entity->name[2]->isEmpty(), 'Assigning NULL empties the item.');
-
-    // Test using isEmpty().
-    unset($entity->name[2]);
-    $this->assertFalse($entity->name[0]->isEmpty(), 'Name item is not empty.');
-    $entity->name->value = NULL;
-    $this->assertTrue($entity->name[0]->isEmpty(), 'Name item is empty.');
-    $this->assertTrue($entity->name->isEmpty(), 'Name field is empty.');
-    $this->assertEqual(count($entity->name), 1, 'Empty item is considered when counting.');
-    $this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), 'Count matches iterator count.');
-    $this->assertTrue($entity->name->getValue() === array(0 => NULL), 'Name field value contains a NULL value.');
-
-    // Test removing all list items by assigning an empty array.
-    $entity->name = array();
-    $this->assertIdentical(count($entity->name), 0, 'Name field contains no items.');
-    $this->assertIdentical($entity->name->getValue(), array(), 'Name field value is an empty array.');
-
-    // Test removing all list items by setting it to NULL.
-    $entity->name = NULL;
-    $this->assertIdentical(count($entity->name), 0, 'Name field contains no items.');
-    $this->assertIdentical($entity->name->getValue(), array(), 'Name field value is an empty array.');
-
-    // Test get and set field values.
-    $entity->name = 'foo';
-    $this->assertEqual($entity->name[0]->getPropertyValues(), array('value' => 'foo'), 'Field value has been retrieved via getPropertyValue()');
-    $entity->name[0]->setPropertyValues(array('value' => 'bar'));
-    $this->assertEqual($entity->name->value, 'bar', 'Field value has been set via setPropertyValue()');
-
-    $values = $entity->getPropertyValues();
-    $this->assertEqual($values['name'], array(0 => array('value' => 'bar')), 'Field value has been retrieved via getPropertyValue() from an entity.');
-    $entity->setPropertyValues(array('name' => 'foo'));
-    $this->assertEqual($entity->name->value, 'foo', 'Field value has been set via setPropertyValue() on an entity.');
-
-    // Make sure the user id can be set to zero.
-    $user_item[0]['value'] = 0;
-    $entity = entity_create('entity_test', array(
-      'name' => $name_item,
-      'user_id' => $user_item,
-      'field_test_text' => $text_item,
-    ));
-    $this->assertNotNull($entity->user_id->value, 'User id is not NULL');
-    $this->assertIdentical($entity->user_id->value, 0, 'User id has been set to 0');
-
-    // Test setting the ID with the value only.
-    $entity = entity_create('entity_test', array(
-      'name' => $name_item,
-      'user_id' => 0,
-      'field_test_text' => $text_item,
-    ));
-    $this->assertNotNull($entity->user_id->value, 'User id is not NULL');
-    $this->assertIdentical($entity->user_id->value, 0, 'User id has been set to 0');
+    // All entity variations have to have the same results.
+    foreach(_entity_test_entity_types() as $entity_type) {
+
+      $entity = $this->createTestEntity($entity_type);
+
+      // Access the name field.
+      $this->assertTrue($entity->name instanceof FieldInterface, $entity_type . ': Field implements interface');
+      $this->assertTrue($entity->name[0] instanceof FieldItemInterface, $entity_type . ': Field item implements interface');
+
+      $this->assertEqual($this->entity_name, $entity->name->value, $entity_type . ': Name value can be read.');
+      $this->assertEqual($this->entity_name, $entity->name[0]->value, $entity_type . ': Name value can be read through list access.');
+      $this->assertEqual($entity->name->getValue(), array(0 => array('value' => $this->entity_name)), $entity_type . ': Plain field value returned.');
+
+      // Change the name.
+      $new_name = $this->randomName();
+      $entity->name->value = $new_name;
+      $this->assertEqual($new_name, $entity->name->value, $entity_type . ': Name can be updated and read.');
+      $this->assertEqual($entity->name->getValue(), array(0 => array('value' => $new_name)), $entity_type . ': Plain field value reflects the update.');
+
+      $new_name = $this->randomName();
+      $entity->name[0]->value = $new_name;
+      $this->assertEqual($new_name, $entity->name->value, $entity_type . ': Name can be updated and read through list access.');
+
+      // Access the user field.
+      $this->assertTrue($entity->user_id instanceof FieldInterface, $entity_type . ': Field implements interface');
+      $this->assertTrue($entity->user_id[0] instanceof FieldItemInterface, $entity_type . ': Field item implements interface');
+
+      $this->assertEqual($this->entity_user->uid, $entity->user_id->value, $entity_type . ': User id can be read.');
+      $this->assertEqual($this->entity_user->name, $entity->user_id->entity->name, $entity_type . ': User name can be read.');
+
+      // Change the assigned user by entity.
+      $new_user = $this->drupalCreateUser();
+      $entity->user_id->entity = $new_user;
+      $this->assertEqual($new_user->uid, $entity->user_id->value, $entity_type . ': Updated user id can be read.');
+      $this->assertEqual($new_user->name, $entity->user_id->entity->name, $entity_type . ': Updated user name value can be read.');
+
+      // Change the assigned user by id.
+      $new_user = $this->drupalCreateUser();
+      $entity->user_id->value = $new_user->uid;
+      $this->assertEqual($new_user->uid, $entity->user_id->value, $entity_type . ': Updated user id can be read.');
+      $this->assertEqual($new_user->name, $entity->user_id->entity->name, $entity_type . ': Updated user name value can be read.');
+
+      // Try unsetting a field.
+      $entity->name->value = NULL;
+      $entity->user_id->value = NULL;
+      $this->assertNull($entity->name->value, $entity_type . ': Name field is not set.');
+      $this->assertNull($entity->user_id->value, $entity_type . ': User ID field is not set.');
+      $this->assertNull($entity->user_id->entity, $entity_type . ': User entity field is not set.');
+
+      // Test using isset(), empty() and unset().
+      $entity->name->value = 'test unset';
+      unset($entity->name->value);
+      $this->assertFalse(isset($entity->name->value), $entity_type . ': Name is not set.');
+      $this->assertFalse(isset($entity->name[0]->value), $entity_type . ': Name is not set.');
+      $this->assertTrue(empty($entity->name->value), $entity_type . ': Name is empty.');
+      $this->assertTrue(empty($entity->name[0]->value), $entity_type . ': Name is empty.');
+
+      $entity->name->value = 'a value';
+      $this->assertTrue(isset($entity->name->value), $entity_type . ': Name is set.');
+      $this->assertTrue(isset($entity->name[0]->value), $entity_type . ': Name is set.');
+      $this->assertFalse(empty($entity->name->value), $entity_type . ': Name is not empty.');
+      $this->assertFalse(empty($entity->name[0]->value), $entity_type . ': Name is not empty.');
+      $this->assertTrue(isset($entity->name[0]), $entity_type . ': Name string item is set.');
+      $this->assertFalse(isset($entity->name[1]), $entity_type . ': Second name string item is not set as it does not exist');
+      $this->assertTrue(isset($entity->name), $entity_type . ': Name field is set.');
+      $this->assertFalse(isset($entity->nameInvalid), $entity_type . ': Not existing field is not set.');
+
+      unset($entity->name[0]);
+      $this->assertFalse(isset($entity->name[0]), $entity_type . ': Name field item is not set.');
+      $this->assertFalse(isset($entity->name[0]->value), $entity_type . ': Name is not set.');
+      $this->assertFalse(isset($entity->name->value), $entity_type . ': Name is not set.');
+
+      $entity->name->value = 'a value';
+      $this->assertTrue(isset($entity->name->value), $entity_type . ': Name is set.');
+      unset($entity->name);
+      $this->assertFalse(isset($entity->name), $entity_type . ': Name field is not set.');
+      $this->assertFalse(isset($entity->name[0]), $entity_type . ': Name field item is not set.');
+      $this->assertFalse(isset($entity->name[0]->value), $entity_type . ': Name is not set.');
+      $this->assertFalse(isset($entity->name->value), $entity_type . ': Name is not set.');
+
+      // Access the language field.
+      $this->assertEqual(LANGUAGE_NOT_SPECIFIED, $entity->langcode->value, $entity_type . ': Language code can be read.');
+      $this->assertEqual(language_load(LANGUAGE_NOT_SPECIFIED), $entity->langcode->language, $entity_type . ': Language object can be read.');
+
+      // Change the language by code.
+      $entity->langcode->value = language_default()->langcode;
+      $this->assertEqual(language_default()->langcode, $entity->langcode->value, $entity_type . ': Language code can be read.');
+      $this->assertEqual(language_default(), $entity->langcode->language, $entity_type . ': Language object can be read.');
+
+      // Revert language by code then try setting it by language object.
+      $entity->langcode->value = LANGUAGE_NOT_SPECIFIED;
+      $entity->langcode->language = language_default();
+      $this->assertEqual(language_default()->langcode, $entity->langcode->value, $entity_type . ': Language code can be read.');
+      $this->assertEqual(language_default(), $entity->langcode->language, $entity_type . ': Language object can be read.');
+
+      // Access the text field and test updating.
+      $this->assertEqual($entity->field_test_text->value, $this->entity_field_text, $entity_type . ': Text field can be read.');
+      $new_text = $this->randomName();
+      $entity->field_test_text->value = $new_text;
+      $this->assertEqual($entity->field_test_text->value, $new_text, $entity_type . ': Updated text field can be read.');
+
+      // Test creating the entity by passing in plain values.
+      $this->entity_name = $this->randomName();
+      $name_item[0]['value'] = $this->entity_name;
+      $this->entity_user = $this->drupalCreateUser();
+      $user_item[0]['value'] = $this->entity_user->uid;
+      $this->entity_field_text = $this->randomName();
+      $text_item[0]['value'] = $this->entity_field_text;
+
+      $entity = entity_create($entity_type, array(
+        'name' => $name_item,
+        'user_id' => $user_item,
+        'field_test_text' => $text_item,
+      ));
+      $this->assertEqual($this->entity_name, $entity->name->value, $entity_type . ': Name value can be read.');
+      $this->assertEqual($this->entity_user->uid, $entity->user_id->value, $entity_type . ': User id can be read.');
+      $this->assertEqual($this->entity_user->name, $entity->user_id->entity->name, $entity_type . ': User name can be read.');
+      $this->assertEqual($this->entity_field_text, $entity->field_test_text->value, $entity_type . ': Text field can be read.');
+
+      // Test copying field values.
+      $entity2 = $this->createTestEntity($entity_type);
+      $entity2->name = $entity->name;
+      $entity2->user_id = $entity->user_id;
+      $entity2->field_test_text = $entity->field_test_text;
+
+      $this->assertTrue($entity->name !== $entity2->name, $entity_type . ': Copying properties results in a different field object.');
+      $this->assertEqual($entity->name->value, $entity2->name->value, $entity_type . ': Name field copied.');
+      $this->assertEqual($entity->user_id->value, $entity2->user_id->value, $entity_type . ': User id field copied.');
+      $this->assertEqual($entity->field_test_text->value, $entity2->field_test_text->value, $entity_type . ': Text field copied.');
+
+      // Tests adding a value to a field item list.
+      $entity->name[] = 'Another name';
+      $this->assertEqual($entity->name[1]->value == 'Another name', $entity_type . ': List item added via [].');
+      $entity->name[2]->value = 'Third name';
+      $this->assertEqual($entity->name[2]->value == 'Third name', $entity_type . ': List item added by a accessing not yet created item.');
+
+      // Test removing and empty-ing list items.
+      $this->assertEqual(count($entity->name), 3, $entity_type . ': List has 3 items.');
+      unset($entity->name[1]);
+      $this->assertEqual(count($entity->name), 2, $entity_type . ': Second list item has been removed.');
+      $entity->name[2] = NULL;
+      $this->assertEqual(count($entity->name), 2, $entity_type . ': Assigning NULL does not reduce array count.');
+      $this->assertTrue($entity->name[2]->isEmpty(), $entity_type . ': Assigning NULL empties the item.');
+
+      // Test using isEmpty().
+      unset($entity->name[2]);
+      $this->assertFalse($entity->name[0]->isEmpty(), $entity_type . ': Name item is not empty.');
+      $entity->name->value = NULL;
+      $this->assertTrue($entity->name[0]->isEmpty(), $entity_type . ': Name item is empty.');
+      $this->assertTrue($entity->name->isEmpty(), $entity_type . ': Name field is empty.');
+      $this->assertEqual(count($entity->name), 1, $entity_type . ': Empty item is considered when counting.');
+      $this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), $entity_type . ': Count matches iterator count.');
+      $this->assertTrue($entity->name->getValue() === array(0 => NULL), $entity_type . ': Name field value contains a NULL value.');
+
+      // Test removing all list items by assigning an empty array.
+      $entity->name = array();
+      $this->assertIdentical(count($entity->name), 0, $entity_type . ': Name field contains no items.');
+      $this->assertIdentical($entity->name->getValue(), array(), $entity_type . ': Name field value is an empty array.');
+
+      // Test removing all list items by setting it to NULL.
+      $entity->name = NULL;
+      $this->assertIdentical(count($entity->name), 0, $entity_type . ': Name field contains no items.');
+      $this->assertIdentical($entity->name->getValue(), array(), $entity_type . ': Name field value is an empty array.');
+
+      // Test get and set field values.
+      $entity->name = 'foo';
+      $this->assertEqual($entity->name[0]->getPropertyValues(), array('value' => 'foo'), $entity_type . ': Field value has been retrieved via getPropertyValue()');
+      $entity->name[0]->setPropertyValues(array('value' => 'bar'));
+      $this->assertEqual($entity->name->value, 'bar', $entity_type . ': Field value has been set via setPropertyValue()');
+
+      $values = $entity->getPropertyValues();
+      $this->assertEqual($values['name'], array(0 => array('value' => 'bar')), $entity_type . ': Field value has been retrieved via getPropertyValue() from an entity.');
+      $entity->setPropertyValues(array('name' => 'foo'));
+      $this->assertEqual($entity->name->value, 'foo', $entity_type . ': Field value has been set via setPropertyValue() on an entity.');
+
+      // Make sure the user id can be set to zero.
+      $user_item[0]['value'] = 0;
+      $entity = entity_create($entity_type, array(
+        'name' => $name_item,
+        'user_id' => $user_item,
+        'field_test_text' => $text_item,
+      ));
+      $this->assertNotNull($entity->user_id->value, $entity_type . ': User id is not NULL');
+      $this->assertIdentical($entity->user_id->value, 0, $entity_type . ': User id has been set to 0');
+
+      // Test setting the ID with the value only.
+      $entity = entity_create($entity_type, array(
+        'name' => $name_item,
+        'user_id' => 0,
+        'field_test_text' => $text_item,
+      ));
+      $this->assertNotNull($entity->user_id->value, $entity_type . ': User id is not NULL');
+      $this->assertIdentical($entity->user_id->value, 0, $entity_type . ': User id has been set to 0');
+    }
   }
 
   /**
    * Tries to save and load an entity again.
    */
   public function testSave() {
-    $entity = $this->createTestEntity();
-    $entity->save();
-    $this->assertTrue((bool) $entity->id(), 'Entity has received an id.');
-
-    $entity = entity_load('entity_test', $entity->id());
-    $this->assertTrue((bool) $entity->id(), 'Entity loaded.');
-
-    // Access the name field.
-    $this->assertEqual(1, $entity->id->value, 'ID value can be read.');
-    $this->assertTrue(is_string($entity->uuid->value), 'UUID value can be read.');
-    $this->assertEqual(LANGUAGE_NOT_SPECIFIED, $entity->langcode->value, 'Language code can be read.');
-    $this->assertEqual(language_load(LANGUAGE_NOT_SPECIFIED), $entity->langcode->language, 'Language object can be read.');
-    $this->assertEqual($this->entity_user->uid, $entity->user_id->value, 'User id can be read.');
-    $this->assertEqual($this->entity_user->name, $entity->user_id->entity->name, 'User name can be read.');
-    $this->assertEqual($this->entity_field_text, $entity->field_test_text->value, 'Text field can be read.');
+    // All entity variations have to have the same results.
+    foreach(_entity_test_entity_types() as $entity_type) {
+      $entity = $this->createTestEntity($entity_type);
+      $entity->save();
+      $this->assertTrue((bool) $entity->id(), $entity_type . ': Entity has received an id.');
+
+      $entity = entity_load($entity_type, $entity->id());
+      $this->assertTrue((bool) $entity->id(), $entity_type . ': Entity loaded.');
+
+      // Access the name field.
+      $this->assertEqual(1, $entity->id->value, $entity_type . ': ID value can be read.');
+      $this->assertTrue(is_string($entity->uuid->value), $entity_type . ': UUID value can be read.');
+      $this->assertEqual(LANGUAGE_NOT_SPECIFIED, $entity->langcode->value, $entity_type . ': Language code can be read.');
+      $this->assertEqual(language_load(LANGUAGE_NOT_SPECIFIED), $entity->langcode->language, $entity_type . ': Language object can be read.');
+      $this->assertEqual($this->entity_user->uid, $entity->user_id->value, $entity_type . ': User id can be read.');
+      $this->assertEqual($this->entity_user->name, $entity->user_id->entity->name, $entity_type . ': User name can be read.');
+      $this->assertEqual($this->entity_field_text, $entity->field_test_text->value, $entity_type . ': Text field can be read.');
+    }
   }
 
   /**
    * Tests introspection and getting metadata upfront.
    */
   public function testIntrospection() {
-    // Test getting metadata upfront, i.e. without having an entity object.
-    $definition = array(
-      'type' => 'entity',
-      'constraints' => array(
-        'entity type' => 'entity_test',
-      ),
-      'label' => t('Test entity'),
-    );
-    $wrapped_entity = typed_data()->create($definition);
-    $definitions = $wrapped_entity->getPropertyDefinitions($definition);
-    $this->assertEqual($definitions['name']['type'], 'string_field', 'Name field found.');
-    $this->assertEqual($definitions['user_id']['type'], 'entityreference_field', 'User field found.');
-    $this->assertEqual($definitions['field_test_text']['type'], 'text_field', 'Test-text-field field found.');
-
-    // Test introspecting an entity object.
-    // @todo: Add bundles and test bundles as well.
-    $entity = entity_create('entity_test', array());
-
-    $definitions = $entity->getPropertyDefinitions();
-    $this->assertEqual($definitions['name']['type'], 'string_field', 'Name field found.');
-    $this->assertEqual($definitions['user_id']['type'], 'entityreference_field', 'User field found.');
-    $this->assertEqual($definitions['field_test_text']['type'], 'text_field', 'Test-text-field field found.');
-
-    $name_properties = $entity->name->getPropertyDefinitions();
-    $this->assertEqual($name_properties['value']['type'], 'string', 'String value property of the name found.');
-
-    $userref_properties = $entity->user_id->getPropertyDefinitions();
-    $this->assertEqual($userref_properties['value']['type'], 'integer', 'Entity id property of the user found.');
-    $this->assertEqual($userref_properties['entity']['type'], 'entity', 'Entity reference property of the user found.');
-
-    $textfield_properties = $entity->field_test_text->getPropertyDefinitions();
-    $this->assertEqual($textfield_properties['value']['type'], 'string', 'String value property of the test-text field found.');
-    $this->assertEqual($textfield_properties['format']['type'], 'string', 'String format field of the test-text field found.');
-    $this->assertEqual($textfield_properties['processed']['type'], 'string', 'String processed property of the test-text field found.');
-
-    // @todo: Once the user entity has definitions, continue testing getting
-    // them from the $userref_values['entity'] property.
+    // All entity variations have to have the same results.
+    foreach(_entity_test_entity_types() as $entity_type) {
+      // Test getting metadata upfront, i.e. without having an entity object.
+      $definition = array(
+        'type' => 'entity',
+        'constraints' => array(
+          'entity type' => $entity_type,
+        ),
+        'label' => t('Test entity'),
+      );
+      $wrapped_entity = typed_data()->create($definition);
+      $definitions = $wrapped_entity->getPropertyDefinitions($definition);
+      $this->assertEqual($definitions['name']['type'], 'string_field', $entity_type .': Name field found.');
+      $this->assertEqual($definitions['user_id']['type'], 'entityreference_field', $entity_type .': User field found.');
+      $this->assertEqual($definitions['field_test_text']['type'], 'text_field', $entity_type .': Test-text-field field found.');
+
+      // Test introspecting an entity object.
+      // @todo: Add bundles and test bundles as well.
+      $entity = entity_create($entity_type, array());
+
+      $definitions = $entity->getPropertyDefinitions();
+      $this->assertEqual($definitions['name']['type'], 'string_field', $entity_type .': Name field found.');
+      $this->assertEqual($definitions['user_id']['type'], 'entityreference_field', $entity_type .': User field found.');
+      $this->assertEqual($definitions['field_test_text']['type'], 'text_field', $entity_type .': Test-text-field field found.');
+
+      $name_properties = $entity->name->getPropertyDefinitions();
+      $this->assertEqual($name_properties['value']['type'], 'string', $entity_type .': String value property of the name found.');
+
+      $userref_properties = $entity->user_id->getPropertyDefinitions();
+      $this->assertEqual($userref_properties['value']['type'], 'integer', $entity_type .': Entity id property of the user found.');
+      $this->assertEqual($userref_properties['entity']['type'], 'entity', $entity_type .': Entity reference property of the user found.');
+
+      $textfield_properties = $entity->field_test_text->getPropertyDefinitions();
+      $this->assertEqual($textfield_properties['value']['type'], 'string', $entity_type .': String value property of the test-text field found.');
+      $this->assertEqual($textfield_properties['format']['type'], 'string', $entity_type .': String format field of the test-text field found.');
+      $this->assertEqual($textfield_properties['processed']['type'], 'string', $entity_type .': String processed property of the test-text field found.');
+
+      // @todo: Once the user entity has definitions, continue testing getting
+      // them from the $userref_values['entity'] property.
+    }
   }
 
   /**
    * Tests iterating over properties.
    */
   public function testIterator() {
-    $entity = $this->createTestEntity();
+    // All entity variations have to have the same results.
+    foreach(_entity_test_entity_types() as $entity_type) {
+      $entity = $this->createTestEntity($entity_type);
 
-    foreach ($entity as $name => $field) {
-      $this->assertTrue($field instanceof FieldInterface, "Field $name implements interface.");
+      foreach ($entity as $name => $field) {
+        $this->assertTrue($field instanceof FieldInterface, $entity_type . ": Field $name implements interface.");
 
-      foreach ($field as $delta => $item) {
-        $this->assertTrue($field[0] instanceof FieldItemInterface, "Item $delta of field $name implements interface.");
+        foreach ($field as $delta => $item) {
+          $this->assertTrue($field[0] instanceof FieldItemInterface, $entity_type . ": Item $delta of field $name implements interface.");
 
-        foreach ($item as $value_name => $value_property) {
-          $this->assertTrue($value_property instanceof TypedDataInterface, "Value $value_name of item $delta of field $name implements interface.");
+          foreach ($item as $value_name => $value_property) {
+            $this->assertTrue($value_property instanceof TypedDataInterface, $entity_type . ": Value $value_name of item $delta of field $name implements interface.");
 
-          $value = $value_property->getValue();
-          $this->assertTrue(!isset($value) || is_scalar($value) || $value instanceof EntityInterface, "Value $value_name of item $delta of field $name is a primitive or an entity.");
+            $value = $value_property->getValue();
+            $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.");
+          }
         }
       }
-    }
 
-    $properties = $entity->getProperties();
-    $this->assertEqual(array_keys($properties), array_keys($entity->getPropertyDefinitions()), 'All properties returned.');
-    $this->assertEqual($properties, iterator_to_array($entity->getIterator()), 'Entity iterator iterates over all properties.');
+      $properties = $entity->getProperties();
+      $this->assertEqual(array_keys($properties), array_keys($entity->getPropertyDefinitions()), $entity_type . ': All properties returned.');
+      $this->assertEqual($properties, iterator_to_array($entity->getIterator()), $entity_type . ': Entity iterator iterates over all properties.');
+    }
   }
 
   /**
@@ -346,33 +359,36 @@ public function testIterator() {
    * list interfaces.
    */
   public function testDataStructureInterfaces() {
-    $entity = $this->createTestEntity();
-    $entity->save();
-    $entity_definition = array(
-      'type' => 'entity',
-      'constraints' => array(
-        'entity type' => 'entity_test',
-      ),
-      'label' => t('Test entity'),
-    );
-    $wrapped_entity = typed_data()->create($entity_definition, $entity);
-
-    // For the test we navigate through the tree of contained properties and get
-    // all contained strings, limited by a certain depth.
-    $strings = array();
-    $this->getContainedStrings($wrapped_entity, 0, $strings);
-
-    // @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,
-      LANGUAGE_NOT_SPECIFIED,
-      $this->entity_name,
-      $this->entity_field_text,
-      // Field format.
-      NULL,
-    );
-    $this->assertEqual($strings, $target_strings, 'All contained strings found.');
+    // All entity variations have to have the same results.
+    foreach(_entity_test_entity_types() as $entity_type) {
+      $entity = $this->createTestEntity($entity_type);
+      $entity->save();
+      $entity_definition = array(
+        'type' => 'entity',
+        'constraints' => array(
+          'entity type' => $entity_type,
+        ),
+        'label' => t('Test entity'),
+      );
+      $wrapped_entity = typed_data()->create($entity_definition, $entity);
+
+      // For the test we navigate through the tree of contained properties and get
+      // all contained strings, limited by a certain depth.
+      $strings = array();
+      $this->getContainedStrings($wrapped_entity, 0, $strings);
+
+      // @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,
+        LANGUAGE_NOT_SPECIFIED,
+        $this->entity_name,
+        $this->entity_field_text,
+        // Field format.
+        NULL,
+      );
+      $this->assertEqual($strings, $target_strings, $entity_type . ': All contained strings found.');
+    }
   }
 
   /**
@@ -404,21 +420,24 @@ public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &
    * Tests getting processed property values via a computed property.
    */
   public function testComputedProperties() {
-    // Make the test text field processed.
-    $instance = field_info_instance('entity_test', 'field_test_text', 'entity_test');
-    $instance['settings']['text_processing'] = 1;
-    field_update_instance($instance);
-
-    $entity = $this->createTestEntity();
-    $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";
-    $this->assertEqual($entity->field_test_text->processed, $target, 'Text is processed with the default filter.');
-
-    // Save and load entity and make sure it still works.
-    $entity->save();
-    $entity = entity_load('entity_test', $entity->id());
-    $this->assertEqual($entity->field_test_text->processed, $target, 'Text is processed with the default filter.');
+    // All entity variations have to have the same results.
+    foreach(_entity_test_entity_types() as $entity_type) {
+      // Make the test text field processed.
+      $instance = field_info_instance($entity_type, 'field_test_text', $entity_type);
+      $instance['settings']['text_processing'] = 1;
+      field_update_instance($instance);
+
+      $entity = $this->createTestEntity($entity_type);
+      $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";
+      $this->assertEqual($entity->field_test_text->processed, $target, $entity_type . ': Text is processed with the default filter.');
+
+      // Save and load entity and make sure it still works.
+      $entity->save();
+      $entity = entity_load($entity_type, $entity->id());
+      $this->assertEqual($entity->field_test_text->processed, $target, $entity_type . ': Text is processed with the default filter.');
+    }
   }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFormTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFormTest.php
index 278e151..7dcf11f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFormTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFormTest.php
@@ -39,38 +39,42 @@ function setUp() {
    * Tests basic form CRUD functionality.
    */
   function testFormCRUD() {
-    $langcode = LANGUAGE_NOT_SPECIFIED;
-    $name1 = $this->randomName(8);
-    $name2 = $this->randomName(10);
+    // All entity variations have to have the same results.
+    foreach(_entity_test_entity_types() as $entity_type) {
+      $entity_type_path = str_replace('_', '-', $entity_type);
 
-    $edit = array(
-      'name' => $name1,
-      'user_id' => mt_rand(0, 128),
-      "field_test_text[$langcode][0][value]" => $this->randomName(16),
-    );
+      $langcode = LANGUAGE_NOT_SPECIFIED;
+      $name1 = $this->randomName(8);
+      $name2 = $this->randomName(10);
+
+      $edit = array(
+        'name' => $name1,
+        'user_id' => mt_rand(0, 128),
+        "field_test_text[$langcode][0][value]" => $this->randomName(16),
+      );
 
-    $this->drupalPost('entity-test/add', $edit, t('Save'));
-    $entity = $this->loadEntityByName($name1);
-    $this->assertTrue($entity, 'Entity found in the database.');
+      $this->drupalPost($entity_type_path . '/add', $edit, t('Save'));
+      $entity = $this->loadEntityByName($entity_type, $name1);
+      $this->assertTrue($entity, $entity_type . ': Entity found in the database.');
 
-    $edit['name'] = $name2;
-    $this->drupalPost('entity-test/manage/' . $entity->id() . '/edit', $edit, t('Save'));
-    $entity = $this->loadEntityByName($name1);
-    $this->assertFalse($entity, 'The entity has been modified.');
-    $entity = $this->loadEntityByName($name2);
-    $this->assertTrue($entity, 'Modified entity found in the database.');
-    $this->assertNotEqual($entity->name->value, $name1, 'The entity name has been modified.');
+      $edit['name'] = $name2;
+      $this->drupalPost($entity_type_path . '/manage/' . $entity->id() . '/edit', $edit, t('Save'));
+      $entity = $this->loadEntityByName($entity_type, $name1);
+      $this->assertFalse($entity, $entity_type . ': The entity has been modified.');
+      $entity = $this->loadEntityByName($entity_type, $name2);
+      $this->assertTrue($entity, $entity_type . ': Modified entity found in the database.');
+      $this->assertNotEqual($entity->name->value, $name1, $entity_type . ': The entity name has been modified.');
 
-    $this->drupalPost('entity-test/manage/' . $entity->id() . '/edit', array(), t('Delete'));
-    $entity = $this->loadEntityByName($name2);
-    $this->assertFalse($entity, 'Entity not found in the database.');
+      $this->drupalPost($entity_type_path . '/manage/' . $entity->id() . '/edit', array(), t('Delete'));
+      $entity = $this->loadEntityByName($entity_type, $name2);
+      $this->assertFalse($entity, $entity_type . ': Entity not found in the database.');
+    }
   }
 
   /**
    * Loads a test entity by name always resetting the storage controller cache.
    */
-  protected function loadEntityByName($name) {
-    $entity_type = 'entity_test';
+  protected function loadEntityByName($entity_type, $name) {
     // Always load the entity from the database to ensure that changes are
     // correctly picked up.
     entity_get_controller($entity_type)->resetCache();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityRevisionsTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityRevisionsTest.php
index c94972d..475922a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityRevisionsTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityRevisionsTest.php
@@ -47,53 +47,58 @@ public function setUp() {
    */
   public function testRevisions() {
 
-    // Create initial entity.
-    $entity = entity_create('entity_test', array(
-      'name' => 'foo',
-      'user_id' => $this->web_user->uid,
-    ));
-    $entity->field_test_text->value = 'bar';
-    $entity->save();
-
-    $entities = array();
-    $names = array();
-    $texts = array();
-    $revision_ids = array();
-
-    // Create three revisions.
-    $revision_count = 3;
-    for ($i = 0; $i < $revision_count; $i++) {
-      $legacy_revision_id = $entity->revision_id->value;
-      $legacy_name = $entity->name->value;
-      $legacy_text = $entity->field_test_text->value;
-
-      $entity = entity_test_load($entity->id->value);
-      $entity->setNewRevision(TRUE);
-      $names[] = $entity->name->value = $this->randomName(32);
-      $texts[] = $entity->field_test_text->value = $this->randomName(32);
+    // All revisionable entity variations have to have the same results.
+    foreach(_entity_test_revisionable_entity_types() as $entity_type) {
+      $entity_type_path = str_replace('_', '-', $entity_type);
+
+      // Create initial entity.
+      $entity = entity_create($entity_type, array(
+        'name' => 'foo',
+        'user_id' => $this->web_user->uid,
+      ));
+      $entity->field_test_text->value = 'bar';
       $entity->save();
-      $revision_ids[] = $entity->revision_id->value;
 
-      // Check that the fields and properties contain new content.
-      $this->assertTrue($entity->revision_id->value > $legacy_revision_id, 'Revision ID changed.');
-      $this->assertNotEqual($entity->name->value, $legacy_name, 'Name changed.');
-      $this->assertNotEqual($entity->field_test_text->value, $legacy_text, 'Text changed.');
+      $entities = array();
+      $names = array();
+      $texts = array();
+      $revision_ids = array();
+
+      // Create three revisions.
+      $revision_count = 3;
+      for ($i = 0; $i < $revision_count; $i++) {
+        $legacy_revision_id = $entity->revision_id->value;
+        $legacy_name = $entity->name->value;
+        $legacy_text = $entity->field_test_text->value;
+
+        $entity = entity_load($entity_type, $entity->id->value);
+        $entity->setNewRevision(TRUE);
+        $names[] = $entity->name->value = $this->randomName(32);
+        $texts[] = $entity->field_test_text->value = $this->randomName(32);
+        $entity->save();
+        $revision_ids[] = $entity->revision_id->value;
+
+        // Check that the fields and properties contain new content.
+        $this->assertTrue($entity->revision_id->value > $legacy_revision_id, $entity_type . ': Revision ID changed.');
+        $this->assertNotEqual($entity->name->value, $legacy_name, $entity_type . ': Name changed.');
+        $this->assertNotEqual($entity->field_test_text->value, $legacy_text, $entity_type . ': Text changed.');
+      }
+
+      for ($i = 0; $i < $revision_count; $i++) {
+        // Load specific revision.
+        $entity_revision = entity_revision_load($entity_type, $revision_ids[$i]);
+
+        // Check if properties and fields contain the revision specific content.
+        $this->assertEqual($entity_revision->revision_id->value, $revision_ids[$i], $entity_type . ': Revision ID matches.');
+        $this->assertEqual($entity_revision->name->value, $names[$i], $entity_type . ': Name matches.');
+        $this->assertEqual($entity_revision->field_test_text->value, $texts[$i], $entity_type . ': Text matches.');
+      }
+
+      // Confirm the correct revision text appears in the edit form.
+      $entity = entity_load($entity_type, $entity->id->value);
+      $this->drupalGet($entity_type_path . '/manage/' . $entity->id->value);
+      $this->assertFieldById('edit-name', $entity->name->value, $entity_type . ': Name matches in UI.');
+      $this->assertFieldById('edit-field-test-text-und-0-value', $entity->field_test_text->value, $entity_type . ': Text matches in UI.');
     }
-
-    for ($i = 0; $i < $revision_count; $i++) {
-      // Load specific revision.
-      $entity_revision = entity_revision_load('entity_test', $revision_ids[$i]);
-
-      // Check if properties and fields contain the revision specific content.
-      $this->assertEqual($entity_revision->revision_id->value, $revision_ids[$i], 'Revision ID matches.');
-      $this->assertEqual($entity_revision->name->value, $names[$i], 'Name matches.');
-      $this->assertEqual($entity_revision->field_test_text->value, $texts[$i], 'Text matches.');
-    }
-
-    // Confirm the correct revision text appears in the edit form.
-    $entity = entity_load('entity_test', $entity->id->value);
-    $this->drupalGet('entity-test/manage/' . $entity->id->value);
-    $this->assertFieldById('edit-name', $entity->name->value, 'Name matches in UI.');
-    $this->assertFieldById('edit-field-test-text-und-0-value', $entity->field_test_text->value, 'Text matches in UI.');
   }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
index b34873a..fc4f711 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
@@ -50,13 +50,16 @@ function setUp() {
     field_create_field($field);
     $this->field = field_read_field($this->field_name);
 
-    $instance = array(
-      'field_name' => $this->field_name,
-      'entity_type' => 'entity_test',
-      'bundle' => 'entity_test',
-    );
-    field_create_instance($instance);
-    $this->instance = field_read_instance('entity_test', $this->field_name, 'entity_test');
+    // Create instance in all entity variations.
+    foreach (_entity_test_entity_types() as $entity_type) {
+      $instance = array(
+        'field_name' => $this->field_name,
+        'entity_type' => $entity_type,
+        'bundle' => $entity_type,
+      );
+      field_create_instance($instance);
+      $this->instance[$entity_type] = field_read_instance($entity_type, $this->field_name, $entity_type);
+    }
 
     // Create test languages.
     $this->langcodes = array();
@@ -74,229 +77,239 @@ function setUp() {
    * Tests language related methods of the Entity class.
    */
   function testEntityLanguageMethods() {
-    $entity = entity_create('entity_test', array(
-      'name' => 'test',
-      'user_id' => $GLOBALS['user']->uid,
-    ));
-    $this->assertEqual($entity->language()->langcode, LANGUAGE_NOT_SPECIFIED, 'Entity language not specified.');
-    $this->assertFalse($entity->getTranslationLanguages(FALSE), 'No translations are available');
-
-    // Set the value in default language.
-    $entity->set($this->field_name, array(0 => array('value' => 'default value')));
-    // Get the value.
-    $this->assertEqual($entity->getTranslation(LANGUAGE_DEFAULT)->get($this->field_name)->value, 'default value', 'Untranslated value retrieved.');
-
-    // Set the value in a certain language. As the entity is not
-    // language-specific it should use the default language and so ignore the
-    // specified language.
-    $entity->getTranslation($this->langcodes[1])->set($this->field_name, array(0 => array('value' => 'default value2')));
-    $this->assertEqual($entity->get($this->field_name)->value, 'default value2', 'Untranslated value updated.');
-    $this->assertFalse($entity->getTranslationLanguages(FALSE), 'No translations are available');
-
-    // Test getting a field value using a specific language for a not
-    // language-specific entity.
-    $this->assertEqual($entity->getTranslation($this->langcodes[1])->get($this->field_name)->value, 'default value2', 'Untranslated value retrieved.');
-
-    // Now, make the entity language-specific by assigning a language and test
-    // translating it.
-    $entity->langcode->value = $this->langcodes[0];
-    $entity->{$this->field_name} = array();
-    $this->assertEqual($entity->language(), language_load($this->langcodes[0]), 'Entity language retrieved.');
-    $this->assertFalse($entity->getTranslationLanguages(FALSE), 'No translations are available');
-
-    // Set the value in default language.
-    $entity->set($this->field_name, array(0 => array('value' => 'default value')));
-    // Get the value.
-    $this->assertEqual($entity->get($this->field_name)->value, 'default value', 'Untranslated value retrieved.');
-
-    // Set a translation.
-    $entity->getTranslation($this->langcodes[1])->set($this->field_name, array(0 => array('value' => 'translation 1')));
-    $this->assertEqual($entity->getTranslation($this->langcodes[1])->{$this->field_name}->value, 'translation 1', 'Translated value set.');
-
-    // Make sure the untranslated value stays.
-    $this->assertEqual($entity->get($this->field_name)->value, 'default value', 'Untranslated value stays.');
-
-    $translations[$this->langcodes[1]] = language_load($this->langcodes[1]);
-    $this->assertEqual($entity->getTranslationLanguages(FALSE), $translations, 'Translations retrieved.');
-
-    // Try to get a not available translation.
-    $this->assertNull($entity->getTranslation($this->langcodes[2])->get($this->field_name)->value, 'A translation that is not available is NULL.');
-
-    // Try to get a value using an invalid language code.
-    try {
-      $entity->getTranslation('invalid')->get($this->field_name)->value;
-      $this->fail('Getting a translation for an invalid language is NULL.');
-    }
-    catch (InvalidArgumentException $e) {
-      $this->pass('A translation for an invalid language is NULL.');
-    }
+    // Test all entity variations.
+    foreach (_entity_test_entity_types() as $entity_type) {
+      $entity = entity_create($entity_type, array(
+        'name' => 'test',
+        'user_id' => $GLOBALS['user']->uid,
+      ));
+      $this->assertEqual($entity->language()->langcode, LANGUAGE_NOT_SPECIFIED, $entity_type . ': Entity language not specified.');
+      $this->assertFalse($entity->getTranslationLanguages(FALSE), $entity_type . ': No translations are available');
+
+      // Set the value in default language.
+      $entity->set($this->field_name, array(0 => array('value' => 'default value')));
+      // Get the value.
+      $this->assertEqual($entity->getTranslation(LANGUAGE_DEFAULT)->get($this->field_name)->value, 'default value', $entity_type . ': Untranslated value retrieved.');
+
+      // Set the value in a certain language. As the entity is not
+      // language-specific it should use the default language and so ignore the
+      // specified language.
+      $entity->getTranslation($this->langcodes[1])->set($this->field_name, array(0 => array('value' => 'default value2')));
+      $this->assertEqual($entity->get($this->field_name)->value, 'default value2', $entity_type . ': Untranslated value updated.');
+      $this->assertFalse($entity->getTranslationLanguages(FALSE), $entity_type . ': No translations are available');
+
+      // Test getting a field value using a specific language for a not
+      // language-specific entity.
+      $this->assertEqual($entity->getTranslation($this->langcodes[1])->get($this->field_name)->value, 'default value2', $entity_type . ': Untranslated value retrieved.');
+
+      // Now, make the entity language-specific by assigning a language and test
+      // translating it.
+      $entity->langcode->value = $this->langcodes[0];
+      $entity->{$this->field_name} = array();
+      $this->assertEqual($entity->language(), language_load($this->langcodes[0]), $entity_type . ': Entity language retrieved.');
+      $this->assertFalse($entity->getTranslationLanguages(FALSE), $entity_type . ': No translations are available');
+
+      // Set the value in default language.
+      $entity->set($this->field_name, array(0 => array('value' => 'default value')));
+      // Get the value.
+      $this->assertEqual($entity->get($this->field_name)->value, 'default value', $entity_type . ': Untranslated value retrieved.');
+
+      // Set a translation.
+      $entity->getTranslation($this->langcodes[1])->set($this->field_name, array(0 => array('value' => 'translation 1')));
+      $this->assertEqual($entity->getTranslation($this->langcodes[1])->{$this->field_name}->value, 'translation 1', $entity_type . ': Translated value set.');
+
+      // Make sure the untranslated value stays.
+      $this->assertEqual($entity->get($this->field_name)->value, 'default value', 'Untranslated value stays.');
+
+      $translations[$this->langcodes[1]] = language_load($this->langcodes[1]);
+      $this->assertEqual($entity->getTranslationLanguages(FALSE), $translations, 'Translations retrieved.');
+
+      // Try to get a not available translation.
+      $this->assertNull($entity->getTranslation($this->langcodes[2])->get($this->field_name)->value, $entity_type . ': A translation that is not available is NULL.');
+
+      // Try to get a value using an invalid language code.
+      try {
+        $entity->getTranslation('invalid')->get($this->field_name)->value;
+        $this->fail('Getting a translation for an invalid language is NULL.');
+      }
+      catch (InvalidArgumentException $e) {
+        $this->pass('A translation for an invalid language is NULL.');
+      }
 
-    // Try to get an untranslatable value from a translation in strict mode.
-    try {
-      $field_name = 'field_test_text';
-      $value = $entity->getTranslation($this->langcodes[1])->get($field_name);
-      $this->fail('Getting an untranslatable value from a translation in strict mode throws an exception.');
-    }
-    catch (InvalidArgumentException $e) {
-      $this->pass('Getting an untranslatable value from a translation in strict mode throws an exception.');
-    }
+      // Try to get an untranslatable value from a translation in strict mode.
+      try {
+        $field_name = 'field_test_text';
+        $value = $entity->getTranslation($this->langcodes[1])->get($field_name);
+        $this->fail($entity_type . ': Getting an untranslatable value from a translation in strict mode throws an exception.');
+      }
+      catch (InvalidArgumentException $e) {
+        $this->pass($entity_type . ': Getting an untranslatable value from a translation in strict mode throws an exception.');
+      }
 
-    // Try to get an untranslatable value from a translation in non-strict
-    // mode.
-    $entity->set($field_name, array(0 => array('value' => 'default value')));
-    $value = $entity->getTranslation($this->langcodes[1], FALSE)->get($field_name)->value;
-    $this->assertEqual($value, 'default value', 'Untranslated value retrieved from translation in non-strict mode.');
+      // Try to get an untranslatable value from a translation in non-strict
+      // mode.
+      $entity->set($field_name, array(0 => array('value' => 'default value')));
+      $value = $entity->getTranslation($this->langcodes[1], FALSE)->get($field_name)->value;
+      $this->assertEqual($value, 'default value', $entity_type . ': Untranslated value retrieved from translation in non-strict mode.');
 
-    // Try to set a value using an invalid language code.
-    try {
-      $entity->getTranslation('invalid')->set($this->field_name, NULL);
-      $this->fail("Setting a translation for an invalid language throws an exception.");
-    }
-    catch (InvalidArgumentException $e) {
-      $this->pass("Setting a translation for an invalid language throws an exception.");
-    }
+      // Try to set a value using an invalid language code.
+      try {
+        $entity->getTranslation('invalid')->set($this->field_name, NULL);
+        $this->fail($entity_type . ': Setting a translation for an invalid language throws an exception.');
+      }
+      catch (InvalidArgumentException $e) {
+        $this->pass($entity_type . ': Setting a translation for an invalid language throws an exception.');
+      }
 
-    // Try to set an untranslatable value into a translation in strict mode.
-    try {
-      $entity->getTranslation($this->langcodes[1])->set($field_name, NULL);
-      $this->fail("Setting an untranslatable value into a translation in strict mode throws an exception.");
-    }
-    catch (InvalidArgumentException $e) {
-      $this->pass("Setting an untranslatable value into a translation in strict mode throws an exception.");
-    }
+      // Try to set an untranslatable value into a translation in strict mode.
+      try {
+        $entity->getTranslation($this->langcodes[1])->set($field_name, NULL);
+        $this->fail($entity_type . ': Setting an untranslatable value into a translation in strict mode throws an exception.');
+      }
+      catch (InvalidArgumentException $e) {
+        $this->pass($entity_type . ': Setting an untranslatable value into a translation in strict mode throws an exception.');
+      }
 
-    // Set the value in default language.
-    $entity->getTranslation($this->langcodes[1], FALSE)->set($field_name, array(0 => array('value' => 'default value2')));
-    // Get the value.
-    $this->assertEqual($entity->get($field_name)->value, 'default value2', 'Untranslated value set into a translation in non-strict mode.');
+      // Set the value in default language.
+      $entity->getTranslation($this->langcodes[1], FALSE)->set($field_name, array(0 => array('value' => 'default value2')));
+      // Get the value.
+      $this->assertEqual($entity->get($field_name)->value, 'default value2', $entity_type . ': Untranslated value set into a translation in non-strict mode.');
+    }
   }
 
   /**
    * Tests multilingual properties.
    */
   function testMultilingualProperties() {
-    $name = $this->randomName();
-    $uid = mt_rand(0, 127);
-    $langcode = $this->langcodes[0];
-
-    // Create a language neutral entity and check that properties are stored
-    // as language neutral.
-    $entity = entity_create('entity_test', array('name' => $name, 'user_id' => $uid));
-    $entity->save();
-    $entity = entity_test_load($entity->id());
-    $this->assertEqual($entity->language()->langcode, LANGUAGE_NOT_SPECIFIED, 'Entity created as language neutral.');
-    $this->assertEqual($name, $entity->getTranslation(LANGUAGE_DEFAULT)->get('name')->value, 'The entity name has been correctly stored as language neutral.');
-    $this->assertEqual($uid, $entity->getTranslation(LANGUAGE_DEFAULT)->get('user_id')->value, 'The entity author has been correctly stored as language neutral.');
-    // As fields, translatable properties should ignore the given langcode and
-    // use neutral language if the entity is not translatable.
-    $this->assertEqual($name, $entity->getTranslation($langcode)->get('name')->value, 'The entity name defaults to neutral language.');
-    $this->assertEqual($uid, $entity->getTranslation($langcode)->get('user_id')->value, 'The entity author defaults to neutral language.');
-    $this->assertEqual($name, $entity->get('name')->value, 'The entity name can be retrieved without specifying a language.');
-    $this->assertEqual($uid, $entity->get('user_id')->value, 'The entity author can be retrieved without specifying a language.');
-
-    // Create a language-aware entity and check that properties are stored
-    // as language-aware.
-    $entity = entity_create('entity_test', array('name' => $name, 'user_id' => $uid, 'langcode' => $langcode));
-    $entity->save();
-    $entity = entity_test_load($entity->id());
-    $this->assertEqual($entity->language()->langcode, $langcode, 'Entity created as language specific.');
-    $this->assertEqual($name, $entity->getTranslation($langcode)->get('name')->value, 'The entity name has been correctly stored as a language-aware property.');
-    $this->assertEqual($uid, $entity->getTranslation($langcode)->get('user_id')->value, 'The entity author has been correctly stored as a language-aware property.');
-    // Translatable properties on a translatable entity should use default
-    // language if LANGUAGE_NOT_SPECIFIED is passed.
-    $this->assertEqual($name, $entity->getTranslation(LANGUAGE_NOT_SPECIFIED)->get('name')->value, 'The entity name defaults to the default language.');
-    $this->assertEqual($uid, $entity->getTranslation(LANGUAGE_NOT_SPECIFIED)->get('user_id')->value, 'The entity author defaults to the default language.');
-    $this->assertEqual($name, $entity->get('name')->value, 'The entity name can be retrieved without specifying a language.');
-    $this->assertEqual($uid, $entity->get('user_id')->value, 'The entity author can be retrieved without specifying a language.');
-
-    // Create property translations.
-    $properties = array();
-    $default_langcode = $langcode;
-    foreach ($this->langcodes as $langcode) {
-      if ($langcode != $default_langcode) {
-        $properties[$langcode] = array(
-          'name' => array(0 => $this->randomName()),
-          'user_id' => array(0 => mt_rand(0, 127)),
-        );
+    // Test all entity variations with data table support.
+    foreach (_entity_test_multilingual_entity_types() as $entity_type) {
+
+      $name = $this->randomName();
+      $uid = mt_rand(0, 127);
+      $langcode = $this->langcodes[0];
+
+      // Create a language neutral entity and check that properties are stored
+      // as language neutral.
+      $entity = entity_create($entity_type, array('name' => $name, 'user_id' => $uid));
+      $entity->save();
+      $entity = entity_load($entity_type, $entity->id());
+      $this->assertEqual($entity->language()->langcode, LANGUAGE_NOT_SPECIFIED, $entity_type . ': Entity created as language neutral.');
+      $this->assertEqual($name, $entity->getTranslation(LANGUAGE_DEFAULT)->get('name')->value, $entity_type . ': The entity name has been correctly stored as language neutral.');
+      $this->assertEqual($uid, $entity->getTranslation(LANGUAGE_DEFAULT)->get('user_id')->value, $entity_type . ': The entity author has been correctly stored as language neutral.');
+      // As fields, translatable properties should ignore the given langcode and
+      // use neutral language if the entity is not translatable.
+      $this->assertEqual($name, $entity->getTranslation($langcode)->get('name')->value, $entity_type . ': The entity name defaults to neutral language.');
+      $this->assertEqual($uid, $entity->getTranslation($langcode)->get('user_id')->value, $entity_type . ': The entity author defaults to neutral language.');
+      $this->assertEqual($name, $entity->get('name')->value, $entity_type . ': The entity name can be retrieved without specifying a language.');
+      $this->assertEqual($uid, $entity->get('user_id')->value, $entity_type . ': The entity author can be retrieved without specifying a language.');
+
+      // Create a language-aware entity and check that properties are stored
+      // as language-aware.
+      $entity = entity_create($entity_type, array('name' => $name, 'user_id' => $uid, 'langcode' => $langcode));
+      $entity->save();
+      $entity = entity_load($entity_type, $entity->id());
+      $this->assertEqual($entity->language()->langcode, $langcode, $entity_type . ': Entity created as language specific.');
+      $this->assertEqual($name, $entity->getTranslation($langcode)->get('name')->value, $entity_type . ': The entity name has been correctly stored as a language-aware property.');
+      $this->assertEqual($uid, $entity->getTranslation($langcode)->get('user_id')->value, $entity_type . ': The entity author has been correctly stored as a language-aware property.');
+      // Translatable properties on a translatable entity should use default
+      // language if LANGUAGE_NOT_SPECIFIED is passed.
+      $this->assertEqual($name, $entity->getTranslation(LANGUAGE_NOT_SPECIFIED)->get('name')->value, $entity_type . ': The entity name defaults to the default language.');
+      $this->assertEqual($uid, $entity->getTranslation(LANGUAGE_NOT_SPECIFIED)->get('user_id')->value, $entity_type . ': The entity author defaults to the default language.');
+      $this->assertEqual($name, $entity->get('name')->value, $entity_type . ': The entity name can be retrieved without specifying a language.');
+      $this->assertEqual($uid, $entity->get('user_id')->value, $entity_type . ': The entity author can be retrieved without specifying a language.');
+
+      // Create property translations.
+      $properties = array();
+      $default_langcode = $langcode;
+      foreach ($this->langcodes as $langcode) {
+        if ($langcode != $default_langcode) {
+          $properties[$langcode] = array(
+            'name' => array(0 => $this->randomName()),
+            'user_id' => array(0 => mt_rand(0, 127)),
+          );
+        }
+        else {
+          $properties[$langcode] = array(
+            'name' => array(0 => $name),
+            'user_id' => array(0 => $uid),
+          );
+        }
+        $entity->getTranslation($langcode)->setPropertyValues($properties[$langcode]);
       }
-      else {
-        $properties[$langcode] = array(
-          'name' => array(0 => $name),
-          'user_id' => array(0 => $uid),
+      $entity->save();
+
+      // Check that property translation were correctly stored.
+      $entity = entity_load($entity_type, $entity->id());
+      foreach ($this->langcodes as $langcode) {
+        $args = array(
+          '%entity_type' => $entity_type,
+          '%langcode' => $langcode,
         );
+        $this->assertEqual($properties[$langcode]['name'][0], $entity->getTranslation($langcode)->get('name')->value, format_string('%entity_type: The entity name has been correctly stored for language %langcode.', $args));
+        $this->assertEqual($properties[$langcode]['user_id'][0], $entity->getTranslation($langcode)->get('user_id')->value, format_string('%entity_type: The entity author has been correctly stored for language %langcode.', $args));
       }
-      $entity->getTranslation($langcode)->setPropertyValues($properties[$langcode]);
-    }
-    $entity->save();
-
-    // Check that property translation were correctly stored.
-    $entity = entity_test_load($entity->id());
-    foreach ($this->langcodes as $langcode) {
-      $args = array('%langcode' => $langcode);
-      $this->assertEqual($properties[$langcode]['name'][0], $entity->getTranslation($langcode)->get('name')->value, format_string('The entity name has been correctly stored for language %langcode.', $args));
-      $this->assertEqual($properties[$langcode]['user_id'][0], $entity->getTranslation($langcode)->get('user_id')->value, format_string('The entity author has been correctly stored for language %langcode.', $args));
-    }
 
-    // Test query conditions (cache is reset at each call).
-    $translated_id = $entity->id();
-    // Create an additional entity with only the uid set. The uid for the
-    // original language is the same of one used for a translation.
-    $langcode = $this->langcodes[1];
-    entity_create('entity_test', array(
-      'user_id' => $properties[$langcode]['user_id'],
-      'name' => 'some name',
-    ))->save();
-
-    $entities = entity_test_load_multiple();
-    $this->assertEqual(count($entities), 3, 'Three entities were created.');
-    $entities = entity_test_load_multiple(array($translated_id));
-    $this->assertEqual(count($entities), 1, 'One entity correctly loaded by id.');
-    $entities = entity_load_multiple_by_properties('entity_test', array('name' => $name));
-    $this->assertEqual(count($entities), 2, 'Two entities correctly loaded by name.');
-    // @todo The default language condition should go away in favor of an
-    // explicit parameter.
-    $entities = entity_load_multiple_by_properties('entity_test', array('name' => $properties[$langcode]['name'][0], 'default_langcode' => 0));
-    $this->assertEqual(count($entities), 1, 'One entity correctly loaded by name translation.');
-    $entities = entity_load_multiple_by_properties('entity_test', array('langcode' => $default_langcode, 'name' => $name));
-    $this->assertEqual(count($entities), 1, 'One entity correctly loaded by name and language.');
-
-    $entities = entity_load_multiple_by_properties('entity_test', array('langcode' => $langcode, 'name' => $properties[$langcode]['name'][0]));
-    $this->assertEqual(count($entities), 0, 'No entity loaded by name translation specifying the translation language.');
-    $entities = entity_load_multiple_by_properties('entity_test', array('langcode' => $langcode, 'name' => $properties[$langcode]['name'][0], 'default_langcode' => 0));
-    $this->assertEqual(count($entities), 1, 'One entity loaded by name translation and language specifying to look for translations.');
-    $entities = entity_load_multiple_by_properties('entity_test', array('user_id' => $properties[$langcode]['user_id'][0], 'default_langcode' => NULL));
-    $this->assertEqual(count($entities), 2, 'Two entities loaded by uid without caring about property translatability.');
-
-    // Test property conditions and orders with multiple languages in the same
-    // query.
-    $query = entity_query('entity_test');
-    $group = $query->andConditionGroup()
-      ->condition('user_id', $properties[$default_langcode]['user_id'], '=', $default_langcode)
-      ->condition('name', $properties[$default_langcode]['name'], '=', $default_langcode);
-    $result = $query
-      ->condition($group)
-      ->condition('name', $properties[$langcode]['name'], '=', $langcode)
-      ->execute();
-    $this->assertEqual(count($result), 1, 'One entity loaded by name and uid using different language meta conditions.');
-
-    // Test mixed property and field conditions.
-    $entity = entity_load('entity_test', reset($result), TRUE);
-    $field_value = $this->randomString();
-    $entity->getTranslation($langcode)->set($this->field_name, array(array('value' => $field_value)));
-    $entity->save();
-    $query = entity_query('entity_test');
-    $default_langcode_group = $query->andConditionGroup()
-      ->condition('user_id', $properties[$default_langcode]['user_id'], '=', $default_langcode)
-      ->condition('name', $properties[$default_langcode]['name'], '=', $default_langcode);
-    $langcode_group = $query->andConditionGroup()
-      ->condition('name', $properties[$langcode]['name'], '=', $langcode)
-      ->condition("$this->field_name.value", $field_value, '=', $langcode);
-    $result = $query
-      ->condition('langcode', $default_langcode)
-      ->condition($default_langcode_group)
-      ->condition($langcode_group)
-      ->execute();
-    $this->assertEqual(count($result), 1, 'One entity loaded by name, uid and field value using different language meta conditions.');
+      // Test query conditions (cache is reset at each call).
+      $translated_id = $entity->id();
+      // Create an additional entity with only the uid set. The uid for the
+      // original language is the same of one used for a translation.
+      $langcode = $this->langcodes[1];
+      entity_create($entity_type, array(
+        'user_id' => $properties[$langcode]['user_id'],
+        'name' => 'some name',
+      ))->save();
+
+      $entities = entity_load_multiple($entity_type);
+      $this->assertEqual(count($entities), 3, $entity_type . ': Three entities were created.');
+      $entities = entity_load_multiple($entity_type, array($translated_id));
+      $this->assertEqual(count($entities), 1, $entity_type . ': One entity correctly loaded by id.');
+      $entities = entity_load_multiple_by_properties($entity_type, array('name' => $name));
+      $this->assertEqual(count($entities), 2, $entity_type . ': Two entities correctly loaded by name.');
+      // @todo The default language condition should go away in favor of an
+      // explicit parameter.
+      $entities = entity_load_multiple_by_properties($entity_type, array('name' => $properties[$langcode]['name'][0], 'default_langcode' => 0));
+      $this->assertEqual(count($entities), 1, $entity_type . ': One entity correctly loaded by name translation.');
+      $entities = entity_load_multiple_by_properties($entity_type, array('langcode' => $default_langcode, 'name' => $name));
+      $this->assertEqual(count($entities), 1, $entity_type . ': One entity correctly loaded by name and language.');
+
+      $entities = entity_load_multiple_by_properties($entity_type, array('langcode' => $langcode, 'name' => $properties[$langcode]['name'][0]));
+      $this->assertEqual(count($entities), 0, $entity_type . ': No entity loaded by name translation specifying the translation language.');
+      $entities = entity_load_multiple_by_properties($entity_type, array('langcode' => $langcode, 'name' => $properties[$langcode]['name'][0], 'default_langcode' => 0));
+      $this->assertEqual(count($entities), 1, $entity_type . ': One entity loaded by name translation and language specifying to look for translations.');
+      $entities = entity_load_multiple_by_properties($entity_type, array('user_id' => $properties[$langcode]['user_id'][0], 'default_langcode' => NULL));
+      $this->assertEqual(count($entities), 2, $entity_type . ': Two entities loaded by uid without caring about property translatability.');
+
+      // Test property conditions and orders with multiple languages in the same
+      // query.
+      $query = entity_query($entity_type);
+      $group = $query->andConditionGroup()
+        ->condition('user_id', $properties[$default_langcode]['user_id'], '=', $default_langcode)
+        ->condition('name', $properties[$default_langcode]['name'], '=', $default_langcode);
+      $result = $query
+        ->condition($group)
+        ->condition('name', $properties[$langcode]['name'], '=', $langcode)
+        ->execute();
+      $this->assertEqual(count($result), 1, $entity_type . ': One entity loaded by name and uid using different language meta conditions.');
+
+      // Test mixed property and field conditions.
+      $entity = entity_load($entity_type, reset($result), TRUE);
+      $field_value = $this->randomString();
+      $entity->getTranslation($langcode)->set($this->field_name, array(array('value' => $field_value)));
+      $entity->save();
+      $query = entity_query($entity_type);
+      $default_langcode_group = $query->andConditionGroup()
+        ->condition('user_id', $properties[$default_langcode]['user_id'], '=', $default_langcode)
+        ->condition('name', $properties[$default_langcode]['name'], '=', $default_langcode);
+      $langcode_group = $query->andConditionGroup()
+        ->condition('name', $properties[$langcode]['name'], '=', $langcode)
+        ->condition("$this->field_name.value", $field_value, '=', $langcode);
+      $result = $query
+        ->condition('langcode', $default_langcode)
+        ->condition($default_langcode_group)
+        ->condition($langcode_group)
+        ->execute();
+      $this->assertEqual(count($result), 1, $entity_type . ': One entity loaded by name, uid and field value using different language meta conditions.');
+    }
   }
 
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
index cb2f7f3..442396d 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
@@ -34,57 +34,60 @@ public static function getInfo() {
    * Tests UUID generation in entity CRUD operations.
    */
   function testCRUD() {
-    // Verify that no UUID is auto-generated when passing one for creation.
-    $uuid_service = new Uuid();
-    $uuid = $uuid_service->generate();
-    $custom_entity = entity_create('entity_test', array(
-      'name' => $this->randomName(),
-      'uuid' => $uuid,
-    ));
-    $this->assertIdentical($custom_entity->uuid(), $uuid);
-    // Save this entity, so we have more than one later.
-    $custom_entity->save();
+    // All entity variations have to have the same results.
+    foreach (_entity_test_entity_types() as $entity_type) {
+      // Verify that no UUID is auto-generated when passing one for creation.
+      $uuid_service = new Uuid();
+      $uuid = $uuid_service->generate();
+      $custom_entity = entity_create($entity_type, array(
+        'name' => $this->randomName(),
+        'uuid' => $uuid,
+      ));
+      $this->assertIdentical($custom_entity->uuid(), $uuid);
+      // Save this entity, so we have more than one later.
+      $custom_entity->save();
 
-    // Verify that a new UUID is generated upon creating an entity.
-    $entity = entity_create('entity_test', array('name' => $this->randomName()));
-    $uuid = $entity->uuid();
-    $this->assertTrue($uuid);
+      // Verify that a new UUID is generated upon creating an entity.
+      $entity = entity_create($entity_type, array('name' => $this->randomName()));
+      $uuid = $entity->uuid();
+      $this->assertTrue($uuid);
 
-    // Verify that the new UUID is different.
-    $this->assertNotEqual($custom_entity->uuid(), $uuid);
+      // Verify that the new UUID is different.
+      $this->assertNotEqual($custom_entity->uuid(), $uuid);
 
-    // Verify that the UUID is retained upon saving.
-    $entity->save();
-    $this->assertIdentical($entity->uuid(), $uuid);
+      // Verify that the UUID is retained upon saving.
+      $entity->save();
+      $this->assertIdentical($entity->uuid(), $uuid);
 
-    // Verify that the UUID is retained upon loading.
-    $entity_loaded = entity_test_load($entity->id(), TRUE);
-    $this->assertIdentical($entity_loaded->uuid(), $uuid);
+      // Verify that the UUID is retained upon loading.
+      $entity_loaded = entity_load($entity_type, $entity->id(), TRUE);
+      $this->assertIdentical($entity_loaded->uuid(), $uuid);
 
-    // Verify that entity_load_by_uuid() loads the same entity.
-    $entity_loaded_by_uuid = entity_load_by_uuid('entity_test', $uuid, TRUE);
-    $this->assertIdentical($entity_loaded_by_uuid->uuid(), $uuid);
-    $this->assertEqual($entity_loaded_by_uuid->id(), $entity_loaded->id());
+      // Verify that entity_load_by_uuid() loads the same entity.
+      $entity_loaded_by_uuid = entity_load_by_uuid($entity_type, $uuid, TRUE);
+      $this->assertIdentical($entity_loaded_by_uuid->uuid(), $uuid);
+      $this->assertEqual($entity_loaded_by_uuid->id(), $entity_loaded->id());
 
-    // Creating a duplicate needs to result in a new UUID.
-    $entity_duplicate = $entity->createDuplicate();
-    foreach ($entity->getProperties() as $property => $value) {
-      switch($property) {
-        case 'uuid':
-          $this->assertNotNull($entity_duplicate->uuid());
-          $this->assertNotNull($entity->uuid());
-          $this->assertNotEqual($entity_duplicate->uuid(), $entity->uuid());
-          break;
-        case 'id':
-          $this->assertNull($entity_duplicate->id());
-          $this->assertNotNull($entity->id());
-          $this->assertNotEqual($entity_duplicate->id(), $entity->id());
-          break;
-        default:
-          $this->assertEqual($entity_duplicate->{$property}->value, $entity->{$property}->value);
+      // Creating a duplicate needs to result in a new UUID.
+      $entity_duplicate = $entity->createDuplicate();
+      foreach ($entity->getProperties() as $property => $value) {
+        switch($property) {
+          case 'uuid':
+            $this->assertNotNull($entity_duplicate->uuid());
+            $this->assertNotNull($entity->uuid());
+            $this->assertNotEqual($entity_duplicate->uuid(), $entity->uuid());
+            break;
+          case 'id':
+            $this->assertNull($entity_duplicate->id());
+            $this->assertNotNull($entity->id());
+            $this->assertNotEqual($entity_duplicate->id(), $entity->id());
+            break;
+          default:
+            $this->assertEqual($entity_duplicate->{$property}->value, $entity->{$property}->value);
+        }
       }
+      $entity_duplicate->save();
+      $this->assertNotEqual($entity->id(), $entity_duplicate->id());
     }
-    $entity_duplicate->save();
-    $this->assertNotEqual($entity->id(), $entity_duplicate->id());
   }
 }
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.install b/core/modules/system/tests/modules/entity_test/entity_test.install
index 617986a..8fed8b2 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.install
+++ b/core/modules/system/tests/modules/entity_test/entity_test.install
@@ -18,23 +18,32 @@ function entity_test_install() {
   );
   field_create_field($field);
 
-  $instance = array(
-    'entity_type' => 'entity_test',
-    'field_name' => 'field_test_text',
-    'bundle' => 'entity_test',
-    'label' => 'Test text-field',
-    'widget' => array(
-      'type' => 'text_textfield',
-      'weight' => 0,
-    ),
+  $entity_types = array(
+    'entity_test',
+    'entity_test_rev',
+    'entity_test_mul',
+    'entity_test_mulrev',
   );
-  field_create_instance($instance);
+  foreach ($entity_types as $entity_type) {
+    $instance = array(
+      'entity_type' => $entity_type,
+      'field_name' => 'field_test_text',
+      'bundle' => $entity_type,
+      'label' => 'Test text-field',
+      'widget' => array(
+        'type' => 'text_textfield',
+        'weight' => 0,
+      ),
+    );
+    field_create_instance($instance);
+  }
 }
 
 /**
  * Implements hook_schema().
  */
 function entity_test_schema() {
+  // Schema for simple entity.
   $schema['entity_test'] = array(
     'description' => 'Stores entity_test items.',
     'fields' => array(
@@ -43,8 +52,51 @@ function entity_test_schema() {
         'not null' => TRUE,
         'description' => 'Primary Key: Unique entity-test item ID.',
       ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of the original variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'name' => array(
+        'description' => 'The name of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'user_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+        'description' => "The {users}.uid of the associated user.",
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+  );
+
+  // Schema for entity with revisions.
+  $schema['entity_test_rev'] = array(
+    'description' => 'Stores entity_test_rev items.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique entity-test item ID.',
+      ),
       'revision_id' => array(
-        'description' => 'The current {entity_test_property_revision}.revision_id version identifier.',
+        'description' => 'The current {entity_test_rev_property_revision}.revision_id version identifier.',
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
@@ -63,23 +115,187 @@ function entity_test_schema() {
         'not null' => TRUE,
         'default' => '',
       ),
+      'name' => array(
+        'description' => 'The name of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
     ),
     'primary key' => array('id'),
     'unique keys' => array(
       'uuid' => array('uuid'),
     ),
   );
-  $schema['entity_test_property_data'] = array(
-    'description' => 'Stores entity_test item properties.',
+  $schema['entity_test_rev_revision'] = array(
+    'description' => 'Stores entity_test_rev item property revisions.',
     'fields' => array(
       'id' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
-        'description' => 'The {entity_test}.id of the test entity.',
+        'description' => 'The {entity_test_rev}.id of the test entity.',
       ),
       'revision_id' => array(
-        'description' => 'The current {entity_test_property_revision}.revision_id version identifier.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The primary identifier for this version.',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'name' => array(
+        'description' => 'The name of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'user_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+        'description' => "The {users}.uid of the associated user.",
+      ),
+    ),
+    'indexes' => array(
+      'user_id' => array('user_id'),
+    ),
+    'foreign keys' => array(
+      'user_id' => array('users' => 'uid'),
+      'id' => array('entity_test_rev' => 'id'),
+    ),
+    'primary key' => array('revision_id', 'id', 'langcode'),
+  );
+
+  // Schema for entity with data table.
+  $schema['entity_test_mul'] = array(
+    'description' => 'Stores entity_test_mul items.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique entity-test item ID.',
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of the original variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+  );
+  $schema['entity_test_mul_property_data'] = array(
+    'description' => 'Stores entity_test_mul item properties.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {entity_test_mul}.id of the test entity.',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'default_langcode' => array(
+        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'name' => array(
+        'description' => 'The name of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'user_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+        'description' => "The {users}.uid of the associated user.",
+      ),
+    ),
+    'indexes' => array(
+      'user_id' => array('user_id'),
+    ),
+    'foreign keys' => array(
+      'user_id' => array('users' => 'uid'),
+      'id' => array('entity_test_mul' => 'id'),
+    ),
+    'primary key' => array('id', 'langcode'),
+  );
+
+  // Schema for entity with data table and revisions.
+  $schema['entity_test_mulrev'] = array(
+    'description' => 'Stores entity_test_mulrev items.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique entity-test item ID.',
+      ),
+      'revision_id' => array(
+        'description' => 'The current {entity_test_mulrev_property_revision}.revision_id version identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of the original variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+  );
+  $schema['entity_test_mulrev_property_data'] = array(
+    'description' => 'Stores entity_test_mulrev item properties.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {entity_test_mulrev}.id of the test entity.',
+      ),
+      'revision_id' => array(
+        'description' => 'The current {entity_test_mulrev_property_revision}.revision_id version identifier.',
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
@@ -118,18 +334,18 @@ function entity_test_schema() {
     ),
     'foreign keys' => array(
       'user_id' => array('users' => 'uid'),
-      'id' => array('entity_test' => 'id'),
+      'id' => array('entity_test_mulrev' => 'id'),
     ),
     'primary key' => array('id', 'langcode'),
   );
-  $schema['entity_test_property_revision'] = array(
-    'description' => 'Stores entity_test item property revisions.',
+  $schema['entity_test_mulrev_property_revision'] = array(
+    'description' => 'Stores entity_test_mulrev item property revisions.',
     'fields' => array(
       'id' => array(
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
-        'description' => 'The {entity_test}.id of the test entity.',
+        'description' => 'The {entity_test_mulrev}.id of the test entity.',
       ),
       'revision_id' => array(
         'type' => 'serial',
@@ -170,9 +386,10 @@ function entity_test_schema() {
     ),
     'foreign keys' => array(
       'user_id' => array('users' => 'uid'),
-      'id' => array('entity_test' => 'id'),
+      'id' => array('entity_test_mulrev' => 'id'),
     ),
     'primary key' => array('revision_id', 'id', 'langcode'),
   );
+
   return $schema;
 }
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index 403c1b9..54d1ac9 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -2,11 +2,54 @@
 
 /**
  * @file
- * Test module for the entity API providing an entity type for testing.
+ * Test module for the entity API providing several entity types for testing.
  */
 
 use Drupal\entity_test\Plugin\Core\Entity\EntityTest;
+use Drupal\entity_test\Plugin\Core\Entity\EntityTestRev;
+use Drupal\entity_test\Plugin\Core\Entity\EntityTestMul;
+use Drupal\entity_test\Plugin\Core\Entity\EntityTestMulRev;
 
+/**
+ * Returns the a list of entity types to deal with.
+ *
+ * @return array
+ *   List with entity_types.
+ */
+function _entity_test_entity_types() {
+  return array(
+    'entity_test' => 'entity_test',
+    'entity_test_rev' => 'entity_test_rev',
+    'entity_test_mul' => 'entity_test_mul',
+    'entity_test_mulrev' => 'entity_test_mulrev',
+  );
+}
+
+/**
+ * Returns the a list of entity types that support revisions.
+ *
+ * @return array
+ *   List with entity_types.
+ */
+function _entity_test_revisionable_entity_types() {
+  return array(
+    'entity_test_rev' => 'entity_test_rev',
+    'entity_test_mulrev' => 'entity_test_mulrev',
+  );
+}
+
+/**
+ * Returns the a list of entity types that have a dedicated data table.
+ *
+ * @return array
+ *   List with entity_types.
+ */
+function _entity_test_multilingual_entity_types() {
+  return array(
+    'entity_test_mul' => 'entity_test_mul',
+    'entity_test_mulrev' => 'entity_test_mulrev',
+  );
+}
 
 /**
  * Implements hook_entity_info_alter().
@@ -14,7 +57,9 @@
 function entity_test_entity_info_alter(&$info) {
   // Optionally specify a translation handler for testing translations.
   if (variable_get('entity_test_translation')) {
-    $info['entity_test']['translation']['entity_test'] = TRUE;
+    foreach(_entity_test_entity_types() as $entity_type) {
+      $info[$entity_type]['translation'][$entity_type] = TRUE;
+    }
   }
   // Optionally unset the access controller to test the fallback.
   if (state()->get('entity_test.default_access_controller')) {
@@ -47,30 +92,41 @@ function entity_test_permission() {
 function entity_test_menu() {
   $items = array();
 
-  $items['entity-test/add'] = array(
-    'title' => 'Add an entity_test',
-    'page callback' => 'entity_test_add',
-    'access arguments' => array('administer entity_test content'),
-    'type' => MENU_NORMAL_ITEM,
-  );
+  foreach(_entity_test_entity_types() as $entity_type) {
+    $entity_type_path = str_replace('_', '-', $entity_type);
+    $items[$entity_type_path . '/add'] = array(
+      'title' => 'Add an ' . $entity_type,
+      'page callback' => $entity_type . '_add',
+      'access arguments' => array('administer entity_test content'),
+      'type' => MENU_NORMAL_ITEM,
+    );
 
-  $items['entity-test/manage/%entity_test'] = array(
-    'title' => 'Edit test entity',
-    'page callback' => 'entity_test_edit',
-    'page arguments' => array(2),
-    'access arguments' => array('administer entity_test content'),
-    'type' => MENU_NORMAL_ITEM,
-  );
+    $items[$entity_type_path . '/manage/%' . $entity_type] = array(
+      'title' => 'Edit ' . $entity_type,
+      'page callback' => $entity_type . '_edit',
+      'page arguments' => array(2),
+      'access arguments' => array('administer entity_test content'),
+      'type' => MENU_NORMAL_ITEM,
+    );
 
-  $items['entity-test/manage/%entity_test/edit'] = array(
-    'title' => 'Edit',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-  );
+    $items[$entity_type_path . '/manage/%' . $entity_type . '/edit'] = array(
+      'title' => 'Edit',
+      'type' => MENU_DEFAULT_LOCAL_TASK,
+    );
+  }
 
   return $items;
 }
 
 /**
+ * Implements hook_form_BASE_FORM_ID_alter().
+ */
+function entity_test_form_node_form_alter(&$form, &$form_state, $form_id) {
+  $langcode = $form_state['controller']->getFormLangcode($form_state);
+  variable_set('entity_form_langcode', $langcode);
+}
+
+/**
  * Menu callback: displays the 'Add new entity_test' form.
  *
  * @return array
@@ -87,7 +143,7 @@ function entity_test_add() {
 /**
  * Menu callback: displays the 'Edit existing entity_test' form.
  *
- * @param array $entity
+ * @param EntityTest $entity
  *   The entity to be edited.
  *
  * @return array
@@ -141,9 +197,211 @@ function entity_test_delete_multiple(array $ids) {
 }
 
 /**
- * Implements hook_form_BASE_FORM_ID_alter().
+ * Menu callback: displays the 'Add new entity_test_rev' form.
+ *
+ * @return array
+ *   The processed form for a new entity_test_rev.
+ *
+ * @see entity_test_menu()
  */
-function entity_test_form_node_form_alter(&$form, &$form_state, $form_id) {
-  $langcode = $form_state['controller']->getFormLangcode($form_state);
-  variable_set('entity_form_langcode', $langcode);
+function entity_test_rev_add() {
+  drupal_set_title(t('Create an entity_test_rev'));
+  $entity = entity_create('entity_test_rev', array());
+  return entity_get_form($entity);
+}
+
+/**
+ * Menu callback: displays the 'Edit existing entity_test_rev' form.
+ *
+ * @param EntityTestRev $entity
+ *   The entity to be edited.
+ *
+ * @return array
+ *   The processed form for the edited entity_test_rev.
+ *
+ * @see entity_test_menu()
+ */
+function entity_test_rev_edit(EntityTestRev $entity) {
+  drupal_set_title($entity->label(), PASS_THROUGH);
+  return entity_get_form($entity);
+}
+
+/**
+ * Loads a test entity.
+ *
+ * @param int $id
+ *   A test entity ID.
+ * @param bool $reset
+ *   A boolean indicating that the internal cache should be reset.
+ *
+ * @return Drupal\entity_test\Plugin\Core\Entity\EntityTest
+ *   The loaded entity object, or FALSE if the entity cannot be loaded.
+ */
+function entity_test_rev_load($id, $reset = FALSE) {
+  return entity_load('entity_test_rev', $id, $reset);
+}
+
+/**
+ * Loads multiple test entities based on certain conditions.
+ *
+ * @param array $ids
+ *   (optional) An array of entity IDs. If omitted, all entities are loaded.
+ * @param bool $reset
+ *   A boolean indicating that the internal cache should be reset.
+ *
+ * @return array
+ *   An array of test entity objects, indexed by ID.
+ */
+function entity_test_rev_load_multiple(array $ids = NULL, $reset = FALSE) {
+  return entity_load_multiple('entity_test_rev', $ids, $reset);
+}
+
+/**
+ * Deletes multiple test entities.
+ *
+ * @param $ids
+ *   An array of test entity IDs.
+ */
+function entity_test_rev_delete_multiple(array $ids) {
+  entity_delete_multiple('entity_test_rev', $ids);
+}
+
+/**
+ * Menu callback: displays the 'Add new entity_test_mul' form.
+ *
+ * @return array
+ *   The processed form for a new entity_test_mul.
+ *
+ * @see entity_test_menu()
+ */
+function entity_test_mul_add() {
+  drupal_set_title(t('Create an entity_test_mul'));
+  $entity = entity_create('entity_test_mul', array());
+  return entity_get_form($entity);
+}
+
+/**
+ * Menu callback: displays the 'Edit existing entity_test_mul' form.
+ *
+ * @param EntityTestMul $entity
+ *   The entity to be edited.
+ *
+ * @return array
+ *   The processed form for the edited entity_test_mul.
+ *
+ * @see entity_test_menu()
+ */
+function entity_test_mul_edit(EntityTestMul $entity) {
+  drupal_set_title($entity->label(), PASS_THROUGH);
+  return entity_get_form($entity);
+}
+
+/**
+ * Loads a test entity.
+ *
+ * @param int $id
+ *   A test entity ID.
+ * @param bool $reset
+ *   A boolean indicating that the internal cache should be reset.
+ *
+ * @return Drupal\entity_test\Plugin\Core\Entity\EntityTest
+ *   The loaded entity object, or FALSE if the entity cannot be loaded.
+ */
+function entity_test_mul_load($id, $reset = FALSE) {
+  return entity_load('entity_test_mul', $id, $reset);
+}
+
+/**
+ * Loads multiple test entities based on certain conditions.
+ *
+ * @param array $ids
+ *   (optional) An array of entity IDs. If omitted, all entities are loaded.
+ * @param bool $reset
+ *   A boolean indicating that the internal cache should be reset.
+ *
+ * @return array
+ *   An array of test entity objects, indexed by ID.
+ */
+function entity_test_mul_load_multiple(array $ids = NULL, $reset = FALSE) {
+  return entity_load_multiple('entity_test_mul', $ids, $reset);
+}
+
+/**
+ * Deletes multiple test entities.
+ *
+ * @param $ids
+ *   An array of test entity IDs.
+ */
+function entity_test_mul_delete_multiple(array $ids) {
+  entity_delete_multiple('entity_test_mul', $ids);
+}
+
+/**
+ * Menu callback: displays the 'Add new entity_test_mulrev' form.
+ *
+ * @return array
+ *   The processed form for a new entity_test_mulrev.
+ *
+ * @see entity_test_menu()
+ */
+function entity_test_mulrev_add() {
+  drupal_set_title(t('Create an entity_test_mulrev'));
+  $entity = entity_create('entity_test_mulrev', array());
+  return entity_get_form($entity);
+}
+
+/**
+ * Menu callback: displays the 'Edit existing entity_test_mulrev' form.
+ *
+ * @param EntityTestMulRev $entity
+ *   The entity to be edited.
+ *
+ * @return array
+ *   The processed form for the edited entity_test_mulrev.
+ *
+ * @see entity_test_menu()
+ */
+function entity_test_mulrev_edit(EntityTestMulRev $entity) {
+  drupal_set_title($entity->label(), PASS_THROUGH);
+  return entity_get_form($entity);
+}
+
+/**
+ * Loads a test entity.
+ *
+ * @param int $id
+ *   A test entity ID.
+ * @param bool $reset
+ *   A boolean indicating that the internal cache should be reset.
+ *
+ * @return Drupal\entity_test\Plugin\Core\Entity\EntityTest
+ *   The loaded entity object, or FALSE if the entity cannot be loaded.
+ */
+function entity_test_mulrev_load($id, $reset = FALSE) {
+  return entity_load('entity_test_mulrev', $id, $reset);
+}
+
+/**
+ * Loads multiple test entities based on certain conditions.
+ *
+ * @param array $ids
+ *   (optional) An array of entity IDs. If omitted, all entities are loaded.
+ * @param bool $reset
+ *   A boolean indicating that the internal cache should be reset.
+ *
+ * @return array
+ *   An array of test entity objects, indexed by ID.
+ */
+function entity_test_mulrev_load_multiple(array $ids = NULL, $reset = FALSE) {
+  return entity_load_multiple('entity_test_mulrev', $ids, $reset);
+}
+
+/**
+ * Deletes multiple test entities.
+ *
+ * @param $ids
+ *   An array of test entity IDs.
+ */
+function entity_test_mulrev_delete_multiple(array $ids) {
+  entity_delete_multiple('entity_test_mulrev', $ids);
 }
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulFormController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulFormController.php
new file mode 100644
index 0000000..90f438b
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulFormController.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\entity_test\EntityTestMulFormController.
+ */
+
+namespace Drupal\entity_test;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityFormControllerNG;
+
+/**
+ * Form controller for the test entity edit forms.
+ */
+class EntityTestMulFormController extends EntityFormControllerNG {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::form().
+   */
+  public function form(array $form, array &$form_state, EntityInterface $entity) {
+    $form = parent::form($form, $form_state, $entity);
+
+    $langcode = $this->getFormLangcode($form_state);
+    $translation = $entity->getTranslation($langcode);
+
+    $form['name'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Name'),
+      '#default_value' => $translation->name->value,
+      '#size' => 60,
+      '#maxlength' => 128,
+      '#required' => TRUE,
+      '#weight' => -10,
+    );
+
+    $form['user_id'] = array(
+      '#type' => 'textfield',
+      '#title' => 'UID',
+      '#default_value' => $translation->user_id->value,
+      '#size' => 60,
+      '#maxlength' => 128,
+      '#required' => TRUE,
+      '#weight' => -10,
+    );
+
+    $form['langcode'] = array(
+      '#title' => t('Language'),
+      '#type' => 'language_select',
+      '#default_value' => $entity->language()->langcode,
+      '#languages' => LANGUAGE_ALL,
+    );
+
+    return $form;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::save().
+   */
+  public function save(array $form, array &$form_state) {
+    $entity = $this->getEntity($form_state);
+    $is_new = $entity->isNew();
+    $entity->save();
+
+    $message = $is_new ? t('entity_test_mul @id has been created.', array('@id' => $entity->id())) : t('entity_test_mul @id has been updated.', array('@id' => $entity->id()));
+    drupal_set_message($message);
+
+    if ($entity->id()) {
+      $form_state['redirect'] = 'entity-test-mul/manage/' . $entity->id() . '/edit';
+    }
+    else {
+      // Error on save.
+      drupal_set_message(t('The entity could not be saved.'), 'error');
+      $form_state['rebuild'] = TRUE;
+    }
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::delete().
+   */
+  public function delete(array $form, array &$form_state) {
+    $entity = $this->getEntity($form_state);
+    $entity->delete();
+    drupal_set_message(t('entity_test_mul @id has been deleted.', array('@id' => $entity->id())));
+    $form_state['redirect'] = '<front>';
+  }
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulRevFormController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulRevFormController.php
new file mode 100644
index 0000000..229f3ef
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulRevFormController.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\entity_test\EntityTestMulRevFormController.
+ */
+
+namespace Drupal\entity_test;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityFormControllerNG;
+
+/**
+ * Form controller for the test entity edit forms.
+ */
+class EntityTestMulRevFormController extends EntityFormControllerNG {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::form().
+   */
+  public function form(array $form, array &$form_state, EntityInterface $entity) {
+    $form = parent::form($form, $form_state, $entity);
+
+    $langcode = $this->getFormLangcode($form_state);
+    $translation = $entity->getTranslation($langcode);
+
+    $form['name'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Name'),
+      '#default_value' => $translation->name->value,
+      '#size' => 60,
+      '#maxlength' => 128,
+      '#required' => TRUE,
+      '#weight' => -10,
+    );
+
+    $form['user_id'] = array(
+      '#type' => 'textfield',
+      '#title' => 'UID',
+      '#default_value' => $translation->user_id->value,
+      '#size' => 60,
+      '#maxlength' => 128,
+      '#required' => TRUE,
+      '#weight' => -10,
+    );
+
+    $form['langcode'] = array(
+      '#title' => t('Language'),
+      '#type' => 'language_select',
+      '#default_value' => $entity->language()->langcode,
+      '#languages' => LANGUAGE_ALL,
+    );
+
+    return $form;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::save().
+   */
+  public function save(array $form, array &$form_state) {
+    $entity = $this->getEntity($form_state);
+    $is_new = $entity->isNew();
+    $entity->save();
+
+    $message = $is_new ? t('entity_test_mulrev @id has been created.', array('@id' => $entity->id())) : t('entity_test_mulrev @id has been updated.', array('@id' => $entity->id()));
+    drupal_set_message($message);
+
+    if ($entity->id()) {
+      $form_state['redirect'] = 'entity-test-mul-rev/manage/' . $entity->id() . '/edit';
+    }
+    else {
+      // Error on save.
+      drupal_set_message(t('The entity could not be saved.'), 'error');
+      $form_state['rebuild'] = TRUE;
+    }
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::delete().
+   */
+  public function delete(array $form, array &$form_state) {
+    $entity = $this->getEntity($form_state);
+    $entity->delete();
+    drupal_set_message(t('entity_test_mulrev @id has been deleted.', array('@id' => $entity->id())));
+    $form_state['redirect'] = '<front>';
+  }
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulRevStorageController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulRevStorageController.php
new file mode 100644
index 0000000..94dffc0
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulRevStorageController.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_test\EntityTestMulRevStorageController.
+ */
+
+namespace Drupal\entity_test;
+
+use PDO;
+use Drupal\Core\Entity\Query\QueryInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\DatabaseStorageControllerNG;
+
+/**
+ * Defines the controller class for the test entity.
+ *
+ * This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
+ * required special handling for test entities.
+ */
+class EntityTestMulRevStorageController extends DatabaseStorageControllerNG {
+
+  /**
+   * Implements \Drupal\Core\Entity\DataBaseStorageControllerNG::baseFieldDefinitions().
+   */
+  public function baseFieldDefinitions() {
+    $fields['id'] = array(
+      'label' => t('ID'),
+      'description' => t('The ID of the test entity.'),
+      'type' => 'integer_field',
+      'read-only' => TRUE,
+    );
+    $fields['revision_id'] = array(
+      'label' => t('ID'),
+      'description' => t('The version id of the test entity.'),
+      'type' => 'integer_field',
+      'read-only' => TRUE,
+    );
+    $fields['uuid'] = array(
+      'label' => t('UUID'),
+      'description' => t('The UUID of the test entity.'),
+      'type' => 'string_field',
+    );
+    $fields['langcode'] = array(
+      'label' => t('Language code'),
+      'description' => t('The language code of the test entity.'),
+      'type' => 'language_field',
+    );
+    $fields['default_langcode'] = array(
+      'label' => t('Default language'),
+      'description' => t('Flag to inditcate whether this is the default language.'),
+      'type' => 'boolean_field',
+    );
+    $fields['name'] = array(
+      'label' => t('Name'),
+      'description' => t('The name of the test entity.'),
+      'type' => 'string_field',
+      'translatable' => TRUE,
+    );
+    $fields['user_id'] = array(
+      'label' => t('User ID'),
+      'description' => t('The ID of the associated user.'),
+      'type' => 'entityreference_field',
+      'settings' => array('entity type' => 'user'),
+      'translatable' => TRUE,
+    );
+    return $fields;
+  }
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulRevTranslationController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulRevTranslationController.php
new file mode 100644
index 0000000..57492ff
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulRevTranslationController.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\translation_entity\EntityTestMulRevTranslationController.
+ */
+
+namespace Drupal\entity_test;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\translation_entity\EntityTranslationController;
+
+/**
+ * Test entity translation controller.
+ */
+class EntityTestMulRevTranslationController extends EntityTranslationController {
+
+  /**
+   * Overrides EntityTranslationControllerInterface::removeTranslation().
+   */
+  public function removeTranslation(EntityInterface $entity, $langcode) {
+    $translation = $entity->getTranslation($langcode);
+    foreach ($translation->getPropertyDefinitions() as $property_name => $langcode) {
+      $translation->$property_name = array();
+    }
+  }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulStorageController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulStorageController.php
new file mode 100644
index 0000000..7187c07
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulStorageController.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_test\EntityTestMulStorageController.
+ */
+
+namespace Drupal\entity_test;
+
+use PDO;
+use Drupal\Core\Entity\Query\QueryInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\DatabaseStorageControllerNG;
+
+/**
+ * Defines the controller class for the test entity.
+ *
+ * This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
+ * required special handling for test entities.
+ */
+class EntityTestMulStorageController extends DatabaseStorageControllerNG {
+
+  /**
+   * Implements \Drupal\Core\Entity\DataBaseStorageControllerNG::baseFieldDefinitions().
+   */
+  public function baseFieldDefinitions() {
+    $fields['id'] = array(
+      'label' => t('ID'),
+      'description' => t('The ID of the test entity.'),
+      'type' => 'integer_field',
+      'read-only' => TRUE,
+    );
+    $fields['uuid'] = array(
+      'label' => t('UUID'),
+      'description' => t('The UUID of the test entity.'),
+      'type' => 'string_field',
+    );
+    $fields['langcode'] = array(
+      'label' => t('Language code'),
+      'description' => t('The language code of the test entity.'),
+      'type' => 'language_field',
+    );
+    $fields['default_langcode'] = array(
+      'label' => t('Default language'),
+      'description' => t('Flag to inditcate whether this is the default language.'),
+      'type' => 'boolean_field',
+    );
+    $fields['name'] = array(
+      'label' => t('Name'),
+      'description' => t('The name of the test entity.'),
+      'type' => 'string_field',
+      'translatable' => TRUE,
+    );
+    $fields['user_id'] = array(
+      'label' => t('User ID'),
+      'description' => t('The ID of the associated user.'),
+      'type' => 'entityreference_field',
+      'settings' => array('entity type' => 'user'),
+      'translatable' => TRUE,
+    );
+    return $fields;
+  }
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulTranslationController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulTranslationController.php
new file mode 100644
index 0000000..2575515
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestMulTranslationController.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\translation_entity\EntityTranslationController.
+ */
+
+namespace Drupal\entity_test;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\translation_entity\EntityTranslationController;
+
+/**
+ * Test entity translation controller.
+ */
+class EntityTestMulTranslationController extends EntityTranslationController {
+
+  /**
+   * Overrides EntityTranslationControllerInterface::removeTranslation().
+   */
+  public function removeTranslation(EntityInterface $entity, $langcode) {
+    $translation = $entity->getTranslation($langcode);
+    foreach ($translation->getPropertyDefinitions() as $property_name => $langcode) {
+      $translation->$property_name = array();
+    }
+  }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestRevFormController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestRevFormController.php
new file mode 100644
index 0000000..d3ca781
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestRevFormController.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\entity_test\EntityTestRevFormController.
+ */
+
+namespace Drupal\entity_test;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityFormControllerNG;
+
+/**
+ * Form controller for the test entity edit forms.
+ */
+class EntityTestRevFormController extends EntityFormControllerNG {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::form().
+   */
+  public function form(array $form, array &$form_state, EntityInterface $entity) {
+    $form = parent::form($form, $form_state, $entity);
+
+    $langcode = $this->getFormLangcode($form_state);
+    $translation = $entity->getTranslation($langcode);
+
+    $form['name'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Name'),
+      '#default_value' => $translation->name->value,
+      '#size' => 60,
+      '#maxlength' => 128,
+      '#required' => TRUE,
+      '#weight' => -10,
+    );
+
+    $form['user_id'] = array(
+      '#type' => 'textfield',
+      '#title' => 'UID',
+      '#default_value' => $translation->user_id->value,
+      '#size' => 60,
+      '#maxlength' => 128,
+      '#required' => TRUE,
+      '#weight' => -10,
+    );
+
+    $form['langcode'] = array(
+      '#title' => t('Language'),
+      '#type' => 'language_select',
+      '#default_value' => $entity->language()->langcode,
+      '#languages' => LANGUAGE_ALL,
+    );
+
+    return $form;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::save().
+   */
+  public function save(array $form, array &$form_state) {
+    $entity = $this->getEntity($form_state);
+    $is_new = $entity->isNew();
+    $entity->save();
+
+    $message = $is_new ? t('entity_test_rev @id has been created.', array('@id' => $entity->id())) : t('entity_test_rev @id has been updated.', array('@id' => $entity->id()));
+    drupal_set_message($message);
+
+    if ($entity->id()) {
+      $form_state['redirect'] = 'entity-test-rev/manage/' . $entity->id() . '/edit';
+    }
+    else {
+      // Error on save.
+      drupal_set_message(t('The entity could not be saved.'), 'error');
+      $form_state['rebuild'] = TRUE;
+    }
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::delete().
+   */
+  public function delete(array $form, array &$form_state) {
+    $entity = $this->getEntity($form_state);
+    $entity->delete();
+    drupal_set_message(t('entity_test_rev @id has been deleted.', array('@id' => $entity->id())));
+    $form_state['redirect'] = '<front>';
+  }
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestRevStorageController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestRevStorageController.php
new file mode 100644
index 0000000..89801a6
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestRevStorageController.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_test\EntityTestRevStorageController.
+ */
+
+namespace Drupal\entity_test;
+
+use PDO;
+use Drupal\Core\Entity\Query\QueryInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\DatabaseStorageControllerNG;
+
+/**
+ * Defines the controller class for the test entity.
+ *
+ * This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
+ * required special handling for test entities.
+ */
+class EntityTestRevStorageController extends DatabaseStorageControllerNG {
+
+  /**
+   * Implements \Drupal\Core\Entity\DataBaseStorageControllerNG::baseFieldDefinitions().
+   */
+  public function baseFieldDefinitions() {
+    $fields['id'] = array(
+      'label' => t('ID'),
+      'description' => t('The ID of the test entity.'),
+      'type' => 'integer_field',
+      'read-only' => TRUE,
+    );
+    $fields['revision_id'] = array(
+      'label' => t('ID'),
+      'description' => t('The version id of the test entity.'),
+      'type' => 'integer_field',
+      'read-only' => TRUE,
+    );
+    $fields['uuid'] = array(
+      'label' => t('UUID'),
+      'description' => t('The UUID of the test entity.'),
+      'type' => 'string_field',
+    );
+    $fields['langcode'] = array(
+      'label' => t('Language code'),
+      'description' => t('The language code of the test entity.'),
+      'type' => 'language_field',
+    );
+    $fields['name'] = array(
+      'label' => t('Name'),
+      'description' => t('The name of the test entity.'),
+      'type' => 'string_field',
+      'translatable' => TRUE,
+    );
+    $fields['user_id'] = array(
+      'label' => t('User ID'),
+      'description' => t('The ID of the associated user.'),
+      'type' => 'entityreference_field',
+      'settings' => array('entity type' => 'user'),
+      'translatable' => TRUE,
+    );
+    return $fields;
+  }
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestRevTranslationController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestRevTranslationController.php
new file mode 100644
index 0000000..1108860
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestRevTranslationController.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\translation_entity\EntityTestRevTranslationController.
+ */
+
+namespace Drupal\entity_test;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\translation_entity\EntityTranslationController;
+
+/**
+ * Test entity translation controller.
+ */
+class EntityTestRevTranslationController extends EntityTranslationController {
+
+  /**
+   * Overrides EntityTranslationControllerInterface::removeTranslation().
+   */
+  public function removeTranslation(EntityInterface $entity, $langcode) {
+    $translation = $entity->getTranslation($langcode);
+    foreach ($translation->getPropertyDefinitions() as $property_name => $langcode) {
+      $translation->$property_name = array();
+    }
+  }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
index e3b45fa..246cb97 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
@@ -21,114 +21,6 @@
 class EntityTestStorageController extends DatabaseStorageControllerNG {
 
   /**
-   * Overrides Drupal\Core\Entity\DatabaseStorageController::buildPropertyQuery().
-   */
-  protected function buildPropertyQuery(QueryInterface $entity_query, array $values) {
-    // @todo We should not be using a condition to specify whether conditions
-    // apply to the default language or not. We need to move this to a
-    // separate parameter during the following API refactoring.
-    // Default to the original entity language if not explicitly specified
-    // otherwise.
-    if (!array_key_exists('default_langcode', $values)) {
-      $values['default_langcode'] = 1;
-    }
-    // If the 'default_langcode' flag is esplicitly not set, we do not care
-    // whether the queried values are in the original entity language or not.
-    elseif ($values['default_langcode'] === NULL) {
-      unset($values['default_langcode']);
-    }
-
-    parent::buildPropertyQuery($entity_query, $values);
-  }
-
-  /**
-   * Maps from storage records to entity objects.
-   *
-   * @return array
-   *   An array of entity objects implementing the EntityInterface.
-   */
-  protected function mapFromStorageRecords(array $records, $load_revision = FALSE) {
-    $records = parent::mapFromStorageRecords($records, $load_revision);
-
-    // Load data of translatable properties.
-    $this->attachPropertyData($records, $load_revision);
-    return $records;
-  }
-
-  /**
-   * Attaches property data in all languages for translatable properties.
-   */
-  protected function attachPropertyData(&$queried_entities, $load_revision = FALSE) {
-    $query = db_select('entity_test_property_data', 'data', array('fetch' => PDO::FETCH_ASSOC))
-      ->fields('data')
-      ->condition('id', array_keys($queried_entities))
-      ->orderBy('data.id');
-    if ($load_revision) {
-      // Get revision id's.
-      $revision_ids = array();
-      foreach ($queried_entities as $id => $entity) {
-        $revision_ids[] = $entity->get('revision_id')->value;
-      }
-      $query->condition('revision_id', $revision_ids);
-    }
-    $data = $query->execute();
-
-    foreach ($data as $values) {
-      $id = $values['id'];
-      // Field values in default language are stored with
-      // LANGUAGE_DEFAULT as key.
-      $langcode = empty($values['default_langcode']) ? $values['langcode'] : LANGUAGE_DEFAULT;
-
-      $queried_entities[$id]->name[$langcode][0]['value'] = $values['name'];
-      $queried_entities[$id]->user_id[$langcode][0]['value'] = $values['user_id'];
-    }
-  }
-
-  /**
-   * Overrides Drupal\Core\Entity\DatabaseStorageController::postSave().
-   *
-   * Stores values of translatable properties.
-   */
-  protected function postSave(EntityInterface $entity, $update) {
-    $default_langcode = $entity->language()->langcode;
-
-    // Delete and insert to handle removed values.
-    db_delete('entity_test_property_data')
-      ->condition('id', $entity->id())
-      ->execute();
-
-    $query = db_insert('entity_test_property_data');
-
-    foreach ($entity->getTranslationLanguages() as $langcode => $language) {
-      $translation = $entity->getTranslation($langcode);
-
-      $values = array(
-        'id' => $entity->id(),
-        'revision_id' => $entity->getRevisionId(),
-        'langcode' => $langcode,
-        'default_langcode' => intval($default_langcode == $langcode),
-        'name' => $translation->name->value,
-        'user_id' => $translation->user_id->value,
-      );
-
-      $query
-        ->fields(array_keys($values))
-        ->values($values);
-    }
-
-    $query->execute();
-  }
-
-  /**
-   * Overrides Drupal\Core\Entity\DatabaseStorageController::postDelete().
-   */
-  protected function postDelete($entities) {
-    db_delete('entity_test_property_data')
-      ->condition('id', array_keys($entities))
-      ->execute();
-  }
-
-  /**
    * Implements \Drupal\Core\Entity\DataBaseStorageControllerNG::baseFieldDefinitions().
    */
   public function baseFieldDefinitions() {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
index 9b4d368..2babb66 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
@@ -25,13 +25,10 @@
  *   },
  *   translation_controller_class = "Drupal\entity_test\EntityTestTranslationController",
  *   base_table = "entity_test",
- *   data_table = "entity_test_property_data",
- *   revision_table = "entity_test_property_revision",
  *   fieldable = TRUE,
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
- *     "revision" = "revision_id"
  *   },
  *   menu_base_path = "entity-test/manage/%entity_test"
  * )
@@ -53,13 +50,6 @@ class EntityTest extends EntityNG {
   public $uuid;
 
   /**
-   * The entity revision id.
-   *
-   * @var \Drupal\Core\Entity\Field\FieldInterface
-   */
-  public $revision_id;
-
-  /**
    * The name of the test entity.
    *
    * @var \Drupal\Core\Entity\Field\FieldInterface
@@ -81,9 +71,7 @@ public function __construct(array $values, $entity_type) {
 
     // We unset all defined properties, so magic getters apply.
     unset($this->id);
-    unset($this->langcode);
     unset($this->uuid);
-    unset($this->revision_id);
     unset($this->name);
     unset($this->user_id);
   }
@@ -94,11 +82,4 @@ public function __construct(array $values, $entity_type) {
   public function label($langcode = LANGUAGE_DEFAULT) {
     return $this->getTranslation($langcode)->name->value;
   }
-
-  /**
-   * Implements Drupal\Core\Entity\EntityInterface::getRevisionId().
-   */
-  public function getRevisionId() {
-    return $this->get('revision_id')->value;
-  }
 }
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php
new file mode 100644
index 0000000..7a25821
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_test\Plugin\Core\Entity\EntityTestMul.
+ */
+
+namespace Drupal\entity_test\Plugin\Core\Entity;
+
+use Drupal\Core\Entity\EntityNG;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the test entity class.
+ *
+ * @Plugin(
+ *   id = "entity_test_mul",
+ *   label = @Translation("Test entity - data table"),
+ *   module = "entity_test",
+ *   controller_class = "Drupal\entity_test\EntityTestMulStorageController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\entity_test\EntityTestMulFormController"
+ *   },
+ *   translation_controller_class = "Drupal\entity_test\EntityTestMulTranslationController",
+ *   base_table = "entity_test_mul",
+ *   data_table = "entity_test_mul_property_data",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid",
+ *   },
+ *   menu_base_path = "entity-test-mul/manage/%entity_test_mul"
+ * )
+ */
+class EntityTestMul extends EntityNG {
+
+  /**
+   * The entity ID.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $id;
+
+  /**
+   * The entity UUID.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $uuid;
+
+  /**
+   * The name of the test entity.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $name;
+
+  /**
+   * The associated user.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $user_id;
+
+  /**
+   * Overrides Entity::__construct().
+   */
+  public function __construct(array $values, $entity_type) {
+    parent::__construct($values, $entity_type);
+
+    // We unset all defined properties, so magic getters apply.
+    unset($this->id);
+    unset($this->uuid);
+    unset($this->name);
+    unset($this->user_id);
+  }
+
+  /**
+   * Overrides Drupal\entity\Entity::label().
+   */
+  public function label($langcode = LANGUAGE_DEFAULT) {
+    return $this->getTranslation($langcode)->name->value;
+  }
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php
new file mode 100644
index 0000000..5caba0a
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_test\Plugin\Core\Entity\EntityTestMulRev.
+ */
+
+namespace Drupal\entity_test\Plugin\Core\Entity;
+
+use Drupal\Core\Entity\EntityNG;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the test entity class.
+ *
+ * @Plugin(
+ *   id = "entity_test_mulrev",
+ *   label = @Translation("Test entity - revisions and data table"),
+ *   module = "entity_test",
+ *   controller_class = "Drupal\entity_test\EntityTestMulRevStorageController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\entity_test\EntityTestMulRevFormController"
+ *   },
+ *   translation_controller_class = "Drupal\entity_test\EntityTestMulRevTranslationController",
+ *   base_table = "entity_test_mulrev",
+ *   data_table = "entity_test_mulrev_property_data",
+ *   revision_table = "entity_test_mulrev_property_revision",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid",
+ *     "revision" = "revision_id",
+ *   },
+ *   menu_base_path = "entity-test-mul-rev/manage/%entity_test_mulrev"
+ * )
+ */
+class EntityTestMulRev extends EntityNG {
+
+  /**
+   * The entity ID.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $id;
+
+  /**
+   * The entity UUID.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $uuid;
+
+  /**
+   * The entity revision id.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $revision_id;
+
+  /**
+   * The name of the test entity.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $name;
+
+  /**
+   * The associated user.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $user_id;
+
+  /**
+   * Overrides Entity::__construct().
+   */
+  public function __construct(array $values, $entity_type) {
+    parent::__construct($values, $entity_type);
+
+    // We unset all defined properties, so magic getters apply.
+    unset($this->id);
+    unset($this->uuid);
+    unset($this->revision_id);
+    unset($this->name);
+    unset($this->user_id);
+  }
+
+  /**
+   * Overrides Drupal\entity\Entity::label().
+   */
+  public function label($langcode = LANGUAGE_DEFAULT) {
+    return $this->getTranslation($langcode)->name->value;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::getRevisionId().
+   */
+  public function getRevisionId() {
+    return $this->get('revision_id')->value;
+  }
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestRev.php
new file mode 100644
index 0000000..76e477c
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestRev.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_test\Plugin\Core\Entity\EntityTestRev.
+ */
+
+namespace Drupal\entity_test\Plugin\Core\Entity;
+
+use Drupal\Core\Entity\EntityNG;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the test entity class.
+ *
+ * @Plugin(
+ *   id = "entity_test_rev",
+ *   label = @Translation("Test entity - revisions"),
+ *   module = "entity_test",
+ *   controller_class = "Drupal\entity_test\EntityTestRevStorageController",
+ *   form_controller_class = {
+ *     "default" = "Drupal\entity_test\EntityTestRevFormController"
+ *   },
+ *   translation_controller_class = "Drupal\entity_test\EntityTestRevTranslationController",
+ *   base_table = "entity_test_rev",
+ *   revision_table = "entity_test_rev_revision",
+ *   fieldable = TRUE,
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid",
+ *     "revision" = "revision_id",
+ *   },
+ *   menu_base_path = "entity-test-rev/manage/%entity_test_rev"
+ * )
+ */
+class EntityTestRev extends EntityNG {
+
+  /**
+   * The entity ID.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $id;
+
+  /**
+   * The entity UUID.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $uuid;
+
+  /**
+   * The entity revision id.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $revision_id;
+
+  /**
+   * The name of the test entity.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $name;
+
+  /**
+   * The associated user.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $user_id;
+
+  /**
+   * Overrides Entity::__construct().
+   */
+  public function __construct(array $values, $entity_type) {
+    parent::__construct($values, $entity_type);
+
+    // We unset all defined properties, so magic getters apply.
+    unset($this->id);
+    unset($this->uuid);
+    unset($this->revision_id);
+    unset($this->name);
+    unset($this->user_id);
+  }
+
+  /**
+   * Overrides Drupal\entity\Entity::label().
+   */
+  public function label($langcode = LANGUAGE_DEFAULT) {
+    return $this->getTranslation($langcode)->name->value;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::getRevisionId().
+   */
+  public function getRevisionId() {
+    return $this->get('revision_id')->value;
+  }
+}
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTestTranslationUITest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTestTranslationUITest.php
index f66fcc3..2fbb509 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTestTranslationUITest.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTestTranslationUITest.php
@@ -31,7 +31,8 @@ public static function getInfo() {
    * Overrides \Drupal\simpletest\WebTestBase::setUp().
    */
   function setUp() {
-    $this->entityType = 'entity_test';
+    // Use the entity_test_mul as this has multilingual property support.
+    $this->entityType = 'entity_test_mul';
     parent::setUp();
   }
 
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php
index eac0b7f..9e212c2 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php
@@ -220,8 +220,10 @@ function testTranslationUI() {
     $this->drupalPost($path, array(), t('Delete translation'));
     $this->drupalPost(NULL, array(), t('Delete'));
     $entity = entity_load($this->entityType, $entity->id(), TRUE);
-    $translations = $entity->getTranslationLanguages();
-    $this->assertTrue(count($translations) == 2 && empty($translations[$enabled_langcode]), 'Translation successfully deleted.');
+    if ($this->assertTrue(is_object($entity), 'Entity found')) {
+      $translations = $entity->getTranslationLanguages();
+      $this->assertTrue(count($translations) == 2 && empty($translations[$enabled_langcode]), 'Translation successfully deleted.');
+    }
   }
 
   /**
