diff --git a/includes/common.inc b/includes/common.inc
index 72fcf76..da5bd4b 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -7275,427 +7275,6 @@ function drupal_check_incompatibility($v, $current_version) {
 }
 
 /**
- * Get the entity info array of an entity type.
- *
- * @see hook_entity_info()
- * @see hook_entity_info_alter()
- *
- * @param $entity_type
- *   The entity type, e.g. node, for which the info shall be returned, or NULL
- *   to return an array with info about all types.
- */
-function entity_get_info($entity_type = NULL) {
-  global $language;
-
-  // Use the advanced drupal_static() pattern, since this is called very often.
-  static $drupal_static_fast;
-  if (!isset($drupal_static_fast)) {
-    $drupal_static_fast['entity_info'] = &drupal_static(__FUNCTION__);
-  }
-  $entity_info = &$drupal_static_fast['entity_info'];
-
-  // hook_entity_info() includes translated strings, so each language is cached
-  // separately.
-  $langcode = $language->language;
-
-  if (empty($entity_info)) {
-    if ($cache = cache()->get("entity_info:$langcode")) {
-      $entity_info = $cache->data;
-    }
-    else {
-      $entity_info = module_invoke_all('entity_info');
-      // Merge in default values.
-      foreach ($entity_info as $name => $data) {
-        $entity_info[$name] += array(
-          'fieldable' => FALSE,
-          'controller class' => 'DrupalDefaultEntityController',
-          'static cache' => TRUE,
-          'field cache' => TRUE,
-          'load hook' => $name . '_load',
-          'bundles' => array(),
-          'view modes' => array(),
-          'entity keys' => array(),
-          'translation' => array(),
-        );
-        $entity_info[$name]['entity keys'] += array(
-          'revision' => '',
-          'bundle' => '',
-        );
-        foreach ($entity_info[$name]['view modes'] as $view_mode => $view_mode_info) {
-          $entity_info[$name]['view modes'][$view_mode] += array(
-            'custom settings' => FALSE,
-          );
-        }
-        // If no bundle key is provided, assume a single bundle, named after
-        // the entity type.
-        if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) {
-          $entity_info[$name]['bundles'] = array($name => array('label' => $entity_info[$name]['label']));
-        }
-        // Prepare entity schema fields SQL info for
-        // DrupalEntityControllerInterface::buildQuery().
-        if (isset($entity_info[$name]['base table'])) {
-          $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']);
-          if (isset($entity_info[$name]['revision table'])) {
-            $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']);
-          }
-        }
-      }
-      // Let other modules alter the entity info.
-      drupal_alter('entity_info', $entity_info);
-      cache()->set("entity_info:$langcode", $entity_info);
-    }
-  }
-
-  if (empty($entity_type)) {
-    return $entity_info;
-  }
-  elseif (isset($entity_info[$entity_type])) {
-    return $entity_info[$entity_type];
-  }
-}
-
-/**
- * Resets the cached information about entity types.
- */
-function entity_info_cache_clear() {
-  drupal_static_reset('entity_get_info');
-  // Clear all languages.
-  cache()->deletePrefix('entity_info:');
-}
-
-/**
- * Helper function to extract id, vid, and bundle name from an entity.
- *
- * @param $entity_type
- *   The entity type; e.g. 'node' or 'user'.
- * @param $entity
- *   The entity from which to extract values.
- * @return
- *   A numerically indexed array (not a hash table) containing these
- *   elements:
- *   0: primary id of the entity
- *   1: revision id of the entity, or NULL if $entity_type is not versioned
- *   2: bundle name of the entity
- */
-function entity_extract_ids($entity_type, $entity) {
-  $info = entity_get_info($entity_type);
-
-  // Objects being created might not have id/vid yet.
-  $id = isset($entity->{$info['entity keys']['id']}) ? $entity->{$info['entity keys']['id']} : NULL;
-  $vid = ($info['entity keys']['revision'] && isset($entity->{$info['entity keys']['revision']})) ? $entity->{$info['entity keys']['revision']} : NULL;
-
-  if (!empty($info['entity keys']['bundle'])) {
-    // Explicitly fail for malformed entities missing the bundle property.
-    if (!isset($entity->{$info['entity keys']['bundle']}) || $entity->{$info['entity keys']['bundle']} === '') {
-      throw new EntityMalformedException(t('Missing bundle property on entity of type @entity_type.', array('@entity_type' => $entity_type)));
-    }
-    $bundle = $entity->{$info['entity keys']['bundle']};
-  }
-  else {
-    // The entity type provides no bundle key: assume a single bundle, named
-    // after the entity type.
-    $bundle = $entity_type;
-  }
-
-  return array($id, $vid, $bundle);
-}
-
-/**
- * Helper function to assemble an object structure with initial ids.
- *
- * This function can be seen as reciprocal to entity_extract_ids().
- *
- * @param $entity_type
- *   The entity type; e.g. 'node' or 'user'.
- * @param $ids
- *   A numerically indexed array, as returned by entity_extract_ids(),
- *   containing these elements:
- *   0: primary id of the entity
- *   1: revision id of the entity, or NULL if $entity_type is not versioned
- *   2: bundle name of the entity, or NULL if $entity_type has no bundles
- * @return
- *   An entity structure, initialized with the ids provided.
- */
-function entity_create_stub_entity($entity_type, $ids) {
-  $entity = new stdClass();
-  $info = entity_get_info($entity_type);
-  $entity->{$info['entity keys']['id']} = $ids[0];
-  if (!empty($info['entity keys']['revision']) && isset($ids[1])) {
-    $entity->{$info['entity keys']['revision']} = $ids[1];
-  }
-  if (!empty($info['entity keys']['bundle']) && isset($ids[2])) {
-    $entity->{$info['entity keys']['bundle']} = $ids[2];
-  }
-  return $entity;
-}
-
-/**
- * Load entities from the database.
- *
- * The entities are stored in a static memory cache, and will not require
- * database access if loaded again during the same page request.
- *
- * The actual loading is done through a class that has to implement the
- * DrupalEntityControllerInterface interface. By default,
- * DrupalDefaultEntityController is used. Entity types can specify that a
- * different class should be used by setting the 'controller class' key in
- * hook_entity_info(). These classes can either implement the
- * DrupalEntityControllerInterface interface, or, most commonly, extend the
- * DrupalDefaultEntityController class. See node_entity_info() and the
- * NodeController in node.module as an example.
- *
- * @see hook_entity_info()
- * @see DrupalEntityControllerInterface
- * @see DrupalDefaultEntityController
- * @see EntityFieldQuery
- *
- * @param $entity_type
- *   The entity type to load, e.g. node or user.
- * @param $ids
- *   An array of entity IDs, or FALSE to load all entities.
- * @param $conditions
- *   (deprecated) An associative array of conditions on the base table, where
- *   the keys are the database fields and the values are the values those
- *   fields must have. Instead, it is preferable to use EntityFieldQuery to
- *   retrieve a list of entity IDs loadable by this function.
- * @param $reset
- *   Whether to reset the internal cache for the requested entity type.
- *
- * @return
- *   An array of entity objects indexed by their ids. When no results are
- *   found, an empty array is returned.
- *
- * @todo Remove $conditions in Drupal 8.
- */
-function entity_load($entity_type, $ids = FALSE, $conditions = array(), $reset = FALSE) {
-  if ($reset) {
-    entity_get_controller($entity_type)->resetCache();
-  }
-  return entity_get_controller($entity_type)->load($ids, $conditions);
-}
-
-/**
- * Loads the unchanged, i.e. not modified, entity from the database.
- *
- * Unlike entity_load() this function ensures the entity is directly loaded from
- * the database, thus bypassing any static cache. In particular, this function
- * is useful to determine changes by comparing the entity being saved to the
- * stored entity.
- *
- * @param $entity_type
- *   The entity type to load, e.g. node or user.
- * @param $id
- *   The id of the entity to load.
- *
- * @return
- *   The unchanged entity, or FALSE if the entity cannot be loaded.
- */
-function entity_load_unchanged($entity_type, $id) {
-  entity_get_controller($entity_type)->resetCache(array($id));
-  $result = entity_get_controller($entity_type)->load(array($id));
-  return reset($result);
-}
-
-/**
- * Get the entity controller class for an entity type.
- */
-function entity_get_controller($entity_type) {
-  $controllers = &drupal_static(__FUNCTION__, array());
-  if (!isset($controllers[$entity_type])) {
-    $type_info = entity_get_info($entity_type);
-    $class = $type_info['controller class'];
-    $controllers[$entity_type] = new $class($entity_type);
-  }
-  return $controllers[$entity_type];
-}
-
-/**
- * Invoke hook_entity_prepare_view().
- *
- * If adding a new entity similar to nodes, comments or users, you should
- * invoke this function during the ENTITY_build_content() or
- * ENTITY_view_multiple() phases of rendering to allow other modules to alter
- * the objects during this phase. This is needed for situations where
- * information needs to be loaded outside of ENTITY_load() - particularly
- * when loading entities into one another - i.e. a user object into a node, due
- * to the potential for unwanted side-effects such as caching and infinite
- * recursion. By convention, entity_prepare_view() is called after
- * field_attach_prepare_view() to allow entity level hooks to act on content
- * loaded by field API.
- * @see hook_entity_prepare_view()
- *
- * @param $entity_type
- *   The type of entity, i.e. 'node', 'user'.
- * @param $entities
- *   The entity objects which are being prepared for view, keyed by object ID.
- * @param $langcode
- *   (optional) A language code to be used for rendering. Defaults to the global
- *   content language of the current request.
- */
-function entity_prepare_view($entity_type, $entities, $langcode = NULL) {
-  if (!isset($langcode)) {
-    $langcode = $GLOBALS['language_content']->language;
-  }
-
-  // To ensure hooks are only run once per entity, check for an
-  // entity_view_prepared flag and only process items without it.
-  // @todo: resolve this more generally for both entity and field level hooks.
-  $prepare = array();
-  foreach ($entities as $id => $entity) {
-    if (empty($entity->entity_view_prepared)) {
-      // Add this entity to the items to be prepared.
-      $prepare[$id] = $entity;
-
-      // Mark this item as prepared.
-      $entity->entity_view_prepared = TRUE;
-    }
-  }
-
-  if (!empty($prepare)) {
-    module_invoke_all('entity_prepare_view', $prepare, $entity_type, $langcode);
-  }
-}
-
-/**
- * Returns the uri elements of an entity.
- *
- * @param $entity_type
- *   The entity type; e.g. 'node' or 'user'.
- * @param $entity
- *   The entity for which to generate a path.
- * @return
- *   An array containing the 'path' and 'options' keys used to build the uri of
- *   the entity, and matching the signature of url(). NULL if the entity has no
- *   uri of its own.
- */
-function entity_uri($entity_type, $entity) {
-  // This check enables the URI of an entity to be easily overridden from what
-  // the callback for the entity type or bundle would return, and it helps
-  // minimize performance overhead when entity_uri() is called multiple times
-  // for the same entity.
-  if (!isset($entity->uri)) {
-    $info = entity_get_info($entity_type);
-    list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
-    // A bundle-specific callback takes precedence over the generic one for the
-    // entity type.
-    if (isset($info['bundles'][$bundle]['uri callback'])) {
-      $uri_callback = $info['bundles'][$bundle]['uri callback'];
-    }
-    elseif (isset($info['uri callback'])) {
-      $uri_callback = $info['uri callback'];
-    }
-    else {
-      $uri_callback = NULL;
-    }
-
-    // Invoke the callback to get the URI. If there is no callback, set the
-    // entity's 'uri' property to FALSE to indicate that it is known to not have
-    // a URI.
-    if (isset($uri_callback) && function_exists($uri_callback)) {
-      $entity->uri = $uri_callback($entity);
-      if (!isset($entity->uri['options'])) {
-        $entity->uri['options'] = array();
-      }
-      // Pass the entity data to url() so that alter functions do not need to
-      // lookup this entity again.
-      $entity->uri['options']['entity_type'] = $entity_type;
-      $entity->uri['options']['entity'] = $entity;
-    }
-    else {
-      $entity->uri = FALSE;
-    }
-  }
-  return $entity->uri ? $entity->uri : NULL;
-}
-
-/**
- * Returns the label of an entity.
- *
- * See the 'label callback' component of the hook_entity_info() return value
- * for more information.
- *
- * @param $entity_type
- *   The entity type; e.g., 'node' or 'user'.
- * @param $entity
- *   The entity for which to generate the label.
- *
- * @return
- *   The entity label, or FALSE if not found.
- */
-function entity_label($entity_type, $entity) {
-  $label = FALSE;
-  $info = entity_get_info($entity_type);
-  if (isset($info['label callback']) && function_exists($info['label callback'])) {
-    $label = $info['label callback']($entity_type, $entity);
-  }
-  elseif (!empty($info['entity keys']['label']) && isset($entity->{$info['entity keys']['label']})) {
-    $label = $entity->{$info['entity keys']['label']};
-  }
-
-  return $label;
-}
-
-/**
- * Helper function for attaching field API validation to entity forms.
- */
-function entity_form_field_validate($entity_type, $form, &$form_state) {
-  // All field attach API functions act on an entity object, but during form
-  // validation, we don't have one. $form_state contains the entity as it was
-  // prior to processing the current form submission, and we must not update it
-  // until we have fully validated the submitted input. Therefore, for
-  // validation, act on a pseudo entity created out of the form values.
-  $pseudo_entity = (object) $form_state['values'];
-  field_attach_form_validate($entity_type, $pseudo_entity, $form, $form_state);
-}
-
-/**
- * Helper function for copying submitted values to entity properties for simple entity forms.
- *
- * During the submission handling of an entity form's "Save", "Preview", and
- * possibly other buttons, the form state's entity needs to be updated with the
- * submitted form values. Each entity form implements its own builder function
- * for doing this, appropriate for the particular entity and form, whereas
- * modules may specify additional builder functions in $form['#entity_builders']
- * for copying the form values of added form elements to entity properties.
- * Many of the main entity builder functions can call this helper function to
- * re-use its logic of copying $form_state['values'][PROPERTY] values to
- * $entity->PROPERTY for all entries in $form_state['values'] that are not field
- * data, and calling field_attach_submit() to copy field data. Apart from that
- * this helper invokes any additional builder functions that have been specified
- * in $form['#entity_builders'].
- *
- * For some entity forms (e.g., forms with complex non-field data and forms that
- * simultaneously edit multiple entities), this behavior may be inappropriate,
- * so the builder function for such forms needs to implement the required
- * functionality instead of calling this function.
- */
-function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_state) {
-  $info = entity_get_info($entity_type);
-  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
-
-  // Copy top-level form values that are not for fields to entity properties,
-  // without changing existing entity properties that are not being edited by
-  // this form. Copying field values must be done using field_attach_submit().
-  $values_excluding_fields = $info['fieldable'] ? array_diff_key($form_state['values'], field_info_instances($entity_type, $bundle)) : $form_state['values'];
-  foreach ($values_excluding_fields as $key => $value) {
-    $entity->$key = $value;
-  }
-
-  // Invoke all specified builders for copying form values to entity properties.
-  if (isset($form['#entity_builders'])) {
-    foreach ($form['#entity_builders'] as $function) {
-      $function($entity_type, $entity, $form, $form_state);
-    }
-  }
-
-  // Copy field values to the entity.
-  if ($info['fieldable']) {
-    field_attach_submit($entity_type, $entity, $form, $form_state);
-  }
-}
-
-/**
  * Performs one or more XML-RPC request(s).
  *
  * Usage example:
diff --git a/includes/entity.inc b/includes/entity.inc
deleted file mode 100644
index 99baf49..0000000
--- a/includes/entity.inc
+++ /dev/null
@@ -1,1336 +0,0 @@
-<?php
-
-/**
- * Interface for entity controller classes.
- *
- * All entity controller classes specified via the 'controller class' key
- * returned by hook_entity_info() or hook_entity_info_alter() have to implement
- * this interface.
- *
- * Most simple, SQL-based entity controllers will do better by extending
- * DrupalDefaultEntityController instead of implementing this interface
- * directly.
- */
-interface DrupalEntityControllerInterface {
-
-  /**
-   * Constructor.
-   *
-   * @param $entityType
-   *   The entity type for which the instance is created.
-   */
-  public function __construct($entityType);
-
-  /**
-   * Resets the internal, static entity cache.
-   *
-   * @param $ids
-   *   (optional) If specified, the cache is reset for the entities with the
-   *   given ids only.
-   */
-  public function resetCache(array $ids = NULL);
-
-  /**
-   * Loads one or more entities.
-   *
-   * @param $ids
-   *   An array of entity IDs, or FALSE to load all entities.
-   * @param $conditions
-   *   An array of conditions in the form 'field' => $value.
-   *
-   * @return
-   *   An array of entity objects indexed by their ids. When no results are
-   *   found, an empty array is returned.
-   */
-  public function load($ids = array(), $conditions = array());
-}
-
-/**
- * Default implementation of DrupalEntityControllerInterface.
- *
- * This class can be used as-is by most simple entity types. Entity types
- * requiring special handling can extend the class.
- */
-class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
-
-  /**
-   * Static cache of entities.
-   *
-   * @var array
-   */
-  protected $entityCache;
-
-  /**
-   * Entity type for this controller instance.
-   *
-   * @var string
-   */
-  protected $entityType;
-
-  /**
-   * Array of information about the entity.
-   *
-   * @var array
-   *
-   * @see entity_get_info()
-   */
-  protected $entityInfo;
-
-  /**
-   * Additional arguments to pass to hook_TYPE_load().
-   *
-   * Set before calling DrupalDefaultEntityController::attachLoad().
-   *
-   * @var array
-   */
-  protected $hookLoadArguments;
-
-  /**
-   * Name of the entity's ID field in the entity database table.
-   *
-   * @var string
-   */
-  protected $idKey;
-
-  /**
-   * Name of entity's revision database table field, if it supports revisions.
-   *
-   * Has the value FALSE if this entity does not use revisions.
-   *
-   * @var string
-   */
-  protected $revisionKey;
-
-  /**
-   * The table that stores revisions, if the entity supports revisions.
-   *
-   * @var string
-   */
-  protected $revisionTable;
-
-  /**
-   * Whether this entity type should use the static cache.
-   *
-   * Set by entity info.
-   *
-   * @var boolean
-   */
-  protected $cache;
-
-  /**
-   * Constructor: sets basic variables.
-   */
-  public function __construct($entityType) {
-    $this->entityType = $entityType;
-    $this->entityInfo = entity_get_info($entityType);
-    $this->entityCache = array();
-    $this->hookLoadArguments = array();
-    $this->idKey = $this->entityInfo['entity keys']['id'];
-
-    // Check if the entity type supports revisions.
-    if (!empty($this->entityInfo['entity keys']['revision'])) {
-      $this->revisionKey = $this->entityInfo['entity keys']['revision'];
-      $this->revisionTable = $this->entityInfo['revision table'];
-    }
-    else {
-      $this->revisionKey = FALSE;
-    }
-
-    // Check if the entity type supports static caching of loaded entities.
-    $this->cache = !empty($this->entityInfo['static cache']);
-  }
-
-  /**
-   * Implements DrupalEntityControllerInterface::resetCache().
-   */
-  public function resetCache(array $ids = NULL) {
-    if (isset($ids)) {
-      foreach ($ids as $id) {
-        unset($this->entityCache[$id]);
-      }
-    }
-    else {
-      $this->entityCache = array();
-    }
-  }
-
-  /**
-   * Implements DrupalEntityControllerInterface::load().
-   */
-  public function load($ids = array(), $conditions = array()) {
-    $entities = array();
-
-    // Revisions are not statically cached, and require a different query to
-    // other conditions, so separate the revision id into its own variable.
-    if ($this->revisionKey && isset($conditions[$this->revisionKey])) {
-      $revision_id = $conditions[$this->revisionKey];
-      unset($conditions[$this->revisionKey]);
-    }
-    else {
-      $revision_id = FALSE;
-    }
-
-    // Create a new variable which is either a prepared version of the $ids
-    // array for later comparison with the entity cache, or FALSE if no $ids
-    // were passed. The $ids array is reduced as items are loaded from cache,
-    // and we need to know if it's empty for this reason to avoid querying the
-    // database when all requested entities are loaded from cache.
-    $passed_ids = !empty($ids) ? array_flip($ids) : FALSE;
-    // Try to load entities from the static cache, if the entity type supports
-    // static caching.
-    if ($this->cache && !$revision_id) {
-      $entities += $this->cacheGet($ids, $conditions);
-      // If any entities were loaded, remove them from the ids still to load.
-      if ($passed_ids) {
-        $ids = array_keys(array_diff_key($passed_ids, $entities));
-      }
-    }
-
-    // Load any remaining entities from the database. This is the case if $ids
-    // is set to FALSE (so we load all entities), if there are any ids left to
-    // load, if loading a revision, or if $conditions was passed without $ids.
-    if ($ids === FALSE || $ids || $revision_id || ($conditions && !$passed_ids)) {
-      // Build the query.
-      $query = $this->buildQuery($ids, $conditions, $revision_id);
-      $queried_entities = $query
-        ->execute()
-        ->fetchAllAssoc($this->idKey);
-    }
-
-    // Pass all entities loaded from the database through $this->attachLoad(),
-    // which attaches fields (if supported by the entity type) and calls the
-    // entity type specific load callback, for example hook_node_load().
-    if (!empty($queried_entities)) {
-      $this->attachLoad($queried_entities, $revision_id);
-      $entities += $queried_entities;
-    }
-
-    if ($this->cache) {
-      // Add entities to the cache if we are not loading a revision.
-      if (!empty($queried_entities) && !$revision_id) {
-        $this->cacheSet($queried_entities);
-      }
-    }
-
-    // Ensure that the returned array is ordered the same as the original
-    // $ids array if this was passed in and remove any invalid ids.
-    if ($passed_ids) {
-      // Remove any invalid ids from the array.
-      $passed_ids = array_intersect_key($passed_ids, $entities);
-      foreach ($entities as $entity) {
-        $passed_ids[$entity->{$this->idKey}] = $entity;
-      }
-      $entities = $passed_ids;
-    }
-
-    return $entities;
-  }
-
-  /**
-   * 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 CommentController::buildQuery() or TaxonomyTermController::buildQuery()
-   * for examples.
-   *
-   * @param $ids
-   *   An array of entity IDs, or FALSE to load all entities.
-   * @param $conditions
-   *   An array of conditions in the form 'field' => $value.
-   * @param $revision_id
-   *   The ID of the revision to load, or FALSE if this query is asking for the
-   *   most current revision(s).
-   *
-   * @return SelectQuery
-   *   A SelectQuery object for loading the entity.
-   */
-  protected function buildQuery($ids, $conditions = array(), $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}");
-    }
-
-    // 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);
-    }
-
-    $query->fields('base', $entity_fields);
-
-    if ($ids) {
-      $query->condition("base.{$this->idKey}", $ids, 'IN');
-    }
-    if ($conditions) {
-      foreach ($conditions as $field => $value) {
-        $query->condition('base.' . $field, $value);
-      }
-    }
-    return $query;
-  }
-
-  /**
-   * Attaches data to entities upon loading.
-   * This will attach fields, if the entity is fieldable. It calls
-   * hook_entity_load() for modules which need to add data to all entities.
-   * It also calls hook_TYPE_load() on the loaded entities. For example
-   * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
-   * expects special parameters apart from the queried entities, you can set
-   * $this->hookLoadArguments prior to calling the method.
-   * See NodeController::attachLoad() for an example.
-   *
-   * @param $queried_entities
-   *   Associative array of query results, keyed on the entity ID.
-   * @param $revision_id
-   *   ID of the revision that was loaded, or FALSE if teh most current revision
-   *   was loaded.
-   */
-  protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
-    // Attach fields.
-    if ($this->entityInfo['fieldable']) {
-      if ($revision_id) {
-        field_attach_load_revision($this->entityType, $queried_entities);
-      }
-      else {
-        field_attach_load($this->entityType, $queried_entities);
-      }
-    }
-
-    // Call hook_entity_load().
-    foreach (module_implements('entity_load') as $module) {
-      $function = $module . '_entity_load';
-      $function($queried_entities, $this->entityType);
-    }
-    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
-    // always the queried entities, followed by additional arguments set in
-    // $this->hookLoadArguments.
-    $args = array_merge(array($queried_entities), $this->hookLoadArguments);
-    foreach (module_implements($this->entityInfo['load hook']) as $module) {
-      call_user_func_array($module . '_' . $this->entityInfo['load hook'], $args);
-    }
-  }
-
-  /**
-   * Gets entities from the static cache.
-   *
-   * @param $ids
-   *   If not empty, return entities that match these IDs.
-   * @param $conditions
-   *   If set, return entities that match all of these conditions.
-   *
-   * @return
-   *   Array of entities from the entity cache.
-   */
-  protected function cacheGet($ids, $conditions = array()) {
-    $entities = array();
-    // Load any available entities from the internal cache.
-    if (!empty($this->entityCache)) {
-      if ($ids) {
-        $entities += array_intersect_key($this->entityCache, array_flip($ids));
-      }
-      // If loading entities only by conditions, fetch all available entities
-      // from the cache. Entities which don't match are removed later.
-      elseif ($conditions) {
-        $entities = $this->entityCache;
-      }
-    }
-
-    // Exclude any entities loaded from cache if they don't match $conditions.
-    // This ensures the same behavior whether loading from memory or database.
-    if ($conditions) {
-      foreach ($entities as $entity) {
-        $entity_values = (array) $entity;
-        if (array_diff_assoc($conditions, $entity_values)) {
-          unset($entities[$entity->{$this->idKey}]);
-        }
-      }
-    }
-    return $entities;
-  }
-
-  /**
-   * Stores entities in the static entity cache.
-   *
-   * @param $entities
-   *   Entities to store in the cache.
-   */
-  protected function cacheSet($entities) {
-    $this->entityCache += $entities;
-  }
-}
-
-/**
- * Exception thrown by EntityFieldQuery() on unsupported query syntax.
- *
- * Some storage modules might not support the full range of the syntax for
- * conditions, and will raise an EntityFieldQueryException when an unsupported
- * condition was specified.
- */
-class EntityFieldQueryException extends Exception {}
-
-/**
- * Retrieves entities matching a given set of conditions.
- *
- * This class allows finding entities based on entity properties (for example,
- * node->changed), field values, and generic entity meta data (bundle,
- * entity type, entity id, and revision ID). It is not possible to query across
- * multiple entity types. For example, there is no facility to find published
- * nodes written by users created in the last hour, as this would require
- * querying both node->status and user->created.
- *
- * Normally we would not want to have public properties on the object, as that
- * allows the object's state to become inconsistent too easily. However, this
- * class's standard use case involves primarily code that does need to have
- * direct access to the collected properties in order to handle alternate
- * execution routines. We therefore use public properties for simplicity. Note
- * that code that is simply creating and running a field query should still use
- * the appropriate methods to add conditions on the query.
- *
- * Storage engines are not required to support every type of query. By default,
- * an EntityFieldQueryException will be raised if an unsupported condition is
- * specified or if the query has field conditions or sorts that are stored in
- * different field storage engines. However, this logic can be overridden in
- * hook_entity_query().
- *
- * Also note that this query does not automatically respect entity access
- * restrictions. Node access control is performed by the SQL storage engine but
- * other storage engines might not do this.
- */
-class EntityFieldQuery {
-  /**
-   * Indicates that both deleted and non-deleted fields should be returned.
-   *
-   * @see EntityFieldQuery::deleted()
-   */
-  const RETURN_ALL = NULL;
-
-  /**
-   * TRUE if the query has already been altered, FALSE if it hasn't.
-   *
-   * Used in alter hooks to check for cloned queries that have already been
-   * altered prior to the clone (for example, the pager count query).
-   *
-   * @var boolean
-   */
-  public $altered = FALSE;
-
-  /**
-   * Associative array of entity-generic metadata conditions.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::entityCondition()
-   */
-  public $entityConditions = array();
-
-  /**
-   * List of field conditions.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::fieldCondition()
-   */
-  public $fieldConditions = array();
-
-  /**
-   * List of field meta conditions (language and delta).
-   *
-   * Field conditions operate on columns specified by hook_field_schema(),
-   * the meta conditions operate on columns added by the system: delta
-   * and language. These can not be mixed with the field conditions because
-   * field columns can have any name including delta and language.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::fieldLanguageCondition()
-   * @see EntityFieldQuery::fielDeltaCondition()
-   */
-  public $fieldMetaConditions = array();
-
-  /**
-   * List of property conditions.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::propertyCondition()
-   */
-  public $propertyConditions = array();
-
-  /**
-   * List of order clauses.
-   *
-   * @var array
-   */
-  public $order = array();
-
-  /**
-   * The query range.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::range()
-   */
-  public $range = array();
-
-  /**
-   * The query pager data.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::pager()
-   */
-  public $pager = array();
-
-  /**
-   * Query behavior for deleted data.
-   *
-   * TRUE to return only deleted data, FALSE to return only non-deleted data,
-   * EntityFieldQuery::RETURN_ALL to return everything.
-   *
-   * @see EntityFieldQuery::deleted()
-   */
-  public $deleted = FALSE;
-
-  /**
-   * A list of field arrays used.
-   *
-   * Field names passed to EntityFieldQuery::fieldCondition() and
-   * EntityFieldQuery::fieldOrderBy() are run through field_info_field() before
-   * stored in this array. This way, the elements of this array are field
-   * arrays.
-   *
-   * @var array
-   */
-  public $fields = array();
-
-  /**
-   * TRUE if this is a count query, FALSE if it isn't.
-   *
-   * @var boolean
-   */
-  public $count = FALSE;
-
-  /**
-   * Flag indicating whether this is querying current or all revisions.
-   *
-   * @var int
-   *
-   * @see EntityFieldQuery::age()
-   */
-  public $age = FIELD_LOAD_CURRENT;
-
-  /**
-   * A list of the tags added to this query.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::addTag()
-   */
-  public $tags = array();
-
-  /**
-   * A list of metadata added to this query.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::addMetaData()
-   */
-  public $metaData = array();
-
-  /**
-   * The ordered results.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::execute().
-   */
-  public $orderedResults = array();
-
-  /**
-   * The method executing the query, if it is overriding the default.
-   *
-   * @var string
-   *
-   * @see EntityFieldQuery::execute().
-   */
-  public $executeCallback = '';
-
-  /**
-   * Adds a condition on entity-generic metadata.
-   *
-   * If the overall query contains only entity conditions or ordering, or if
-   * there are property conditions, then specifying the entity type is
-   * mandatory. If there are field conditions or ordering but no property
-   * conditions or ordering, then specifying an entity type is optional. While
-   * the field storage engine might support field conditions on more than one
-   * entity type, there is no way to query across multiple entity base tables by
-   * default. To specify the entity type, pass in 'entity_type' for $name,
-   * the type as a string for $value, and no $operator (it's disregarded).
-   *
-   * 'bundle', 'revision_id' and 'entity_id' have no such restrictions.
-   *
-   * Note: The "comment" and "taxonomy_term" entity types don't support bundle
-   * conditions. For "taxonomy_term", propertyCondition('vid') can be used
-   * instead.
-   *
-   * @param $name
-   *   'entity_type', 'bundle', 'revision_id' or 'entity_id'.
-   * @param $value
-   *   The value for $name. In most cases, this is a scalar. For more complex
-   *   options, it is an array. The meaning of each element in the array is
-   *   dependent on $operator.
-   * @param $operator
-   *   Possible values:
-   *   - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These
-   *     operators expect $value to be a literal of the same type as the
-   *     column.
-   *   - 'IN', 'NOT IN': These operators expect $value to be an array of
-   *     literals of the same type as the column.
-   *   - 'BETWEEN': This operator expects $value to be an array of two literals
-   *     of the same type as the column.
-   *   The operator can be omitted, and will default to 'IN' if the value is an
-   *   array, or to '=' otherwise.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function entityCondition($name, $value, $operator = NULL) {
-    $this->entityConditions[$name] = array(
-      'value' => $value,
-      'operator' => $operator,
-    );
-    return $this;
-  }
-
-  /**
-   * Adds a condition on field values.
-   *
-   * @param $field
-   *   Either a field name or a field array.
-   * @param $column
-   *   The column that should hold the value to be matched.
-   * @param $value
-   *   The value to test the column value against.
-   * @param $operator
-   *   The operator to be used to test the given value.
-   * @param $delta_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $delta_group.
-   * @param $language_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $language_group.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   *
-   * @see EntityFieldQuery::addFieldCondition
-   * @see EntityFieldQuery::deleted
-   */
-  public function fieldCondition($field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
-    return $this->addFieldCondition($this->fieldConditions, $field, $column, $value, $operator, $delta_group, $language_group);
-  }
-
-  /**
-   * Adds a condition on the field language column.
-   *
-   * @param $field
-   *   Either a field name or a field array.
-   * @param $value
-   *   The value to test the column value against.
-   * @param $operator
-   *   The operator to be used to test the given value.
-   * @param $delta_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $delta_group.
-   * @param $language_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $language_group.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   *
-   * @see EntityFieldQuery::addFieldCondition
-   * @see EntityFieldQuery::deleted
-   */
-  public function fieldLanguageCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
-    return $this->addFieldCondition($this->fieldMetaConditions, $field, 'language', $value, $operator, $delta_group, $language_group);
-  }
-
-  /**
-   * Adds a condition on the field delta column.
-   *
-   * @param $field
-   *   Either a field name or a field array.
-   * @param $value
-   *   The value to test the column value against.
-   * @param $operator
-   *   The operator to be used to test the given value.
-   * @param $delta_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $delta_group.
-   * @param $language_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $language_group.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   *
-   * @see EntityFieldQuery::addFieldCondition
-   * @see EntityFieldQuery::deleted
-   */
-  public function fieldDeltaCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
-    return $this->addFieldCondition($this->fieldMetaConditions, $field, 'delta', $value, $operator, $delta_group, $language_group);
-  }
-
-  /**
-   * Adds the given condition to the proper condition array.
-   *
-   * @param $conditions
-   *   A reference to an array of conditions.
-   * @param $field
-   *   Either a field name or a field array.
-   * @param $column
-   *   A column defined in the hook_field_schema() of this field. If this is
-   *   omitted then the query will find only entities that have data in this
-   *   field, using the entity and property conditions if there are any.
-   * @param $value
-   *   The value to test the column value against. In most cases, this is a
-   *   scalar. For more complex options, it is an array. The meaning of each
-   *   element in the array is dependent on $operator.
-   * @param $operator
-   *   Possible values:
-   *   - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These
-   *     operators expect $value to be a literal of the same type as the
-   *     column.
-   *   - 'IN', 'NOT IN': These operators expect $value to be an array of
-   *     literals of the same type as the column.
-   *   - 'BETWEEN': This operator expects $value to be an array of two literals
-   *     of the same type as the column.
-   *   The operator can be omitted, and will default to 'IN' if the value is an
-   *   array, or to '=' otherwise.
-   * @param $delta_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $delta_group. For example, let's presume a multivalue field which has
-   *   two columns, 'color' and 'shape', and for entity id 1, there are two
-   *   values: red/square and blue/circle. Entity ID 1 does not have values
-   *   corresponding to 'red circle', however if you pass 'red' and 'circle' as
-   *   conditions, it will appear in the  results - by default queries will run
-   *   against any combination of deltas. By passing the conditions with the
-   *   same $delta_group it will ensure that only values attached to the same
-   *   delta are matched, and entity 1 would then be excluded from the results.
-   * @param $language_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $language_group.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  protected function addFieldCondition(&$conditions, $field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
-    if (is_scalar($field)) {
-      $field_definition = field_info_field($field);
-      if (empty($field_definition)) {
-        throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field)));
-      }
-      $field = $field_definition;
-    }
-    // Ensure the same index is used for field conditions as for fields.
-    $index = count($this->fields);
-    $this->fields[$index] = $field;
-    if (isset($column)) {
-      $conditions[$index] = array(
-        'field' => $field,
-        'column' => $column,
-        'value' => $value,
-        'operator' => $operator,
-        'delta_group' => $delta_group,
-        'language_group' => $language_group,
-      );
-    }
-    return $this;
-  }
-
-  /**
-   * Adds a condition on an entity-specific property.
-   *
-   * An $entity_type must be specified by calling
-   * EntityFieldCondition::entityCondition('entity_type', $entity_type) before
-   * executing the query. Also, by default only entities stored in SQL are
-   * supported; however, EntityFieldQuery::executeCallback can be set to handle
-   * different entity storage.
-   *
-   * @param $column
-   *   A column defined in the hook_schema() of the base table of the entity.
-   * @param $value
-   *   The value to test the field against. In most cases, this is a scalar. For
-   *   more complex options, it is an array. The meaning of each element in the
-   *   array is dependent on $operator.
-   * @param $operator
-   *   Possible values:
-   *   - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These
-   *     operators expect $value to be a literal of the same type as the
-   *     column.
-   *   - 'IN', 'NOT IN': These operators expect $value to be an array of
-   *     literals of the same type as the column.
-   *   - 'BETWEEN': This operator expects $value to be an array of two literals
-   *     of the same type as the column.
-   *   The operator can be omitted, and will default to 'IN' if the value is an
-   *   array, or to '=' otherwise.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function propertyCondition($column, $value, $operator = NULL) {
-    $this->propertyConditions[] = array(
-      'column' => $column,
-      'value' => $value,
-      'operator' => $operator,
-    );
-    return $this;
-  }
-
-  /**
-   * Orders the result set by entity-generic metadata.
-   *
-   * If called multiple times, the query will order by each specified column in
-   * the order this method is called.
-   *
-   * Note: The "comment" and "taxonomy_term" entity types don't support ordering
-   * by bundle. For "taxonomy_term", propertyOrderBy('vid') can be used instead.
-   *
-   * @param $name
-   *   'entity_type', 'bundle', 'revision_id' or 'entity_id'.
-   * @param $direction
-   *   The direction to sort. Legal values are "ASC" and "DESC".
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function entityOrderBy($name, $direction = 'ASC') {
-    $this->order[] = array(
-      'type' => 'entity',
-      'specifier' => $name,
-      'direction' => $direction,
-    );
-    return $this;
-  }
-
-  /**
-   * Orders the result set by a given field column.
-   *
-   * If called multiple times, the query will order by each specified column in
-   * the order this method is called.
-   *
-   * @param $field
-   *   Either a field name or a field array.
-   * @param $column
-   *   A column defined in the hook_field_schema() of this field. entity_id and
-   *   bundle can also be used.
-   * @param $direction
-   *   The direction to sort. Legal values are "ASC" and "DESC".
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function fieldOrderBy($field, $column, $direction = 'ASC') {
-    if (is_scalar($field)) {
-      $field_definition = field_info_field($field);
-      if (empty($field_definition)) {
-        throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field)));
-      }
-      $field = $field_definition;
-    }
-    // Save the index used for the new field, for later use in field storage.
-    $index = count($this->fields);
-    $this->fields[$index] = $field;
-    $this->order[] = array(
-      'type' => 'field',
-      'specifier' => array(
-        'field' => $field,
-        'index' => $index,
-        'column' => $column,
-      ),
-      'direction' => $direction,
-    );
-    return $this;
-  }
-
-  /**
-   * Orders the result set by an entity-specific property.
-   *
-   * An $entity_type must be specified by calling
-   * EntityFieldCondition::entityCondition('entity_type', $entity_type) before
-   * executing the query.
-   *
-   * If called multiple times, the query will order by each specified column in
-   * the order this method is called.
-   *
-   * @param $column
-   *   The column on which to order.
-   * @param $direction
-   *   The direction to sort. Legal values are "ASC" and "DESC".
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function propertyOrderBy($column, $direction = 'ASC') {
-    $this->order[] = array(
-      'type' => 'property',
-      'specifier' => $column,
-      'direction' => $direction,
-    );
-    return $this;
-  }
-
-  /**
-   * Sets the query to be a count query only.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function count() {
-    $this->count = TRUE;
-    return $this;
-  }
-
-  /**
-   * Restricts a query to a given range in the result set.
-   *
-   * @param $start
-   *   The first entity from the result set to return. If NULL, removes any
-   *   range directives that are set.
-   * @param $length
-   *   The number of entities to return from the result set.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function range($start = NULL, $length = NULL) {
-    $this->range = array(
-      'start' => $start,
-      'length' => $length,
-    );
-    return $this;
-  }
-
-  /**
-   * Enable a pager for the query.
-   *
-   * @param $limit
-   *   An integer specifying the number of elements per page.  If passed a false
-   *   value (FALSE, 0, NULL), the pager is disabled.
-   * @param $element
-   *   An optional integer to distinguish between multiple pagers on one page.
-   *   If not provided, one is automatically calculated.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function pager($limit = 10, $element = NULL) {
-    if (!isset($element)) {
-      $element = PagerDefault::$maxElement++;
-    }
-    elseif ($element >= PagerDefault::$maxElement) {
-      PagerDefault::$maxElement = $element + 1;
-    }
-
-    $this->pager = array(
-      'limit' => $limit,
-      'element' => $element,
-    );
-    return $this;
-  }
-
-  /**
-   * Enable sortable tables for this query.
-   *
-   * @param $headers
-   *   An EFQ Header array based on which the order clause is added to the query.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function tableSort(&$headers) {
-    // If 'field' is not initialized, the header columns aren't clickable
-    foreach ($headers as $key =>$header) {
-      if (is_array($header) && isset($header['specifier'])) {
-        $headers[$key]['field'] = '';
-      }
-    }
-
-    $order = tablesort_get_order($headers);
-    $direction = tablesort_get_sort($headers);
-    foreach ($headers as $header) {
-      if (is_array($header) && ($header['data'] == $order['name'])) {
-        if ($header['type'] == 'field') {
-          $this->fieldOrderBy($header['specifier']['field'], $header['specifier']['column'], $direction);
-        }
-        else {
-          $header['direction'] = $direction;
-          $this->order[] = $header;
-        }
-      }
-    }
-
-    return $this;
-  }
-
-  /**
-   * Filters on the data being deleted.
-   *
-   * @param $deleted
-   *   TRUE to only return deleted data, FALSE to return non-deleted data,
-   *   EntityFieldQuery::RETURN_ALL to return everything. Defaults to FALSE.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function deleted($deleted = TRUE) {
-    $this->deleted = $deleted;
-    return $this;
-  }
-
-  /**
-   * Queries the current or every revision.
-   *
-   * Note that this only affects field conditions. Property conditions always
-   * apply to the current revision.
-   * @TODO: Once revision tables have been cleaned up, revisit this.
-   *
-   * @param $age
-   *   - FIELD_LOAD_CURRENT (default): Query the most recent revisions for all
-   *     entities. The results will be keyed by entity type and entity ID.
-   *   - FIELD_LOAD_REVISION: Query all revisions. The results will be keyed by
-   *     entity type and entity revision ID.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function age($age) {
-    $this->age = $age;
-    return $this;
-  }
-
-  /**
-   * Adds a tag to the query.
-   *
-   * Tags are strings that mark a query so that hook_query_alter() and
-   * hook_query_TAG_alter() implementations may decide if they wish to alter
-   * the query. A query may have any number of tags, and they must be valid PHP
-   * identifiers (composed of letters, numbers, and underscores). For example,
-   * queries involving nodes that will be displayed for a user need to add the
-   * tag 'node_access', so that the node module can add access restrictions to
-   * the query.
-   *
-   * If an entity field query has tags, it must also have an entity type
-   * specified, because the alter hook will need the entity base table.
-   *
-   * @param string $tag
-   *   The tag to add.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function addTag($tag) {
-    $this->tags[$tag] = $tag;
-    return $this;
-  }
-
-  /**
-   * Adds additional metadata to the query.
-   *
-   * Sometimes a query may need to provide additional contextual data for the
-   * alter hook. The alter hook implementations may then use that information
-   * to decide if and how to take action.
-   *
-   * @param $key
-   *   The unique identifier for this piece of metadata. Must be a string that
-   *   follows the same rules as any other PHP identifier.
-   * @param $object
-   *   The additional data to add to the query. May be any valid PHP variable.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function addMetaData($key, $object) {
-    $this->metaData[$key] = $object;
-    return $this;
-  }
-
-  /**
-   * Executes the query.
-   *
-   * After executing the query, $this->orderedResults will contain a list of
-   * the same stub entities in the order returned by the query. This is only
-   * relevant if there are multiple entity types in the returned value and
-   * a field ordering was requested. In every other case, the returned value
-   * contains everything necessary for processing.
-   *
-   * @return
-   *   Either a number if count() was called or an array of associative
-   *   arrays of stub entities. The outer array keys are entity types, and the
-   *   inner array keys are the relevant ID. (In most this cases this will be
-   *   the entity ID. The only exception is when age=FIELD_LOAD_REVISION is used
-   *   and field conditions or sorts are present -- in this case, the key will
-   *   be the revision ID.) The inner array values are always stub entities, as
-   *   returned by entity_create_stub_entity(). To traverse the returned array:
-   *   @code
-   *     foreach ($query->execute() as $entity_type => $entities) {
-   *       foreach ($entities as $entity_id => $entity) {
-   *   @endcode
-   *   Note if the entity type is known, then the following snippet will load
-   *   the entities found:
-   *   @code
-   *     $result = $query->execute();
-   *     $entities = entity_load($my_type, array_keys($result[$my_type]));
-   *   @endcode
-   */
-  public function execute() {
-    // Give a chance to other modules to alter the query.
-    drupal_alter('entity_query', $this);
-    $this->altered = TRUE;
-
-    // Initialize the pager.
-    $this->initializePager();
-
-    // Execute the query using the correct callback.
-    $result = call_user_func($this->queryCallback(), $this);
-
-    return $result;
-  }
-
-  /**
-   * Determines the query callback to use for this entity query.
-   *
-   * @return
-   *   A callback that can be used with call_user_func().
-   */
-  public function queryCallback() {
-    // Use the override from $this->executeCallback. It can be set either
-    // while building the query, or using hook_entity_query_alter().
-    if (function_exists($this->executeCallback)) {
-      return $this->executeCallback;
-    }
-    // If there are no field conditions and sorts, and no execute callback
-    // then we default to querying entity tables in SQL.
-    if (empty($this->fields)) {
-      return array($this, 'propertyQuery');
-    }
-    // If no override, find the storage engine to be used.
-    foreach ($this->fields as $field) {
-      if (!isset($storage)) {
-        $storage = $field['storage']['module'];
-      }
-      elseif ($storage != $field['storage']['module']) {
-        throw new EntityFieldQueryException(t("Can't handle more than one field storage engine"));
-      }
-    }
-    if ($storage) {
-      // Use hook_field_storage_query() from the field storage.
-      return $storage . '_field_storage_query';
-    }
-    else {
-      throw new EntityFieldQueryException(t("Field storage engine not found."));
-    }
-  }
-
-  /**
-   * Queries entity tables in SQL for property conditions and sorts.
-   *
-   * This method is only used if there are no field conditions and sorts.
-   *
-   * @return
-   *   See EntityFieldQuery::execute().
-   */
-  protected function propertyQuery() {
-    if (empty($this->entityConditions['entity_type'])) {
-      throw new EntityFieldQueryException(t('For this query an entity type must be specified.'));
-    }
-    $entity_type = $this->entityConditions['entity_type']['value'];
-    $entity_info = entity_get_info($entity_type);
-    if (empty($entity_info['base table'])) {
-      throw new EntityFieldQueryException(t('Entity %entity has no base table.', array('%entity' => $entity_type)));
-    }
-    $base_table = $entity_info['base table'];
-    $base_table_schema = drupal_get_schema($base_table);
-    $select_query = db_select($base_table);
-    $select_query->addExpression(':entity_type', 'entity_type', array(':entity_type' => $entity_type));
-    // Process the property conditions.
-    foreach ($this->propertyConditions as $property_condition) {
-      $this->addCondition($select_query, "$base_table." . $property_condition['column'], $property_condition);
-    }
-    // Process the four possible entity condition.
-    // The id field is always present in entity keys.
-    $sql_field = $entity_info['entity keys']['id'];
-    $id_map['entity_id'] = $sql_field;
-    $select_query->addField($base_table, $sql_field, 'entity_id');
-    if (isset($this->entityConditions['entity_id'])) {
-      $this->addCondition($select_query, $sql_field, $this->entityConditions['entity_id']);
-    }
-
-    // If there is a revision key defined, use it.
-    if (!empty($entity_info['entity keys']['revision'])) {
-      $sql_field = $entity_info['entity keys']['revision'];
-      $select_query->addField($base_table, $sql_field, 'revision_id');
-      if (isset($this->entityConditions['revision_id'])) {
-        $this->addCondition($select_query, $sql_field, $this->entityConditions['revision_id']);
-      }
-    }
-    else {
-      $sql_field = 'revision_id';
-      $select_query->addExpression('NULL', 'revision_id');
-    }
-    $id_map['revision_id'] = $sql_field;
-
-    // Handle bundles.
-    if (!empty($entity_info['entity keys']['bundle'])) {
-      $sql_field = $entity_info['entity keys']['bundle'];
-      $having = FALSE;
-
-      if (!empty($base_table_schema['fields'][$sql_field])) {
-        $select_query->addField($base_table, $sql_field, 'bundle');
-      }
-    }
-    else {
-      $sql_field = 'bundle';
-      $select_query->addExpression(':bundle', 'bundle', array(':bundle' => $entity_type));
-      $having = TRUE;
-    }
-    $id_map['bundle'] = $sql_field;
-    if (isset($this->entityConditions['bundle'])) {
-      $this->addCondition($select_query, $sql_field, $this->entityConditions['bundle'], $having);
-    }
-
-    // Order the query.
-    foreach ($this->order as $order) {
-      if ($order['type'] == 'entity') {
-        $key = $order['specifier'];
-        if (!isset($id_map[$key])) {
-          throw new EntityFieldQueryException(t('Do not know how to order on @key for @entity_type', array('@key' => $key, '@entity_type' => $entity_type)));
-        }
-        $select_query->orderBy($id_map[$key], $order['direction']);
-      }
-      elseif ($order['type'] == 'property') {
-        $select_query->orderBy("$base_table." . $order['specifier'], $order['direction']);
-      }
-    }
-
-    return $this->finishQuery($select_query);
-  }
-
-  /**
-   * Get the total number of results and initialize a pager for the query.
-   *
-   * This query can be detected by checking for ($this->pager && $this->count),
-   * which allows a driver to return 0 from the count query and disable
-   * the pager.
-   */
-  function initializePager() {
-    if ($this->pager && !$this->count) {
-      $page = pager_find_page($this->pager['element']);
-      $count_query = clone $this;
-      $this->pager['total'] = $count_query->count()->execute();
-      $this->pager['start'] = $page * $this->pager['limit'];
-      pager_default_initialize($this->pager['total'], $this->pager['limit'], $this->pager['element']);
-      $this->range($this->pager['start'], $this->pager['limit']);
-    }
-  }
-
-  /**
-   * Finishes the query.
-   *
-   * Adds tags, metaData, range and returns the requested list or count.
-   *
-   * @param SelectQuery $select_query
-   *   A SelectQuery which has entity_type, entity_id, revision_id and bundle
-   *   fields added.
-   * @param $id_key
-   *   Which field's values to use as the returned array keys.
-   *
-   * @return
-   *   See EntityFieldQuery::execute().
-   */
-  function finishQuery($select_query, $id_key = 'entity_id') {
-    foreach ($this->tags as $tag) {
-      $select_query->addTag($tag);
-    }
-    foreach ($this->metaData as $key => $object) {
-      $select_query->addMetaData($key, $object);
-    }
-    $select_query->addMetaData('entity_field_query', $this);
-    if ($this->range) {
-      $select_query->range($this->range['start'], $this->range['length']);
-    }
-    if ($this->count) {
-      return $select_query->countQuery()->execute()->fetchField();
-    }
-    $return = array();
-    foreach ($select_query->execute() as $partial_entity) {
-      $bundle = isset($partial_entity->bundle) ? $partial_entity->bundle : NULL;
-      $entity = entity_create_stub_entity($partial_entity->entity_type, array($partial_entity->entity_id, $partial_entity->revision_id, $bundle));
-      $return[$partial_entity->entity_type][$partial_entity->$id_key] = $entity;
-      $this->ordered_results[] = $partial_entity;
-    }
-    return $return;
-  }
-
-  /**
-   * Adds a condition to an already built SelectQuery (internal function).
-   *
-   * This is a helper for hook_entity_query() and hook_field_storage_query().
-   *
-   * @param SelectQuery $select_query
-   *   A SelectQuery object.
-   * @param $sql_field
-   *   The name of the field.
-   * @param $condition
-   *   A condition as described in EntityFieldQuery::fieldCondition() and
-   *   EntityFieldQuery::entityCondition().
-   * @param $having
-   *   HAVING or WHERE. This is necessary because SQL can't handle WHERE
-   *   conditions on aliased columns.
-   */
-  public function addCondition(SelectQuery $select_query, $sql_field, $condition, $having = FALSE) {
-    $method = $having ? 'havingCondition' : 'condition';
-    $like_prefix = '';
-    switch ($condition['operator']) {
-      case 'CONTAINS':
-        $like_prefix = '%';
-      case 'STARTS_WITH':
-        $select_query->$method($sql_field, $like_prefix . db_like($condition['value']) . '%', 'LIKE');
-        break;
-      default:
-        $select_query->$method($sql_field, $condition['value'], $condition['operator']);
-    }
-  }
-
-}
-
-/**
- * Exception thrown when a malformed entity is passed.
- */
-class EntityMalformedException extends Exception { }
diff --git a/includes/install.core.inc b/includes/install.core.inc
index 1040bf3..3791d71 100644
--- a/includes/install.core.inc
+++ b/includes/install.core.inc
@@ -253,12 +253,13 @@ function install_begin_request(&$install_state) {
   // Set up $language, so t() caller functions will still work.
   drupal_language_initialize();
 
-  include_once DRUPAL_ROOT . '/includes/entity.inc';
   require_once DRUPAL_ROOT . '/includes/ajax.inc';
   $module_list['system']['filename'] = 'modules/system/system.module';
-  $module_list['user']['filename'] = 'modules/user/user.module';
+  $module_list['entity']['filename'] = 'modules/entity/entity.module';
+  $module_list['user']['filename']   = 'modules/user/user.module';
   module_list(TRUE, FALSE, FALSE, $module_list);
   drupal_load('module', 'system');
+  drupal_load('module', 'entity');
   drupal_load('module', 'user');
 
   // Load the cache infrastructure using a "fake" cache implementation that
diff --git a/includes/module.inc b/includes/module.inc
index cc3aa8e..77fc98a 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -424,8 +424,9 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
       registry_update();
       // Refresh the schema to include it.
       drupal_get_schema(NULL, TRUE);
-      // Clear entity cache.
-      entity_info_cache_clear();
+
+      // Allow modules to react prior to the installation of a module.
+      module_invoke_all('modules_preinstall', array($module));
 
       // Now install the module if necessary.
       if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
@@ -450,6 +451,9 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
         watchdog('system', '%module module installed.', array('%module' => $module), WATCHDOG_INFO);
       }
 
+      // Allow modules to react prior to the enabling of a module.
+      module_invoke_all('modules_preenable', array($module));
+
       // Enable the module.
       module_invoke($module, 'enable');
 
diff --git a/modules/comment/comment.info b/modules/comment/comment.info
index a5837af..949ffc2 100644
--- a/modules/comment/comment.info
+++ b/modules/comment/comment.info
@@ -4,6 +4,7 @@ package = Core
 version = VERSION
 core = 8.x
 dependencies[] = text
+dependencies[] = entity
 files[] = comment.module
 files[] = comment.test
 configure = admin/content/comment
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index 37a208f..44a6cdc 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -99,6 +99,7 @@ function comment_entity_info() {
       'uri callback' => 'comment_uri',
       'fieldable' => TRUE,
       'controller class' => 'CommentController',
+      'entity class' => 'Comment',
       'entity keys' => array(
         'id' => 'cid',
         'bundle' => 'node_type',
@@ -735,7 +736,7 @@ function comment_node_page_additions($node) {
 
   // Append comment form if needed.
   if (user_access('post comments') && $node->comment == COMMENT_NODE_OPEN && (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW)) {
-    $build = drupal_get_form("comment_node_{$node->type}_form", (object) array('nid' => $node->nid));
+    $build = drupal_get_form("comment_node_{$node->type}_form", entity_create('comment', array('nid' => $node->nid)));
     $additions['comment_form'] = $build;
   }
 
@@ -1435,147 +1436,7 @@ function comment_access($op, $comment) {
  *   A comment object.
  */
 function comment_save($comment) {
-  global $user;
-
-  $transaction = db_transaction();
-  try {
-    $defaults =  array(
-      'mail' => '',
-      'homepage' => '',
-      'name' => '',
-      'status' => user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED,
-    );
-    foreach ($defaults as $key => $default) {
-      if (!isset($comment->$key)) {
-        $comment->$key = $default;
-      }
-    }
-    // Make sure we have a bundle name.
-    if (!isset($comment->node_type)) {
-      $node = node_load($comment->nid);
-      $comment->node_type = 'comment_node_' . $node->type;
-    }
-
-    // Load the stored entity, if any.
-    if (!empty($comment->cid) && !isset($comment->original)) {
-      $comment->original = entity_load_unchanged('comment', $comment->cid);
-    }
-
-    field_attach_presave('comment', $comment);
-
-    // Allow modules to alter the comment before saving.
-    module_invoke_all('comment_presave', $comment);
-    module_invoke_all('entity_presave', $comment, 'comment');
-
-    if ($comment->cid) {
-
-      drupal_write_record('comment', $comment, 'cid');
-
-      // Ignore slave server temporarily to give time for the
-      // saved comment to be propagated to the slave.
-      db_ignore_slave();
-
-      // Update the {node_comment_statistics} table prior to executing hooks.
-      _comment_update_node_statistics($comment->nid);
-
-      field_attach_update('comment', $comment);
-      // Allow modules to respond to the updating of a comment.
-      module_invoke_all('comment_update', $comment);
-      module_invoke_all('entity_update', $comment, 'comment');
-    }
-    else {
-      // Add the comment to database. This next section builds the thread field.
-      // Also see the documentation for comment_view().
-      if (!empty($comment->thread)) {
-        // Allow calling code to set thread itself.
-        $thread = $comment->thread;
-      }
-      elseif ($comment->pid == 0) {
-        // This is a comment with no parent comment (depth 0): we start
-        // by retrieving the maximum thread level.
-        $max = db_query('SELECT MAX(thread) FROM {comment} WHERE nid = :nid', array(':nid' => $comment->nid))->fetchField();
-        // Strip the "/" from the end of the thread.
-        $max = rtrim($max, '/');
-        // Finally, build the thread field for this new comment.
-        $thread = int2vancode(vancode2int($max) + 1) . '/';
-      }
-      else {
-        // This is a comment with a parent comment, so increase the part of the
-        // thread value at the proper depth.
-
-        // Get the parent comment:
-        $parent = comment_load($comment->pid);
-        // Strip the "/" from the end of the parent thread.
-        $parent->thread = (string) rtrim((string) $parent->thread, '/');
-        // Get the max value in *this* thread.
-        $max = db_query("SELECT MAX(thread) FROM {comment} WHERE thread LIKE :thread AND nid = :nid", array(
-          ':thread' => $parent->thread . '.%',
-          ':nid' => $comment->nid,
-        ))->fetchField();
-
-        if ($max == '') {
-          // First child of this parent.
-          $thread = $parent->thread . '.' . int2vancode(0) . '/';
-        }
-        else {
-          // Strip the "/" at the end of the thread.
-          $max = rtrim($max, '/');
-          // Get the value at the correct depth.
-          $parts = explode('.', $max);
-          $parent_depth = count(explode('.', $parent->thread));
-          $last = $parts[$parent_depth];
-          // Finally, build the thread field for this new comment.
-          $thread = $parent->thread . '.' . int2vancode(vancode2int($last) + 1) . '/';
-        }
-      }
-
-      if (empty($comment->created)) {
-        $comment->created = REQUEST_TIME;
-      }
-
-      if (empty($comment->changed)) {
-        $comment->changed = $comment->created;
-      }
-
-      if ($comment->uid === $user->uid && isset($user->name)) { // '===' Need to modify anonymous users as well.
-        $comment->name = $user->name;
-      }
-
-      // Ensure the parent id (pid) has a value set.
-      if (empty($comment->pid)) {
-        $comment->pid = 0;
-      }
-
-      // Add the values which aren't passed into the function.
-      $comment->thread = $thread;
-      $comment->hostname = ip_address();
-
-      drupal_write_record('comment', $comment);
-
-      // Ignore slave server temporarily to give time for the
-      // created comment to be propagated to the slave.
-      db_ignore_slave();
-
-      // Update the {node_comment_statistics} table prior to executing hooks.
-      _comment_update_node_statistics($comment->nid);
-
-      field_attach_insert('comment', $comment);
-
-      // Tell the other modules a new comment has been submitted.
-      module_invoke_all('comment_insert', $comment);
-      module_invoke_all('entity_insert', $comment, 'comment');
-    }
-    if ($comment->status == COMMENT_PUBLISHED) {
-      module_invoke_all('comment_publish', $comment);
-    }
-    unset($comment->original);
-  }
-  catch (Exception $e) {
-    $transaction->rollback('comment');
-    watchdog_exception('comment', $e);
-    throw $e;
-  }
-
+  $comment->save();
 }
 
 /**
@@ -1595,31 +1456,7 @@ function comment_delete($cid) {
  *   The comment to delete.
  */
 function comment_delete_multiple($cids) {
-  $comments = comment_load_multiple($cids);
-  if ($comments) {
-    $transaction = db_transaction();
-    try {
-      // Delete the comments.
-      db_delete('comment')
-        ->condition('cid', array_keys($comments), 'IN')
-        ->execute();
-      foreach ($comments as $comment) {
-        field_attach_delete('comment', $comment);
-        module_invoke_all('comment_delete', $comment);
-        module_invoke_all('entity_delete', $comment, 'comment');
-
-        // Delete the comment's replies.
-        $child_cids = db_query('SELECT cid FROM {comment} WHERE pid = :cid', array(':cid' => $comment->cid))->fetchCol();
-        comment_delete_multiple($child_cids);
-        _comment_update_node_statistics($comment->nid);
-      }
-    }
-    catch (Exception $e) {
-      $transaction->rollback();
-      watchdog_exception('comment', $e);
-      throw $e;
-    }
-  }
+  entity_delete_multiple('comment', $cids);
 }
 
 /**
@@ -1662,10 +1499,10 @@ function comment_load($cid) {
 /**
  * Controller class for comments.
  *
- * This extends the DrupalDefaultEntityController class, adding required
- * special handling for comment objects.
+ * This extends the EntityDefaultStorageController class, adding required
+ * special handling for comment entities.
  */
-class CommentController extends DrupalDefaultEntityController {
+class CommentController extends EntityDefaultStorageController {
 
   protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
     $query = parent::buildQuery($ids, $conditions, $revision_id);
@@ -1691,6 +1528,117 @@ class CommentController extends DrupalDefaultEntityController {
 }
 
 /**
+ * Entity class for comments.
+ */
+class Comment extends Entity {
+
+  public $cid, $pid;
+  public $uid = 0;
+  public $language = LANGUAGE_NONE;
+  public $mail, $homepage, $subject, $name = '';
+
+
+  public function invokeHookPreSave() {
+    global $user;
+
+    if (!isset($this->status)) {
+      $this->status = user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED;
+    }
+    // Make sure we have a proper bundle name.
+    if (!isset($this->node_type) || $this->node_type == 'comment') {
+      $node = node_load($this->nid);
+      $this->node_type = 'comment_node_' . $node->type;
+    }
+    if (!$this->cid) {
+      // Add the comment to database. This next section builds the thread field.
+      // Also see the documentation for comment_view().
+      if (!empty($this->thread)) {
+        // Allow calling code to set thread itself.
+        $thread = $this->thread;
+      }
+      elseif ($this->pid == 0) {
+        // This is a comment with no parent comment (depth 0): we start
+        // by retrieving the maximum thread level.
+        $max = db_query('SELECT MAX(thread) FROM {comment} WHERE nid = :nid', array(':nid' => $this->nid))->fetchField();
+        // Strip the "/" from the end of the thread.
+        $max = rtrim($max, '/');
+        // Finally, build the thread field for this new comment.
+        $thread = int2vancode(vancode2int($max) + 1) . '/';
+      }
+      else {
+        // This is a comment with a parent comment, so increase the part of
+        // the thread value at the proper depth.
+
+        // Get the parent comment:
+        $parent = comment_load($this->pid);
+        // Strip the "/" from the end of the parent thread.
+        $parent->thread = (string) rtrim((string) $parent->thread, '/');
+        // Get the max value in *this* thread.
+        $max = db_query("SELECT MAX(thread) FROM {comment} WHERE thread LIKE :thread AND nid = :nid", array(
+          ':thread' => $parent->thread . '.%',
+          ':nid' => $this->nid,
+        ))->fetchField();
+
+        if ($max == '') {
+          // First child of this parent.
+          $thread = $parent->thread . '.' . int2vancode(0) . '/';
+        }
+        else {
+          // Strip the "/" at the end of the thread.
+          $max = rtrim($max, '/');
+          // Get the value at the correct depth.
+          $parts = explode('.', $max);
+          $parent_depth = count(explode('.', $parent->thread));
+          $last = $parts[$parent_depth];
+          // Finally, build the thread field for this new comment.
+          $thread = $parent->thread . '.' . int2vancode(vancode2int($last) + 1) . '/';
+        }
+      }
+      if (empty($this->created)) {
+        $this->created = REQUEST_TIME;
+      }
+      if (empty($this->changed)) {
+        $this->changed = $this->created;
+      }
+      if ($this->uid === $user->uid && isset($user->name)) { // '===' Need to modify anonymous users as well.
+        $this->name = $user->name;
+      }
+      // Add the values which aren't passed into the function.
+      $this->thread = $thread;
+      $this->hostname = ip_address();
+    }
+
+    parent::invokeHookPreSave();
+  }
+
+  public function invokeHookInsert() {
+    // Update the {node_comment_statistics} table prior to executing the hook.
+    _comment_update_node_statistics($this->nid);
+    parent::invokeHookInsert();
+    if ($this->status == COMMENT_PUBLISHED) {
+      module_invoke_all('comment_publish', $this);
+    }
+  }
+
+  public function invokeHookUpdate() {
+    // Update the {node_comment_statistics} table prior to executing the hook.
+    _comment_update_node_statistics($this->nid);
+    parent::invokeHookUpdate();
+    if ($this->status == COMMENT_PUBLISHED) {
+      module_invoke_all('comment_publish', $this);
+    }
+  }
+
+  public function invokeHookDelete() {
+    parent::invokeHookDelete();
+    // Delete the comment's replies.
+    $child_cids = db_query('SELECT cid FROM {comment} WHERE pid = :cid', array(':cid' => $this->cid))->fetchCol();
+    comment_delete_multiple($child_cids);
+    _comment_update_node_statistics($this->nid);
+  }
+}
+
+/**
  * Get number of new comments for current user and specified node.
  *
  * @param $nid
@@ -1819,22 +1767,6 @@ function comment_form($form, &$form_state, $comment) {
   // use during form building and processing. During a rebuild, use what is in
   // the form state.
   if (!isset($form_state['comment'])) {
-    $defaults = array(
-      'name' => '',
-      'mail' => '',
-      'homepage' => '',
-      'subject' => '',
-      'comment' => '',
-      'cid' => NULL,
-      'pid' => NULL,
-      'language' => LANGUAGE_NONE,
-      'uid' => 0,
-    );
-    foreach ($defaults as $key => $value) {
-      if (!isset($comment->$key)) {
-        $comment->$key = $value;
-      }
-    }
     $form_state['comment'] = $comment;
   }
   else {
@@ -2132,12 +2064,6 @@ function comment_form_validate($form, &$form_state) {
  * Prepare a comment for submission.
  */
 function comment_submit($comment) {
-  // @todo Legacy support. Remove in Drupal 8.
-  if (is_array($comment)) {
-    $comment += array('subject' => '');
-    $comment = (object) $comment;
-  }
-
   if (empty($comment->date)) {
     $comment->date = 'now';
   }
diff --git a/modules/comment/comment.pages.inc b/modules/comment/comment.pages.inc
index 7e88bff..b176105 100644
--- a/modules/comment/comment.pages.inc
+++ b/modules/comment/comment.pages.inc
@@ -35,7 +35,7 @@ function comment_reply($node, $pid = NULL) {
   // The user is previewing a comment prior to submitting it.
   if ($op == t('Preview')) {
     if (user_access('post comments')) {
-      $build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", (object) array('pid' => $pid, 'nid' => $node->nid));
+      $build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", entity_create('comment', array('pid' => $pid, 'nid' => $node->nid)));
     }
     else {
       drupal_set_message(t('You are not authorized to post comments.'), 'error');
@@ -86,8 +86,8 @@ function comment_reply($node, $pid = NULL) {
       drupal_goto("node/$node->nid");
     }
     elseif (user_access('post comments')) {
-      $edit = array('nid' => $node->nid, 'pid' => $pid);
-      $build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", (object) $edit);
+      $comment = entity_create('comment', array('nid' => $node->nid, 'pid' => $pid));
+      $build['comment_form'] = drupal_get_form("comment_node_{$node->type}_form", $comment);
     }
     else {
       drupal_set_message(t('You are not authorized to post comments.'), 'error');
diff --git a/modules/comment/comment.test b/modules/comment/comment.test
index e5cae5e..246972a 100644
--- a/modules/comment/comment.test
+++ b/modules/comment/comment.test
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * @file 
+ * @file
  * Tests for comment.module.
  */
 
@@ -87,7 +87,7 @@ class CommentHelperCase extends DrupalWebTestCase {
     }
 
     if (isset($match[1])) {
-      return (object) array('id' => $match[1], 'subject' => $subject, 'comment' => $comment);
+      return entity_create('comment', array('id' => $match[1], 'subject' => $subject, 'comment' => $comment));
     }
   }
 
@@ -269,7 +269,7 @@ class CommentHelperCase extends DrupalWebTestCase {
 
     // Create a new comment. This helper function may be run with different
     // comment settings so use comment_save() to avoid complex setup.
-    $comment = (object) array(
+    $comment = entity_create('comment', array(
       'cid' => NULL,
       'nid' => $this->node->nid,
       'node_type' => $this->node->type,
@@ -280,7 +280,7 @@ class CommentHelperCase extends DrupalWebTestCase {
       'hostname' => ip_address(),
       'language' => LANGUAGE_NONE,
       'comment_body' => array(LANGUAGE_NONE => array($this->randomName())),
-    );
+    ));
     comment_save($comment);
     $this->drupalLogout();
 
@@ -654,7 +654,7 @@ class CommentInterfaceTest extends CommentHelperCase {
       if ($info['comment count']) {
         // Create a comment via CRUD API functionality, since
         // $this->postComment() relies on actual user permissions.
-        $comment = (object) array(
+        $comment = entity_create('comment', array(
           'cid' => NULL,
           'nid' => $this->node->nid,
           'node_type' => $this->node->type,
@@ -665,7 +665,7 @@ class CommentInterfaceTest extends CommentHelperCase {
           'hostname' => ip_address(),
           'language' => LANGUAGE_NONE,
           'comment_body' => array(LANGUAGE_NONE => array($this->randomName())),
-        );
+        ));
         comment_save($comment);
         $this->comment = $comment;
 
@@ -1454,7 +1454,7 @@ class CommentApprovalTest extends CommentHelperCase {
     // Get unapproved comment id.
     $this->drupalLogin($this->admin_user);
     $anonymous_comment4 = $this->getUnapprovedComment($subject);
-    $anonymous_comment4 = (object) array('id' => $anonymous_comment4, 'subject' => $subject, 'comment' => $body);
+    $anonymous_comment4 = entity_create('comment', array('id' => $anonymous_comment4, 'subject' => $subject, 'comment' => $body));
     $this->drupalLogout();
 
     $this->assertFalse($this->commentExists($anonymous_comment4), t('Anonymous comment was not published.'));
@@ -1518,7 +1518,7 @@ class CommentApprovalTest extends CommentHelperCase {
     // Get unapproved comment id.
     $this->drupalLogin($this->admin_user);
     $anonymous_comment4 = $this->getUnapprovedComment($subject);
-    $anonymous_comment4 = (object) array('id' => $anonymous_comment4, 'subject' => $subject, 'comment' => $body);
+    $anonymous_comment4 = entity_create('comment', array('id' => $anonymous_comment4, 'subject' => $subject, 'comment' => $body));
     $this->drupalLogout();
 
     $this->assertFalse($this->commentExists($anonymous_comment4), t('Anonymous comment was not published.'));
diff --git a/modules/entity/entity.api.php b/modules/entity/entity.api.php
new file mode 100644
index 0000000..9b19477
--- /dev/null
+++ b/modules/entity/entity.api.php
@@ -0,0 +1,414 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided the Entity module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Inform the base system and the Field API about one or more entity types.
+ *
+ * Inform the system about one or more entity types (i.e., object types that
+ * can be loaded via entity_load() and, optionally, to which fields can be
+ * attached).
+ *
+ * @return
+ *   An array whose keys are entity type names and whose values identify
+ *   properties of those types that the system needs to know about:
+ *   - label: The human-readable name of the type.
+ *   - controller class: The name of the class that is used to load the objects.
+ *     The class has to implement the DrupalEntityControllerInterface interface.
+ *     Leave blank to use the DrupalDefaultEntityController implementation.
+ *   - base table: (used by DrupalDefaultEntityController) The name of the
+ *     entity type's base table.
+ *   - static cache: (used by DrupalDefaultEntityController) FALSE to disable
+ *     static caching of entities during a page request. Defaults to TRUE.
+ *   - field cache: (used by Field API loading and saving of field data) FALSE
+ *     to disable Field API's persistent cache of field data. Only recommended
+ *     if a higher level persistent cache is available for the entity type.
+ *     Defaults to TRUE.
+ *   - load hook: The name of the hook which should be invoked by
+ *     DrupalDefaultEntityController:attachLoad(), for example 'node_load'.
+ *   - uri callback: A function taking an entity as argument and returning the
+ *     uri elements of the entity, e.g. 'path' and 'options'. The actual entity
+ *     uri can be constructed by passing these elements to url().
+ *   - label callback: (optional) A function taking an entity as argument and
+ *     returning the label of the entity. The entity label is the main string
+ *     associated with an entity; for example, the title of a node or the
+ *     subject of a comment. If there is an entity object property that defines
+ *     the label, use the 'label' element of the 'entity keys' return
+ *     value component to provide this information (see below). If more complex
+ *     logic is needed to determine the label of an entity, you can instead
+ *     specify a callback function here, which will be called to determine the
+ *     entity label. See also the entity_label() function, which implements this
+ *     logic.
+ *   - fieldable: Set to TRUE if you want your entity type to be fieldable.
+ *   - translation: An associative array of modules registered as field
+ *     translation handlers. Array keys are the module names, array values
+ *     can be any data structure the module uses to provide field translation.
+ *     Any empty value disallows the module to appear as a translation handler.
+ *   - entity keys: An array describing how the Field API can extract the
+ *     information it needs from the objects of the type. Elements:
+ *     - id: The name of the property that contains the primary id of the
+ *       entity. Every entity object passed to the Field API must have this
+ *       property and its value must be numeric.
+ *     - revision: The name of the property that contains the revision id of
+ *       the entity. The Field API assumes that all revision ids are unique
+ *       across all entities of a type. This entry can be omitted if the
+ *       entities of this type are not versionable.
+ *     - bundle: The name of the property that contains the bundle name for the
+ *       entity. The bundle name defines which set of fields are attached to
+ *       the entity (e.g. what nodes call "content type"). This entry can be
+ *       omitted if this entity type exposes a single bundle (all entities have
+ *       the same collection of fields). The name of this single bundle will be
+ *       the same as the entity type.
+ *     - label: The name of the property that contains the entity label. For
+ *       example, if the entity's label is located in $entity->subject, then
+ *       'subject' should be specified here. If complex logic is required to
+ *       build the label, a 'label callback' should be defined instead (see
+ *       the 'label callback' section above for details).
+ *   - bundle keys: An array describing how the Field API can extract the
+ *     information it needs from the bundle objects for this type (e.g
+ *     $vocabulary objects for terms; not applicable for nodes). This entry can
+ *     be omitted if this type's bundles do not exist as standalone objects.
+ *     Elements:
+ *     - bundle: The name of the property that contains the name of the bundle
+ *       object.
+ *   - bundles: An array describing all bundles for this object type. Keys are
+ *     bundles machine names, as found in the objects' 'bundle' property
+ *     (defined in the 'entity keys' entry above). Elements:
+ *     - label: The human-readable name of the bundle.
+ *     - uri callback: Same as the 'uri callback' key documented above for the
+ *       entity type, but for the bundle only. When determining the URI of an
+ *       entity, if a 'uri callback' is defined for both the entity type and
+ *       the bundle, the one for the bundle is used.
+ *     - admin: An array of information that allows Field UI pages to attach
+ *       themselves to the existing administration pages for the bundle.
+ *       Elements:
+ *       - path: the path of the bundle's main administration page, as defined
+ *         in hook_menu(). If the path includes a placeholder for the bundle,
+ *         the 'bundle argument', 'bundle helper' and 'real path' keys below
+ *         are required.
+ *       - bundle argument: The position of the placeholder in 'path', if any.
+ *       - real path: The actual path (no placeholder) of the bundle's main
+ *         administration page. This will be used to generate links.
+ *       - access callback: As in hook_menu(). 'user_access' will be assumed if
+ *         no value is provided.
+ *       - access arguments: As in hook_menu().
+ *   - view modes: An array describing the view modes for the entity type. View
+ *     modes let entities be displayed differently depending on the context.
+ *     For instance, a node can be displayed differently on its own page
+ *     ('full' mode), on the home page or taxonomy listings ('teaser' mode), or
+ *     in an RSS feed ('rss' mode). Modules taking part in the display of the
+ *     entity (notably the Field API) can adjust their behavior depending on
+ *     the requested view mode. An additional 'default' view mode is available
+ *     for all entity types. This view mode is not intended for actual entity
+ *     display, but holds default display settings. For each available view
+ *     mode, administrators can configure whether it should use its own set of
+ *     field display settings, or just replicate the settings of the 'default'
+ *     view mode, thus reducing the amount of display configurations to keep
+ *     track of. Keys of the array are view mode names. Each view mode is
+ *     described by an array with the following key/value pairs:
+ *     - label: The human-readable name of the view mode
+ *     - custom settings: A boolean specifying whether the view mode should by
+ *       default use its own custom field display settings. If FALSE, entities
+ *       displayed in this view mode will reuse the 'default' display settings
+ *       by default (e.g. right after the module exposing the view mode is
+ *       enabled), but administrators can later use the Field UI to apply custom
+ *       display settings specific to the view mode.
+ *
+ * @see entity_load()
+ * @see hook_entity_info_alter()
+ */
+function hook_entity_info() {
+  $return = array(
+    'node' => array(
+      'label' => t('Node'),
+      'controller class' => 'NodeController',
+      'base table' => 'node',
+      'revision table' => 'node_revision',
+      'uri callback' => 'node_uri',
+      'fieldable' => TRUE,
+      'translation' => array(
+        'locale' => TRUE,
+      ),
+      'entity keys' => array(
+        'id' => 'nid',
+        'revision' => 'vid',
+        'bundle' => 'type',
+      ),
+      'bundle keys' => array(
+        'bundle' => 'type',
+      ),
+      'bundles' => array(),
+      'view modes' => array(
+        'full' => array(
+          'label' => t('Full content'),
+          'custom settings' => FALSE,
+        ),
+        'teaser' => array(
+          'label' => t('Teaser'),
+          'custom settings' => TRUE,
+        ),
+        'rss' => array(
+          'label' => t('RSS'),
+          'custom settings' => FALSE,
+        ),
+      ),
+    ),
+  );
+
+  // Search integration is provided by node.module, so search-related
+  // view modes for nodes are defined here and not in search.module.
+  if (module_exists('search')) {
+    $return['node']['view modes'] += array(
+      'search_index' => array(
+        'label' => t('Search index'),
+        'custom settings' => FALSE,
+      ),
+      'search_result' => array(
+        'label' => t('Search result'),
+        'custom settings' => FALSE,
+      ),
+    );
+  }
+
+  // Bundles must provide a human readable name so we can create help and error
+  // messages, and the path to attach Field admin pages to.
+  foreach (node_type_get_names() as $type => $name) {
+    $return['node']['bundles'][$type] = array(
+      'label' => $name,
+      'admin' => array(
+        'path' => 'admin/structure/types/manage/%node_type',
+        'real path' => 'admin/structure/types/manage/' . str_replace('_', '-', $type),
+        'bundle argument' => 4,
+        'access arguments' => array('administer content types'),
+      ),
+    );
+  }
+
+  return $return;
+}
+
+/**
+ * Alter the entity info.
+ *
+ * Modules may implement this hook to alter the information that defines an
+ * entity. All properties that are available in hook_entity_info() can be
+ * altered here.
+ *
+ * @param $entity_info
+ *   The entity info array, keyed by entity name.
+ *
+ * @see hook_entity_info()
+ */
+function hook_entity_info_alter(&$entity_info) {
+  // Set the controller class for nodes to an alternate implementation of the
+  // DrupalEntityController interface.
+  $entity_info['node']['controller class'] = 'MyCustomNodeController';
+}
+
+/**
+ * Act on entities when loaded.
+ *
+ * This is a generic load hook called for all entity types loaded via the
+ * entity API.
+ *
+ * @param $entities
+ *   The entities keyed by entity ID.
+ * @param $type
+ *   The type of entities being loaded (i.e. node, user, comment).
+ */
+function hook_entity_load($entities, $type) {
+  foreach ($entities as $entity) {
+    $entity->foo = mymodule_add_something($entity, $type);
+  }
+}
+
+/**
+ * Act on an entity before it is about to be created or updated.
+ *
+ * @param $entity
+ *   The entity object.
+ * @param $type
+ *   The type of entity being saved (i.e. node, user, comment).
+ */
+function hook_entity_presave($entity, $type) {
+  $entity->changed = REQUEST_TIME;
+}
+
+/**
+ * Act on entities when inserted.
+ *
+ * @param $entity
+ *   The entity object.
+ * @param $type
+ *   The type of entity being inserted (i.e. node, user, comment).
+ */
+function hook_entity_insert($entity, $type) {
+  // Insert the new entity into a fictional table of all entities.
+  $info = entity_get_info($type);
+  list($id) = entity_extract_ids($type, $entity);
+  db_insert('example_entity')
+    ->fields(array(
+      'type' => $type,
+      'id' => $id,
+      'created' => REQUEST_TIME,
+      'updated' => REQUEST_TIME,
+    ))
+    ->execute();
+}
+
+/**
+ * Act on entities when updated.
+ *
+ * @param $entity
+ *   The entity object.
+ * @param $type
+ *   The type of entity being updated (i.e. node, user, comment).
+ */
+function hook_entity_update($entity, $type) {
+  // Update the entity's entry in a fictional table of all entities.
+  $info = entity_get_info($type);
+  list($id) = entity_extract_ids($type, $entity);
+  db_update('example_entity')
+    ->fields(array(
+      'updated' => REQUEST_TIME,
+    ))
+    ->condition('type', $type)
+    ->condition('id', $id)
+    ->execute();
+}
+
+/**
+ * Act on entities when deleted.
+ *
+ * @param $entity
+ *   The entity object.
+ * @param $type
+ *   The type of entity being deleted (i.e. node, user, comment).
+ */
+function hook_entity_delete($entity, $type) {
+  // Delete the entity's entry from a fictional table of all entities.
+  $info = entity_get_info($type);
+  list($id) = entity_extract_ids($type, $entity);
+  db_delete('example_entity')
+    ->condition('type', $type)
+    ->condition('id', $id)
+    ->execute();
+}
+
+/**
+ * Alter or execute an EntityFieldQuery.
+ *
+ * @param EntityFieldQuery $query
+ *   An EntityFieldQuery. One of the most important properties to be changed is
+ *   EntityFieldQuery::executeCallback. If this is set to an existing function,
+ *   this function will get the query as its single argument and its result
+ *   will be the returned as the result of EntityFieldQuery::execute(). This can
+ *   be used to change the behavior of EntityFieldQuery entirely. For example,
+ *   the default implementation can only deal with one field storage engine, but
+ *   it is possible to write a module that can query across field storage
+ *   engines. Also, the default implementation presumes entities are stored in
+ *   SQL, but the execute callback could instead query any other entity storage,
+ *   local or remote.
+ *
+ *   Note the $query->altered attribute which is TRUE in case the query has
+ *   already been altered once. This happens with cloned queries.
+ *   If there is a pager, then such a cloned query will be executed to count
+ *   all elements. This query can be detected by checking for
+ *   ($query->pager && $query->count), allowing the driver to return 0 from
+ *   the count query and disable the pager.
+ */
+function hook_entity_query_alter($query) {
+  $query->executeCallback = 'my_module_query_callback';
+}
+
+/**
+ * Act on entities being assembled before rendering.
+ *
+ * @param $entity
+ *   The entity object.
+ * @param $type
+ *   The type of entity being rendered (i.e. node, user, comment).
+ * @param $view_mode
+ *   The view mode the entity is rendered in.
+ * @param $langcode
+ *   The language code used for rendering.
+ *
+ * The module may add elements to $entity->content prior to rendering. The
+ * structure of $entity->content is a renderable array as expected by
+ * drupal_render().
+ *
+ * @see hook_entity_view_alter()
+ * @see hook_comment_view()
+ * @see hook_node_view()
+ * @see hook_user_view()
+ */
+function hook_entity_view($entity, $type, $view_mode, $langcode) {
+  $entity->content['my_additional_field'] = array(
+    '#markup' => $additional_field,
+    '#weight' => 10,
+    '#theme' => 'mymodule_my_additional_field',
+  );
+}
+
+/**
+ * Alter the results of ENTITY_view().
+ *
+ * This hook is called after the content has been assembled in a structured
+ * array and may be used for doing processing which requires that the complete
+ * entity content structure has been built.
+ *
+ * If a module wishes to act on the rendered HTML of the entity rather than the
+ * structured content array, it may use this hook to add a #post_render
+ * callback. Alternatively, it could also implement hook_preprocess_ENTITY().
+ * See drupal_render() and theme() for details.
+ *
+ * @param $build
+ *   A renderable array representing the entity content.
+ * @param $type
+ *   The type of entity being rendered (i.e. node, user, comment).
+ *
+ * @see hook_entity_view()
+ * @see hook_comment_view_alter()
+ * @see hook_node_view_alter()
+ * @see hook_taxonomy_term_view_alter()
+ * @see hook_user_view_alter()
+ */
+function hook_entity_view_alter(&$build, $type) {
+  if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
+    // Change its weight.
+    $build['an_additional_field']['#weight'] = -10;
+
+    // Add a #post_render callback to act on the rendered HTML of the entity.
+    $build['#post_render'][] = 'my_module_node_post_render';
+  }
+}
+
+/**
+ * Act on entities as they are being prepared for view.
+ *
+ * Allows you to operate on multiple entities as they are being prepared for
+ * view. Only use this if attaching the data during the entity_load() phase
+ * is not appropriate, for example when attaching other 'entity' style objects.
+ *
+ * @param $entities
+ *   The entities keyed by entity ID.
+ * @param $type
+ *   The type of entities being loaded (i.e. node, user, comment).
+ */
+function hook_entity_prepare_view($entities, $type) {
+  // Load a specific node into the user object for later theming.
+  if ($type == 'user') {
+    $nodes = mymodule_get_user_nodes(array_keys($entities));
+    foreach ($entities as $uid => $entity) {
+      $entity->user_node = $nodes[$uid];
+    }
+  }
+}
diff --git a/modules/entity/entity.class.inc b/modules/entity/entity.class.inc
new file mode 100644
index 0000000..ce34bb3
--- /dev/null
+++ b/modules/entity/entity.class.inc
@@ -0,0 +1,342 @@
+<?php
+
+/**
+ * @file
+ * Provides an interface and a base class for entities.
+ */
+
+/**
+ * Interface for all entity objects.
+ */
+interface EntityInterface {
+
+  /**
+   * Creates a new entity object.
+   *
+   * @param $values
+   *   An array of values to set, keyed by property name. If the entity type has
+   *   bundles the bundle key has to be specified.
+   * @param $entity_type
+   *   The type of the entity to create.
+   */
+  public function __construct(array $values, $entity_type);
+
+  /**
+   * Returns the entity identifier, i.e. the entities machine name or numeric id.
+   *
+   * @return
+   *   The identifier of the entity. In case the entity has no identifier yet,
+   *   it returns NULL.
+   */
+  public function id();
+
+  /**
+   * Returns whether the entity is new; i.e. whether it has been already saved.
+   */
+  public function isNew();
+
+  /**
+   * Returns the type of the entity.
+   *
+   * @return
+   *   The type of the entity.
+   */
+  public function entityType();
+
+  /**
+   * Returns the bundle of the entity.
+   *
+   * @return
+   *   The bundle of the entity. Defaults to the entity type if the entity type
+   *   does not make use of different bundles.
+   */
+  public function bundle();
+
+  /**
+   * Returns the UUID of the entity.
+   *
+   * @return
+   *   The UUID of the entity, or NULL if the entity type does not provide
+   *   UUIDs.
+   */
+  public function uuid();
+
+  /**
+   * Returns the UUID of the entity's revision.
+   *
+   * @return
+   *   The UUID of the entity's revision, or NULL if the entity type does not
+   *   make use of revisions.
+   */
+  public function revisionUuid();
+
+  /**
+   * Returns the label of the entity.
+   *
+   * @return
+   *   The label of the entity, or NULL if there is no label defined.
+   */
+  public function label();
+
+  /**
+   * Returns the uri elements of the entity.
+   *
+   * @return
+   *   An array containing the 'path' and 'options' keys used to build the uri
+   *   of the entity, and matching the signature of url(). NULL if the entity
+   *   has no uri of its own.
+   */
+  public function uri();
+
+  /**
+   * Returns the value of an entity property.
+   *
+   * @param $property_name
+   *   The name of the property to return; e.g., 'title'.
+   * @param $language
+   *   (optional) In case the property is translatable, the language object of
+   *   the language that should be used for getting the property. If set to
+   *   NULL, the default language is being used.
+   * @todo
+   *   Which default language should be used.
+   *
+   * @return
+   *   The property value, or NULL in case it is not defined.
+   */
+  public function get($property_name, $language = NULL);
+
+  /**
+   * Sets the value of an entity property.
+   *
+   * @param $property_name
+   *   The name of the property to set; e.g., 'title'.
+   * @param $value
+   *   The value to set, or NULL to unset the property.
+   * @param $language
+   *   (optional) In case the property is translatable, the language object of
+   *   the language that should be used for getting the property. If set to
+   *   NULL, the default language is being used.
+   * @todo
+   *   Which default language should be used.
+   */
+  public function set($property_name, $value, $language = NULL);
+
+  /**
+   * Saves an entity permanently.
+   *
+   * @throws EntityStorageException
+   *   In case of failures an exception is thrown.
+   *
+   * @return
+   *   Either SAVED_NEW or SAVED_UPDATED is returned, depending on the operation
+   *   performed.
+   */
+  public function save();
+
+  /**
+   * Deletes an entity permanently.
+   *
+   * @throws EntityStorageException
+   *   In case of failures an exception is thrown.
+   */
+  public function delete();
+
+  /**
+   * Creates a duplicate of the entity.
+   *
+   * @return EntityInterface
+   *   A clone of the current entity with all identifiers unset, so saving
+   *   it inserts a new entity into the storage system.
+   */
+  public function createDuplicate();
+
+  /**
+   * Returns the info of the type of the entity.
+   *
+   * @see entity_get_info()
+   */
+  public function entityInfo();
+
+  /**
+   * @todo Add invokeHook*() methods.
+   */
+}
+
+/**
+ * Exception thrown when storage operations fail.
+ */
+class EntityStorageException extends Exception { }
+
+/**
+ * A class implementing the EntityInterface.
+ *
+ * This class can be used as-is by simple entity types. Entity types requiring
+ * special handling can extend the class.
+ */
+class Entity implements EntityInterface {
+
+  protected $entityType;
+
+
+  public function __construct(array $values = array(), $entity_type) {
+    $this->entityType = $entity_type;
+    $this->setUp();
+    // Set initial values.
+    foreach ($values as $key => $value) {
+      $this->$key = $value;
+    }
+    // Apply defaults.
+    if (!empty($this->bundleKey) && !isset($this->{$this->bundleKey})) {
+      $this->{$this->bundleKey} = $this->entityType;
+    }
+  }
+
+  /**
+   * Set up the object instance on construction or unserializiation.
+   */
+  protected function setUp() {
+    $this->entityInfo = entity_get_info($this->entityType);
+    $this->idKey = $this->entityInfo['entity keys']['id'];
+    $this->bundleKey = isset($this->entityInfo['entity keys']['bundle']) ? $this->entityInfo['entity keys']['bundle'] : NULL;
+  }
+
+  public function id() {
+    return isset($this->{$this->idKey}) ? $this->{$this->idKey} : NULL;
+  }
+
+  public function isNew() {
+    return empty($this->{$this->idKey});
+  }
+
+  public function entityType() {
+    return $this->entityType;
+  }
+
+  public function bundle() {
+    return isset($this->bundleKey) ? $this->{$this->bundleKey} : $this->entityType;
+  }
+
+  public function uuid() {
+    return NULL;
+  }
+
+  public function revisionUuid() {
+    return NULL;
+  }
+
+  public function label() {
+    return entity_label($this->entityType, $this);
+  }
+
+  public function uri() {
+    return entity_uri($this->entityType, $this);
+  }
+
+  /**
+   * @todo
+   *   Implement default handling of language.
+   */
+  public function get($property_name, $language = NULL) {
+    $value = isset($this->$property_name) ? $this->$property_name : NULL;
+    if (isset($language)) {
+      return isset($value[$language]) ? $value[$language] : NULL;
+    }
+    return $value;
+  }
+
+  /**
+   * @todo
+   *   Implement default handling of language.
+   */
+  public function set($property_name, $value, $language = NULL) {
+    if (isset($language)) {
+      $this->$property_name[$language] = $value;
+    }
+    else {
+      $this->$property_name = $value;
+    }
+  }
+
+  public function save() {
+    return entity_get_controller($this->entityType)->save($this);
+  }
+
+  public function delete() {
+    if (!$this->isNew()) {
+      entity_get_controller($this->entityType)->delete(array($this->id()));
+    }
+  }
+
+  public function createDuplicate() {
+    $duplicate = clone $this;
+    $duplicate->{$this->idKey} = NULL;
+    return $duplicate;
+  }
+
+  public function entityInfo() {
+    return $this->entityInfo;
+  }
+
+  /**
+   * Magic method to only serialize what is necessary.
+   */
+  public function __sleep() {
+    $vars = get_object_vars($this);
+    unset($vars['entityInfo'], $vars['idKey'], $vars['bundleKey']);
+    // Also key the returned array with the variable names so the method may
+    // be easily overridden and customized.
+    return drupal_map_assoc(array_keys($vars));
+  }
+
+  /**
+   * Magic method to invoke setUp() on unserialization.
+   */
+  public function __wakeup() {
+    $this->setUp();
+  }
+
+  /**
+   * Invokes hook_ENTITY_TYPE_presave() on behalf the entity.
+   */
+  public function invokeHookPreSave() {
+    if ($this->entityInfo['fieldable']) {
+      field_attach_presave($this->entityType, $this);
+    }
+    module_invoke_all($this->entityType . '_presave', $this);
+    module_invoke_all('entity_presave', $this, $this->entityType);
+  }
+
+  /**
+   * Invokes hook_ENTITY_TYPE_insert() on behalf the entity.
+   */
+  public function invokeHookInsert() {
+    if ($this->entityInfo['fieldable']) {
+      field_attach_insert($this->entityType, $this);
+    }
+    module_invoke_all($this->entityType . '_insert', $this);
+    module_invoke_all('entity_insert', $this, $this->entityType);
+  }
+
+  /**
+   * Invokes hook_ENTITY_TYPE_update() on behalf the entity.
+   */
+  public function invokeHookUpdate() {
+    if ($this->entityInfo['fieldable']) {
+      field_attach_update($this->entityType, $this);
+    }
+    module_invoke_all($this->entityType . '_update', $this);
+    module_invoke_all('entity_update', $this, $this->entityType);
+  }
+
+  /**
+   * Invokes hook_ENTITY_TYPE_delete() on behalf the entity.
+   */
+  public function invokeHookDelete() {
+    if ($this->entityInfo['fieldable']) {
+      field_attach_delete($this->entityType, $this);
+    }
+    module_invoke_all($this->entityType . '_delete', $this);
+    module_invoke_all('entity_delete', $this, $this->entityType);
+  }
+}
+
diff --git a/modules/entity/entity.controller.inc b/modules/entity/entity.controller.inc
new file mode 100644
index 0000000..6cd13d3
--- /dev/null
+++ b/modules/entity/entity.controller.inc
@@ -0,0 +1,511 @@
+<?php
+
+/**
+ * @file
+ * Entity API controller classes and interface.
+ */
+
+/**
+ * Interface for entity controller classes.
+ *
+ * All entity controller classes specified via the 'controller class' key
+ * returned by hook_entity_info() or hook_entity_info_alter() have to implement
+ * this interface.
+ *
+ * Most simple, SQL-based entity controllers will do better by extending
+ * DrupalDefaultEntityController instead of implementing this interface
+ * directly.
+ */
+interface DrupalEntityControllerInterface {
+
+  /**
+   * Constructor.
+   *
+   * @param $entityType
+   *   The entity type for which the instance is created.
+   */
+  public function __construct($entityType);
+
+  /**
+   * Resets the internal, static entity cache.
+   *
+   * @param $ids
+   *   (optional) If specified, the cache is reset for the entities with the
+   *   given ids only.
+   */
+  public function resetCache(array $ids = NULL);
+
+  /**
+   * Loads one or more entities.
+   *
+   * @param $ids
+   *   An array of entity IDs, or FALSE to load all entities.
+   * @param $conditions
+   *   An array of conditions in the form 'field' => $value.
+   *
+   * @return
+   *   An array of entity objects indexed by their ids.
+   */
+  public function load($ids = array(), $conditions = array());
+}
+
+/**
+ * Default implementation of DrupalEntityControllerInterface.
+ *
+ * This class can be used as-is by most simple entity types. Entity types
+ * requiring special handling can extend the class.
+ */
+class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
+
+  /**
+   * Static cache of entities.
+   *
+   * @var array
+   */
+  protected $entityCache;
+
+  /**
+   * Entity type for this controller instance.
+   *
+   * @var string
+   */
+  protected $entityType;
+
+  /**
+   * Array of information about the entity.
+   *
+   * @var array
+   *
+   * @see entity_get_info()
+   */
+  protected $entityInfo;
+
+  /**
+   * Additional arguments to pass to hook_TYPE_load().
+   *
+   * Set before calling DrupalDefaultEntityController::attachLoad().
+   *
+   * @var array
+   */
+  protected $hookLoadArguments;
+
+  /**
+   * Name of the entity's ID field in the entity database table.
+   *
+   * @var string
+   */
+  protected $idKey;
+
+  /**
+   * Name of entity's revision database table field, if it supports revisions.
+   *
+   * Has the value FALSE if this entity does not use revisions.
+   *
+   * @var string
+   */
+  protected $revisionKey;
+
+  /**
+   * The table that stores revisions, if the entity supports revisions.
+   *
+   * @var string
+   */
+  protected $revisionTable;
+
+  /**
+   * Whether this entity type should use the static cache.
+   *
+   * Set by entity info.
+   *
+   * @var boolean
+   */
+  protected $cache;
+
+  /**
+   * Constructor: sets basic variables.
+   */
+  public function __construct($entityType) {
+    $this->entityType = $entityType;
+    $this->entityInfo = entity_get_info($entityType);
+    $this->entityCache = array();
+    $this->hookLoadArguments = array();
+    $this->idKey = $this->entityInfo['entity keys']['id'];
+
+    // Check if the entity type supports revisions.
+    if (!empty($this->entityInfo['entity keys']['revision'])) {
+      $this->revisionKey = $this->entityInfo['entity keys']['revision'];
+      $this->revisionTable = $this->entityInfo['revision table'];
+    }
+    else {
+      $this->revisionKey = FALSE;
+    }
+
+    // Check if the entity type supports static caching of loaded entities.
+    $this->cache = !empty($this->entityInfo['static cache']);
+  }
+
+  /**
+   * Implements DrupalEntityControllerInterface::resetCache().
+   */
+  public function resetCache(array $ids = NULL) {
+    if (isset($ids)) {
+      foreach ($ids as $id) {
+        unset($this->entityCache[$id]);
+      }
+    }
+    else {
+      $this->entityCache = array();
+    }
+  }
+
+  /**
+   * Implements DrupalEntityControllerInterface::load().
+   */
+  public function load($ids = array(), $conditions = array()) {
+    $entities = array();
+
+    // Revisions are not statically cached, and require a different query to
+    // other conditions, so separate the revision id into its own variable.
+    if ($this->revisionKey && isset($conditions[$this->revisionKey])) {
+      $revision_id = $conditions[$this->revisionKey];
+      unset($conditions[$this->revisionKey]);
+    }
+    else {
+      $revision_id = FALSE;
+    }
+
+    // Create a new variable which is either a prepared version of the $ids
+    // array for later comparison with the entity cache, or FALSE if no $ids
+    // were passed. The $ids array is reduced as items are loaded from cache,
+    // and we need to know if it's empty for this reason to avoid querying the
+    // database when all requested entities are loaded from cache.
+    $passed_ids = !empty($ids) ? array_flip($ids) : FALSE;
+    // Try to load entities from the static cache, if the entity type supports
+    // static caching.
+    if ($this->cache && !$revision_id) {
+      $entities += $this->cacheGet($ids, $conditions);
+      // If any entities were loaded, remove them from the ids still to load.
+      if ($passed_ids) {
+        $ids = array_keys(array_diff_key($passed_ids, $entities));
+      }
+    }
+
+    // Load any remaining entities from the database. This is the case if $ids
+    // is set to FALSE (so we load all entities), if there are any ids left to
+    // load, if loading a revision, or if $conditions was passed without $ids.
+    if ($ids === FALSE || $ids || $revision_id || ($conditions && !$passed_ids)) {
+      // Build and execute the query.
+      $query_result = $this->buildQuery($ids, $conditions, $revision_id)->execute();
+
+      if (!empty($this->entityInfo['entity class'])) {
+        $query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['entity class'], array(array(), $this->entityType));
+      }
+      $queried_entities = $query_result->fetchAllAssoc($this->idKey);
+    }
+
+    // Pass all entities loaded from the database through $this->attachLoad(),
+    // which attaches fields (if supported by the entity type) and calls the
+    // entity type specific load callback, for example hook_node_load().
+    if (!empty($queried_entities)) {
+      $this->attachLoad($queried_entities, $revision_id);
+      $entities += $queried_entities;
+    }
+
+    if ($this->cache) {
+      // Add entities to the cache if we are not loading a revision.
+      if (!empty($queried_entities) && !$revision_id) {
+        $this->cacheSet($queried_entities);
+      }
+    }
+
+    // Ensure that the returned array is ordered the same as the original
+    // $ids array if this was passed in and remove any invalid ids.
+    if ($passed_ids) {
+      // Remove any invalid ids from the array.
+      $passed_ids = array_intersect_key($passed_ids, $entities);
+      foreach ($entities as $entity) {
+        $passed_ids[$entity->{$this->idKey}] = $entity;
+      }
+      $entities = $passed_ids;
+    }
+
+    return $entities;
+  }
+
+  /**
+   * 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 CommentController::buildQuery() or TaxonomyTermController::buildQuery()
+   * for examples.
+   *
+   * @param $ids
+   *   An array of entity IDs, or FALSE to load all entities.
+   * @param $conditions
+   *   An array of conditions in the form 'field' => $value.
+   * @param $revision_id
+   *   The ID of the revision to load, or FALSE if this query is asking for the
+   *   most current revision(s).
+   *
+   * @return SelectQuery
+   *   A SelectQuery object for loading the entity.
+   */
+  protected function buildQuery($ids, $conditions = array(), $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}");
+    }
+
+    // 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);
+    }
+
+    $query->fields('base', $entity_fields);
+
+    if ($ids) {
+      $query->condition("base.{$this->idKey}", $ids, 'IN');
+    }
+    if ($conditions) {
+      foreach ($conditions as $field => $value) {
+        $query->condition('base.' . $field, $value);
+      }
+    }
+    return $query;
+  }
+
+  /**
+   * Attaches data to entities upon loading.
+   *
+   * This will attach fields, if the entity is fieldable. It calls
+   * hook_entity_load() for modules which need to add data to all entities.
+   * It also calls hook_TYPE_load() on the loaded entities. For example
+   * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
+   * expects special parameters apart from the queried entities, you can set
+   * $this->hookLoadArguments prior to calling the method.
+   * See NodeController::attachLoad() for an example.
+   *
+   * @param $queried_entities
+   *   Associative array of query results, keyed on the entity ID.
+   * @param $revision_id
+   *   ID of the revision that was loaded, or FALSE if teh most current revision
+   *   was loaded.
+   */
+  protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
+    // Attach fields.
+    if ($this->entityInfo['fieldable']) {
+      if ($revision_id) {
+        field_attach_load_revision($this->entityType, $queried_entities);
+      }
+      else {
+        field_attach_load($this->entityType, $queried_entities);
+      }
+    }
+
+    // Call hook_entity_load().
+    foreach (module_implements('entity_load') as $module) {
+      $function = $module . '_entity_load';
+      $function($queried_entities, $this->entityType);
+    }
+    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
+    // always the queried entities, followed by additional arguments set in
+    // $this->hookLoadArguments.
+    $args = array_merge(array($queried_entities), $this->hookLoadArguments);
+    foreach (module_implements($this->entityInfo['load hook']) as $module) {
+      call_user_func_array($module . '_' . $this->entityInfo['load hook'], $args);
+    }
+  }
+
+  /**
+   * Gets entities from the static cache.
+   *
+   * @param $ids
+   *   If not empty, return entities that match these IDs.
+   * @param $conditions
+   *   If set, return entities that match all of these conditions.
+   *
+   * @return
+   *   Array of entities from the entity cache.
+   */
+  protected function cacheGet($ids, $conditions = array()) {
+    $entities = array();
+    // Load any available entities from the internal cache.
+    if (!empty($this->entityCache)) {
+      if ($ids) {
+        $entities += array_intersect_key($this->entityCache, array_flip($ids));
+      }
+      // If loading entities only by conditions, fetch all available entities
+      // from the cache. Entities which don't match are removed later.
+      elseif ($conditions) {
+        $entities = $this->entityCache;
+      }
+    }
+
+    // Exclude any entities loaded from cache if they don't match $conditions.
+    // This ensures the same behavior whether loading from memory or database.
+    if ($conditions) {
+      foreach ($entities as $entity) {
+        $entity_values = (array) $entity;
+        if (array_diff_assoc($conditions, $entity_values)) {
+          unset($entities[$entity->{$this->idKey}]);
+        }
+      }
+    }
+    return $entities;
+  }
+
+  /**
+   * Stores entities in the static entity cache.
+   *
+   * @param $entities
+   *   Entities to store in the cache.
+   */
+  protected function cacheSet($entities) {
+    $this->entityCache += $entities;
+  }
+}
+
+/**
+ * Interface for entity storage controllers.
+ */
+interface EntityAPIStorageControllerInterface extends DrupalEntityControllerInterface {
+
+  /**
+   * Deletes permanently saved entities.
+   *
+   * In case of failures, an exception is thrown.
+   *
+   * @param $ids
+   *   An array of entity IDs.
+   */
+  public function delete($ids);
+
+  /**
+   * Saves the entity permanently.
+   *
+   * In case of failures, an exception is thrown.
+   *
+   * @param EntityInterface $entity
+   *   The entity to save.
+   *
+   * @return
+   *   SAVED_NEW or SAVED_UPDATED is returned depending on the operation
+   *   performed.
+   */
+  public function save(EntityInterface $entity);
+
+}
+
+/**
+ * A controller implementing EntityAPIStorageControllerInterface for the database.
+ */
+class EntityDefaultStorageController extends DrupalDefaultEntityController implements EntityAPIStorageControllerInterface {
+
+  /**
+   * Implements EntityAPIStorageControllerInterface.
+   *
+   * @param $transaction
+   *   Optionally a DatabaseTransaction object to use. Allows overrides to pass
+   *   in their transaction object.
+   */
+  public function delete($ids, DatabaseTransaction $transaction = NULL) {
+    $entities = $ids ? $this->load($ids) : FALSE;
+    if (!$entities) {
+      // Do nothing, in case invalid or no ids have been passed.
+      return;
+    }
+    $transaction = isset($transaction) ? $transaction : db_transaction();
+
+    try {
+      $ids = array_keys($entities);
+
+      db_delete($this->entityInfo['base table'])
+        ->condition($this->idKey, $ids, 'IN')
+        ->execute();
+      // Reset the cache as soon as the changes have been applied.
+      $this->resetCache($ids);
+
+      foreach ($entities as $id => $entity) {
+        $entity->invokeHookDelete();
+      }
+      // Ignore slave server temporarily.
+      db_ignore_slave();
+    }
+    catch (Exception $e) {
+      if (isset($transaction)) {
+        $transaction->rollback();
+      }
+      watchdog_exception($this->entityType, $e);
+      throw $e;
+    }
+  }
+
+  /**
+   * Implements EntityAPIStorageControllerInterface.
+   *
+   * @param $transaction
+   *   Optionally a DatabaseTransaction object to use. Allows overrides to pass
+   *   in their transaction object.
+   */
+  public function save(EntityInterface $entity, DatabaseTransaction $transaction = NULL) {
+    $transaction = isset($transaction) ? $transaction : db_transaction();
+    try {
+      // Load the stored entity, if any.
+      if (!empty($entity->{$this->idKey}) && !isset($entity->original)) {
+        // In order to properly work in case of name changes, load the original
+        // entity using the id key if it is available.
+        $entity->original = entity_load_unchanged($this->entityType, $entity->{$this->idKey});
+      }
+
+      $entity->invokeHookPreSave();
+
+      if (!empty($entity->{$this->idKey}) && empty($entity->is_new)) {
+        $return = drupal_write_record($this->entityInfo['base table'], $entity, $this->idKey);
+        $this->resetCache(array($entity->{$this->idKey}));
+        $entity->invokeHookUpdate();
+      }
+      else {
+        $return = drupal_write_record($this->entityInfo['base table'], $entity);
+        $entity->invokeHookInsert();
+      }
+      // Ignore slave server temporarily.
+      db_ignore_slave();
+      unset($entity->is_new);
+      unset($entity->original);
+
+      return $return;
+    }
+    catch (Exception $e) {
+      $transaction->rollback();
+      watchdog_exception($this->entityType, $e);
+      throw $e;
+    }
+  }
+}
+
diff --git a/modules/entity/entity.info b/modules/entity/entity.info
new file mode 100644
index 0000000..b15fadd
--- /dev/null
+++ b/modules/entity/entity.info
@@ -0,0 +1,12 @@
+name = Entity
+description = API for managing entities like nodes and users.
+package = Core
+version = VERSION
+core = 8.x
+required = TRUE
+files[] = entity.class.inc
+files[] = entity.query.inc
+files[] = entity.controller.inc
+files[] = tests/entity_crud_hook_test.test
+files[] = tests/entity_query.test
+files[] = tests/entity.test
diff --git a/modules/entity/entity.module b/modules/entity/entity.module
new file mode 100644
index 0000000..5c2aeef
--- /dev/null
+++ b/modules/entity/entity.module
@@ -0,0 +1,489 @@
+<?php
+
+/**
+ * @file
+ * Entity API for handling entities like nodes or users.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function entity_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#entity':
+      $output = '';
+      $output .= '<h3>' . t('About') . '</h3>';
+      $output .= '<p>' . t('The Entity module provides an API for managing entities like nodes and users, i.e. an API for loading and identifying entities. For more information, see the online handbook entry for <a href="!url">Entity module</a>', array('!url' => 'http://drupal.org/handbook/modules/entity')) . '</p>';
+      return $output;
+  }
+}
+
+/**
+ * Implements hook_modules_preenable().
+ */
+function entity_modules_preenable() {
+  entity_info_cache_clear();
+}
+
+/**
+ * Implements hook_modules_disabled().
+ */
+function entity_modules_disabled() {
+  entity_info_cache_clear();
+}
+
+/**
+ * Gets the entity info array of an entity type.
+ *
+ * @see hook_entity_info()
+ * @see hook_entity_info_alter()
+ *
+ * @param $entity_type
+ *   The entity type, e.g. node, for which the info shall be returned, or NULL
+ *   to return an array with info about all types.
+ */
+function entity_get_info($entity_type = NULL) {
+  global $language;
+
+  // Use the advanced drupal_static() pattern, since this is called very often.
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast['entity_info'] = &drupal_static(__FUNCTION__);
+  }
+  $entity_info = &$drupal_static_fast['entity_info'];
+
+  // hook_entity_info() includes translated strings, so each language is cached
+  // separately.
+  $langcode = $language->language;
+
+  if (empty($entity_info)) {
+    if ($cache = cache()->get("entity_info:$langcode")) {
+      $entity_info = $cache->data;
+    }
+    else {
+      $entity_info = module_invoke_all('entity_info');
+      // Merge in default values.
+      foreach ($entity_info as $name => $data) {
+        $entity_info[$name] += array(
+          'fieldable' => FALSE,
+          'controller class' => 'DrupalDefaultEntityController',
+          'static cache' => TRUE,
+          'field cache' => TRUE,
+          'load hook' => $name . '_load',
+          'bundles' => array(),
+          'view modes' => array(),
+          'entity keys' => array(),
+          'translation' => array(),
+        );
+        $entity_info[$name]['entity keys'] += array(
+          'revision' => '',
+          'bundle' => '',
+        );
+        foreach ($entity_info[$name]['view modes'] as $view_mode => $view_mode_info) {
+          $entity_info[$name]['view modes'][$view_mode] += array(
+            'custom settings' => FALSE,
+          );
+        }
+        // If no bundle key is provided, assume a single bundle, named after
+        // the entity type.
+        if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) {
+          $entity_info[$name]['bundles'] = array($name => array('label' => $entity_info[$name]['label']));
+        }
+        // Prepare entity schema fields SQL info for
+        // DrupalEntityControllerInterface::buildQuery().
+        if (isset($entity_info[$name]['base table'])) {
+          $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']);
+          if (isset($entity_info[$name]['revision table'])) {
+            $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']);
+          }
+        }
+      }
+      // Let other modules alter the entity info.
+      drupal_alter('entity_info', $entity_info);
+      cache()->set("entity_info:$langcode", $entity_info);
+    }
+  }
+
+  if (empty($entity_type)) {
+    return $entity_info;
+  }
+  elseif (isset($entity_info[$entity_type])) {
+    return $entity_info[$entity_type];
+  }
+}
+
+/**
+ * Resets the cached information about entity types.
+ */
+function entity_info_cache_clear() {
+  drupal_static_reset('entity_get_info');
+  // Clear all languages.
+  cache()->deletePrefix('entity_info:');
+}
+
+/**
+ * Helper function to extract id, vid, and bundle name from an entity.
+ *
+ * @param $entity_type
+ *   The entity type; e.g. 'node' or 'user'.
+ * @param $entity
+ *   The entity from which to extract values.
+ * @return
+ *   A numerically indexed array (not a hash table) containing these
+ *   elements:
+ *   0: primary id of the entity
+ *   1: revision id of the entity, or NULL if $entity_type is not versioned
+ *   2: bundle name of the entity
+ */
+function entity_extract_ids($entity_type, $entity) {
+  $info = entity_get_info($entity_type);
+
+  // Objects being created might not have id/vid yet.
+  $id = isset($entity->{$info['entity keys']['id']}) ? $entity->{$info['entity keys']['id']} : NULL;
+  $vid = ($info['entity keys']['revision'] && isset($entity->{$info['entity keys']['revision']})) ? $entity->{$info['entity keys']['revision']} : NULL;
+
+  if (!empty($info['entity keys']['bundle'])) {
+    // Explicitly fail for malformed entities missing the bundle property.
+    if (!isset($entity->{$info['entity keys']['bundle']}) || $entity->{$info['entity keys']['bundle']} === '') {
+      throw new EntityMalformedException(t('Missing bundle property on entity of type @entity_type.', array('@entity_type' => $entity_type)));
+    }
+    $bundle = $entity->{$info['entity keys']['bundle']};
+  }
+  else {
+    // The entity type provides no bundle key: assume a single bundle, named
+    // after the entity type.
+    $bundle = $entity_type;
+  }
+
+  return array($id, $vid, $bundle);
+}
+
+/**
+ * Helper function to assemble an object structure with initial ids.
+ *
+ * This function can be seen as reciprocal to entity_extract_ids().
+ *
+ * @param $entity_type
+ *   The entity type; e.g. 'node' or 'user'.
+ * @param $ids
+ *   A numerically indexed array, as returned by entity_extract_ids(),
+ *   containing these elements:
+ *   0: primary id of the entity
+ *   1: revision id of the entity, or NULL if $entity_type is not versioned
+ *   2: bundle name of the entity, or NULL if $entity_type has no bundles
+ *
+ * @return
+ *   An entity object, initialized with the ids provided.
+ */
+function entity_create_stub_entity($entity_type, $ids) {
+  $values = array();
+  $info = entity_get_info($entity_type);
+  $values[$info['entity keys']['id']] = $ids[0];
+  if (!empty($info['entity keys']['revision']) && isset($ids[1])) {
+    $values[$info['entity keys']['revision']] = $ids[1];
+  }
+  if (!empty($info['entity keys']['bundle']) && isset($ids[2])) {
+    $values[$info['entity keys']['bundle']] = $ids[2];
+  }
+  // @todo: Once all entities are converted, just rely on entity_create().
+  return isset($info['entity class']) ? entity_create($entity_type, $values) : (object) $values;
+}
+
+/**
+ * Loads entities from the database.
+ *
+ * This function should be used whenever you need to load more than one entity
+ * from the database. The entities are loaded into memory and will not require
+ * database access if loaded again during the same page request.
+ *
+ * The actual loading is done through a class that has to implement the
+ * DrupalEntityControllerInterface interface. By default,
+ * DrupalDefaultEntityController is used. Entity types can specify that a
+ * different class should be used by setting the 'controller class' key in
+ * hook_entity_info(). These classes can either implement the
+ * DrupalEntityControllerInterface interface, or, most commonly, extend the
+ * DrupalDefaultEntityController class. See node_entity_info() and the
+ * NodeController in node.module as an example.
+ *
+ * @see hook_entity_info()
+ * @see DrupalEntityControllerInterface
+ * @see DrupalDefaultEntityController
+ * @see EntityFieldQuery
+ *
+ * @param $entity_type
+ *   The entity type to load, e.g. node or user.
+ * @param $ids
+ *   An array of entity IDs, or FALSE to load all entities.
+ * @param $conditions
+ *   (deprecated) An associative array of conditions on the base table, where
+ *   the keys are the database fields and the values are the values those
+ *   fields must have. Instead, it is preferable to use EntityFieldQuery to
+ *   retrieve a list of entity IDs loadable by this function.
+ * @param $reset
+ *   Whether to reset the internal cache for the requested entity type.
+ *
+ * @return
+ *   An array of entity objects indexed by their ids.
+ *
+ * @todo Remove $conditions in Drupal 8.
+ */
+function entity_load($entity_type, $ids = FALSE, $conditions = array(), $reset = FALSE) {
+  if ($reset) {
+    entity_get_controller($entity_type)->resetCache();
+  }
+  return entity_get_controller($entity_type)->load($ids, $conditions);
+}
+
+/**
+ * Loads the unchanged, i.e. not modified, entity from the database.
+ *
+ * Unlike entity_load() this function ensures the entity is directly loaded from
+ * the database, thus bypassing any static cache. In particular, this function
+ * is useful to determine changes by comparing the entity being saved to the
+ * stored entity.
+ *
+ * @param $entity_type
+ *   The entity type to load, e.g. node or user.
+ * @param $id
+ *   The id of the entity to load.
+ *
+ * @return
+ *   The unchanged entity, or FALSE if the entity cannot be loaded.
+ */
+function entity_load_unchanged($entity_type, $id) {
+  entity_get_controller($entity_type)->resetCache(array($id));
+  $result = entity_get_controller($entity_type)->load(array($id));
+  return reset($result);
+}
+
+/**
+ * Permanently delete multiple entities.
+ *
+ * @param $entity_type
+ *   The type of the entity.
+ * @param $ids
+ *   An array of entity ids of the entities to delete.
+ */
+function entity_delete_multiple($entity_type, $ids) {
+  entity_get_controller($entity_type)->delete($ids);
+}
+
+/**
+ * Creates a new entity object.
+ *
+ * @param $entity_type
+ *   The type of the entity.
+ * @param $values
+ *   An array of values to set, keyed by property name. If the entity type has
+ *   bundles the bundle key has to be specified.
+ *
+ * @return Entity
+ *   A new entity object.
+ */
+function entity_create($entity_type, array $values) {
+  $info = entity_get_info($entity_type) + array('entity class' => 'Entity');
+  if ($class = $info['entity class']) {
+    return new $class($values, $entity_type);
+  }
+  throw new Exception("Entity class is missing");
+}
+
+/**
+ * Gets the entity controller class for an entity type.
+ */
+function entity_get_controller($entity_type) {
+  $controllers = &drupal_static(__FUNCTION__, array());
+  if (!isset($controllers[$entity_type])) {
+    $type_info = entity_get_info($entity_type);
+    $class = $type_info['controller class'];
+    $controllers[$entity_type] = new $class($entity_type);
+  }
+  return $controllers[$entity_type];
+}
+
+/**
+ * Invokes hook_entity_prepare_view().
+ *
+ * If adding a new entity similar to nodes, comments or users, you should
+ * invoke this function during the ENTITY_build_content() or
+ * ENTITY_view_multiple() phases of rendering to allow other modules to alter
+ * the objects during this phase. This is needed for situations where
+ * information needs to be loaded outside of ENTITY_load() - particularly
+ * when loading entities into one another - i.e. a user object into a node, due
+ * to the potential for unwanted side-effects such as caching and infinite
+ * recursion. By convention, entity_prepare_view() is called after
+ * field_attach_prepare_view() to allow entity level hooks to act on content
+ * loaded by field API.
+ *
+ * @see hook_entity_prepare_view()
+ *
+ * @param $entity_type
+ *   The type of entity, i.e. 'node', 'user'.
+ * @param $entities
+ *   The entity objects which are being prepared for view, keyed by object ID.
+ */
+function entity_prepare_view($entity_type, $entities) {
+  // To ensure hooks are only run once per entity, check for an
+  // entity_view_prepared flag and only process items without it.
+  // @todo: resolve this more generally for both entity and field level hooks.
+  $prepare = array();
+  foreach ($entities as $id => $entity) {
+    if (empty($entity->entity_view_prepared)) {
+      // Add this entity to the items to be prepared.
+      $prepare[$id] = $entity;
+
+      // Mark this item as prepared.
+      $entity->entity_view_prepared = TRUE;
+    }
+  }
+
+  if (!empty($prepare)) {
+    module_invoke_all('entity_prepare_view', $prepare, $entity_type);
+  }
+}
+
+/**
+ * Returns the uri elements of an entity.
+ *
+ * @param $entity_type
+ *   The entity type; e.g. 'node' or 'user'.
+ * @param $entity
+ *   The entity for which to generate a path.
+ *
+ * @return
+ *   An array containing the 'path' and 'options' keys used to build the uri of
+ *   the entity, and matching the signature of url(). NULL if the entity has no
+ *   uri of its own.
+ */
+function entity_uri($entity_type, $entity) {
+  // This check enables the URI of an entity to be easily overridden from what
+  // the callback for the entity type or bundle would return, and it helps
+  // minimize performance overhead when entity_uri() is called multiple times
+  // for the same entity.
+  if (!isset($entity->uri)) {
+    $info = entity_get_info($entity_type);
+    list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+    // A bundle-specific callback takes precedence over the generic one for the
+    // entity type.
+    if (isset($info['bundles'][$bundle]['uri callback'])) {
+      $uri_callback = $info['bundles'][$bundle]['uri callback'];
+    }
+    elseif (isset($info['uri callback'])) {
+      $uri_callback = $info['uri callback'];
+    }
+    else {
+      $uri_callback = NULL;
+    }
+
+    // Invoke the callback to get the URI. If there is no callback, set the
+    // entity's 'uri' property to FALSE to indicate that it is known to not have
+    // a URI.
+    if (isset($uri_callback) && function_exists($uri_callback)) {
+      $entity->uri = $uri_callback($entity);
+      if (!isset($entity->uri['options'])) {
+        $entity->uri['options'] = array();
+      }
+      // Pass the entity data to url() so that alter functions do not need to
+      // lookup this entity again.
+      $entity->uri['options']['entity_type'] = $entity_type;
+      $entity->uri['options']['entity'] = $entity;
+    }
+    else {
+      $entity->uri = FALSE;
+    }
+  }
+  return $entity->uri ? $entity->uri : NULL;
+}
+
+/**
+ * Returns the label of an entity.
+ *
+ * See the 'label callback' component of the hook_entity_info() return value
+ * for more information.
+ *
+ * @param $entity_type
+ *   The entity type; e.g., 'node' or 'user'.
+ * @param $entity
+ *   The entity for which to generate the label.
+ *
+ * @return
+ *   The entity label, or FALSE if not found.
+ */
+function entity_label($entity_type, $entity) {
+  $label = FALSE;
+  $info = entity_get_info($entity_type);
+  if (isset($info['label callback']) && function_exists($info['label callback'])) {
+    $label = $info['label callback']($entity_type, $entity);
+  }
+  elseif (!empty($info['entity keys']['label']) && isset($entity->{$info['entity keys']['label']})) {
+    $label = $entity->{$info['entity keys']['label']};
+  }
+
+  return $label;
+}
+
+/**
+ * Helper function for attaching field API validation to entity forms.
+ */
+function entity_form_field_validate($entity_type, $form, &$form_state) {
+  // All field attach API functions act on an entity object, but during form
+  // validation, we don't have one. $form_state contains the entity as it was
+  // prior to processing the current form submission, and we must not update it
+  // until we have fully validated the submitted input. Therefore, for
+  // validation, act on a pseudo entity created out of the form values.
+  $pseudo_entity = (object) $form_state['values'];
+  field_attach_form_validate($entity_type, $pseudo_entity, $form, $form_state);
+}
+
+/**
+ * Helper function for copying submitted values to entity properties for simple entity forms.
+ *
+ * During the submission handling of an entity form's "Save", "Preview", and
+ * possibly other buttons, the form state's entity needs to be updated with the
+ * submitted form values. Each entity form implements its own builder function
+ * for doing this, appropriate for the particular entity and form, whereas
+ * modules may specify additional builder functions in $form['#entity_builders']
+ * for copying the form values of added form elements to entity properties.
+ * Many of the main entity builder functions can call this helper function to
+ * re-use its logic of copying $form_state['values'][PROPERTY] values to
+ * $entity->PROPERTY for all entries in $form_state['values'] that are not field
+ * data, and calling field_attach_submit() to copy field data. Apart from that
+ * this helper invokes any additional builder functions that have been specified
+ * in $form['#entity_builders'].
+ *
+ * For some entity forms (e.g., forms with complex non-field data and forms that
+ * simultaneously edit multiple entities), this behavior may be inappropriate,
+ * so the builder function for such forms needs to implement the required
+ * functionality instead of calling this function.
+ */
+function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_state) {
+  $info = entity_get_info($entity_type);
+  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
+
+  // Copy top-level form values that are not for fields to entity properties,
+  // without changing existing entity properties that are not being edited by
+  // this form. Copying field values must be done using field_attach_submit().
+  $values_excluding_fields = $info['fieldable'] ? array_diff_key($form_state['values'], field_info_instances($entity_type, $bundle)) : $form_state['values'];
+  foreach ($values_excluding_fields as $key => $value) {
+    $entity->$key = $value;
+  }
+
+  // Invoke all specified builders for copying form values to entity properties.
+  if (isset($form['#entity_builders'])) {
+    foreach ($form['#entity_builders'] as $function) {
+      $function($entity_type, $entity, $form, $form_state);
+    }
+  }
+
+  // Copy field values to the entity.
+  if ($info['fieldable']) {
+    field_attach_submit($entity_type, $entity, $form, $form_state);
+  }
+}
+
+/**
+ * Exception thrown when a malformed entity is passed.
+ */
+class EntityMalformedException extends Exception { }
+
diff --git a/modules/entity/entity.query.inc b/modules/entity/entity.query.inc
new file mode 100644
index 0000000..b4e91a2
--- /dev/null
+++ b/modules/entity/entity.query.inc
@@ -0,0 +1,953 @@
+<?php
+
+/**
+ * @file
+ * Entity query API.
+ */
+
+/**
+ * Exception thrown by EntityFieldQuery() on unsupported query syntax.
+ *
+ * Some storage modules might not support the full range of the syntax for
+ * conditions, and will raise an EntityFieldQueryException when an unsupported
+ * condition was specified.
+ */
+class EntityFieldQueryException extends Exception {}
+
+/**
+ * Retrieves entities matching a given set of conditions.
+ *
+ * This class allows finding entities based on entity properties (for example,
+ * node->changed), field values, and generic entity meta data (bundle,
+ * entity type, entity id, and revision ID). It is not possible to query across
+ * multiple entity types. For example, there is no facility to find published
+ * nodes written by users created in the last hour, as this would require
+ * querying both node->status and user->created.
+ *
+ * Normally we would not want to have public properties on the object, as that
+ * allows the object's state to become inconsistent too easily. However, this
+ * class's standard use case involves primarily code that does need to have
+ * direct access to the collected properties in order to handle alternate
+ * execution routines. We therefore use public properties for simplicity. Note
+ * that code that is simply creating and running a field query should still use
+ * the appropriate methods to add conditions on the query.
+ *
+ * Storage engines are not required to support every type of query. By default,
+ * an EntityFieldQueryException will be raised if an unsupported condition is
+ * specified or if the query has field conditions or sorts that are stored in
+ * different field storage engines. However, this logic can be overridden in
+ * hook_entity_query().
+ *
+ * Also note that this query does not automatically respect entity access
+ * restrictions. Node access control is performed by the SQL storage engine but
+ * other storage engines might not do this.
+ */
+class EntityFieldQuery {
+  /**
+   * Indicates that both deleted and non-deleted fields should be returned.
+   *
+   * @see EntityFieldQuery::deleted()
+   */
+  const RETURN_ALL = NULL;
+
+  /**
+   * TRUE if the query has already been altered, FALSE if it hasn't.
+   *
+   * Used in alter hooks to check for cloned queries that have already been
+   * altered prior to the clone (for example, the pager count query).
+   *
+   * @var boolean
+   */
+  public $altered = FALSE;
+
+  /**
+   * Associative array of entity-generic metadata conditions.
+   *
+   * @var array
+   *
+   * @see EntityFieldQuery::entityCondition()
+   */
+  public $entityConditions = array();
+
+  /**
+   * List of field conditions.
+   *
+   * @var array
+   *
+   * @see EntityFieldQuery::fieldCondition()
+   */
+  public $fieldConditions = array();
+
+  /**
+   * List of field meta conditions (language and delta).
+   *
+   * Field conditions operate on columns specified by hook_field_schema(),
+   * the meta conditions operate on columns added by the system: delta
+   * and language. These can not be mixed with the field conditions because
+   * field columns can have any name including delta and language.
+   *
+   * @var array
+   *
+   * @see EntityFieldQuery::fieldLanguageCondition()
+   * @see EntityFieldQuery::fielDeltaCondition()
+   */
+  public $fieldMetaConditions = array();
+
+  /**
+   * List of property conditions.
+   *
+   * @var array
+   *
+   * @see EntityFieldQuery::propertyCondition()
+   */
+  public $propertyConditions = array();
+
+  /**
+   * List of order clauses.
+   *
+   * @var array
+   */
+  public $order = array();
+
+  /**
+   * The query range.
+   *
+   * @var array
+   *
+   * @see EntityFieldQuery::range()
+   */
+  public $range = array();
+
+  /**
+   * The query pager data.
+   *
+   * @var array
+   *
+   * @see EntityFieldQuery::pager()
+   */
+  public $pager = array();
+
+  /**
+   * Query behavior for deleted data.
+   *
+   * TRUE to return only deleted data, FALSE to return only non-deleted data,
+   * EntityFieldQuery::RETURN_ALL to return everything.
+   *
+   * @see EntityFieldQuery::deleted()
+   */
+  public $deleted = FALSE;
+
+  /**
+   * A list of field arrays used.
+   *
+   * Field names passed to EntityFieldQuery::fieldCondition() and
+   * EntityFieldQuery::fieldOrderBy() are run through field_info_field() before
+   * stored in this array. This way, the elements of this array are field
+   * arrays.
+   *
+   * @var array
+   */
+  public $fields = array();
+
+  /**
+   * TRUE if this is a count query, FALSE if it isn't.
+   *
+   * @var boolean
+   */
+  public $count = FALSE;
+
+  /**
+   * Flag indicating whether this is querying current or all revisions.
+   *
+   * @var int
+   *
+   * @see EntityFieldQuery::age()
+   */
+  public $age = FIELD_LOAD_CURRENT;
+
+  /**
+   * A list of the tags added to this query.
+   *
+   * @var array
+   *
+   * @see EntityFieldQuery::addTag()
+   */
+  public $tags = array();
+
+  /**
+   * A list of metadata added to this query.
+   *
+   * @var array
+   *
+   * @see EntityFieldQuery::addMetaData()
+   */
+  public $metaData = array();
+
+  /**
+   * The ordered results.
+   *
+   * @var array
+   *
+   * @see EntityFieldQuery::execute().
+   */
+  public $orderedResults = array();
+
+  /**
+   * The method executing the query, if it is overriding the default.
+   *
+   * @var string
+   *
+   * @see EntityFieldQuery::execute().
+   */
+  public $executeCallback = '';
+
+  /**
+   * Adds a condition on entity-generic metadata.
+   *
+   * If the overall query contains only entity conditions or ordering, or if
+   * there are property conditions, then specifying the entity type is
+   * mandatory. If there are field conditions or ordering but no property
+   * conditions or ordering, then specifying an entity type is optional. While
+   * the field storage engine might support field conditions on more than one
+   * entity type, there is no way to query across multiple entity base tables by
+   * default. To specify the entity type, pass in 'entity_type' for $name,
+   * the type as a string for $value, and no $operator (it's disregarded).
+   *
+   * 'bundle', 'revision_id' and 'entity_id' have no such restrictions.
+   *
+   * Note: The "comment" and "taxonomy_term" entity types don't support bundle
+   * conditions. For "taxonomy_term", propertyCondition('vid') can be used
+   * instead.
+   *
+   * @param $name
+   *   'entity_type', 'bundle', 'revision_id' or 'entity_id'.
+   * @param $value
+   *   The value for $name. In most cases, this is a scalar. For more complex
+   *   options, it is an array. The meaning of each element in the array is
+   *   dependent on $operator.
+   * @param $operator
+   *   Possible values:
+   *   - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These
+   *     operators expect $value to be a literal of the same type as the
+   *     column.
+   *   - 'IN', 'NOT IN': These operators expect $value to be an array of
+   *     literals of the same type as the column.
+   *   - 'BETWEEN': This operator expects $value to be an array of two literals
+   *     of the same type as the column.
+   *   The operator can be omitted, and will default to 'IN' if the value is an
+   *   array, or to '=' otherwise.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function entityCondition($name, $value, $operator = NULL) {
+    $this->entityConditions[$name] = array(
+      'value' => $value,
+      'operator' => $operator,
+    );
+    return $this;
+  }
+
+  /**
+   * Adds a condition on field values.
+   *
+   * @param $field
+   *   Either a field name or a field array.
+   * @param $column
+   *   The column that should hold the value to be matched.
+   * @param $value
+   *   The value to test the column value against.
+   * @param $operator
+   *   The operator to be used to test the given value.
+   * @param $delta_group
+   *   An arbitrary identifier: conditions in the same group must have the same
+   *   $delta_group.
+   * @param $language_group
+   *   An arbitrary identifier: conditions in the same group must have the same
+   *   $language_group.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   *
+   * @see EntityFieldQuery::addFieldCondition
+   * @see EntityFieldQuery::deleted
+   */
+  public function fieldCondition($field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
+    return $this->addFieldCondition($this->fieldConditions, $field, $column, $value, $operator, $delta_group, $language_group);
+  }
+
+  /**
+   * Adds a condition on the field language column.
+   *
+   * @param $field
+   *   Either a field name or a field array.
+   * @param $value
+   *   The value to test the column value against.
+   * @param $operator
+   *   The operator to be used to test the given value.
+   * @param $delta_group
+   *   An arbitrary identifier: conditions in the same group must have the same
+   *   $delta_group.
+   * @param $language_group
+   *   An arbitrary identifier: conditions in the same group must have the same
+   *   $language_group.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   *
+   * @see EntityFieldQuery::addFieldCondition
+   * @see EntityFieldQuery::deleted
+   */
+  public function fieldLanguageCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
+    return $this->addFieldCondition($this->fieldMetaConditions, $field, 'language', $value, $operator, $delta_group, $language_group);
+  }
+
+  /**
+   * Adds a condition on the field delta column.
+   *
+   * @param $field
+   *   Either a field name or a field array.
+   * @param $value
+   *   The value to test the column value against.
+   * @param $operator
+   *   The operator to be used to test the given value.
+   * @param $delta_group
+   *   An arbitrary identifier: conditions in the same group must have the same
+   *   $delta_group.
+   * @param $language_group
+   *   An arbitrary identifier: conditions in the same group must have the same
+   *   $language_group.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   *
+   * @see EntityFieldQuery::addFieldCondition
+   * @see EntityFieldQuery::deleted
+   */
+  public function fieldDeltaCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
+    return $this->addFieldCondition($this->fieldMetaConditions, $field, 'delta', $value, $operator, $delta_group, $language_group);
+  }
+
+  /**
+   * Adds the given condition to the proper condition array.
+   *
+   * @param $conditions
+   *   A reference to an array of conditions.
+   * @param $field
+   *   Either a field name or a field array.
+   * @param $column
+   *   A column defined in the hook_field_schema() of this field. If this is
+   *   omitted then the query will find only entities that have data in this
+   *   field, using the entity and property conditions if there are any.
+   * @param $value
+   *   The value to test the column value against. In most cases, this is a
+   *   scalar. For more complex options, it is an array. The meaning of each
+   *   element in the array is dependent on $operator.
+   * @param $operator
+   *   Possible values:
+   *   - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These
+   *     operators expect $value to be a literal of the same type as the
+   *     column.
+   *   - 'IN', 'NOT IN': These operators expect $value to be an array of
+   *     literals of the same type as the column.
+   *   - 'BETWEEN': This operator expects $value to be an array of two literals
+   *     of the same type as the column.
+   *   The operator can be omitted, and will default to 'IN' if the value is an
+   *   array, or to '=' otherwise.
+   * @param $delta_group
+   *   An arbitrary identifier: conditions in the same group must have the same
+   *   $delta_group. For example, let's presume a multivalue field which has
+   *   two columns, 'color' and 'shape', and for entity id 1, there are two
+   *   values: red/square and blue/circle. Entity ID 1 does not have values
+   *   corresponding to 'red circle', however if you pass 'red' and 'circle' as
+   *   conditions, it will appear in the  results - by default queries will run
+   *   against any combination of deltas. By passing the conditions with the
+   *   same $delta_group it will ensure that only values attached to the same
+   *   delta are matched, and entity 1 would then be excluded from the results.
+   * @param $language_group
+   *   An arbitrary identifier: conditions in the same group must have the same
+   *   $language_group.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  protected function addFieldCondition(&$conditions, $field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
+    if (is_scalar($field)) {
+      $field_definition = field_info_field($field);
+      if (empty($field_definition)) {
+        throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field)));
+      }
+      $field = $field_definition;
+    }
+    // Ensure the same index is used for field conditions as for fields.
+    $index = count($this->fields);
+    $this->fields[$index] = $field;
+    if (isset($column)) {
+      $conditions[$index] = array(
+        'field' => $field,
+        'column' => $column,
+        'value' => $value,
+        'operator' => $operator,
+        'delta_group' => $delta_group,
+        'language_group' => $language_group,
+      );
+    }
+    return $this;
+  }
+
+  /**
+   * Adds a condition on an entity-specific property.
+   *
+   * An $entity_type must be specified by calling
+   * EntityFieldCondition::entityCondition('entity_type', $entity_type) before
+   * executing the query. Also, by default only entities stored in SQL are
+   * supported; however, EntityFieldQuery::executeCallback can be set to handle
+   * different entity storage.
+   *
+   * @param $column
+   *   A column defined in the hook_schema() of the base table of the entity.
+   * @param $value
+   *   The value to test the field against. In most cases, this is a scalar. For
+   *   more complex options, it is an array. The meaning of each element in the
+   *   array is dependent on $operator.
+   * @param $operator
+   *   Possible values:
+   *   - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These
+   *     operators expect $value to be a literal of the same type as the
+   *     column.
+   *   - 'IN', 'NOT IN': These operators expect $value to be an array of
+   *     literals of the same type as the column.
+   *   - 'BETWEEN': This operator expects $value to be an array of two literals
+   *     of the same type as the column.
+   *   The operator can be omitted, and will default to 'IN' if the value is an
+   *   array, or to '=' otherwise.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function propertyCondition($column, $value, $operator = NULL) {
+    $this->propertyConditions[] = array(
+      'column' => $column,
+      'value' => $value,
+      'operator' => $operator,
+    );
+    return $this;
+  }
+
+  /**
+   * Orders the result set by entity-generic metadata.
+   *
+   * If called multiple times, the query will order by each specified column in
+   * the order this method is called.
+   *
+   * Note: The "comment" and "taxonomy_term" entity types don't support ordering
+   * by bundle. For "taxonomy_term", propertyOrderBy('vid') can be used instead.
+   *
+   * @param $name
+   *   'entity_type', 'bundle', 'revision_id' or 'entity_id'.
+   * @param $direction
+   *   The direction to sort. Legal values are "ASC" and "DESC".
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function entityOrderBy($name, $direction = 'ASC') {
+    $this->order[] = array(
+      'type' => 'entity',
+      'specifier' => $name,
+      'direction' => $direction,
+    );
+    return $this;
+  }
+
+  /**
+   * Orders the result set by a given field column.
+   *
+   * If called multiple times, the query will order by each specified column in
+   * the order this method is called.
+   *
+   * @param $field
+   *   Either a field name or a field array.
+   * @param $column
+   *   A column defined in the hook_field_schema() of this field. entity_id and
+   *   bundle can also be used.
+   * @param $direction
+   *   The direction to sort. Legal values are "ASC" and "DESC".
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function fieldOrderBy($field, $column, $direction = 'ASC') {
+    if (is_scalar($field)) {
+      $field_definition = field_info_field($field);
+      if (empty($field_definition)) {
+        throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field)));
+      }
+      $field = $field_definition;
+    }
+    // Save the index used for the new field, for later use in field storage.
+    $index = count($this->fields);
+    $this->fields[$index] = $field;
+    $this->order[] = array(
+      'type' => 'field',
+      'specifier' => array(
+        'field' => $field,
+        'index' => $index,
+        'column' => $column,
+      ),
+      'direction' => $direction,
+    );
+    return $this;
+  }
+
+  /**
+   * Orders the result set by an entity-specific property.
+   *
+   * An $entity_type must be specified by calling
+   * EntityFieldCondition::entityCondition('entity_type', $entity_type) before
+   * executing the query.
+   *
+   * If called multiple times, the query will order by each specified column in
+   * the order this method is called.
+   *
+   * @param $column
+   *   The column on which to order.
+   * @param $direction
+   *   The direction to sort. Legal values are "ASC" and "DESC".
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function propertyOrderBy($column, $direction = 'ASC') {
+    $this->order[] = array(
+      'type' => 'property',
+      'specifier' => $column,
+      'direction' => $direction,
+    );
+    return $this;
+  }
+
+  /**
+   * Sets the query to be a count query only.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function count() {
+    $this->count = TRUE;
+    return $this;
+  }
+
+  /**
+   * Restricts a query to a given range in the result set.
+   *
+   * @param $start
+   *   The first entity from the result set to return. If NULL, removes any
+   *   range directives that are set.
+   * @param $length
+   *   The number of entities to return from the result set.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function range($start = NULL, $length = NULL) {
+    $this->range = array(
+      'start' => $start,
+      'length' => $length,
+    );
+    return $this;
+  }
+
+  /**
+   * Enables a pager for the query.
+   *
+   * @param $limit
+   *   An integer specifying the number of elements per page.  If passed a false
+   *   value (FALSE, 0, NULL), the pager is disabled.
+   * @param $element
+   *   An optional integer to distinguish between multiple pagers on one page.
+   *   If not provided, one is automatically calculated.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function pager($limit = 10, $element = NULL) {
+    if (!isset($element)) {
+      $element = PagerDefault::$maxElement++;
+    }
+    elseif ($element >= PagerDefault::$maxElement) {
+      PagerDefault::$maxElement = $element + 1;
+    }
+
+    $this->pager = array(
+      'limit' => $limit,
+      'element' => $element,
+    );
+    return $this;
+  }
+
+  /**
+   * Enables sortable tables for this query.
+   *
+   * @param $headers
+   *   An EFQ Header array based on which the order clause is added to the query.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function tableSort(&$headers) {
+    // If 'field' is not initialized, the header columns aren't clickable
+    foreach ($headers as $key =>$header) {
+      if (is_array($header) && isset($header['specifier'])) {
+        $headers[$key]['field'] = '';
+      }
+    }
+
+    $order = tablesort_get_order($headers);
+    $direction = tablesort_get_sort($headers);
+    foreach ($headers as $header) {
+      if (is_array($header) && ($header['data'] == $order['name'])) {
+        if ($header['type'] == 'field') {
+          $this->fieldOrderBy($header['specifier']['field'], $header['specifier']['column'], $direction);
+        }
+        else {
+          $header['direction'] = $direction;
+          $this->order[] = $header;
+        }
+      }
+    }
+
+    return $this;
+  }
+
+  /**
+   * Filters on the data being deleted.
+   *
+   * @param $deleted
+   *   TRUE to only return deleted data, FALSE to return non-deleted data,
+   *   EntityFieldQuery::RETURN_ALL to return everything. Defaults to FALSE.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function deleted($deleted = TRUE) {
+    $this->deleted = $deleted;
+    return $this;
+  }
+
+  /**
+   * Queries the current or every revision.
+   *
+   * Note that this only affects field conditions. Property conditions always
+   * apply to the current revision.
+   * @TODO: Once revision tables have been cleaned up, revisit this.
+   *
+   * @param $age
+   *   - FIELD_LOAD_CURRENT (default): Query the most recent revisions for all
+   *     entities. The results will be keyed by entity type and entity ID.
+   *   - FIELD_LOAD_REVISION: Query all revisions. The results will be keyed by
+   *     entity type and entity revision ID.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function age($age) {
+    $this->age = $age;
+    return $this;
+  }
+
+  /**
+   * Adds a tag to the query.
+   *
+   * Tags are strings that mark a query so that hook_query_alter() and
+   * hook_query_TAG_alter() implementations may decide if they wish to alter
+   * the query. A query may have any number of tags, and they must be valid PHP
+   * identifiers (composed of letters, numbers, and underscores). For example,
+   * queries involving nodes that will be displayed for a user need to add the
+   * tag 'node_access', so that the node module can add access restrictions to
+   * the query.
+   *
+   * If an entity field query has tags, it must also have an entity type
+   * specified, because the alter hook will need the entity base table.
+   *
+   * @param string $tag
+   *   The tag to add.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function addTag($tag) {
+    $this->tags[$tag] = $tag;
+    return $this;
+  }
+
+  /**
+   * Adds additional metadata to the query.
+   *
+   * Sometimes a query may need to provide additional contextual data for the
+   * alter hook. The alter hook implementations may then use that information
+   * to decide if and how to take action.
+   *
+   * @param $key
+   *   The unique identifier for this piece of metadata. Must be a string that
+   *   follows the same rules as any other PHP identifier.
+   * @param $object
+   *   The additional data to add to the query. May be any valid PHP variable.
+   *
+   * @return EntityFieldQuery
+   *   The called object.
+   */
+  public function addMetaData($key, $object) {
+    $this->metaData[$key] = $object;
+    return $this;
+  }
+
+  /**
+   * Executes the query.
+   *
+   * After executing the query, $this->orderedResults will contain a list of
+   * the same stub entities in the order returned by the query. This is only
+   * relevant if there are multiple entity types in the returned value and
+   * a field ordering was requested. In every other case, the returned value
+   * contains everything necessary for processing.
+   *
+   * @return
+   *   Either a number if count() was called or an array of associative
+   *   arrays of stub entities. The outer array keys are entity types, and the
+   *   inner array keys are the relevant ID. (In most this cases this will be
+   *   the entity ID. The only exception is when age=FIELD_LOAD_REVISION is used
+   *   and field conditions or sorts are present -- in this case, the key will
+   *   be the revision ID.) The inner array values are always stub entities, as
+   *   returned by entity_create_stub_entity(). To traverse the returned array:
+   *   @code
+   *     foreach ($query->execute() as $entity_type => $entities) {
+   *       foreach ($entities as $entity_id => $entity) {
+   *   @endcode
+   *   Note if the entity type is known, then the following snippet will load
+   *   the entities found:
+   *   @code
+   *     $result = $query->execute();
+   *     $entities = entity_load($my_type, array_keys($result[$my_type]));
+   *   @endcode
+   */
+  public function execute() {
+    // Give a chance to other modules to alter the query.
+    drupal_alter('entity_query', $this);
+    $this->altered = TRUE;
+
+    // Initialize the pager.
+    $this->initializePager();
+
+    // Execute the query using the correct callback.
+    $result = call_user_func($this->queryCallback(), $this);
+
+    return $result;
+  }
+
+  /**
+   * Determines the query callback to use for this entity query.
+   *
+   * @return
+   *   A callback that can be used with call_user_func().
+   */
+  public function queryCallback() {
+    // Use the override from $this->executeCallback. It can be set either
+    // while building the query, or using hook_entity_query_alter().
+    if (function_exists($this->executeCallback)) {
+      return $this->executeCallback;
+    }
+    // If there are no field conditions and sorts, and no execute callback
+    // then we default to querying entity tables in SQL.
+    if (empty($this->fields)) {
+      return array($this, 'propertyQuery');
+    }
+    // If no override, find the storage engine to be used.
+    foreach ($this->fields as $field) {
+      if (!isset($storage)) {
+        $storage = $field['storage']['module'];
+      }
+      elseif ($storage != $field['storage']['module']) {
+        throw new EntityFieldQueryException(t("Can't handle more than one field storage engine"));
+      }
+    }
+    if ($storage) {
+      // Use hook_field_storage_query() from the field storage.
+      return $storage . '_field_storage_query';
+    }
+    else {
+      throw new EntityFieldQueryException(t("Field storage engine not found."));
+    }
+  }
+
+  /**
+   * Queries entity tables in SQL for property conditions and sorts.
+   *
+   * This method is only used if there are no field conditions and sorts.
+   *
+   * @return
+   *   See EntityFieldQuery::execute().
+   */
+  protected function propertyQuery() {
+    if (empty($this->entityConditions['entity_type'])) {
+      throw new EntityFieldQueryException(t('For this query an entity type must be specified.'));
+    }
+    $entity_type = $this->entityConditions['entity_type']['value'];
+    $entity_info = entity_get_info($entity_type);
+    if (empty($entity_info['base table'])) {
+      throw new EntityFieldQueryException(t('Entity %entity has no base table.', array('%entity' => $entity_type)));
+    }
+    $base_table = $entity_info['base table'];
+    $base_table_schema = drupal_get_schema($base_table);
+    $select_query = db_select($base_table);
+    $select_query->addExpression(':entity_type', 'entity_type', array(':entity_type' => $entity_type));
+    // Process the property conditions.
+    foreach ($this->propertyConditions as $property_condition) {
+      $this->addCondition($select_query, "$base_table." . $property_condition['column'], $property_condition);
+    }
+    // Process the four possible entity condition.
+    // The id field is always present in entity keys.
+    $sql_field = $entity_info['entity keys']['id'];
+    $id_map['entity_id'] = $sql_field;
+    $select_query->addField($base_table, $sql_field, 'entity_id');
+    if (isset($this->entityConditions['entity_id'])) {
+      $this->addCondition($select_query, $sql_field, $this->entityConditions['entity_id']);
+    }
+
+    // If there is a revision key defined, use it.
+    if (!empty($entity_info['entity keys']['revision'])) {
+      $sql_field = $entity_info['entity keys']['revision'];
+      $select_query->addField($base_table, $sql_field, 'revision_id');
+      if (isset($this->entityConditions['revision_id'])) {
+        $this->addCondition($select_query, $sql_field, $this->entityConditions['revision_id']);
+      }
+    }
+    else {
+      $sql_field = 'revision_id';
+      $select_query->addExpression('NULL', 'revision_id');
+    }
+    $id_map['revision_id'] = $sql_field;
+
+    // Handle bundles.
+    if (!empty($entity_info['entity keys']['bundle'])) {
+      $sql_field = $entity_info['entity keys']['bundle'];
+      $having = FALSE;
+
+      if (!empty($base_table_schema['fields'][$sql_field])) {
+        $select_query->addField($base_table, $sql_field, 'bundle');
+      }
+    }
+    else {
+      $sql_field = 'bundle';
+      $select_query->addExpression(':bundle', 'bundle', array(':bundle' => $entity_type));
+      $having = TRUE;
+    }
+    $id_map['bundle'] = $sql_field;
+    if (isset($this->entityConditions['bundle'])) {
+      $this->addCondition($select_query, $sql_field, $this->entityConditions['bundle'], $having);
+    }
+
+    // Order the query.
+    foreach ($this->order as $order) {
+      if ($order['type'] == 'entity') {
+        $key = $order['specifier'];
+        if (!isset($id_map[$key])) {
+          throw new EntityFieldQueryException(t('Do not know how to order on @key for @entity_type', array('@key' => $key, '@entity_type' => $entity_type)));
+        }
+        $select_query->orderBy($id_map[$key], $order['direction']);
+      }
+      elseif ($order['type'] == 'property') {
+        $select_query->orderBy("$base_table." . $order['specifier'], $order['direction']);
+      }
+    }
+
+    return $this->finishQuery($select_query);
+  }
+
+  /**
+   * Gets the total number of results and initialize a pager for the query.
+   *
+   * This query can be detected by checking for ($this->pager && $this->count),
+   * which allows a driver to return 0 from the count query and disable
+   * the pager.
+   */
+  function initializePager() {
+    if ($this->pager && !$this->count) {
+      $page = pager_find_page($this->pager['element']);
+      $count_query = clone $this;
+      $this->pager['total'] = $count_query->count()->execute();
+      $this->pager['start'] = $page * $this->pager['limit'];
+      pager_default_initialize($this->pager['total'], $this->pager['limit'], $this->pager['element']);
+      $this->range($this->pager['start'], $this->pager['limit']);
+    }
+  }
+
+  /**
+   * Finishes the query.
+   *
+   * Adds tags, metaData, range and returns the requested list or count.
+   *
+   * @param SelectQuery $select_query
+   *   A SelectQuery which has entity_type, entity_id, revision_id and bundle
+   *   fields added.
+   * @param $id_key
+   *   Which field's values to use as the returned array keys.
+   *
+   * @return
+   *   See EntityFieldQuery::execute().
+   */
+  function finishQuery($select_query, $id_key = 'entity_id') {
+    foreach ($this->tags as $tag) {
+      $select_query->addTag($tag);
+    }
+    foreach ($this->metaData as $key => $object) {
+      $select_query->addMetaData($key, $object);
+    }
+    $select_query->addMetaData('entity_field_query', $this);
+    if ($this->range) {
+      $select_query->range($this->range['start'], $this->range['length']);
+    }
+    if ($this->count) {
+      return $select_query->countQuery()->execute()->fetchField();
+    }
+    $return = array();
+    foreach ($select_query->execute() as $partial_entity) {
+      $bundle = isset($partial_entity->bundle) ? $partial_entity->bundle : NULL;
+      $entity = entity_create_stub_entity($partial_entity->entity_type, array($partial_entity->entity_id, $partial_entity->revision_id, $bundle));
+      $return[$partial_entity->entity_type][$partial_entity->$id_key] = $entity;
+      $this->ordered_results[] = $partial_entity;
+    }
+    return $return;
+  }
+
+  /**
+   * Adds a condition to an already built SelectQuery (internal function).
+   *
+   * This is a helper for hook_entity_query() and hook_field_storage_query().
+   *
+   * @param SelectQuery $select_query
+   *   A SelectQuery object.
+   * @param $sql_field
+   *   The name of the field.
+   * @param $condition
+   *   A condition as described in EntityFieldQuery::fieldCondition() and
+   *   EntityFieldQuery::entityCondition().
+   * @param $having
+   *   HAVING or WHERE. This is necessary because SQL can't handle WHERE
+   *   conditions on aliased columns.
+   */
+  public function addCondition(SelectQuery $select_query, $sql_field, $condition, $having = FALSE) {
+    $method = $having ? 'havingCondition' : 'condition';
+    $like_prefix = '';
+    switch ($condition['operator']) {
+      case 'CONTAINS':
+        $like_prefix = '%';
+      case 'STARTS_WITH':
+        $select_query->$method($sql_field, $like_prefix . db_like($condition['value']) . '%', 'LIKE');
+        break;
+      default:
+        $select_query->$method($sql_field, $condition['value'], $condition['operator']);
+    }
+  }
+
+}
+
diff --git a/modules/entity/tests/entity.test b/modules/entity/tests/entity.test
new file mode 100644
index 0000000..c435541
--- /dev/null
+++ b/modules/entity/tests/entity.test
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Entity CRUD API tests.
+ */
+
+/**
+ * Test basic API.
+ */
+class EntityAPITestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity CRUD',
+      'description' => 'Tests basic CRUD functionality.',
+      'group' => 'Entity API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('entity', 'entity_test');
+  }
+
+  /**
+   * Tests basic CRUD functionality of the entity API.
+   */
+  function testCRUD() {
+    $user1 = $this->drupalCreateUser();
+
+    // Create some test entities.
+    $entity = entity_create('entity_test', array('name' => 'test', 'uid' => $user1->uid));
+    $entity->save();
+    $entity = entity_create('entity_test', array('name' => 'test2', 'uid' => $user1->uid));
+    $entity->save();
+    $entity = entity_create('entity_test', array('name' => 'test', 'uid' => NULL));
+    $entity->save();
+
+    $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test')));
+
+    $this->assertEqual($entities[0]->name, 'test', 'Created and loaded entity.');
+    $this->assertEqual($entities[1]->name, 'test', '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 deleting an entity.
+    $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test2')));
+    $entities[0]->delete();
+    $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test2')));
+    $this->assertEqual($entities, array(), 'Entity deleted.');
+
+    // Test updating an entity.
+    $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test')));
+    $entities[0]->name = 'test3';
+    $entities[0]->save();
+    $entity = entity_test_load($entities[0]->id);
+    $this->assertEqual($entity->name, 'test3', 'Entity updated.');
+
+    // Try deleting multiple test entities by deleting all.
+    $ids = array_keys(entity_test_load_multiple(FALSE));
+    entity_test_delete_multiple($ids);
+
+    $all = entity_test_load_multiple(FALSE);
+    $this->assertTrue(empty($all), 'Deleted all entities.');
+  }
+}
+
diff --git a/modules/entity/tests/entity_cache_test.info b/modules/entity/tests/entity_cache_test.info
new file mode 100644
index 0000000..c13496e
--- /dev/null
+++ b/modules/entity/tests/entity_cache_test.info
@@ -0,0 +1,7 @@
+name = "Entity cache test"
+description = "Support module for testing entity cache."
+package = Testing
+version = VERSION
+core = 8.x
+dependencies[] = entity_cache_test_dependency
+hidden = TRUE
diff --git a/modules/entity/tests/entity_cache_test.module b/modules/entity/tests/entity_cache_test.module
new file mode 100644
index 0000000..5ae9ecc
--- /dev/null
+++ b/modules/entity/tests/entity_cache_test.module
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Helper module for entity cache tests.
+ */
+
+/**
+ * Implements hook_watchdog().
+ *
+ * This hook is called during module_enable() and since this hook
+ * implementation is invoked, we have to expect that this module and dependent
+ * modules have been properly installed already. So we expect to be able to
+ * retrieve the entity information that has been registered by the required
+ * dependency module.
+ *
+ * @see EnableDisableTestCase::testEntityCache()
+ * @see entity_cache_test_dependency_entity_info()
+ */
+function entity_cache_test_watchdog($log_entry) {
+  if ($log_entry['type'] == 'system' && $log_entry['message'] == '%module module installed.') {
+    $info = entity_get_info('entity_cache_test');
+    // Store the information in a system variable to analyze it later in the
+    // test case.
+    variable_set('entity_cache_test', $info);
+  }
+}
diff --git a/modules/entity/tests/entity_cache_test_dependency.info b/modules/entity/tests/entity_cache_test_dependency.info
new file mode 100644
index 0000000..17d551c
--- /dev/null
+++ b/modules/entity/tests/entity_cache_test_dependency.info
@@ -0,0 +1,6 @@
+name = "Entity cache test dependency"
+description = "Support dependency module for testing entity cache."
+package = Testing
+version = VERSION
+core = 8.x
+hidden = TRUE
diff --git a/modules/entity/tests/entity_cache_test_dependency.module b/modules/entity/tests/entity_cache_test_dependency.module
new file mode 100644
index 0000000..73a1149
--- /dev/null
+++ b/modules/entity/tests/entity_cache_test_dependency.module
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Helper module for entity cache tests.
+ */
+
+/**
+ * Implements hook_entity_info().
+ */
+function entity_cache_test_dependency_entity_info() {
+  return array(
+    'entity_cache_test' => array(
+      'label' => 'Entity Cache Test',
+    ),
+  );
+}
diff --git a/modules/entity/tests/entity_crud_hook_test.info b/modules/entity/tests/entity_crud_hook_test.info
new file mode 100644
index 0000000..28ce1b5
--- /dev/null
+++ b/modules/entity/tests/entity_crud_hook_test.info
@@ -0,0 +1,6 @@
+name = "Entity CRUD Hooks Test"
+description = "Support module for CRUD hook tests."
+core = 8.x
+package = Testing
+version = VERSION
+hidden = TRUE
diff --git a/modules/entity/tests/entity_crud_hook_test.module b/modules/entity/tests/entity_crud_hook_test.module
new file mode 100644
index 0000000..873a162
--- /dev/null
+++ b/modules/entity/tests/entity_crud_hook_test.module
@@ -0,0 +1,266 @@
+<?php
+
+//
+// Presave hooks
+//
+
+/**
+ * Implements hook_entity_presave().
+ */
+function entity_crud_hook_test_entity_presave($entity, $type) {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_presave().
+ */
+function entity_crud_hook_test_comment_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_presave().
+ */
+function entity_crud_hook_test_file_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_presave().
+ */
+function entity_crud_hook_test_node_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_presave().
+ */
+function entity_crud_hook_test_taxonomy_term_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_presave().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_presave().
+ */
+function entity_crud_hook_test_user_presave() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+//
+// Insert hooks
+//
+
+/**
+ * Implements hook_entity_insert().
+ */
+function entity_crud_hook_test_entity_insert($entity, $type) {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_insert().
+ */
+function entity_crud_hook_test_comment_insert() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_insert().
+ */
+function entity_crud_hook_test_file_insert() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_insert().
+ */
+function entity_crud_hook_test_node_insert() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_insert().
+ */
+function entity_crud_hook_test_taxonomy_term_insert() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_insert().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_insert() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_insert().
+ */
+function entity_crud_hook_test_user_insert() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+//
+// Load hooks
+//
+
+/**
+ * Implements hook_entity_load().
+ */
+function entity_crud_hook_test_entity_load(array $entities, $type) {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_load().
+ */
+function entity_crud_hook_test_comment_load() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_load().
+ */
+function entity_crud_hook_test_file_load() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_load().
+ */
+function entity_crud_hook_test_node_load() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_load().
+ */
+function entity_crud_hook_test_taxonomy_term_load() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_load().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_load() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_load().
+ */
+function entity_crud_hook_test_user_load() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+//
+// Update hooks
+//
+
+/**
+ * Implements hook_entity_update().
+ */
+function entity_crud_hook_test_entity_update($entity, $type) {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_update().
+ */
+function entity_crud_hook_test_comment_update() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_update().
+ */
+function entity_crud_hook_test_file_update() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_update().
+ */
+function entity_crud_hook_test_node_update() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_update().
+ */
+function entity_crud_hook_test_taxonomy_term_update() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_update().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_update() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_update().
+ */
+function entity_crud_hook_test_user_update() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+//
+// Delete hooks
+//
+
+/**
+ * Implements hook_entity_delete().
+ */
+function entity_crud_hook_test_entity_delete($entity, $type) {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_delete().
+ */
+function entity_crud_hook_test_comment_delete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_delete().
+ */
+function entity_crud_hook_test_file_delete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_delete().
+ */
+function entity_crud_hook_test_node_delete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_delete().
+ */
+function entity_crud_hook_test_taxonomy_term_delete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_delete().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_delete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_delete().
+ */
+function entity_crud_hook_test_user_delete() {
+  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
diff --git a/modules/entity/tests/entity_crud_hook_test.test b/modules/entity/tests/entity_crud_hook_test.test
new file mode 100644
index 0000000..782201e
--- /dev/null
+++ b/modules/entity/tests/entity_crud_hook_test.test
@@ -0,0 +1,332 @@
+<?php
+
+/**
+ * Test invocation of hooks when inserting, loading, updating or deleting an
+ * entity. Tested hooks are:
+ * - hook_entity_insert()
+ * - hook_entity_load()
+ * - hook_entity_update()
+ * - hook_entity_delete()
+ * As well as all type-specific hooks, like hook_node_insert(),
+ * hook_comment_update(), etc.
+ */
+class EntityCrudHookTestCase extends DrupalWebTestCase {
+
+  protected $ids = array();
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity CRUD hooks',
+      'description' => 'Tests the invocation of hooks when inserting, loading, updating or deleting an entity.',
+      'group' => 'Entity API',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp('entity_crud_hook_test', 'taxonomy', 'comment');
+  }
+
+  /**
+   * Pass if the message $text was set by one of the CRUD hooks in
+   * entity_crud_hook_test.module, i.e., if the $text is an element of
+   * $_SESSION['entity_crud_hook_test'].
+   *
+   * @param $text
+   *   Plain text to look for.
+   * @param $message
+   *   Message to display.
+   * @param $group
+   *   The group this message belongs to, defaults to 'Other'.
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function assertHookMessage($text, $message = NULL, $group = 'Other') {
+    if (!isset($message)) {
+      $message = $text;
+    }
+    return $this->assertTrue(array_search($text, $_SESSION['entity_crud_hook_test']) !== FALSE, $message, $group);
+  }
+
+  /**
+   * Test hook invocations for CRUD operations on comments.
+   */
+  public function testCommentHooks() {
+    $node = (object) array(
+      'uid' => 1,
+      'type' => 'article',
+      'title' => 'Test node',
+      'status' => 1,
+      'comment' => 2,
+      'promote' => 0,
+      'sticky' => 0,
+      'language' => LANGUAGE_NONE,
+      'created' => REQUEST_TIME,
+      'changed' => REQUEST_TIME,
+    );
+    node_save($node);
+    $nid = $node->nid;
+
+    $comment = entity_create('comment', array(
+      'cid' => NULL,
+      'pid' => 0,
+      'nid' => $nid,
+      'uid' => 1,
+      'subject' => 'Test comment',
+      'created' => REQUEST_TIME,
+      'changed' => REQUEST_TIME,
+      'status' => 1,
+      'language' => LANGUAGE_NONE,
+    ));
+    $_SESSION['entity_crud_hook_test'] = array();
+    comment_save($comment);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
+    $this->assertHookMessage('entity_crud_hook_test_comment_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type comment');
+    $this->assertHookMessage('entity_crud_hook_test_comment_insert called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $comment = comment_load($comment->cid);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type comment');
+    $this->assertHookMessage('entity_crud_hook_test_comment_load called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $comment->subject = 'New subject';
+    comment_save($comment);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
+    $this->assertHookMessage('entity_crud_hook_test_comment_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type comment');
+    $this->assertHookMessage('entity_crud_hook_test_comment_update called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    comment_delete($comment->cid);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type comment');
+    $this->assertHookMessage('entity_crud_hook_test_comment_delete called');
+  }
+
+  /**
+   * Test hook invocations for CRUD operations on files.
+   */
+  public function testFileHooks() {
+    $url = 'public://entity_crud_hook_test.file';
+    file_put_contents($url, 'Test test test');
+    $file = (object) array(
+      'fid' => NULL,
+      'uid' => 1,
+      'filename' => 'entity_crud_hook_test.file',
+      'uri' => $url,
+      'filemime' => 'text/plain',
+      'filesize' => filesize($url),
+      'status' => 1,
+      'timestamp' => REQUEST_TIME,
+    );
+    $_SESSION['entity_crud_hook_test'] = array();
+    file_save($file);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
+    $this->assertHookMessage('entity_crud_hook_test_file_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type file');
+    $this->assertHookMessage('entity_crud_hook_test_file_insert called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $file = file_load($file->fid);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type file');
+    $this->assertHookMessage('entity_crud_hook_test_file_load called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $file->filename = 'new.entity_crud_hook_test.file';
+    file_save($file);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
+    $this->assertHookMessage('entity_crud_hook_test_file_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type file');
+    $this->assertHookMessage('entity_crud_hook_test_file_update called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    file_delete($file);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type file');
+    $this->assertHookMessage('entity_crud_hook_test_file_delete called');
+  }
+
+  /**
+   * Test hook invocations for CRUD operations on nodes.
+   */
+  public function testNodeHooks() {
+    $node = (object) array(
+      'uid' => 1,
+      'type' => 'article',
+      'title' => 'Test node',
+      'status' => 1,
+      'comment' => 2,
+      'promote' => 0,
+      'sticky' => 0,
+      'language' => LANGUAGE_NONE,
+      'created' => REQUEST_TIME,
+      'changed' => REQUEST_TIME,
+    );
+    $_SESSION['entity_crud_hook_test'] = array();
+    node_save($node);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
+    $this->assertHookMessage('entity_crud_hook_test_node_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type node');
+    $this->assertHookMessage('entity_crud_hook_test_node_insert called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $node = node_load($node->nid);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type node');
+    $this->assertHookMessage('entity_crud_hook_test_node_load called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $node->title = 'New title';
+    node_save($node);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
+    $this->assertHookMessage('entity_crud_hook_test_node_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type node');
+    $this->assertHookMessage('entity_crud_hook_test_node_update called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    node_delete($node->nid);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type node');
+    $this->assertHookMessage('entity_crud_hook_test_node_delete called');
+  }
+
+  /**
+   * Test hook invocations for CRUD operations on taxonomy terms.
+   */
+  public function testTaxonomyTermHooks() {
+    $vocabulary = (object) array(
+      'name' => 'Test vocabulary',
+      'machine_name' => 'test',
+      'description' => NULL,
+      'module' => 'entity_crud_hook_test',
+    );
+    taxonomy_vocabulary_save($vocabulary);
+
+    $term = (object) array(
+      'vid' => $vocabulary->vid,
+      'name' => 'Test term',
+      'description' => NULL,
+      'format' => 1,
+    );
+    $_SESSION['entity_crud_hook_test'] = array();
+    taxonomy_term_save($term);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_term');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_insert called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $term = taxonomy_term_load($term->tid);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_term');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_load called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $term->name = 'New name';
+    taxonomy_term_save($term);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_term');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_update called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    taxonomy_term_delete($term->tid);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_term');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_delete called');
+  }
+
+  /**
+   * Test hook invocations for CRUD operations on taxonomy vocabularies.
+   */
+  public function testTaxonomyVocabularyHooks() {
+    $vocabulary = (object) array(
+      'name' => 'Test vocabulary',
+      'machine_name' => 'test',
+      'description' => NULL,
+      'module' => 'entity_crud_hook_test',
+    );
+    $_SESSION['entity_crud_hook_test'] = array();
+    taxonomy_vocabulary_save($vocabulary);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_insert called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $vocabulary = taxonomy_vocabulary_load($vocabulary->vid);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_vocabulary');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_load called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $vocabulary->name = 'New name';
+    taxonomy_vocabulary_save($vocabulary);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_vocabulary');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_update called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    taxonomy_vocabulary_delete($vocabulary->vid);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary');
+    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_delete called');
+  }
+
+  /**
+   * Test hook invocations for CRUD operations on users.
+   */
+  public function testUserHooks() {
+    $edit = array(
+      'name' => 'Test user',
+      'mail' => 'test@example.com',
+      'created' => REQUEST_TIME,
+      'status' => 1,
+      'language' => 'en',
+    );
+    $account = (object) $edit;
+    $_SESSION['entity_crud_hook_test'] = array();
+    $account = user_save($account, $edit);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
+    $this->assertHookMessage('entity_crud_hook_test_user_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type user');
+    $this->assertHookMessage('entity_crud_hook_test_user_insert called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $account = user_load($account->uid);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type user');
+    $this->assertHookMessage('entity_crud_hook_test_user_load called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    $edit['name'] = 'New name';
+    $account = user_save($account, $edit);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
+    $this->assertHookMessage('entity_crud_hook_test_user_presave called');
+    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type user');
+    $this->assertHookMessage('entity_crud_hook_test_user_update called');
+
+    $_SESSION['entity_crud_hook_test'] = array();
+    user_delete($account->uid);
+
+    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type user');
+    $this->assertHookMessage('entity_crud_hook_test_user_delete called');
+  }
+
+}
diff --git a/modules/entity/tests/entity_query.test b/modules/entity/tests/entity_query.test
new file mode 100644
index 0000000..e540a90
--- /dev/null
+++ b/modules/entity/tests/entity_query.test
@@ -0,0 +1,1497 @@
+<?php
+
+/**
+ * @file
+ * Unit test file for the entity API.
+ */
+
+/**
+ * Tests EntityFieldQuery.
+ */
+class EntityFieldQueryTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity query',
+      'description' => 'Test the EntityFieldQuery class.',
+      'group' => 'Entity API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('field_test'));
+
+    field_attach_create_bundle('test_entity_bundle_key', 'bundle1');
+    field_attach_create_bundle('test_entity_bundle_key', 'bundle2');
+    field_attach_create_bundle('test_entity', 'test_bundles');
+    field_attach_create_bundle('test_entity_bundle', 'test_entity_bundle');
+
+    $instances = array();
+    $this->fields = array();
+    $this->field_names[0] = $field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 4);
+    $field = field_create_field($field);
+    $this->fields[0] = $field;
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => '',
+      'bundle' => '',
+      'label' => $this->randomName() . '_label',
+      'description' => $this->randomName() . '_description',
+      'weight' => mt_rand(0, 127),
+      'settings' => array(
+        'test_instance_setting' => $this->randomName(),
+      ),
+      'widget' => array(
+        'type' => 'test_field_widget',
+        'label' => 'Test Field',
+        'settings' => array(
+          'test_widget_setting' => $this->randomName(),
+        )
+      )
+    );
+
+    $instances[0] = $instance;
+
+    // Add an instance to that bundle.
+    $instances[0]['bundle'] = 'bundle1';
+    $instances[0]['entity_type'] = 'test_entity_bundle_key';
+    field_create_instance($instances[0]);
+    $instances[0]['bundle'] = 'bundle2';
+    field_create_instance($instances[0]);
+    $instances[0]['bundle'] = $instances[0]['entity_type'] = 'test_entity_bundle';
+    field_create_instance($instances[0]);
+
+    $this->field_names[1] = $field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $field = array('field_name' => $field_name, 'type' => 'shape', 'cardinality' => 4);
+    $field = field_create_field($field);
+    $this->fields[1] = $field;
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => '',
+      'bundle' => '',
+      'label' => $this->randomName() . '_label',
+      'description' => $this->randomName() . '_description',
+      'weight' => mt_rand(0, 127),
+      'settings' => array(
+        'test_instance_setting' => $this->randomName(),
+      ),
+      'widget' => array(
+        'type' => 'test_field_widget',
+        'label' => 'Test Field',
+        'settings' => array(
+          'test_widget_setting' => $this->randomName(),
+        )
+      )
+    );
+
+    $instances[1] = $instance;
+
+    // Add a field instance to the bundles.
+    $instances[1]['bundle'] = 'bundle1';
+    $instances[1]['entity_type'] = 'test_entity_bundle_key';
+    field_create_instance($instances[1]);
+    $instances[1]['bundle'] = $instances[1]['entity_type'] = 'test_entity_bundle';
+    field_create_instance($instances[1]);
+
+    $this->instances = $instances;
+    // Write entity base table if there is one.
+    $entities = array();
+
+    // Create entities which have a 'bundle key' defined.
+    for ($i = 1; $i < 7; $i++) {
+      $entity = new stdClass();
+      $entity->ftid = $i;
+      $entity->fttype = ($i < 5) ? 'bundle1' : 'bundle2';
+
+      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
+      drupal_write_record('test_entity_bundle_key', $entity);
+      field_attach_insert('test_entity_bundle_key', $entity);
+    }
+
+    $entity = new stdClass();
+    $entity->ftid = 5;
+    $entity->fttype = 'test_entity_bundle';
+    $entity->{$this->field_names[1]}[LANGUAGE_NONE][0]['shape'] = 'square';
+    $entity->{$this->field_names[1]}[LANGUAGE_NONE][0]['color'] = 'red';
+    $entity->{$this->field_names[1]}[LANGUAGE_NONE][1]['shape'] = 'circle';
+    $entity->{$this->field_names[1]}[LANGUAGE_NONE][1]['color'] = 'blue';
+    drupal_write_record('test_entity_bundle', $entity);
+    field_attach_insert('test_entity_bundle', $entity);
+
+    $instances[2] = $instance;
+    $instances[2]['bundle'] = 'test_bundle';
+    $instances[2]['field_name'] = $this->field_names[0];
+    $instances[2]['entity_type'] = 'test_entity';
+    field_create_instance($instances[2]);
+
+    // Create entities with support for revisions.
+    for ($i = 1; $i < 5; $i++) {
+      $entity = new stdClass();
+      $entity->ftid = $i;
+      $entity->ftvid = $i;
+      $entity->fttype = 'test_bundle';
+      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
+
+      drupal_write_record('test_entity', $entity);
+      field_attach_insert('test_entity', $entity);
+      drupal_write_record('test_entity_revision', $entity);
+    }
+
+    // Add two revisions to an entity.
+    for ($i = 100; $i < 102; $i++) {
+      $entity = new stdClass();
+      $entity->ftid = 4;
+      $entity->ftvid = $i;
+      $entity->fttype = 'test_bundle';
+      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
+
+      drupal_write_record('test_entity', $entity, 'ftid');
+      drupal_write_record('test_entity_revision', $entity);
+
+      db_update('test_entity')
+       ->fields(array('ftvid' => $entity->ftvid))
+       ->condition('ftid', $entity->ftid)
+       ->execute();
+
+      field_attach_update('test_entity', $entity);
+    }
+  }
+
+  /**
+   * Tests EntityFieldQuery.
+   */
+  function testEntityFieldQuery() {
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle')
+      ->entityCondition('entity_id', '5');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle', 5),
+    ), t('Test query on an entity type with a generated bundle.'));
+
+    // Test entity_type condition.
+    $query = new EntityFieldQuery();
+    $query->entityCondition('entity_type', 'test_entity_bundle_key');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test entity entity_type condition.'));
+
+    // Test entity_id condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityCondition('entity_id', '3');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+    ), t('Test entity entity_id condition.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', '3');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+    ), t('Test entity entity_id condition and entity_id property condition.'));
+
+    // Test bundle condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityCondition('bundle', 'bundle1');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test entity bundle condition: bundle1.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityCondition('bundle', 'bundle2');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test entity bundle condition: bundle2.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('fttype', 'bundle2');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test entity bundle condition and bundle property condition.'));
+
+    // Test revision_id condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->entityCondition('revision_id', '3');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 3),
+    ), t('Test entity revision_id condition.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->propertyCondition('ftvid', '3');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 3),
+    ), t('Test entity revision_id condition and revision_id property condition.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->fieldCondition($this->fields[0], 'value', 100, '>=')
+      ->age(FIELD_LOAD_REVISION);
+    $this->assertEntityFieldQuery($query, array(
+        array('test_entity', 100),
+        array('test_entity', 101),
+    ), t('Test revision age.'));
+
+    // Test that fields attached to the non-revision supporting entity
+    // 'test_entity_bundle_key' are reachable in FIELD_LOAD_REVISION.
+    $query = new EntityFieldQuery();
+    $query
+      ->fieldCondition($this->fields[0], 'value', 100, '<')
+      ->age(FIELD_LOAD_REVISION);
+    $this->assertEntityFieldQuery($query, array(
+        array('test_entity_bundle_key', 1),
+        array('test_entity_bundle_key', 2),
+        array('test_entity_bundle_key', 3),
+        array('test_entity_bundle_key', 4),
+        array('test_entity_bundle_key', 5),
+        array('test_entity_bundle_key', 6),
+        array('test_entity', 1),
+        array('test_entity', 2),
+        array('test_entity', 3),
+        array('test_entity', 4),
+    ), t('Test that fields are reachable from FIELD_LOAD_REVISION even for non-revision entities.'));
+
+    // Test entity sort by entity_id.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityOrderBy('entity_id', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test sort entity entity_id in ascending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityOrderBy('entity_id', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test sort entity entity_id in descending order.'), TRUE);
+
+    // Test entity sort by entity_id, with a field condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->entityOrderBy('entity_id', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test sort entity entity_id in ascending order, with a field condition.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->propertyOrderBy('ftid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test sort entity entity_id property in descending order, with a field condition.'), TRUE);
+
+    // Test property sort by entity id.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyOrderBy('ftid', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test sort entity entity_id property in ascending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyOrderBy('ftid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test sort entity entity_id property in descending order.'), TRUE);
+
+    // Test property sort by entity id, with a field condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->propertyOrderBy('ftid', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test sort entity entity_id property in ascending order, with a field condition.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->propertyOrderBy('ftid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test sort entity entity_id property in descending order, with a field condition.'), TRUE);
+
+    // Test entity sort by bundle.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityOrderBy('bundle', 'ASC')
+      ->propertyOrderBy('ftid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+    ), t('Test sort entity bundle in ascending order, property in descending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityOrderBy('bundle', 'DESC')
+      ->propertyOrderBy('ftid', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test sort entity bundle in descending order, property in ascending order.'), TRUE);
+
+    // Test entity sort by bundle, with a field condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->entityOrderBy('bundle', 'ASC')
+      ->propertyOrderBy('ftid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+    ), t('Test sort entity bundle in ascending order, property in descending order, with a field condition.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->entityOrderBy('bundle', 'DESC')
+      ->propertyOrderBy('ftid', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test sort entity bundle in descending order, property in ascending order, with a field condition.'), TRUE);
+
+    // Test entity sort by bundle, field.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityOrderBy('bundle', 'ASC')
+      ->fieldOrderBy($this->fields[0], 'value', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+    ), t('Test sort entity bundle in ascending order, field in descending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityOrderBy('bundle', 'DESC')
+      ->fieldOrderBy($this->fields[0], 'value', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test sort entity bundle in descending order, field in ascending order.'), TRUE);
+
+    // Test entity sort by revision_id.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->entityOrderBy('revision_id', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 1),
+      array('test_entity', 2),
+      array('test_entity', 3),
+      array('test_entity', 4),
+    ), t('Test sort entity revision_id in ascending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->entityOrderBy('revision_id', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 4),
+      array('test_entity', 3),
+      array('test_entity', 2),
+      array('test_entity', 1),
+    ), t('Test sort entity revision_id in descending order.'), TRUE);
+
+    // Test entity sort by revision_id, with a field condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->entityOrderBy('revision_id', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 1),
+      array('test_entity', 2),
+      array('test_entity', 3),
+      array('test_entity', 4),
+    ), t('Test sort entity revision_id in ascending order, with a field condition.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->entityOrderBy('revision_id', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 4),
+      array('test_entity', 3),
+      array('test_entity', 2),
+      array('test_entity', 1),
+    ), t('Test sort entity revision_id in descending order, with a field condition.'), TRUE);
+
+    // Test property sort by revision_id.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->propertyOrderBy('ftvid', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 1),
+      array('test_entity', 2),
+      array('test_entity', 3),
+      array('test_entity', 4),
+    ), t('Test sort entity revision_id property in ascending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->propertyOrderBy('ftvid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 4),
+      array('test_entity', 3),
+      array('test_entity', 2),
+      array('test_entity', 1),
+    ), t('Test sort entity revision_id property in descending order.'), TRUE);
+
+    // Test property sort by revision_id, with a field condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->propertyOrderBy('ftvid', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 1),
+      array('test_entity', 2),
+      array('test_entity', 3),
+      array('test_entity', 4),
+    ), t('Test sort entity revision_id property in ascending order, with a field condition.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->propertyOrderBy('ftvid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 4),
+      array('test_entity', 3),
+      array('test_entity', 2),
+      array('test_entity', 1),
+    ), t('Test sort entity revision_id property in descending order, with a field condition.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldOrderBy($this->fields[0], 'value', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test sort field in ascending order without field condition.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldOrderBy($this->fields[0], 'value', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test sort field in descending order without field condition.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->fieldOrderBy($this->fields[0], 'value', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test sort field in ascending order.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->fieldOrderBy($this->fields[0], 'value', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test sort field in descending order.'), TRUE);
+
+    // Test "in" operation with entity entity_type condition and entity_id
+    // property condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', array(1, 3, 4), 'IN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test "in" operation with entity entity_type condition and entity_id property condition.'));
+
+    // Test "in" operation with entity entity_type condition and entity_id
+    // property condition. Sort in descending order by entity_id.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', array(1, 3, 4), 'IN')
+      ->propertyOrderBy('ftid', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 1),
+    ), t('Test "in" operation with entity entity_type condition and entity_id property condition. Sort entity_id in descending order.'), TRUE);
+
+    // Test query count
+    $query = new EntityFieldQuery();
+    $query_count = $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->count()
+      ->execute();
+    $this->assertEqual($query_count, 6, t('Test query count on entity condition.'));
+
+    $query = new EntityFieldQuery();
+    $query_count = $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', '1')
+      ->count()
+      ->execute();
+    $this->assertEqual($query_count, 1, t('Test query count on entity and property condition.'));
+
+    $query = new EntityFieldQuery();
+    $query_count = $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', '4', '>')
+      ->count()
+      ->execute();
+    $this->assertEqual($query_count, 2, t('Test query count on entity and property condition with operator.'));
+
+    $query = new EntityFieldQuery();
+    $query_count = $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 3, '=')
+      ->count()
+      ->execute();
+    $this->assertEqual($query_count, 1, t('Test query count on field condition.'));
+
+    // First, test without options.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('fttype', 'und', 'CONTAINS');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test the "contains" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[1], 'shape', 'uar', 'CONTAINS');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle', 5),
+    ), t('Test the "contains" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 1, '=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+    ), t('Test the "equal to" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 3, '=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity', 3),
+    ), t('Test the "equal to" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 3, '<>');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test the "not equal to" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 3, '<>');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+      array('test_entity', 1),
+      array('test_entity', 2),
+      array('test_entity', 4),
+    ), t('Test the "not equal to" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 2, '<');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+    ), t('Test the "less than" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 2, '<');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity', 1),
+    ), t('Test the "less than" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 2, '<=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+    ), t('Test the "less than or equal to" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 2, '<=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity', 1),
+      array('test_entity', 2),
+    ), t('Test the "less than or equal to" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 4, '>');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test the "greater than" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 2, '>');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+      array('test_entity', 3),
+      array('test_entity', 4),
+    ), t('Test the "greater than" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 4, '>=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test the "greater than or equal to" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 3, '>=');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+      array('test_entity', 3),
+      array('test_entity', 4),
+    ), t('Test the "greater than or equal to" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', array(3, 4), 'NOT IN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test the "not in" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', array(3, 4, 100, 101), 'NOT IN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+      array('test_entity', 1),
+      array('test_entity', 2),
+    ), t('Test the "not in" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', array(3, 4), 'IN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test the "in" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', array(2, 3), 'IN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity', 2),
+      array('test_entity', 3),
+    ), t('Test the "in" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', array(1, 3), 'BETWEEN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+    ), t('Test the "between" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', array(1, 3), 'BETWEEN');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity', 1),
+      array('test_entity', 2),
+      array('test_entity', 3),
+    ), t('Test the "between" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('fttype', 'bun', 'STARTS_WITH');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test the "starts_with" operation on a property.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle', 5),
+    ), t('Test the "starts_with" operation on a field.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 3);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity', 3),
+    ), t('Test omission of an operator with a single item.'));
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', array(2, 3));
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity', 2),
+      array('test_entity', 3),
+    ), t('Test omission of an operator with multiple items.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyCondition('ftid', 1, '>')
+      ->fieldCondition($this->fields[0], 'value', 4, '<');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+    ), t('Test entity, property and field conditions.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->entityCondition('bundle', 'bundle', 'STARTS_WITH')
+      ->propertyCondition('ftid', 4)
+      ->fieldCondition($this->fields[0], 'value', 4);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+    ), t('Test entity condition with "starts_with" operation, and property and field conditions.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyOrderBy('ftid', 'ASC')
+      ->range(0, 2);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+    ), t('Test limit on a property.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>=')
+      ->fieldOrderBy($this->fields[0], 'value', 'ASC')
+      ->range(0, 2);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+    ), t('Test limit on a field.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyOrderBy('ftid', 'ASC')
+      ->range(4, 6);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test offset on a property.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->fieldOrderBy($this->fields[0], 'value', 'ASC')
+      ->range(2, 4);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test offset on a field.'), TRUE);
+
+    for ($i = 6; $i < 10; $i++) {
+      $entity = new stdClass();
+      $entity->ftid = $i;
+      $entity->fttype = 'test_entity_bundle';
+      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i - 5;
+      drupal_write_record('test_entity_bundle', $entity);
+      field_attach_insert('test_entity_bundle', $entity);
+    }
+
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', 2, '>');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+      array('test_entity', 3),
+      array('test_entity', 4),
+      array('test_entity_bundle', 8),
+      array('test_entity_bundle', 9),
+    ), t('Select a field across multiple entities.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->fieldCondition($this->fields[1], 'shape', 'square')
+      ->fieldCondition($this->fields[1], 'color', 'blue');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle', 5),
+    ), t('Test without a delta group.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->fieldCondition($this->fields[1], 'shape', 'square', '=', 'group')
+      ->fieldCondition($this->fields[1], 'color', 'blue', '=', 'group');
+    $this->assertEntityFieldQuery($query, array(), t('Test with a delta group.'));
+
+    // Test query on a deleted field.
+    field_attach_delete_bundle('test_entity_bundle_key', 'bundle1');
+    field_attach_delete_bundle('test_entity', 'test_bundle');
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', '3');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle', 8),
+    ), t('Test query on a field after deleting field from some entities.'));
+
+    field_attach_delete_bundle('test_entity_bundle', 'test_entity_bundle');
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', '3');
+    $this->assertEntityFieldQuery($query, array(), t('Test query on a field after deleting field from all entities.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->fieldCondition($this->fields[0], 'value', '3')
+      ->deleted(TRUE);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle', 8),
+      array('test_entity', 3),
+    ), t('Test query on a deleted field with deleted option set to TRUE.'));
+
+    $pass = FALSE;
+    $query = new EntityFieldQuery();
+    try {
+      $query->execute();
+    }
+    catch (EntityFieldQueryException $exception) {
+      $pass = ($exception->getMessage() == t('For this query an entity type must be specified.'));
+    }
+    $this->assertTrue($pass, t("Can't query the universe."));
+  }
+
+  /**
+   * Tests field meta conditions.
+   */
+  function testEntityFieldQueryMetaConditions() {
+    // Make a test field translatable.
+    $this->fields[0]['translatable'] = TRUE;
+    field_update_field($this->fields[0]);
+    field_test_entity_info_translatable('test_entity', TRUE);
+    drupal_static_reset('field_available_languages');
+
+    // Create more items with different languages.
+    $entity = new stdClass();
+    $entity->ftid = 1;
+    $entity->ftvid = 1;
+    $entity->fttype = 'test_bundle';
+    $j = 0;
+
+    foreach (array(LANGUAGE_NONE, 'en') as $langcode) {
+      for ($i = 0; $i < 4; $i++) {
+        $entity->{$this->field_names[0]}[$langcode][$i]['value'] = $i + $j;
+      }
+      $j += 4;
+    }
+
+    field_attach_update('test_entity', $entity);
+
+    // Test delta field meta condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity', '=')
+      ->fieldDeltaCondition($this->fields[0], 0, '>');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 1),
+    ), t('Test with a delta meta condition.'));
+
+    // Test language field meta condition.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity', '=')
+      ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 1),
+    ), t('Test with a language meta condition.'));
+
+    // Test delta grouping.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity', '=')
+      ->fieldCondition($this->fields[0], 'value', 0, '=', 'group')
+      ->fieldDeltaCondition($this->fields[0], 1, '<', 'group');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 1),
+    ), t('Test with a grouped delta meta condition.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity', '=')
+      ->fieldCondition($this->fields[0], 'value', 0, '=', 'group')
+      ->fieldDeltaCondition($this->fields[0], 1, '>=', 'group');
+    $this->assertEntityFieldQuery($query, array(), t('Test with a grouped delta meta condition (empty result set).'));
+
+    // Test language grouping.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity', '=')
+      ->fieldCondition($this->fields[0], 'value', 0, '=', NULL, 'group')
+      ->fieldLanguageCondition($this->fields[0], 'en', '<>', NULL, 'group');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 1),
+    ), t('Test with a grouped language meta condition.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity', '=')
+      ->fieldCondition($this->fields[0], 'value', 0, '=', NULL, 'group')
+      ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>', NULL, 'group');
+    $this->assertEntityFieldQuery($query, array(), t('Test with a grouped language meta condition (empty result set).'));
+
+    // Test delta and language grouping.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity', '=')
+      ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+      ->fieldDeltaCondition($this->fields[0], 1, '<', 'delta', 'language')
+      ->fieldLanguageCondition($this->fields[0], 'en', '<>', 'delta', 'language');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity', 1),
+    ), t('Test with a grouped delta + language meta condition.'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity', '=')
+      ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+      ->fieldDeltaCondition($this->fields[0], 1, '>=', 'delta', 'language')
+      ->fieldLanguageCondition($this->fields[0], 'en', '<>', 'delta', 'language');
+    $this->assertEntityFieldQuery($query, array(), t('Test with a grouped delta + language meta condition (empty result set, delta condition unsatisifed).'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity', '=')
+      ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+      ->fieldDeltaCondition($this->fields[0], 1, '<', 'delta', 'language')
+      ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>', 'delta', 'language');
+    $this->assertEntityFieldQuery($query, array(), t('Test with a grouped delta + language meta condition (empty result set, language condition unsatisifed).'));
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity', '=')
+      ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+      ->fieldDeltaCondition($this->fields[0], 1, '>=', 'delta', 'language')
+      ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>', 'delta', 'language');
+    $this->assertEntityFieldQuery($query, array(), t('Test with a grouped delta + language meta condition (empty result set, both conditions unsatisifed).'));
+
+    // Test grouping with another field to ensure that grouping cache is reset
+    // properly.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle', '=')
+      ->fieldCondition($this->fields[1], 'shape', 'circle', '=', 'delta', 'language')
+      ->fieldCondition($this->fields[1], 'color', 'blue', '=', 'delta', 'language')
+      ->fieldDeltaCondition($this->fields[1], 1, '=', 'delta', 'language')
+      ->fieldLanguageCondition($this->fields[1], LANGUAGE_NONE, '=', 'delta', 'language');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle', 5),
+    ), t('Test grouping cache.'));
+  }
+
+  /**
+   * Tests the routing feature of EntityFieldQuery.
+   */
+  function testEntityFieldQueryRouting() {
+    // Entity-only query.
+    $query = new EntityFieldQuery();
+    $query->entityCondition('entity_type', 'test_entity_bundle_key');
+    $this->assertIdentical($query->queryCallback(), array($query, 'propertyQuery'), t('Entity-only queries are handled by the propertyQuery handler.'));
+
+    // Field-only query.
+    $query = new EntityFieldQuery();
+    $query->fieldCondition($this->fields[0], 'value', '3');
+    $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', t('Pure field queries are handled by the Field storage handler.'));
+
+    // Mixed entity and field query.
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', '3');
+    $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', t('Mixed queries are handled by the Field storage handler.'));
+
+    // Overriding with $query->executeCallback.
+    $query = new EntityFieldQuery();
+    $query->entityCondition('entity_type', 'test_entity_bundle_key');
+    $query->executeCallback = 'field_test_dummy_field_storage_query';
+    $this->assertEntityFieldQuery($query, array(
+      array('user', 1),
+    ), t('executeCallback can override the query handler.'));
+
+    // Overriding with $query->executeCallback via hook_entity_query_alter().
+    $query = new EntityFieldQuery();
+    $query->entityCondition('entity_type', 'test_entity_bundle_key');
+    // Add a flag that will be caught by field_test_entity_query_alter().
+    $query->alterMyExecuteCallbackPlease = TRUE;
+    $this->assertEntityFieldQuery($query, array(
+      array('user', 1),
+    ), t('executeCallback can override the query handler when set in a hook_entity_query_alter().'));
+
+    // Mixed-storage queries.
+    $query = new EntityFieldQuery();
+    $query
+      ->fieldCondition($this->fields[0], 'value', '3')
+      ->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH');
+    // Alter the storage of the field.
+    $query->fields[1]['storage']['module'] = 'dummy_storage';
+    try {
+      $query->queryCallback();
+    }
+    catch (EntityFieldQueryException $exception) {
+      $pass = ($exception->getMessage() == t("Can't handle more than one field storage engine"));
+    }
+    $this->assertTrue($pass, t('Cannot query across field storage engines.'));
+  }
+
+  /**
+   * Tests the pager integration of EntityFieldQuery.
+   */
+  function testEntityFieldQueryPager() {
+    // Test pager in propertyQuery
+    $_GET['page'] = '0,1';
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyOrderBy('ftid', 'ASC')
+      ->pager(3, 0);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+    ), t('Test pager integration in propertyQuery: page 1.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->propertyOrderBy('ftid', 'ASC')
+      ->pager(3, 1);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test pager integration in propertyQuery: page 2.'), TRUE);
+
+    // Test pager in field storage
+    $_GET['page'] = '0,1';
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->propertyOrderBy('ftid', 'ASC')
+      ->pager(2, 0);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+    ), t('Test pager integration in field storage: page 1.'), TRUE);
+
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->propertyOrderBy('ftid', 'ASC')
+      ->pager(2, 1);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test pager integration in field storage: page 2.'), TRUE);
+
+    unset($_GET['page']);
+  }
+
+  /**
+   * Tests the TableSort integration of EntityFieldQuery.
+   */
+  function testEntityFieldQueryTableSort() {
+    // Test TableSort in propertyQuery
+    $_GET['sort'] = 'asc';
+    $_GET['order'] = 'Id';
+    $header = array(
+      'id' => array('data' => 'Id', 'type' => 'property',  'specifier' => 'ftid'),
+      'type' => array('data' => 'Type', 'type' => 'entity', 'specifier' => 'bundle'),
+    );
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->tableSort($header);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test TableSort by property: ftid ASC in propertyQuery.'), TRUE);
+
+    $_GET['sort'] = 'desc';
+    $_GET['order'] = 'Id';
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->tableSort($header);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test TableSort by property: ftid DESC in propertyQuery.'), TRUE);
+
+    $_GET['sort'] = 'asc';
+    $_GET['order'] = 'Type';
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->tableSort($header);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test TableSort by entity: bundle ASC in propertyQuery.'), TRUE);
+
+    $_GET['sort'] = 'desc';
+    $_GET['order'] = 'Type';
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->tableSort($header);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test TableSort by entity: bundle DESC in propertyQuery.'), TRUE);
+
+    // Test TableSort in field storage
+    $_GET['sort'] = 'asc';
+    $_GET['order'] = 'Id';
+    $header = array(
+      'id' => array('data' => 'Id', 'type' => 'property',  'specifier' => 'ftid'),
+      'type' => array('data' => 'Type', 'type' => 'entity', 'specifier' => 'bundle'),
+      'field' => array('data' => 'Field', 'type' => 'field', 'specifier' => array('field' => $this->field_names[0], 'column' => 'value')),
+    );
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->tableSort($header);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test TableSort by property: ftid ASC in field storage.'), TRUE);
+
+    $_GET['sort'] = 'desc';
+    $_GET['order'] = 'Id';
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->tableSort($header);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test TableSort by property: ftid DESC in field storage.'), TRUE);
+
+    $_GET['sort'] = 'asc';
+    $_GET['order'] = 'Type';
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->tableSort($header)
+      ->entityOrderBy('entity_id', 'DESC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+    ), t('Test TableSort by entity: bundle ASC in field storage.'), TRUE);
+
+    $_GET['sort'] = 'desc';
+    $_GET['order'] = 'Type';
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->tableSort($header)
+      ->entityOrderBy('entity_id', 'ASC');
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+    ), t('Test TableSort by entity: bundle DESC in field storage.'), TRUE);
+
+    $_GET['sort'] = 'asc';
+    $_GET['order'] = 'Field';
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->tableSort($header);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 1),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 6),
+    ), t('Test TableSort by field ASC.'), TRUE);
+
+    $_GET['sort'] = 'desc';
+    $_GET['order'] = 'Field';
+    $query = new EntityFieldQuery();
+    $query
+      ->entityCondition('entity_type', 'test_entity_bundle_key')
+      ->fieldCondition($this->fields[0], 'value', 0, '>')
+      ->tableSort($header);
+    $this->assertEntityFieldQuery($query, array(
+      array('test_entity_bundle_key', 6),
+      array('test_entity_bundle_key', 5),
+      array('test_entity_bundle_key', 4),
+      array('test_entity_bundle_key', 3),
+      array('test_entity_bundle_key', 2),
+      array('test_entity_bundle_key', 1),
+    ), t('Test TableSort by field DESC.'), TRUE);
+
+    unset($_GET['sort']);
+    unset($_GET['order']);
+  }
+
+  /**
+   * Fetches the results of an EntityFieldQuery and compares.
+   *
+   * @param $query
+   *   An EntityFieldQuery to run.
+   * @param $intended_results
+   *   A list of results, every entry is again a list, first being the entity
+   *   type, the second being the entity_id.
+   * @param $message
+   *   The message to be displayed as the result of this test.
+   * @param $ordered
+   *   If FALSE then the result of EntityFieldQuery will match
+   *   $intended_results even if the order is not the same. If TRUE then order
+   *   should match too.
+   */
+  function assertEntityFieldQuery($query, $intended_results, $message, $ordered = FALSE) {
+    $results = array();
+    try {
+      foreach ($query->execute() as $entity_type => $entity_ids) {
+        foreach ($entity_ids as $entity_id => $stub_entity) {
+          $results[] = array($entity_type, $entity_id);
+        }
+      }
+      if (!isset($ordered) || !$ordered) {
+        sort($results);
+        sort($intended_results);
+      }
+      $this->assertEqual($results, $intended_results, $message);
+    }
+    catch (Exception $e) {
+      $this->fail('Exception thrown: '. $e->getMessage());
+    }
+  }
+}
diff --git a/modules/entity/tests/entity_test.info b/modules/entity/tests/entity_test.info
new file mode 100644
index 0000000..ae6006b
--- /dev/null
+++ b/modules/entity/tests/entity_test.info
@@ -0,0 +1,7 @@
+name = Entity CRUD test module
+description = Provides entity types based upon the CRUD API.
+package = Testing
+version = VERSION
+core = 8.x
+dependencies[] = entity
+hidden = TRUE
diff --git a/modules/entity/tests/entity_test.install b/modules/entity/tests/entity_test.install
new file mode 100644
index 0000000..ec2e5bd
--- /dev/null
+++ b/modules/entity/tests/entity_test.install
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the entity_test module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function entity_test_install() {
+  // Auto-create a field for testing.
+  $field = array(
+    'field_name' => 'field_test_text',
+    'type' => 'text',
+    'cardinality' => 1,
+    'translatable' => FALSE,
+  );
+  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,
+    ),
+  );
+  field_create_instance($instance);
+}
+
+/**
+ * Implements hook_schema().
+ */
+function entity_test_schema() {
+  $schema['entity_test'] = array(
+    'description' => 'Stores entity_test items.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique entity-test item ID.',
+      ),
+      'name' => array(
+        'description' => 'The name of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'uid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+        'description' => "The {users}.uid of the associated user.",
+      ),
+    ),
+    'indexes' => array(
+      'uid' => array('uid'),
+    ),
+    'foreign keys' => array(
+      'uid' => array('users' => 'uid'),
+    ),
+    'primary key' => array('id'),
+  );
+  return $schema;
+}
diff --git a/modules/entity/tests/entity_test.module b/modules/entity/tests/entity_test.module
new file mode 100644
index 0000000..2a24a18
--- /dev/null
+++ b/modules/entity/tests/entity_test.module
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @file
+ * Test module for the entity API providing an entity type for testing.
+ */
+
+/**
+ * Implements hook_entity_info().
+ */
+function entity_test_entity_info() {
+  $return = array(
+    'entity_test' => array(
+      'label' => t('Test entity'),
+      'entity class' => 'Entity',
+      'controller class' => 'EntityDefaultStorageController',
+      'base table' => 'entity_test',
+      'fieldable' => TRUE,
+      'entity keys' => array(
+        'id' => 'id',
+      ),
+    ),
+  );
+  return $return;
+}
+
+/**
+ * Load a test-entity.
+ *
+ * @param $id
+ *   A test-entity id.
+ * @param $reset
+ *   A boolean indicating that the internal cache should be reset.
+ *
+ * @return Entity
+ *   The loaded entity object or FALSE.
+ */
+function entity_test_load($id, $reset = FALSE) {
+  $result = entity_load('entity_test', array($id), array(), $reset);
+  return reset($result);
+}
+
+/**
+ * Loads multiple test entities based on certain conditions.
+ *
+ * @param $ids
+ *   An array of entity IDs.
+ * @param $conditions
+ *   An array of conditions to match against the {entity} table.
+ * @param $reset
+ *   A boolean indicating that the internal cache should be reset.
+ *
+ * @return
+ *   An array of test entity objects, indexed by id.
+ */
+function entity_test_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) {
+  return entity_load('entity_test', $ids, $conditions, $reset);
+}
+
+/**
+ * Deletes multiple test entities.
+ *
+ * @param $ids
+ *   An array of test entity IDs.
+ */
+function entity_test_delete_multiple(array $ids) {
+  entity_get_controller('entity_test')->delete($ids);
+}
diff --git a/modules/field/field.info b/modules/field/field.info
index bf419e3..c61c501 100644
--- a/modules/field/field.info
+++ b/modules/field/field.info
@@ -7,5 +7,6 @@ files[] = field.module
 files[] = field.attach.inc
 files[] = tests/field.test
 dependencies[] = field_sql_storage
+dependencies[] = entity
 required = TRUE
 stylesheets[all][] = theme/field.css
diff --git a/modules/node/node.info b/modules/node/node.info
index 7f2c7ff..2e410ed 100644
--- a/modules/node/node.info
+++ b/modules/node/node.info
@@ -6,5 +6,6 @@ core = 8.x
 files[] = node.module
 files[] = node.test
 required = TRUE
+dependencies[] = entity
 configure = admin/structure/types
 stylesheets[all][] = node.css
diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info
index 54b020d..926ade6 100644
--- a/modules/simpletest/simpletest.info
+++ b/modules/simpletest/simpletest.info
@@ -15,8 +15,6 @@ files[] = tests/bootstrap.test
 files[] = tests/cache.test
 files[] = tests/common.test
 files[] = tests/database_test.test
-files[] = tests/entity_crud_hook_test.test
-files[] = tests/entity_query.test
 files[] = tests/error.test
 files[] = tests/file.test
 files[] = tests/filetransfer.test
diff --git a/modules/simpletest/tests/entity_cache_test.info b/modules/simpletest/tests/entity_cache_test.info
deleted file mode 100644
index c13496e..0000000
--- a/modules/simpletest/tests/entity_cache_test.info
+++ /dev/null
@@ -1,7 +0,0 @@
-name = "Entity cache test"
-description = "Support module for testing entity cache."
-package = Testing
-version = VERSION
-core = 8.x
-dependencies[] = entity_cache_test_dependency
-hidden = TRUE
diff --git a/modules/simpletest/tests/entity_cache_test.module b/modules/simpletest/tests/entity_cache_test.module
deleted file mode 100644
index 5ae9ecc..0000000
--- a/modules/simpletest/tests/entity_cache_test.module
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-/**
- * @file
- * Helper module for entity cache tests.
- */
-
-/**
- * Implements hook_watchdog().
- *
- * This hook is called during module_enable() and since this hook
- * implementation is invoked, we have to expect that this module and dependent
- * modules have been properly installed already. So we expect to be able to
- * retrieve the entity information that has been registered by the required
- * dependency module.
- *
- * @see EnableDisableTestCase::testEntityCache()
- * @see entity_cache_test_dependency_entity_info()
- */
-function entity_cache_test_watchdog($log_entry) {
-  if ($log_entry['type'] == 'system' && $log_entry['message'] == '%module module installed.') {
-    $info = entity_get_info('entity_cache_test');
-    // Store the information in a system variable to analyze it later in the
-    // test case.
-    variable_set('entity_cache_test', $info);
-  }
-}
diff --git a/modules/simpletest/tests/entity_cache_test_dependency.info b/modules/simpletest/tests/entity_cache_test_dependency.info
deleted file mode 100644
index 17d551c..0000000
--- a/modules/simpletest/tests/entity_cache_test_dependency.info
+++ /dev/null
@@ -1,6 +0,0 @@
-name = "Entity cache test dependency"
-description = "Support dependency module for testing entity cache."
-package = Testing
-version = VERSION
-core = 8.x
-hidden = TRUE
diff --git a/modules/simpletest/tests/entity_cache_test_dependency.module b/modules/simpletest/tests/entity_cache_test_dependency.module
deleted file mode 100644
index 73a1149..0000000
--- a/modules/simpletest/tests/entity_cache_test_dependency.module
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
- * @file
- * Helper module for entity cache tests.
- */
-
-/**
- * Implements hook_entity_info().
- */
-function entity_cache_test_dependency_entity_info() {
-  return array(
-    'entity_cache_test' => array(
-      'label' => 'Entity Cache Test',
-    ),
-  );
-}
diff --git a/modules/simpletest/tests/entity_crud_hook_test.info b/modules/simpletest/tests/entity_crud_hook_test.info
deleted file mode 100644
index 28ce1b5..0000000
--- a/modules/simpletest/tests/entity_crud_hook_test.info
+++ /dev/null
@@ -1,6 +0,0 @@
-name = "Entity CRUD Hooks Test"
-description = "Support module for CRUD hook tests."
-core = 8.x
-package = Testing
-version = VERSION
-hidden = TRUE
diff --git a/modules/simpletest/tests/entity_crud_hook_test.module b/modules/simpletest/tests/entity_crud_hook_test.module
deleted file mode 100644
index 873a162..0000000
--- a/modules/simpletest/tests/entity_crud_hook_test.module
+++ /dev/null
@@ -1,266 +0,0 @@
-<?php
-
-//
-// Presave hooks
-//
-
-/**
- * Implements hook_entity_presave().
- */
-function entity_crud_hook_test_entity_presave($entity, $type) {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
-}
-
-/**
- * Implements hook_comment_presave().
- */
-function entity_crud_hook_test_comment_presave() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_file_presave().
- */
-function entity_crud_hook_test_file_presave() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_node_presave().
- */
-function entity_crud_hook_test_node_presave() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_taxonomy_term_presave().
- */
-function entity_crud_hook_test_taxonomy_term_presave() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_taxonomy_vocabulary_presave().
- */
-function entity_crud_hook_test_taxonomy_vocabulary_presave() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_user_presave().
- */
-function entity_crud_hook_test_user_presave() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-//
-// Insert hooks
-//
-
-/**
- * Implements hook_entity_insert().
- */
-function entity_crud_hook_test_entity_insert($entity, $type) {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
-}
-
-/**
- * Implements hook_comment_insert().
- */
-function entity_crud_hook_test_comment_insert() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_file_insert().
- */
-function entity_crud_hook_test_file_insert() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_node_insert().
- */
-function entity_crud_hook_test_node_insert() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_taxonomy_term_insert().
- */
-function entity_crud_hook_test_taxonomy_term_insert() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_taxonomy_vocabulary_insert().
- */
-function entity_crud_hook_test_taxonomy_vocabulary_insert() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_user_insert().
- */
-function entity_crud_hook_test_user_insert() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-//
-// Load hooks
-//
-
-/**
- * Implements hook_entity_load().
- */
-function entity_crud_hook_test_entity_load(array $entities, $type) {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
-}
-
-/**
- * Implements hook_comment_load().
- */
-function entity_crud_hook_test_comment_load() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_file_load().
- */
-function entity_crud_hook_test_file_load() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_node_load().
- */
-function entity_crud_hook_test_node_load() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_taxonomy_term_load().
- */
-function entity_crud_hook_test_taxonomy_term_load() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_taxonomy_vocabulary_load().
- */
-function entity_crud_hook_test_taxonomy_vocabulary_load() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_user_load().
- */
-function entity_crud_hook_test_user_load() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-//
-// Update hooks
-//
-
-/**
- * Implements hook_entity_update().
- */
-function entity_crud_hook_test_entity_update($entity, $type) {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
-}
-
-/**
- * Implements hook_comment_update().
- */
-function entity_crud_hook_test_comment_update() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_file_update().
- */
-function entity_crud_hook_test_file_update() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_node_update().
- */
-function entity_crud_hook_test_node_update() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_taxonomy_term_update().
- */
-function entity_crud_hook_test_taxonomy_term_update() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_taxonomy_vocabulary_update().
- */
-function entity_crud_hook_test_taxonomy_vocabulary_update() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_user_update().
- */
-function entity_crud_hook_test_user_update() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-//
-// Delete hooks
-//
-
-/**
- * Implements hook_entity_delete().
- */
-function entity_crud_hook_test_entity_delete($entity, $type) {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
-}
-
-/**
- * Implements hook_comment_delete().
- */
-function entity_crud_hook_test_comment_delete() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_file_delete().
- */
-function entity_crud_hook_test_file_delete() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_node_delete().
- */
-function entity_crud_hook_test_node_delete() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_taxonomy_term_delete().
- */
-function entity_crud_hook_test_taxonomy_term_delete() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_taxonomy_vocabulary_delete().
- */
-function entity_crud_hook_test_taxonomy_vocabulary_delete() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
-
-/**
- * Implements hook_user_delete().
- */
-function entity_crud_hook_test_user_delete() {
-  $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
-}
diff --git a/modules/simpletest/tests/entity_crud_hook_test.test b/modules/simpletest/tests/entity_crud_hook_test.test
deleted file mode 100644
index 3f18fc8..0000000
--- a/modules/simpletest/tests/entity_crud_hook_test.test
+++ /dev/null
@@ -1,332 +0,0 @@
-<?php
-
-/**
- * Test invocation of hooks when inserting, loading, updating or deleting an
- * entity. Tested hooks are:
- * - hook_entity_insert()
- * - hook_entity_load()
- * - hook_entity_update()
- * - hook_entity_delete()
- * As well as all type-specific hooks, like hook_node_insert(),
- * hook_comment_update(), etc.
- */
-class EntityCrudHookTestCase extends DrupalWebTestCase {
-
-  protected $ids = array();
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Entity CRUD hooks',
-      'description' => 'Tests the invocation of hooks when inserting, loading, updating or deleting an entity.',
-      'group' => 'Entity API',
-    );
-  }
-
-  public function setUp() {
-    parent::setUp('entity_crud_hook_test', 'taxonomy', 'comment');
-  }
-
-  /**
-   * Pass if the message $text was set by one of the CRUD hooks in
-   * entity_crud_hook_test.module, i.e., if the $text is an element of
-   * $_SESSION['entity_crud_hook_test'].
-   *
-   * @param $text
-   *   Plain text to look for.
-   * @param $message
-   *   Message to display.
-   * @param $group
-   *   The group this message belongs to, defaults to 'Other'.
-   * @return
-   *   TRUE on pass, FALSE on fail.
-   */
-  protected function assertHookMessage($text, $message = NULL, $group = 'Other') {
-    if (!isset($message)) {
-      $message = $text;
-    }
-    return $this->assertTrue(array_search($text, $_SESSION['entity_crud_hook_test']) !== FALSE, $message, $group);
-  }
-
-  /**
-   * Test hook invocations for CRUD operations on comments.
-   */
-  public function testCommentHooks() {
-    $node = (object) array(
-      'uid' => 1,
-      'type' => 'article',
-      'title' => 'Test node',
-      'status' => 1,
-      'comment' => 2,
-      'promote' => 0,
-      'sticky' => 0,
-      'language' => LANGUAGE_NONE,
-      'created' => REQUEST_TIME,
-      'changed' => REQUEST_TIME,
-    );
-    node_save($node);
-    $nid = $node->nid;
-
-    $comment = (object) array(
-      'cid' => NULL,
-      'pid' => 0,
-      'nid' => $nid,
-      'uid' => 1,
-      'subject' => 'Test comment',
-      'created' => REQUEST_TIME,
-      'changed' => REQUEST_TIME,
-      'status' => 1,
-      'language' => LANGUAGE_NONE,
-    );
-    $_SESSION['entity_crud_hook_test'] = array();
-    comment_save($comment);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
-    $this->assertHookMessage('entity_crud_hook_test_comment_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type comment');
-    $this->assertHookMessage('entity_crud_hook_test_comment_insert called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $comment = comment_load($comment->cid);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type comment');
-    $this->assertHookMessage('entity_crud_hook_test_comment_load called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $comment->subject = 'New subject';
-    comment_save($comment);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
-    $this->assertHookMessage('entity_crud_hook_test_comment_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type comment');
-    $this->assertHookMessage('entity_crud_hook_test_comment_update called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    comment_delete($comment->cid);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type comment');
-    $this->assertHookMessage('entity_crud_hook_test_comment_delete called');
-  }
-
-  /**
-   * Test hook invocations for CRUD operations on files.
-   */
-  public function testFileHooks() {
-    $url = 'public://entity_crud_hook_test.file';
-    file_put_contents($url, 'Test test test');
-    $file = (object) array(
-      'fid' => NULL,
-      'uid' => 1,
-      'filename' => 'entity_crud_hook_test.file',
-      'uri' => $url,
-      'filemime' => 'text/plain',
-      'filesize' => filesize($url),
-      'status' => 1,
-      'timestamp' => REQUEST_TIME,
-    );
-    $_SESSION['entity_crud_hook_test'] = array();
-    file_save($file);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
-    $this->assertHookMessage('entity_crud_hook_test_file_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type file');
-    $this->assertHookMessage('entity_crud_hook_test_file_insert called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $file = file_load($file->fid);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type file');
-    $this->assertHookMessage('entity_crud_hook_test_file_load called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $file->filename = 'new.entity_crud_hook_test.file';
-    file_save($file);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
-    $this->assertHookMessage('entity_crud_hook_test_file_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type file');
-    $this->assertHookMessage('entity_crud_hook_test_file_update called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    file_delete($file);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type file');
-    $this->assertHookMessage('entity_crud_hook_test_file_delete called');
-  }
-
-  /**
-   * Test hook invocations for CRUD operations on nodes.
-   */
-  public function testNodeHooks() {
-    $node = (object) array(
-      'uid' => 1,
-      'type' => 'article',
-      'title' => 'Test node',
-      'status' => 1,
-      'comment' => 2,
-      'promote' => 0,
-      'sticky' => 0,
-      'language' => LANGUAGE_NONE,
-      'created' => REQUEST_TIME,
-      'changed' => REQUEST_TIME,
-    );
-    $_SESSION['entity_crud_hook_test'] = array();
-    node_save($node);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
-    $this->assertHookMessage('entity_crud_hook_test_node_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type node');
-    $this->assertHookMessage('entity_crud_hook_test_node_insert called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $node = node_load($node->nid);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type node');
-    $this->assertHookMessage('entity_crud_hook_test_node_load called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $node->title = 'New title';
-    node_save($node);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
-    $this->assertHookMessage('entity_crud_hook_test_node_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type node');
-    $this->assertHookMessage('entity_crud_hook_test_node_update called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    node_delete($node->nid);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type node');
-    $this->assertHookMessage('entity_crud_hook_test_node_delete called');
-  }
-
-  /**
-   * Test hook invocations for CRUD operations on taxonomy terms.
-   */
-  public function testTaxonomyTermHooks() {
-    $vocabulary = (object) array(
-      'name' => 'Test vocabulary',
-      'machine_name' => 'test',
-      'description' => NULL,
-      'module' => 'entity_crud_hook_test',
-    );
-    taxonomy_vocabulary_save($vocabulary);
-
-    $term = (object) array(
-      'vid' => $vocabulary->vid,
-      'name' => 'Test term',
-      'description' => NULL,
-      'format' => 1,
-    );
-    $_SESSION['entity_crud_hook_test'] = array();
-    taxonomy_term_save($term);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_term');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_insert called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $term = taxonomy_term_load($term->tid);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_term');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_load called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $term->name = 'New name';
-    taxonomy_term_save($term);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_term');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_update called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    taxonomy_term_delete($term->tid);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_term');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_delete called');
-  }
-
-  /**
-   * Test hook invocations for CRUD operations on taxonomy vocabularies.
-   */
-  public function testTaxonomyVocabularyHooks() {
-    $vocabulary = (object) array(
-      'name' => 'Test vocabulary',
-      'machine_name' => 'test',
-      'description' => NULL,
-      'module' => 'entity_crud_hook_test',
-    );
-    $_SESSION['entity_crud_hook_test'] = array();
-    taxonomy_vocabulary_save($vocabulary);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_insert called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $vocabulary = taxonomy_vocabulary_load($vocabulary->vid);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_vocabulary');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_load called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $vocabulary->name = 'New name';
-    taxonomy_vocabulary_save($vocabulary);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_vocabulary');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_update called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    taxonomy_vocabulary_delete($vocabulary->vid);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary');
-    $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_delete called');
-  }
-
-  /**
-   * Test hook invocations for CRUD operations on users.
-   */
-  public function testUserHooks() {
-    $edit = array(
-      'name' => 'Test user',
-      'mail' => 'test@example.com',
-      'created' => REQUEST_TIME,
-      'status' => 1,
-      'language' => 'en',
-    );
-    $account = (object) $edit;
-    $_SESSION['entity_crud_hook_test'] = array();
-    $account = user_save($account, $edit);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
-    $this->assertHookMessage('entity_crud_hook_test_user_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type user');
-    $this->assertHookMessage('entity_crud_hook_test_user_insert called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $account = user_load($account->uid);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_load called for type user');
-    $this->assertHookMessage('entity_crud_hook_test_user_load called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    $edit['name'] = 'New name';
-    $account = user_save($account, $edit);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
-    $this->assertHookMessage('entity_crud_hook_test_user_presave called');
-    $this->assertHookMessage('entity_crud_hook_test_entity_update called for type user');
-    $this->assertHookMessage('entity_crud_hook_test_user_update called');
-
-    $_SESSION['entity_crud_hook_test'] = array();
-    user_delete($account->uid);
-
-    $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type user');
-    $this->assertHookMessage('entity_crud_hook_test_user_delete called');
-  }
-
-}
diff --git a/modules/simpletest/tests/entity_query.test b/modules/simpletest/tests/entity_query.test
deleted file mode 100644
index fb95518..0000000
--- a/modules/simpletest/tests/entity_query.test
+++ /dev/null
@@ -1,1498 +0,0 @@
-<?php
-
-
-/**
- * @file
- * Unit test file for the entity API.
- */
-
-/**
- * Tests EntityFieldQuery.
- */
-class EntityFieldQueryTestCase extends DrupalWebTestCase {
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Entity query',
-      'description' => 'Test the EntityFieldQuery class.',
-      'group' => 'Entity API',
-    );
-  }
-
-  function setUp() {
-    parent::setUp(array('field_test'));
-
-    field_attach_create_bundle('test_entity_bundle_key', 'bundle1');
-    field_attach_create_bundle('test_entity_bundle_key', 'bundle2');
-    field_attach_create_bundle('test_entity', 'test_bundles');
-    field_attach_create_bundle('test_entity_bundle', 'test_entity_bundle');
-
-    $instances = array();
-    $this->fields = array();
-    $this->field_names[0] = $field_name = drupal_strtolower($this->randomName() . '_field_name');
-    $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 4);
-    $field = field_create_field($field);
-    $this->fields[0] = $field;
-    $instance = array(
-      'field_name' => $field_name,
-      'entity_type' => '',
-      'bundle' => '',
-      'label' => $this->randomName() . '_label',
-      'description' => $this->randomName() . '_description',
-      'weight' => mt_rand(0, 127),
-      'settings' => array(
-        'test_instance_setting' => $this->randomName(),
-      ),
-      'widget' => array(
-        'type' => 'test_field_widget',
-        'label' => 'Test Field',
-        'settings' => array(
-          'test_widget_setting' => $this->randomName(),
-        )
-      )
-    );
-
-    $instances[0] = $instance;
-
-    // Add an instance to that bundle.
-    $instances[0]['bundle'] = 'bundle1';
-    $instances[0]['entity_type'] = 'test_entity_bundle_key';
-    field_create_instance($instances[0]);
-    $instances[0]['bundle'] = 'bundle2';
-    field_create_instance($instances[0]);
-    $instances[0]['bundle'] = $instances[0]['entity_type'] = 'test_entity_bundle';
-    field_create_instance($instances[0]);
-
-    $this->field_names[1] = $field_name = drupal_strtolower($this->randomName() . '_field_name');
-    $field = array('field_name' => $field_name, 'type' => 'shape', 'cardinality' => 4);
-    $field = field_create_field($field);
-    $this->fields[1] = $field;
-    $instance = array(
-      'field_name' => $field_name,
-      'entity_type' => '',
-      'bundle' => '',
-      'label' => $this->randomName() . '_label',
-      'description' => $this->randomName() . '_description',
-      'weight' => mt_rand(0, 127),
-      'settings' => array(
-        'test_instance_setting' => $this->randomName(),
-      ),
-      'widget' => array(
-        'type' => 'test_field_widget',
-        'label' => 'Test Field',
-        'settings' => array(
-          'test_widget_setting' => $this->randomName(),
-        )
-      )
-    );
-
-    $instances[1] = $instance;
-
-    // Add a field instance to the bundles.
-    $instances[1]['bundle'] = 'bundle1';
-    $instances[1]['entity_type'] = 'test_entity_bundle_key';
-    field_create_instance($instances[1]);
-    $instances[1]['bundle'] = $instances[1]['entity_type'] = 'test_entity_bundle';
-    field_create_instance($instances[1]);
-
-    $this->instances = $instances;
-    // Write entity base table if there is one.
-    $entities = array();
-
-    // Create entities which have a 'bundle key' defined.
-    for ($i = 1; $i < 7; $i++) {
-      $entity = new stdClass();
-      $entity->ftid = $i;
-      $entity->fttype = ($i < 5) ? 'bundle1' : 'bundle2';
-
-      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
-      drupal_write_record('test_entity_bundle_key', $entity);
-      field_attach_insert('test_entity_bundle_key', $entity);
-    }
-
-    $entity = new stdClass();
-    $entity->ftid = 5;
-    $entity->fttype = 'test_entity_bundle';
-    $entity->{$this->field_names[1]}[LANGUAGE_NONE][0]['shape'] = 'square';
-    $entity->{$this->field_names[1]}[LANGUAGE_NONE][0]['color'] = 'red';
-    $entity->{$this->field_names[1]}[LANGUAGE_NONE][1]['shape'] = 'circle';
-    $entity->{$this->field_names[1]}[LANGUAGE_NONE][1]['color'] = 'blue';
-    drupal_write_record('test_entity_bundle', $entity);
-    field_attach_insert('test_entity_bundle', $entity);
-
-    $instances[2] = $instance;
-    $instances[2]['bundle'] = 'test_bundle';
-    $instances[2]['field_name'] = $this->field_names[0];
-    $instances[2]['entity_type'] = 'test_entity';
-    field_create_instance($instances[2]);
-
-    // Create entities with support for revisions.
-    for ($i = 1; $i < 5; $i++) {
-      $entity = new stdClass();
-      $entity->ftid = $i;
-      $entity->ftvid = $i;
-      $entity->fttype = 'test_bundle';
-      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
-
-      drupal_write_record('test_entity', $entity);
-      field_attach_insert('test_entity', $entity);
-      drupal_write_record('test_entity_revision', $entity);
-    }
-
-    // Add two revisions to an entity.
-    for ($i = 100; $i < 102; $i++) {
-      $entity = new stdClass();
-      $entity->ftid = 4;
-      $entity->ftvid = $i;
-      $entity->fttype = 'test_bundle';
-      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
-
-      drupal_write_record('test_entity', $entity, 'ftid');
-      drupal_write_record('test_entity_revision', $entity);
-
-      db_update('test_entity')
-       ->fields(array('ftvid' => $entity->ftvid))
-       ->condition('ftid', $entity->ftid)
-       ->execute();
-
-      field_attach_update('test_entity', $entity);
-    }
-  }
-
-  /**
-   * Tests EntityFieldQuery.
-   */
-  function testEntityFieldQuery() {
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle')
-      ->entityCondition('entity_id', '5');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle', 5),
-    ), t('Test query on an entity type with a generated bundle.'));
-
-    // Test entity_type condition.
-    $query = new EntityFieldQuery();
-    $query->entityCondition('entity_type', 'test_entity_bundle_key');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test entity entity_type condition.'));
-
-    // Test entity_id condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityCondition('entity_id', '3');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-    ), t('Test entity entity_id condition.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', '3');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-    ), t('Test entity entity_id condition and entity_id property condition.'));
-
-    // Test bundle condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityCondition('bundle', 'bundle1');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test entity bundle condition: bundle1.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityCondition('bundle', 'bundle2');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test entity bundle condition: bundle2.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('fttype', 'bundle2');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test entity bundle condition and bundle property condition.'));
-
-    // Test revision_id condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->entityCondition('revision_id', '3');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 3),
-    ), t('Test entity revision_id condition.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->propertyCondition('ftvid', '3');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 3),
-    ), t('Test entity revision_id condition and revision_id property condition.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->fieldCondition($this->fields[0], 'value', 100, '>=')
-      ->age(FIELD_LOAD_REVISION);
-    $this->assertEntityFieldQuery($query, array(
-        array('test_entity', 100),
-        array('test_entity', 101),
-    ), t('Test revision age.'));
-
-    // Test that fields attached to the non-revision supporting entity
-    // 'test_entity_bundle_key' are reachable in FIELD_LOAD_REVISION.
-    $query = new EntityFieldQuery();
-    $query
-      ->fieldCondition($this->fields[0], 'value', 100, '<')
-      ->age(FIELD_LOAD_REVISION);
-    $this->assertEntityFieldQuery($query, array(
-        array('test_entity_bundle_key', 1),
-        array('test_entity_bundle_key', 2),
-        array('test_entity_bundle_key', 3),
-        array('test_entity_bundle_key', 4),
-        array('test_entity_bundle_key', 5),
-        array('test_entity_bundle_key', 6),
-        array('test_entity', 1),
-        array('test_entity', 2),
-        array('test_entity', 3),
-        array('test_entity', 4),
-    ), t('Test that fields are reachable from FIELD_LOAD_REVISION even for non-revision entities.'));
-
-    // Test entity sort by entity_id.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityOrderBy('entity_id', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test sort entity entity_id in ascending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityOrderBy('entity_id', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test sort entity entity_id in descending order.'), TRUE);
-
-    // Test entity sort by entity_id, with a field condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->entityOrderBy('entity_id', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test sort entity entity_id in ascending order, with a field condition.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->propertyOrderBy('ftid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test sort entity entity_id property in descending order, with a field condition.'), TRUE);
-
-    // Test property sort by entity id.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyOrderBy('ftid', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test sort entity entity_id property in ascending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyOrderBy('ftid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test sort entity entity_id property in descending order.'), TRUE);
-
-    // Test property sort by entity id, with a field condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->propertyOrderBy('ftid', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test sort entity entity_id property in ascending order, with a field condition.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->propertyOrderBy('ftid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test sort entity entity_id property in descending order, with a field condition.'), TRUE);
-
-    // Test entity sort by bundle.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityOrderBy('bundle', 'ASC')
-      ->propertyOrderBy('ftid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-    ), t('Test sort entity bundle in ascending order, property in descending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityOrderBy('bundle', 'DESC')
-      ->propertyOrderBy('ftid', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test sort entity bundle in descending order, property in ascending order.'), TRUE);
-
-    // Test entity sort by bundle, with a field condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->entityOrderBy('bundle', 'ASC')
-      ->propertyOrderBy('ftid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-    ), t('Test sort entity bundle in ascending order, property in descending order, with a field condition.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->entityOrderBy('bundle', 'DESC')
-      ->propertyOrderBy('ftid', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test sort entity bundle in descending order, property in ascending order, with a field condition.'), TRUE);
-
-    // Test entity sort by bundle, field.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityOrderBy('bundle', 'ASC')
-      ->fieldOrderBy($this->fields[0], 'value', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-    ), t('Test sort entity bundle in ascending order, field in descending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityOrderBy('bundle', 'DESC')
-      ->fieldOrderBy($this->fields[0], 'value', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test sort entity bundle in descending order, field in ascending order.'), TRUE);
-
-    // Test entity sort by revision_id.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->entityOrderBy('revision_id', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 1),
-      array('test_entity', 2),
-      array('test_entity', 3),
-      array('test_entity', 4),
-    ), t('Test sort entity revision_id in ascending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->entityOrderBy('revision_id', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 4),
-      array('test_entity', 3),
-      array('test_entity', 2),
-      array('test_entity', 1),
-    ), t('Test sort entity revision_id in descending order.'), TRUE);
-
-    // Test entity sort by revision_id, with a field condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->entityOrderBy('revision_id', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 1),
-      array('test_entity', 2),
-      array('test_entity', 3),
-      array('test_entity', 4),
-    ), t('Test sort entity revision_id in ascending order, with a field condition.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->entityOrderBy('revision_id', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 4),
-      array('test_entity', 3),
-      array('test_entity', 2),
-      array('test_entity', 1),
-    ), t('Test sort entity revision_id in descending order, with a field condition.'), TRUE);
-
-    // Test property sort by revision_id.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->propertyOrderBy('ftvid', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 1),
-      array('test_entity', 2),
-      array('test_entity', 3),
-      array('test_entity', 4),
-    ), t('Test sort entity revision_id property in ascending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->propertyOrderBy('ftvid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 4),
-      array('test_entity', 3),
-      array('test_entity', 2),
-      array('test_entity', 1),
-    ), t('Test sort entity revision_id property in descending order.'), TRUE);
-
-    // Test property sort by revision_id, with a field condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->propertyOrderBy('ftvid', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 1),
-      array('test_entity', 2),
-      array('test_entity', 3),
-      array('test_entity', 4),
-    ), t('Test sort entity revision_id property in ascending order, with a field condition.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->propertyOrderBy('ftvid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 4),
-      array('test_entity', 3),
-      array('test_entity', 2),
-      array('test_entity', 1),
-    ), t('Test sort entity revision_id property in descending order, with a field condition.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldOrderBy($this->fields[0], 'value', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test sort field in ascending order without field condition.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldOrderBy($this->fields[0], 'value', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test sort field in descending order without field condition.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->fieldOrderBy($this->fields[0], 'value', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test sort field in ascending order.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->fieldOrderBy($this->fields[0], 'value', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test sort field in descending order.'), TRUE);
-
-    // Test "in" operation with entity entity_type condition and entity_id
-    // property condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', array(1, 3, 4), 'IN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test "in" operation with entity entity_type condition and entity_id property condition.'));
-
-    // Test "in" operation with entity entity_type condition and entity_id
-    // property condition. Sort in descending order by entity_id.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', array(1, 3, 4), 'IN')
-      ->propertyOrderBy('ftid', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 1),
-    ), t('Test "in" operation with entity entity_type condition and entity_id property condition. Sort entity_id in descending order.'), TRUE);
-
-    // Test query count
-    $query = new EntityFieldQuery();
-    $query_count = $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->count()
-      ->execute();
-    $this->assertEqual($query_count, 6, t('Test query count on entity condition.'));
-
-    $query = new EntityFieldQuery();
-    $query_count = $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', '1')
-      ->count()
-      ->execute();
-    $this->assertEqual($query_count, 1, t('Test query count on entity and property condition.'));
-
-    $query = new EntityFieldQuery();
-    $query_count = $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', '4', '>')
-      ->count()
-      ->execute();
-    $this->assertEqual($query_count, 2, t('Test query count on entity and property condition with operator.'));
-
-    $query = new EntityFieldQuery();
-    $query_count = $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 3, '=')
-      ->count()
-      ->execute();
-    $this->assertEqual($query_count, 1, t('Test query count on field condition.'));
-
-    // First, test without options.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('fttype', 'und', 'CONTAINS');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test the "contains" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[1], 'shape', 'uar', 'CONTAINS');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle', 5),
-    ), t('Test the "contains" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 1, '=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-    ), t('Test the "equal to" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 3, '=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity', 3),
-    ), t('Test the "equal to" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 3, '<>');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test the "not equal to" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 3, '<>');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-      array('test_entity', 1),
-      array('test_entity', 2),
-      array('test_entity', 4),
-    ), t('Test the "not equal to" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 2, '<');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-    ), t('Test the "less than" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 2, '<');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity', 1),
-    ), t('Test the "less than" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 2, '<=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-    ), t('Test the "less than or equal to" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 2, '<=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity', 1),
-      array('test_entity', 2),
-    ), t('Test the "less than or equal to" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 4, '>');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test the "greater than" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 2, '>');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-      array('test_entity', 3),
-      array('test_entity', 4),
-    ), t('Test the "greater than" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 4, '>=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test the "greater than or equal to" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 3, '>=');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-      array('test_entity', 3),
-      array('test_entity', 4),
-    ), t('Test the "greater than or equal to" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', array(3, 4), 'NOT IN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test the "not in" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', array(3, 4, 100, 101), 'NOT IN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-      array('test_entity', 1),
-      array('test_entity', 2),
-    ), t('Test the "not in" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', array(3, 4), 'IN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test the "in" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', array(2, 3), 'IN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity', 2),
-      array('test_entity', 3),
-    ), t('Test the "in" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', array(1, 3), 'BETWEEN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-    ), t('Test the "between" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', array(1, 3), 'BETWEEN');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity', 1),
-      array('test_entity', 2),
-      array('test_entity', 3),
-    ), t('Test the "between" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('fttype', 'bun', 'STARTS_WITH');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test the "starts_with" operation on a property.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle', 5),
-    ), t('Test the "starts_with" operation on a field.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 3);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity', 3),
-    ), t('Test omission of an operator with a single item.'));
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', array(2, 3));
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity', 2),
-      array('test_entity', 3),
-    ), t('Test omission of an operator with multiple items.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyCondition('ftid', 1, '>')
-      ->fieldCondition($this->fields[0], 'value', 4, '<');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-    ), t('Test entity, property and field conditions.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->entityCondition('bundle', 'bundle', 'STARTS_WITH')
-      ->propertyCondition('ftid', 4)
-      ->fieldCondition($this->fields[0], 'value', 4);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-    ), t('Test entity condition with "starts_with" operation, and property and field conditions.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyOrderBy('ftid', 'ASC')
-      ->range(0, 2);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-    ), t('Test limit on a property.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>=')
-      ->fieldOrderBy($this->fields[0], 'value', 'ASC')
-      ->range(0, 2);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-    ), t('Test limit on a field.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyOrderBy('ftid', 'ASC')
-      ->range(4, 6);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test offset on a property.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->fieldOrderBy($this->fields[0], 'value', 'ASC')
-      ->range(2, 4);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test offset on a field.'), TRUE);
-
-    for ($i = 6; $i < 10; $i++) {
-      $entity = new stdClass();
-      $entity->ftid = $i;
-      $entity->fttype = 'test_entity_bundle';
-      $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i - 5;
-      drupal_write_record('test_entity_bundle', $entity);
-      field_attach_insert('test_entity_bundle', $entity);
-    }
-
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', 2, '>');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-      array('test_entity', 3),
-      array('test_entity', 4),
-      array('test_entity_bundle', 8),
-      array('test_entity_bundle', 9),
-    ), t('Select a field across multiple entities.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->fieldCondition($this->fields[1], 'shape', 'square')
-      ->fieldCondition($this->fields[1], 'color', 'blue');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle', 5),
-    ), t('Test without a delta group.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->fieldCondition($this->fields[1], 'shape', 'square', '=', 'group')
-      ->fieldCondition($this->fields[1], 'color', 'blue', '=', 'group');
-    $this->assertEntityFieldQuery($query, array(), t('Test with a delta group.'));
-
-    // Test query on a deleted field.
-    field_attach_delete_bundle('test_entity_bundle_key', 'bundle1');
-    field_attach_delete_bundle('test_entity', 'test_bundle');
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', '3');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle', 8),
-    ), t('Test query on a field after deleting field from some entities.'));
-
-    field_attach_delete_bundle('test_entity_bundle', 'test_entity_bundle');
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', '3');
-    $this->assertEntityFieldQuery($query, array(), t('Test query on a field after deleting field from all entities.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->fieldCondition($this->fields[0], 'value', '3')
-      ->deleted(TRUE);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle', 8),
-      array('test_entity', 3),
-    ), t('Test query on a deleted field with deleted option set to TRUE.'));
-
-    $pass = FALSE;
-    $query = new EntityFieldQuery();
-    try {
-      $query->execute();
-    }
-    catch (EntityFieldQueryException $exception) {
-      $pass = ($exception->getMessage() == t('For this query an entity type must be specified.'));
-    }
-    $this->assertTrue($pass, t("Can't query the universe."));
-  }
-
-  /**
-   * Tests field meta conditions.
-   */
-  function testEntityFieldQueryMetaConditions() {
-    // Make a test field translatable.
-    $this->fields[0]['translatable'] = TRUE;
-    field_update_field($this->fields[0]);
-    field_test_entity_info_translatable('test_entity', TRUE);
-    drupal_static_reset('field_available_languages');
-
-    // Create more items with different languages.
-    $entity = new stdClass();
-    $entity->ftid = 1;
-    $entity->ftvid = 1;
-    $entity->fttype = 'test_bundle';
-    $j = 0;
-
-    foreach (array(LANGUAGE_NONE, 'en') as $langcode) {
-      for ($i = 0; $i < 4; $i++) {
-        $entity->{$this->field_names[0]}[$langcode][$i]['value'] = $i + $j;
-      }
-      $j += 4;
-    }
-
-    field_attach_update('test_entity', $entity);
-
-    // Test delta field meta condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity', '=')
-      ->fieldDeltaCondition($this->fields[0], 0, '>');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 1),
-    ), t('Test with a delta meta condition.'));
-
-    // Test language field meta condition.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity', '=')
-      ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 1),
-    ), t('Test with a language meta condition.'));
-
-    // Test delta grouping.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity', '=')
-      ->fieldCondition($this->fields[0], 'value', 0, '=', 'group')
-      ->fieldDeltaCondition($this->fields[0], 1, '<', 'group');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 1),
-    ), t('Test with a grouped delta meta condition.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity', '=')
-      ->fieldCondition($this->fields[0], 'value', 0, '=', 'group')
-      ->fieldDeltaCondition($this->fields[0], 1, '>=', 'group');
-    $this->assertEntityFieldQuery($query, array(), t('Test with a grouped delta meta condition (empty result set).'));
-
-    // Test language grouping.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity', '=')
-      ->fieldCondition($this->fields[0], 'value', 0, '=', NULL, 'group')
-      ->fieldLanguageCondition($this->fields[0], 'en', '<>', NULL, 'group');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 1),
-    ), t('Test with a grouped language meta condition.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity', '=')
-      ->fieldCondition($this->fields[0], 'value', 0, '=', NULL, 'group')
-      ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>', NULL, 'group');
-    $this->assertEntityFieldQuery($query, array(), t('Test with a grouped language meta condition (empty result set).'));
-
-    // Test delta and language grouping.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity', '=')
-      ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
-      ->fieldDeltaCondition($this->fields[0], 1, '<', 'delta', 'language')
-      ->fieldLanguageCondition($this->fields[0], 'en', '<>', 'delta', 'language');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity', 1),
-    ), t('Test with a grouped delta + language meta condition.'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity', '=')
-      ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
-      ->fieldDeltaCondition($this->fields[0], 1, '>=', 'delta', 'language')
-      ->fieldLanguageCondition($this->fields[0], 'en', '<>', 'delta', 'language');
-    $this->assertEntityFieldQuery($query, array(), t('Test with a grouped delta + language meta condition (empty result set, delta condition unsatisifed).'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity', '=')
-      ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
-      ->fieldDeltaCondition($this->fields[0], 1, '<', 'delta', 'language')
-      ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>', 'delta', 'language');
-    $this->assertEntityFieldQuery($query, array(), t('Test with a grouped delta + language meta condition (empty result set, language condition unsatisifed).'));
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity', '=')
-      ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
-      ->fieldDeltaCondition($this->fields[0], 1, '>=', 'delta', 'language')
-      ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>', 'delta', 'language');
-    $this->assertEntityFieldQuery($query, array(), t('Test with a grouped delta + language meta condition (empty result set, both conditions unsatisifed).'));
-
-    // Test grouping with another field to ensure that grouping cache is reset
-    // properly.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle', '=')
-      ->fieldCondition($this->fields[1], 'shape', 'circle', '=', 'delta', 'language')
-      ->fieldCondition($this->fields[1], 'color', 'blue', '=', 'delta', 'language')
-      ->fieldDeltaCondition($this->fields[1], 1, '=', 'delta', 'language')
-      ->fieldLanguageCondition($this->fields[1], LANGUAGE_NONE, '=', 'delta', 'language');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle', 5),
-    ), t('Test grouping cache.'));
-  }
-
-  /**
-   * Tests the routing feature of EntityFieldQuery.
-   */
-  function testEntityFieldQueryRouting() {
-    // Entity-only query.
-    $query = new EntityFieldQuery();
-    $query->entityCondition('entity_type', 'test_entity_bundle_key');
-    $this->assertIdentical($query->queryCallback(), array($query, 'propertyQuery'), t('Entity-only queries are handled by the propertyQuery handler.'));
-
-    // Field-only query.
-    $query = new EntityFieldQuery();
-    $query->fieldCondition($this->fields[0], 'value', '3');
-    $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', t('Pure field queries are handled by the Field storage handler.'));
-
-    // Mixed entity and field query.
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', '3');
-    $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', t('Mixed queries are handled by the Field storage handler.'));
-
-    // Overriding with $query->executeCallback.
-    $query = new EntityFieldQuery();
-    $query->entityCondition('entity_type', 'test_entity_bundle_key');
-    $query->executeCallback = 'field_test_dummy_field_storage_query';
-    $this->assertEntityFieldQuery($query, array(
-      array('user', 1),
-    ), t('executeCallback can override the query handler.'));
-
-    // Overriding with $query->executeCallback via hook_entity_query_alter().
-    $query = new EntityFieldQuery();
-    $query->entityCondition('entity_type', 'test_entity_bundle_key');
-    // Add a flag that will be caught by field_test_entity_query_alter().
-    $query->alterMyExecuteCallbackPlease = TRUE;
-    $this->assertEntityFieldQuery($query, array(
-      array('user', 1),
-    ), t('executeCallback can override the query handler when set in a hook_entity_query_alter().'));
-
-    // Mixed-storage queries.
-    $query = new EntityFieldQuery();
-    $query
-      ->fieldCondition($this->fields[0], 'value', '3')
-      ->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH');
-    // Alter the storage of the field.
-    $query->fields[1]['storage']['module'] = 'dummy_storage';
-    try {
-      $query->queryCallback();
-    }
-    catch (EntityFieldQueryException $exception) {
-      $pass = ($exception->getMessage() == t("Can't handle more than one field storage engine"));
-    }
-    $this->assertTrue($pass, t('Cannot query across field storage engines.'));
-  }
-
-  /**
-   * Tests the pager integration of EntityFieldQuery.
-   */
-  function testEntityFieldQueryPager() {
-    // Test pager in propertyQuery
-    $_GET['page'] = '0,1';
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyOrderBy('ftid', 'ASC')
-      ->pager(3, 0);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-    ), t('Test pager integration in propertyQuery: page 1.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->propertyOrderBy('ftid', 'ASC')
-      ->pager(3, 1);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test pager integration in propertyQuery: page 2.'), TRUE);
-
-    // Test pager in field storage
-    $_GET['page'] = '0,1';
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->propertyOrderBy('ftid', 'ASC')
-      ->pager(2, 0);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-    ), t('Test pager integration in field storage: page 1.'), TRUE);
-
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->propertyOrderBy('ftid', 'ASC')
-      ->pager(2, 1);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test pager integration in field storage: page 2.'), TRUE);
-
-    unset($_GET['page']);
-  }
-
-  /**
-   * Tests the TableSort integration of EntityFieldQuery.
-   */
-  function testEntityFieldQueryTableSort() {
-    // Test TableSort in propertyQuery
-    $_GET['sort'] = 'asc';
-    $_GET['order'] = 'Id';
-    $header = array(
-      'id' => array('data' => 'Id', 'type' => 'property',  'specifier' => 'ftid'),
-      'type' => array('data' => 'Type', 'type' => 'entity', 'specifier' => 'bundle'),
-    );
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->tableSort($header);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test TableSort by property: ftid ASC in propertyQuery.'), TRUE);
-
-    $_GET['sort'] = 'desc';
-    $_GET['order'] = 'Id';
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->tableSort($header);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test TableSort by property: ftid DESC in propertyQuery.'), TRUE);
-
-    $_GET['sort'] = 'asc';
-    $_GET['order'] = 'Type';
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->tableSort($header);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test TableSort by entity: bundle ASC in propertyQuery.'), TRUE);
-
-    $_GET['sort'] = 'desc';
-    $_GET['order'] = 'Type';
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->tableSort($header);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test TableSort by entity: bundle DESC in propertyQuery.'), TRUE);
-
-    // Test TableSort in field storage
-    $_GET['sort'] = 'asc';
-    $_GET['order'] = 'Id';
-    $header = array(
-      'id' => array('data' => 'Id', 'type' => 'property',  'specifier' => 'ftid'),
-      'type' => array('data' => 'Type', 'type' => 'entity', 'specifier' => 'bundle'),
-      'field' => array('data' => 'Field', 'type' => 'field', 'specifier' => array('field' => $this->field_names[0], 'column' => 'value')),
-    );
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->tableSort($header);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test TableSort by property: ftid ASC in field storage.'), TRUE);
-
-    $_GET['sort'] = 'desc';
-    $_GET['order'] = 'Id';
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->tableSort($header);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test TableSort by property: ftid DESC in field storage.'), TRUE);
-
-    $_GET['sort'] = 'asc';
-    $_GET['order'] = 'Type';
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->tableSort($header)
-      ->entityOrderBy('entity_id', 'DESC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-    ), t('Test TableSort by entity: bundle ASC in field storage.'), TRUE);
-
-    $_GET['sort'] = 'desc';
-    $_GET['order'] = 'Type';
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->tableSort($header)
-      ->entityOrderBy('entity_id', 'ASC');
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-    ), t('Test TableSort by entity: bundle DESC in field storage.'), TRUE);
-
-    $_GET['sort'] = 'asc';
-    $_GET['order'] = 'Field';
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->tableSort($header);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 1),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 6),
-    ), t('Test TableSort by field ASC.'), TRUE);
-
-    $_GET['sort'] = 'desc';
-    $_GET['order'] = 'Field';
-    $query = new EntityFieldQuery();
-    $query
-      ->entityCondition('entity_type', 'test_entity_bundle_key')
-      ->fieldCondition($this->fields[0], 'value', 0, '>')
-      ->tableSort($header);
-    $this->assertEntityFieldQuery($query, array(
-      array('test_entity_bundle_key', 6),
-      array('test_entity_bundle_key', 5),
-      array('test_entity_bundle_key', 4),
-      array('test_entity_bundle_key', 3),
-      array('test_entity_bundle_key', 2),
-      array('test_entity_bundle_key', 1),
-    ), t('Test TableSort by field DESC.'), TRUE);
-
-    unset($_GET['sort']);
-    unset($_GET['order']);
-  }
-
-  /**
-   * Fetches the results of an EntityFieldQuery and compares.
-   *
-   * @param $query
-   *   An EntityFieldQuery to run.
-   * @param $intended_results
-   *   A list of results, every entry is again a list, first being the entity
-   *   type, the second being the entity_id.
-   * @param $message
-   *   The message to be displayed as the result of this test.
-   * @param $ordered
-   *   If FALSE then the result of EntityFieldQuery will match
-   *   $intended_results even if the order is not the same. If TRUE then order
-   *   should match too.
-   */
-  function assertEntityFieldQuery($query, $intended_results, $message, $ordered = FALSE) {
-    $results = array();
-    try {
-      foreach ($query->execute() as $entity_type => $entity_ids) {
-        foreach ($entity_ids as $entity_id => $stub_entity) {
-          $results[] = array($entity_type, $entity_id);
-        }
-      }
-      if (!isset($ordered) || !$ordered) {
-        sort($results);
-        sort($intended_results);
-      }
-      $this->assertEqual($results, $intended_results, $message);
-    }
-    catch (Exception $e) {
-      $this->fail('Exception thrown: '. $e->getMessage());
-    }
-  }
-}
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index ec7f6b8..998ddba 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -60,392 +60,6 @@ function hook_hook_info_alter(&$hooks) {
 }
 
 /**
- * Inform the base system and the Field API about one or more entity types.
- *
- * Inform the system about one or more entity types (i.e., object types that
- * can be loaded via entity_load() and, optionally, to which fields can be
- * attached).
- *
- * @return
- *   An array whose keys are entity type names and whose values identify
- *   properties of those types that the system needs to know about:
- *   - label: The human-readable name of the type.
- *   - controller class: The name of the class that is used to load the objects.
- *     The class has to implement the DrupalEntityControllerInterface interface.
- *     Leave blank to use the DrupalDefaultEntityController implementation.
- *   - base table: (used by DrupalDefaultEntityController) The name of the
- *     entity type's base table.
- *   - revision table: The name of the entity type's revision table (if any).
- *   - static cache: (used by DrupalDefaultEntityController) FALSE to disable
- *     static caching of entities during a page request. Defaults to TRUE.
- *   - field cache: (used by Field API loading and saving of field data) FALSE
- *     to disable Field API's persistent cache of field data. Only recommended
- *     if a higher level persistent cache is available for the entity type.
- *     Defaults to TRUE.
- *   - load hook: The name of the hook which should be invoked by
- *     DrupalDefaultEntityController:attachLoad(), for example 'node_load'.
- *   - uri callback: A function taking an entity as argument and returning the
- *     uri elements of the entity, e.g. 'path' and 'options'. The actual entity
- *     uri can be constructed by passing these elements to url().
- *   - label callback: (optional) A function taking an entity type and an entity
- *     as arguments and returning the label of the entity. The entity label is
- *     the main string associated with an entity; for example, the title of a
- *     node or the subject of a comment. If there is an entity object property
- *     that defines the label, use the 'label' element of the 'entity keys'
- *     return value component to provide this information (see below). If more
- *     complex logic is needed to determine the label of an entity, you can
- *     instead specify a callback function here, which will be called to
- *     determine the entity label. See also the entity_label() function, which
- *     implements this logic.
- *   - fieldable: Set to TRUE if you want your entity type to accept fields
- *     being attached to it.
- *   - translation: An associative array of modules registered as field
- *     translation handlers. Array keys are the module names, array values
- *     can be any data structure the module uses to provide field translation.
- *     Any empty value disallows the module to appear as a translation handler.
- *   - entity keys: An array describing how the Field API can extract the
- *     information it needs from the objects of the type. Elements:
- *     - id: The name of the property that contains the primary id of the
- *       entity. Every entity object passed to the Field API must have this
- *       property and its value must be numeric.
- *     - revision: The name of the property that contains the revision id of
- *       the entity. The Field API assumes that all revision ids are unique
- *       across all entities of a type. This entry can be omitted if the
- *       entities of this type are not versionable.
- *     - bundle: The name of the property that contains the bundle name for the
- *       entity. The bundle name defines which set of fields are attached to
- *       the entity (e.g. what nodes call "content type"). This entry can be
- *       omitted if this entity type exposes a single bundle (all entities have
- *       the same collection of fields). The name of this single bundle will be
- *       the same as the entity type.
- *     - label: The name of the property that contains the entity label. For
- *       example, if the entity's label is located in $entity->subject, then
- *       'subject' should be specified here. If complex logic is required to
- *       build the label, a 'label callback' should be defined instead (see
- *       the 'label callback' section above for details).
- *   - bundle keys: An array describing how the Field API can extract the
- *     information it needs from the bundle objects for this type. This entry
- *     is required if the 'path' provided in the 'bundles'/'admin' section
- *     identifies the bundle using a named menu placeholder whose loader
- *     callback returns an object (e.g., $vocabulary for taxonomy terms, or
- *     $node_type for nodes). If the path does not include the bundle, or the
- *     bundle is just a string rather than an automatically loaded object, then
- *     this can be omitted. Elements:
- *     - bundle: The name of the property of the bundle object that contains
- *       the name of the bundle object.
- *   - bundles: An array describing all bundles for this object type. Keys are
- *     bundles machine names, as found in the objects' 'bundle' property
- *     (defined in the 'entity keys' entry above). Elements:
- *     - label: The human-readable name of the bundle.
- *     - uri callback: Same as the 'uri callback' key documented above for the
- *       entity type, but for the bundle only. When determining the URI of an
- *       entity, if a 'uri callback' is defined for both the entity type and
- *       the bundle, the one for the bundle is used.
- *     - admin: An array of information that allows Field UI pages to attach
- *       themselves to the existing administration pages for the bundle.
- *       Elements:
- *       - path: the path of the bundle's main administration page, as defined
- *         in hook_menu(). If the path includes a placeholder for the bundle,
- *         the 'bundle argument' and 'real path' keys below are required.
- *       - bundle argument: The position of the bundle placeholder in 'path', if
- *         any.
- *       - real path: The actual path (no placeholder) of the bundle's main
- *         administration page. This will be used to generate links.
- *       - access callback: As in hook_menu(). 'user_access' will be assumed if
- *         no value is provided.
- *       - access arguments: As in hook_menu().
- *   - view modes: An array describing the view modes for the entity type. View
- *     modes let entities be displayed differently depending on the context.
- *     For instance, a node can be displayed differently on its own page
- *     ('full' mode), on the home page or taxonomy listings ('teaser' mode), or
- *     in an RSS feed ('rss' mode). Modules taking part in the display of the
- *     entity (notably the Field API) can adjust their behavior depending on
- *     the requested view mode. An additional 'default' view mode is available
- *     for all entity types. This view mode is not intended for actual entity
- *     display, but holds default display settings. For each available view
- *     mode, administrators can configure whether it should use its own set of
- *     field display settings, or just replicate the settings of the 'default'
- *     view mode, thus reducing the amount of display configurations to keep
- *     track of. Keys of the array are view mode names. Each view mode is
- *     described by an array with the following key/value pairs:
- *     - label: The human-readable name of the view mode
- *     - custom settings: A boolean specifying whether the view mode should by
- *       default use its own custom field display settings. If FALSE, entities
- *       displayed in this view mode will reuse the 'default' display settings
- *       by default (e.g. right after the module exposing the view mode is
- *       enabled), but administrators can later use the Field UI to apply custom
- *       display settings specific to the view mode.
- *
- * @see entity_load()
- * @see hook_entity_info_alter()
- */
-function hook_entity_info() {
-  $return = array(
-    'node' => array(
-      'label' => t('Node'),
-      'controller class' => 'NodeController',
-      'base table' => 'node',
-      'revision table' => 'node_revision',
-      'uri callback' => 'node_uri',
-      'fieldable' => TRUE,
-      'translation' => array(
-        'locale' => TRUE,
-      ),
-      'entity keys' => array(
-        'id' => 'nid',
-        'revision' => 'vid',
-        'bundle' => 'type',
-      ),
-      'bundle keys' => array(
-        'bundle' => 'type',
-      ),
-      'bundles' => array(),
-      'view modes' => array(
-        'full' => array(
-          'label' => t('Full content'),
-          'custom settings' => FALSE,
-        ),
-        'teaser' => array(
-          'label' => t('Teaser'),
-          'custom settings' => TRUE,
-        ),
-        'rss' => array(
-          'label' => t('RSS'),
-          'custom settings' => FALSE,
-        ),
-      ),
-    ),
-  );
-
-  // Search integration is provided by node.module, so search-related
-  // view modes for nodes are defined here and not in search.module.
-  if (module_exists('search')) {
-    $return['node']['view modes'] += array(
-      'search_index' => array(
-        'label' => t('Search index'),
-        'custom settings' => FALSE,
-      ),
-      'search_result' => array(
-        'label' => t('Search result'),
-        'custom settings' => FALSE,
-      ),
-    );
-  }
-
-  // Bundles must provide a human readable name so we can create help and error
-  // messages, and the path to attach Field admin pages to.
-  foreach (node_type_get_names() as $type => $name) {
-    $return['node']['bundles'][$type] = array(
-      'label' => $name,
-      'admin' => array(
-        'path' => 'admin/structure/types/manage/%node_type',
-        'real path' => 'admin/structure/types/manage/' . str_replace('_', '-', $type),
-        'bundle argument' => 4,
-        'access arguments' => array('administer content types'),
-      ),
-    );
-  }
-
-  return $return;
-}
-
-/**
- * Alter the entity info.
- *
- * Modules may implement this hook to alter the information that defines an
- * entity. All properties that are available in hook_entity_info() can be
- * altered here.
- *
- * @param $entity_info
- *   The entity info array, keyed by entity name.
- *
- * @see hook_entity_info()
- */
-function hook_entity_info_alter(&$entity_info) {
-  // Set the controller class for nodes to an alternate implementation of the
-  // DrupalEntityController interface.
-  $entity_info['node']['controller class'] = 'MyCustomNodeController';
-}
-
-/**
- * Act on entities when loaded.
- *
- * This is a generic load hook called for all entity types loaded via the
- * entity API.
- *
- * @param $entities
- *   The entities keyed by entity ID.
- * @param $type
- *   The type of entities being loaded (i.e. node, user, comment).
- */
-function hook_entity_load($entities, $type) {
-  foreach ($entities as $entity) {
-    $entity->foo = mymodule_add_something($entity, $type);
-  }
-}
-
-/**
- * Act on an entity before it is about to be created or updated.
- *
- * @param $entity
- *   The entity object.
- * @param $type
- *   The type of entity being saved (i.e. node, user, comment).
- */
-function hook_entity_presave($entity, $type) {
-  $entity->changed = REQUEST_TIME;
-}
-
-/**
- * Act on entities when inserted.
- *
- * @param $entity
- *   The entity object.
- * @param $type
- *   The type of entity being inserted (i.e. node, user, comment).
- */
-function hook_entity_insert($entity, $type) {
-  // Insert the new entity into a fictional table of all entities.
-  $info = entity_get_info($type);
-  list($id) = entity_extract_ids($type, $entity);
-  db_insert('example_entity')
-    ->fields(array(
-      'type' => $type,
-      'id' => $id,
-      'created' => REQUEST_TIME,
-      'updated' => REQUEST_TIME,
-    ))
-    ->execute();
-}
-
-/**
- * Act on entities when updated.
- *
- * @param $entity
- *   The entity object.
- * @param $type
- *   The type of entity being updated (i.e. node, user, comment).
- */
-function hook_entity_update($entity, $type) {
-  // Update the entity's entry in a fictional table of all entities.
-  $info = entity_get_info($type);
-  list($id) = entity_extract_ids($type, $entity);
-  db_update('example_entity')
-    ->fields(array(
-      'updated' => REQUEST_TIME,
-    ))
-    ->condition('type', $type)
-    ->condition('id', $id)
-    ->execute();
-}
-
-/**
- * Act on entities when deleted.
- *
- * @param $entity
- *   The entity object.
- * @param $type
- *   The type of entity being deleted (i.e. node, user, comment).
- */
-function hook_entity_delete($entity, $type) {
-  // Delete the entity's entry from a fictional table of all entities.
-  $info = entity_get_info($type);
-  list($id) = entity_extract_ids($type, $entity);
-  db_delete('example_entity')
-    ->condition('type', $type)
-    ->condition('id', $id)
-    ->execute();
-}
-
-/**
- * Alter or execute an EntityFieldQuery.
- *
- * @param EntityFieldQuery $query
- *   An EntityFieldQuery. One of the most important properties to be changed is
- *   EntityFieldQuery::executeCallback. If this is set to an existing function,
- *   this function will get the query as its single argument and its result
- *   will be the returned as the result of EntityFieldQuery::execute(). This can
- *   be used to change the behavior of EntityFieldQuery entirely. For example,
- *   the default implementation can only deal with one field storage engine, but
- *   it is possible to write a module that can query across field storage
- *   engines. Also, the default implementation presumes entities are stored in
- *   SQL, but the execute callback could instead query any other entity storage,
- *   local or remote.
- *
- *   Note the $query->altered attribute which is TRUE in case the query has
- *   already been altered once. This happens with cloned queries.
- *   If there is a pager, then such a cloned query will be executed to count
- *   all elements. This query can be detected by checking for
- *   ($query->pager && $query->count), allowing the driver to return 0 from
- *   the count query and disable the pager.
- */
-function hook_entity_query_alter($query) {
-  $query->executeCallback = 'my_module_query_callback';
-}
-
-/**
- * Act on entities being assembled before rendering.
- *
- * @param $entity
- *   The entity object.
- * @param $type
- *   The type of entity being rendered (i.e. node, user, comment).
- * @param $view_mode
- *   The view mode the entity is rendered in.
- * @param $langcode
- *   The language code used for rendering.
- *
- * The module may add elements to $entity->content prior to rendering. The
- * structure of $entity->content is a renderable array as expected by
- * drupal_render().
- *
- * @see hook_entity_view_alter()
- * @see hook_comment_view()
- * @see hook_node_view()
- * @see hook_user_view()
- */
-function hook_entity_view($entity, $type, $view_mode, $langcode) {
-  $entity->content['my_additional_field'] = array(
-    '#markup' => $additional_field,
-    '#weight' => 10,
-    '#theme' => 'mymodule_my_additional_field',
-  );
-}
-
-/**
- * Alter the results of ENTITY_view().
- *
- * This hook is called after the content has been assembled in a structured
- * array and may be used for doing processing which requires that the complete
- * entity content structure has been built.
- *
- * If a module wishes to act on the rendered HTML of the entity rather than the
- * structured content array, it may use this hook to add a #post_render
- * callback. Alternatively, it could also implement hook_preprocess_ENTITY().
- * See drupal_render() and theme() for details.
- *
- * @param $build
- *   A renderable array representing the entity content.
- * @param $type
- *   The type of entity being rendered (i.e. node, user, comment).
- *
- * @see hook_entity_view()
- * @see hook_comment_view_alter()
- * @see hook_node_view_alter()
- * @see hook_taxonomy_term_view_alter()
- * @see hook_user_view_alter()
- */
-function hook_entity_view_alter(&$build, $type) {
-  if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
-    // Change its weight.
-    $build['an_additional_field']['#weight'] = -10;
-
-    // Add a #post_render callback to act on the rendered HTML of the entity.
-    $build['#post_render'][] = 'my_module_node_post_render';
-  }
-}
-
-/**
  * Define administrative paths.
  *
  * Modules may specify whether or not the paths they define in hook_menu() are
@@ -492,30 +106,6 @@ function hook_admin_paths_alter(&$paths) {
 }
 
 /**
- * Act on entities as they are being prepared for view.
- *
- * Allows you to operate on multiple entities as they are being prepared for
- * view. Only use this if attaching the data during the entity_load() phase
- * is not appropriate, for example when attaching other 'entity' style objects.
- *
- * @param $entities
- *   The entities keyed by entity ID.
- * @param $type
- *   The type of entities being loaded (i.e. node, user, comment).
- * @param $langcode
- *   The language to display the entity in.
- */
-function hook_entity_prepare_view($entities, $type, $langcode) {
-  // Load a specific node into the user object for later theming.
-  if ($type == 'user') {
-    $nodes = mymodule_get_user_nodes(array_keys($entities));
-    foreach ($entities as $uid => $entity) {
-      $entity->user_node = $nodes[$uid];
-    }
-  }
-}
-
-/**
  * Perform periodic actions.
  *
  * Modules that require some commands to be executed periodically can
@@ -2391,6 +1981,30 @@ function hook_flush_caches() {
 }
 
 /**
+ * Perform necessary actions before modules are installed.
+ *
+ * This function allows all modules to react prior to a module being installed.
+ *
+ * @param $modules
+ *   An array of modules about to be installed.
+ */
+function hook_modules_preinstall($modules) {
+  mymodule_cache_clear();
+}
+
+/**
+ * Perform necessary actions before modules are enabled.
+ *
+ * This function allows all modules to react prior to a module being enabled.
+ *
+ * @param $module
+ *   An array of modules about to be enabled.
+ */
+function hook_modules_preenable($modules) {
+  mymodule_cache_clear();
+}
+
+/**
  * Perform necessary actions after modules are installed.
  *
  * This function differs from hook_install() in that it gives all other modules
diff --git a/modules/taxonomy/taxonomy.info b/modules/taxonomy/taxonomy.info
index cba3869..6a13f81 100644
--- a/modules/taxonomy/taxonomy.info
+++ b/modules/taxonomy/taxonomy.info
@@ -4,6 +4,7 @@ package = Core
 version = VERSION
 core = 8.x
 dependencies[] = options
+dependencies[] = entity
 files[] = taxonomy.module
 files[] = taxonomy.test
 configure = admin/structure/taxonomy
diff --git a/modules/user/user.entity.inc b/modules/user/user.entity.inc
new file mode 100644
index 0000000..5549c77
--- /dev/null
+++ b/modules/user/user.entity.inc
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file Controller class for users.
+ */
+
+/**
+ * Controller class for users.
+ *
+ * This extends the DrupalDefaultEntityController class, adding required
+ * special handling for user objects.
+ */
+class UserController extends DrupalDefaultEntityController {
+
+  function attachLoad(&$queried_users, $revision_id = FALSE) {
+    // Build an array of user picture IDs so that these can be fetched later.
+    $picture_fids = array();
+    foreach ($queried_users as $key => $record) {
+      $picture_fids[] = $record->picture;
+      $queried_users[$key]->data = unserialize($record->data);
+      $queried_users[$key]->roles = array();
+      if ($record->uid) {
+        $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
+      }
+      else {
+        $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
+      }
+    }
+
+    // Add any additional roles from the database.
+    $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
+    foreach ($result as $record) {
+      $queried_users[$record->uid]->roles[$record->rid] = $record->name;
+    }
+
+    // Add the full file objects for user pictures if enabled.
+    if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
+      $pictures = file_load_multiple($picture_fids);
+      foreach ($queried_users as $account) {
+        if (!empty($account->picture) && isset($pictures[$account->picture])) {
+          $account->picture = $pictures[$account->picture];
+        }
+        else {
+          $account->picture = NULL;
+        }
+      }
+    }
+    // Call the default attachLoad() method. This will add fields and call
+    // hook_user_load().
+    parent::attachLoad($queried_users, $revision_id);
+  }
+}
diff --git a/modules/user/user.info b/modules/user/user.info
index a4d18d6..d887352 100644
--- a/modules/user/user.info
+++ b/modules/user/user.info
@@ -3,7 +3,7 @@ description = Manages the user registration and login system.
 package = Core
 version = VERSION
 core = 8.x
-files[] = user.module
+files[] = user.entity.inc
 files[] = user.test
 required = TRUE
 configure = admin/config/people
diff --git a/modules/user/user.module b/modules/user/user.module
index 1355159..4794258 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -288,53 +288,6 @@ function user_load_multiple($uids = array(), $conditions = array(), $reset = FAL
 }
 
 /**
- * Controller class for users.
- *
- * This extends the DrupalDefaultEntityController class, adding required
- * special handling for user objects.
- */
-class UserController extends DrupalDefaultEntityController {
-
-  function attachLoad(&$queried_users, $revision_id = FALSE) {
-    // Build an array of user picture IDs so that these can be fetched later.
-    $picture_fids = array();
-    foreach ($queried_users as $key => $record) {
-      $picture_fids[] = $record->picture;
-      $queried_users[$key]->data = unserialize($record->data);
-      $queried_users[$key]->roles = array();
-      if ($record->uid) {
-        $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
-      }
-      else {
-        $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
-      }
-    }
-
-    // Add any additional roles from the database.
-    $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
-    foreach ($result as $record) {
-      $queried_users[$record->uid]->roles[$record->rid] = $record->name;
-    }
-
-    // Add the full file objects for user pictures if enabled.
-    if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
-      $pictures = file_load_multiple($picture_fids);
-      foreach ($queried_users as $account) {
-        if (!empty($account->picture) && isset($pictures[$account->picture])) {
-          $account->picture = $pictures[$account->picture];
-        }
-        else {
-          $account->picture = NULL;
-        }
-      }
-    }
-    // Call the default attachLoad() method. This will add fields and call
-    // hook_user_load().
-    parent::attachLoad($queried_users, $revision_id);
-  }
-}
-
-/**
  * Loads a user object.
  *
  * Drupal has a global $user object, which represents the currently-logged-in
diff --git a/modules/user/user.test b/modules/user/user.test
index 6ecbfac..edd127f 100644
--- a/modules/user/user.test
+++ b/modules/user/user.test
@@ -732,7 +732,7 @@ class UserCancelTestCase extends DrupalWebTestCase {
     $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
     $this->drupalPost(NULL, array(), t('Save'));
     $this->assertText(t('Your comment has been posted.'));
-    $comments = comment_load_multiple(array(), array('subject' => $edit['subject']));
+    $comments = comment_load_multiple(FALSE, array('subject' => $edit['subject']));
     $comment = reset($comments);
     $this->assertTrue($comment->cid, t('Comment found.'));
 
diff --git a/update.php b/update.php
index b759436..e1ad8c0 100644
--- a/update.php
+++ b/update.php
@@ -345,7 +345,6 @@ require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
 require_once DRUPAL_ROOT . '/includes/update.inc';
 require_once DRUPAL_ROOT . '/includes/common.inc';
 require_once DRUPAL_ROOT . '/includes/file.inc';
-require_once DRUPAL_ROOT . '/includes/entity.inc';
 require_once DRUPAL_ROOT . '/includes/unicode.inc';
 update_prepare_d8_bootstrap();
 
