summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel Catchpole2014-12-09 13:05:03 (GMT)
committerNathaniel Catchpole2014-12-09 13:05:03 (GMT)
commit82989783cc3329259986cdf6f7885cde1d6027c0 (patch)
treeb350c2009eb7f5e39e2e8e2a5b8d333d9b36b43f
parent77c613e36fb852fab1b3f5365ed54f206f44416b (diff)
Issue #2335879 by alexpott, dixon_, Wim Leers, chx, mauzeh, ygerasimov, jeqq: Change SqlContentEntityStorageSchema::requiresEntityDataMigration() to ask the old storage handler if it has data rather than assuming yes unless NULL storage
-rw-r--r--core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php4
-rw-r--r--core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php9
-rw-r--r--core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php10
-rw-r--r--core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php8
-rw-r--r--core/lib/Drupal/Core/Entity/EntityManager.php33
-rw-r--r--core/lib/Drupal/Core/Entity/EntityManagerInterface.php21
-rw-r--r--core/lib/Drupal/Core/Entity/EntityStorageBase.php21
-rw-r--r--core/lib/Drupal/Core/Entity/EntityStorageInterface.php25
-rw-r--r--core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php2
-rw-r--r--core/lib/Drupal/Core/Entity/Query/QueryFactory.php14
-rw-r--r--core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php2
-rw-r--r--core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php22
-rw-r--r--core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php82
-rw-r--r--core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php50
14 files changed, 254 insertions, 49 deletions
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php
index 53be393..a031ac8 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php
@@ -334,9 +334,9 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
}
/**
- * Implements Drupal\Core\Entity\EntityStorageInterface::getQueryServiceName().
+ * {@inheritdoc}
*/
- public function getQueryServiceName() {
+ protected function getQueryServiceName() {
return 'entity.query.config';
}
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php
index 12aeca3..b9843c8 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php
@@ -78,7 +78,7 @@ class ContentEntityNullStorage extends ContentEntityStorageBase {
/**
* {@inheritdoc}
*/
- public function getQueryServiceName() {
+ protected function getQueryServiceName() {
throw new QueryException('Null implementation can not be queried.');
}
@@ -138,4 +138,11 @@ class ContentEntityNullStorage extends ContentEntityStorageBase {
return $as_bool ? FALSE : 0;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function hasData() {
+ return FALSE;
+ }
+
}
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
index 6415285..3a65176 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
@@ -45,6 +45,16 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Dyn
/**
* {@inheritdoc}
*/
+ public function hasData() {
+ return (bool) $this->getQuery()
+ ->accessCheck(FALSE)
+ ->range(0, 1)
+ ->execute();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
protected function doCreate(array $values) {
// We have to determine the bundle first.
$bundle = FALSE;
diff --git a/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php b/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php
index 6d12c98..a5a9a94 100644
--- a/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php
+++ b/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php
@@ -85,6 +85,14 @@ interface DynamicallyFieldableEntityStorageInterface extends EntityStorageInterf
public function countFieldData($storage_definition, $as_bool = FALSE);
/**
+ * Determines if the storage contains any data.
+ *
+ * @return bool
+ * TRUE if the storage contains data, FALSE if not.
+ */
+ public function hasData();
+
+ /**
* Performs final cleanup after all data of a field has been purged.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 128d21d..7d7eded 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -324,19 +324,7 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
if (!$class) {
throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a %s handler.', $entity_type, $handler_type));
}
- if (is_subclass_of($class, 'Drupal\Core\Entity\EntityHandlerInterface')) {
- $handler = $class::createInstance($this->container, $definition);
- }
- else {
- $handler = new $class($definition);
- }
- if (method_exists($handler, 'setModuleHandler')) {
- $handler->setModuleHandler($this->moduleHandler);
- }
- if (method_exists($handler, 'setStringTranslation')) {
- $handler->setStringTranslation($this->translationManager);
- }
- $this->handlers[$handler_type][$entity_type] = $handler;
+ $this->handlers[$handler_type][$entity_type] = $this->createHandlerInstance($class, $definition);
}
return $this->handlers[$handler_type][$entity_type];
}
@@ -344,6 +332,25 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
/**
* {@inheritdoc}
*/
+ public function createHandlerInstance($class, EntityTypeInterface $definition = null) {
+ if (is_subclass_of($class, 'Drupal\Core\Entity\EntityHandlerInterface')) {
+ $handler = $class::createInstance($this->container, $definition);
+ }
+ else {
+ $handler = new $class($definition);
+ }
+ if (method_exists($handler, 'setModuleHandler')) {
+ $handler->setModuleHandler($this->moduleHandler);
+ }
+ if (method_exists($handler, 'setStringTranslation')) {
+ $handler->setStringTranslation($this->translationManager);
+ }
+ return $handler;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function getBaseFieldDefinitions($entity_type_id) {
// Check the static cache.
if (!isset($this->baseFieldDefinitions[$entity_type_id])) {
diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
index 8cf95ee..13f9862 100644
--- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
@@ -237,14 +237,14 @@ interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListe
public function hasHandler($entity_type, $handler_type);
/**
- * Creates a new handler instance.
+ * Creates a new handler instance for a entity type and handler type.
*
* @param string $entity_type
* The entity type for this controller.
* @param string $handler_type
* The controller type to create an instance for.
*
- * @return mixed
+ * @return object
* A handler instance.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
@@ -252,6 +252,23 @@ interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListe
public function getHandler($entity_type, $handler_type);
/**
+ * Creates new handler instance.
+ *
+ * Usually \Drupal\Core\Entity\EntityManagerInterface::getHandler() is
+ * preferred since that method has additional checking that the class exists
+ * and has static caches.
+ *
+ * @param mixed $class
+ * The handler class to instantiate.
+ * @param \Drupal\Core\Entity\EntityTypeInterface $definition
+ * The entity type definition.
+ *
+ * @return object
+ * A handler instance.
+ */
+ public function createHandlerInstance($class, EntityTypeInterface $definition = null);
+
+ /**
* Get the bundle info of an entity type.
*
* @param string $entity_type
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageBase.php b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
index 4a40a9e..4333c47 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
@@ -460,7 +460,26 @@ abstract class EntityStorageBase extends EntityHandlerBase implements EntityStor
* {@inheritdoc}
*/
public function getQuery($conjunction = 'AND') {
- return \Drupal::entityQuery($this->getEntityTypeId(), $conjunction);
+ // Access the service directly rather than entity.query factory so the
+ // storage's current entity type is used.
+ return \Drupal::service($this->getQueryServiceName())->get($this->entityType, $conjunction);
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getAggregateQuery($conjunction = 'AND') {
+ // Access the service directly rather than entity.query factory so the
+ // storage's current entity type is used.
+ return \Drupal::service($this->getQueryServiceName())->getAggregate($this->entityType, $conjunction);
+ }
+
+ /**
+ * Gets the name of the service for the query for this entity storage.
+ *
+ * @return string
+ * The name of the service for the query for this entity storage.
+ */
+ abstract protected function getQueryServiceName();
+
}
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageInterface.php b/core/lib/Drupal/Core/Entity/EntityStorageInterface.php
index 6da72fe..b4c897e 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageInterface.php
@@ -148,14 +148,6 @@ interface EntityStorageInterface {
public function save(EntityInterface $entity);
/**
- * Gets the name of the service for the query for this entity storage.
- *
- * @return string
- * The name of the service for the query for this entity storage.
- */
- public function getQueryServiceName();
-
- /**
* Returns an entity query instance.
*
* @param string $conjunction
@@ -166,11 +158,26 @@ interface EntityStorageInterface {
* @return \Drupal\Core\Entity\Query\QueryInterface
* The query instance.
*
- * @see \Drupal\Core\Entity\EntityStorageInterface::getQueryServiceName()
+ * @see \Drupal\Core\Entity\EntityStorageBase::getQueryServiceName()
*/
public function getQuery($conjunction = 'AND');
/**
+ * Returns an aggregated query instance.
+ *
+ * @param string $conjunction
+ * (optional) The logical operator for the query, either:
+ * - AND: all of the conditions on the query need to match.
+ * - OR: at least one of the conditions on the query need to match.
+ *
+ * @return \Drupal\Core\Entity\Query\QueryAggregateInterface
+ * The aggregated query object that can query the given entity type.
+ *
+ * @see \Drupal\Core\Entity\EntityStorageBase::getQueryServiceName()
+ */
+ public function getAggregateQuery($conjunction = 'AND');
+
+ /**
* Returns the entity type ID.
*
* @return string
diff --git a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php
index 59ca4d8..3507502 100644
--- a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php
@@ -202,7 +202,7 @@ class KeyValueEntityStorage extends EntityStorageBase {
/**
* {@inheritdoc}
*/
- public function getQueryServiceName() {
+ protected function getQueryServiceName() {
return 'entity.query.keyvalue';
}
diff --git a/core/lib/Drupal/Core/Entity/Query/QueryFactory.php b/core/lib/Drupal/Core/Entity/Query/QueryFactory.php
index 5f1ab13..d7777c9 100644
--- a/core/lib/Drupal/Core/Entity/Query/QueryFactory.php
+++ b/core/lib/Drupal/Core/Entity/Query/QueryFactory.php
@@ -13,6 +13,14 @@ use Symfony\Component\DependencyInjection\ContainerAwareTrait;
/**
* Factory class Creating entity query objects.
+ *
+ * Any implementation of this service must call getQuery()/getAggregateQuery()
+ * of the corresponding entity storage.
+ *
+ * @see \Drupal\Core\Entity\EntityStorageBase::getQuery()
+ *
+ * @todo https://www.drupal.org/node/2389335 remove entity.query service and
+ * replace with using the entity storage's getQuery() method.
*/
class QueryFactory implements ContainerAwareInterface {
@@ -48,8 +56,7 @@ class QueryFactory implements ContainerAwareInterface {
* The query object that can query the given entity type.
*/
public function get($entity_type_id, $conjunction = 'AND') {
- $service_name = $this->entityManager->getStorage($entity_type_id)->getQueryServiceName();
- return $this->container->get($service_name)->get($this->entityManager->getDefinition($entity_type_id), $conjunction);
+ return $this->entityManager->getStorage($entity_type_id)->getQuery($conjunction);
}
/**
@@ -65,8 +72,7 @@ class QueryFactory implements ContainerAwareInterface {
* The aggregated query object that can query the given entity type.
*/
public function getAggregate($entity_type_id, $conjunction = 'AND') {
- $service_name = $this->entityManager->getStorage($entity_type_id)->getQueryServiceName();
- return $this->container->get($service_name)->getAggregate($this->entityManager->getDefinition($entity_type_id), $conjunction);
+ return $this->entityManager->getStorage($entity_type_id)->getAggregateQuery($conjunction);
}
}
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
index b8db248..d46c37e 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -1198,7 +1198,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
/**
* {@inheritdoc}
*/
- public function getQueryServiceName() {
+ protected function getQueryServiceName() {
return 'entity.query.sql';
}
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
index dac6584..d3087b7 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
@@ -178,23 +178,15 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
* {@inheritdoc}
*/
public function requiresEntityDataMigration(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
- // If we're updating from NULL storage, then there's no stored data that
- // requires migration.
- // @todo Remove in https://www.drupal.org/node/2335879.
+ // If the original storage has existing entities, or it is impossible to
+ // determine if that is the case, require entity data to be migrated.
$original_storage_class = $original->getStorageClass();
- $null_storage_class = 'Drupal\Core\Entity\ContentEntityNullStorage';
- if ($original_storage_class == $null_storage_class || is_subclass_of($original_storage_class, $null_storage_class)) {
- return FALSE;
+ if (!class_exists($original_storage_class)) {
+ return TRUE;
}
-
- return
- // If the original storage class is different, then there might be
- // existing entities in that storage even if the new storage's base
- // table is empty.
- // @todo Ask the old storage handler rather than assuming:
- // https://www.drupal.org/node/2335879.
- $entity_type->getStorageClass() != $original_storage_class ||
- !$this->isTableEmpty($this->storage->getBaseTable());
+ // Use the original entity type since the storage has not been updated.
+ $original_storage = $this->entityManager->createHandlerInstance($original_storage_class, $original);
+ return $original_storage->hasData();
}
/**
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php
index 3a28de0..a3a7013 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php
@@ -1056,6 +1056,88 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
);
}
+ public function providerTestRequiresEntityDataMigration() {
+ $updated_entity_type_definition = $this->getMockBuilder('\Drupal\Core\Entity\EntityTypeInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $updated_entity_type_definition->expects($this->any())
+ ->method('getStorageClass')
+ // A class that exists, *any* class.
+ ->willReturn('\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema');
+ $original_entity_type_definition = $this->getMockBuilder('\Drupal\Core\Entity\EntityTypeInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $original_entity_type_definition->expects($this->any())
+ ->method('getStorageClass')
+ // A class that exists, *any* class.
+ ->willReturn('\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema');
+ $original_entity_type_definition_other_nonexisting = $this->getMockBuilder('\Drupal\Core\Entity\EntityTypeInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $original_entity_type_definition_other_nonexisting->expects($this->any())
+ ->method('getStorageClass')
+ ->willReturn('bar');
+ $original_entity_type_definition_other_existing = $this->getMockBuilder('\Drupal\Core\Entity\EntityTypeInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $original_entity_type_definition_other_existing->expects($this->any())
+ ->method('getStorageClass')
+ // A class that exists, *any* class.
+ ->willReturn('\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema');
+
+ return [
+ // Case 1: same storage class, ::hasData() === TRUE.
+ [$updated_entity_type_definition, $original_entity_type_definition, TRUE, TRUE],
+ // Case 2: same storage class, ::hasData() === FALSE.
+ [$updated_entity_type_definition, $original_entity_type_definition, FALSE, FALSE],
+ // Case 3: different storage class, original storage class does not exist.
+ [$updated_entity_type_definition, $original_entity_type_definition_other_nonexisting, NULL, TRUE],
+ // Case 4: different storage class, original storage class exists, ::hasData() === TRUE.
+ [$updated_entity_type_definition, $original_entity_type_definition_other_existing, TRUE, TRUE],
+ // Case 5: different storage class, original storage class exists, ::hasData() === FALSE.
+ [$updated_entity_type_definition, $original_entity_type_definition_other_existing, FALSE, FALSE],
+ ];
+ }
+
+ /**
+ * @covers ::requiresEntityDataMigration
+ *
+ * @dataProvider providerTestRequiresEntityDataMigration
+ */
+ public function testRequiresEntityDataMigration($updated_entity_type_definition, $original_entity_type_definition, $original_storage_has_data, $migration_required) {
+ $this->entityType = new ContentEntityType(array(
+ 'id' => 'entity_test',
+ 'entity_keys' => array('id' => 'id'),
+ ));
+
+ $original_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $original_storage->expects($this->exactly(is_null($original_storage_has_data) ? 0 : 1))
+ ->method('hasData')
+ ->willReturn($original_storage_has_data);
+
+ // Assert hasData() is never called on the new storage definition.
+ $this->storage->expects($this->never())
+ ->method('hasData');
+
+ $connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->entityManager->expects($this->any())
+ ->method('createHandlerInstance')
+ ->willReturn($original_storage);
+
+ $this->storageSchema = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema')
+ ->setConstructorArgs(array($this->entityManager, $this->entityType, $this->storage, $connection))
+ ->setMethods(array('installedStorageSchema'))
+ ->getMock();
+
+ $this->assertEquals($migration_required, $this->storageSchema->requiresEntityDataMigration($updated_entity_type_definition, $original_entity_type_definition));
+ }
+
/**
* Sets up the storage schema object to test.
*
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
index 3589a63..3a37f90 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
@@ -1181,6 +1181,56 @@ class SqlContentEntityStorageTest extends UnitTestCase {
}
/**
+ * @covers ::hasData
+ */
+ public function testHasData() {
+ $query = $this->getMock('Drupal\Core\Entity\Query\QueryInterface');
+ $query->expects(($this->once()))
+ ->method('accessCheck')
+ ->with(FALSE)
+ ->willReturn($query);
+ $query->expects(($this->once()))
+ ->method('range')
+ ->with(0, 1)
+ ->willReturn($query);
+ $query->expects(($this->once()))
+ ->method('execute')
+ ->willReturn(array(5));
+
+ $factory = $this->getMockBuilder('Drupal\Core\Entity\Query\QueryFactory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $factory->expects($this->once())
+ ->method('get')
+ ->with($this->entityType, 'AND')
+ ->willReturn($query);
+
+ $this->container->set('entity.query.sql', $factory);
+
+ $database = $this->getMockBuilder('Drupal\Core\Database\Connection')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->entityManager->expects($this->any())
+ ->method('getDefinition')
+ ->will($this->returnValue($this->entityType));
+
+ $this->entityManager->expects($this->any())
+ ->method('getFieldStorageDefinitions')
+ ->will($this->returnValue($this->fieldDefinitions));
+
+ $this->entityManager->expects($this->any())
+ ->method('getBaseFieldDefinitions')
+ ->will($this->returnValue($this->fieldDefinitions));
+
+ $this->entityStorage = new SqlContentEntityStorage($this->entityType, $database, $this->entityManager, $this->cache);
+
+ $result = $this->entityStorage->hasData();
+
+ $this->assertTrue($result, 'hasData returned TRUE');
+ }
+
+ /**
* Tests entity ID sanitization.
*/
public function testCleanIds() {