Commit 81ff7cbd authored by Mateu Aguiló Bosch's avatar Mateu Aguiló Bosch
Browse files

WIP: Unit testing autoloader

parent e472bb34
......@@ -61,6 +61,7 @@ script:
- cd $TRAVIS_BUILD_DIR/../drupal
# Download and enable module and its dependencies
- drush --yes dl entity --dev
- drush --yes dl xautoload-7.x-5.x
- drush --yes dl file_entity
......@@ -70,6 +71,12 @@ script:
- patch -p1 < 2456877-module-invoke-wrapping-1.patch
- cd $TRAVIS_BUILD_DIR/../drupal
# Patch entity to add module invoke wrapping.
- cd sites/all/modules/entity
- curl -LO https://www.drupal.org/files/issues/2455851-add-additional-interfaces-1.patch
- patch -p1 < 2455851-add-additional-interfaces-1.patch
- cd $TRAVIS_BUILD_DIR/../drupal
# Enable the modules
- drush --yes pm-enable simpletest typed_entity typed_entity_example
......
......@@ -164,5 +164,6 @@ This module uses unit testing as an example of how you should test your custom b
logic contains calls to the drupal api that is not loaded for unit testing. To work around that you can use X Autoload
mock classes.
This module needs an extra patch to do this, so you will have to patch `xautoload` with:
- https://www.drupal.org/files/issues/2456877-module-invoke-wrapping-1.patch
This module needs an extra patch to do this, so you will have to patch:
- `xautoload` with: https://www.drupal.org/files/issues/2456877-module-invoke-wrapping-1.patch
- `entity` with: https://www.drupal.org/files/issues/2455851-add-additional-interfaces-1.patch
......@@ -8,7 +8,12 @@
namespace Drupal\typed_entity\Tests;
use Drupal\typed_entity\Exception\TypedEntityException;
use Drupal\typed_entity\TypedEntity\Tests\TypedEntityModules;
use Drupal\typed_entity\TypedEntity\TypedEntity;
use Drupal\typed_entity\TypedEntity\TypedEntityManager;
use Drupal\xautoload\Tests\Example\ExampleModules;
use Drupal\xautoload\Tests\Mock\MockDrupalSystem;
use Drupal\xautoload\Tests\VirtualDrupal\DrupalComponentContainer;
class TypedEntityUnitTestCase extends \DrupalUnitTestCase {
......@@ -60,4 +65,49 @@ class TypedEntityUnitTestCase extends \DrupalUnitTestCase {
}
}
/**
* Test TypedEntityManager.
*/
public function testTypedEntityManager() {
// Test the discovery.
$entity = $this->loadFixture(__DIR__ . '/fixtures/article.inc');
$manager = TypedEntityManager::create('node', $entity);
}
/**
* Helper function to set up the mocked Drupal instance.
*/
protected function setUpMockDrupalSystem() {
// Set up the system with the following modules enabled:
// - system
// - xautoload
// - typed_entity
// - typed_entity_example
$example_modules = new TypedEntityModules();
$components = new DrupalComponentContainer($example_modules);
$system = new MockDrupalSystem($components);
xautoload()->getServiceContainer()->set('system', $system);
$entity_wrapper = new MockEntityWrapperService();
xautoload()->getServiceContainer()->set('entity_wrapper', $entity_wrapper);
}
/**
* Loads a serialized object from a file.
*
* @param string $path
* The location of the fixture file.
*
* @return mixed
* The unserialized value.
*/
protected static function loadFixture($path) {
if (!file_exists($path)) {
return NULL;
}
$contents = file_get_contents($path);
return unserialize($contents);
}
}
<?php
/**
* @file
* Contains \Drupal\typed_entity\Entity\EntityWrapperService.
*/
namespace Drupal\typed_entity\Entity;
class EntityWrapperService implements EntityWrapperServiceInterface {
/**
* {@inheritdoc}
*/
public static function wrap($entity_type, $entity) {
return new \EntityDrupalWrapper($entity_type, $entity);
}
}
<?php
/**
* @file
* Contains \Drupal\typed_entity\Entity\EntityWrapperServiceInterface.
*/
namespace Drupal\typed_entity\Entity;
interface EntityWrapperServiceInterface {
/**
* Wraps an entity based on its type.
*
* @param string $entity_type
* The entity type.
* @param mixed $entity
* The loaded entity or entity ID.
*
* @return \EntityDrupalWrapperInterface
*/
public static function wrap($entity_type, $entity);
}
<?php
/**
* @file
* Contains \Drupal\typed_entity\Entity\MockEntityWrapperService.
*/
namespace Drupal\typed_entity\Entity;
use Drupal\typed_entity\TypedEntity\Tests\MockEntityDrupalWrapper;
class MockEntityWrapperService implements EntityWrapperServiceInterface {
/**
* {@inheritdoc}
*/
public static function wrap($entity_type, $entity) {
return new MockEntityDrupalWrapper($entity_type, $entity);
}
}
<?php
/**
* @file
* Contains \Drupal\typed_entity\TypedEntity\Tests\MockEntityDrupalWrapper.
*/
namespace Drupal\typed_entity\TypedEntity\Tests;
use Drupal\typed_entity\Exception\TypedEntityException;
class MockEntityDrupalWrapper implements MockEntityDrupalWrapperInterface {
/**
* Entity type.
*
* @var string
*/
protected $entityType;
/**
* Bundle.
*
* @var string
*/
protected $bundle;
/**
* Entity object.
*
* @var object
*/
protected $entity;
/**
* Constructs a MockEntityDrupalWrapper.
*
* @param string $entity_type
* The entity type.
* @param string $bundle
* The bundle.
* @param string $fixture
* The object to set.
*/
public function __construct($entity_type, $bundle = NULL, $fixture = NULL) {
$this->entityType = $entity_type;
$this->bundle = $bundle;
$this->entity = $fixture;
}
/**
* Load a fixture from a file with a serialized entity.
*
* @param string $fixture_path
* The path to the file containing the serialized version of the entity. Or
* the object itself.
*
* @throws TypedEntityException
*/
public function loadFixture($fixture_path) {
if (!file_exists($fixture_path)) {
throw new TypedEntityException('The provided fixture file does not exist.');
}
if (!$this->entity = (object) unserialize(file_get_contents($fixture_path))) {
throw new TypedEntityException('The contents of the fixture is not valid.');
}
}
/**
* {@inheritdoc}
*/
public function info() {
$entity_array = (array) $this->entity;
return array_keys($entity_array);
}
/**
* {@inheritdoc}
*/
public function type() {
return $this->entityType;
}
/**
* {@inheritdoc}
*/
public function value(array $options = array()) {
return $this->entity;
}
/**
* {@inheritdoc}
*/
public function raw() {
return $this->value();
}
/**
* {@inheritdoc}
*/
public function set($value) {
$this->entity = $value;
}
/**
* {@inheritdoc}
*/
public function validate($value) {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function optionsList($op = 'edit') {
FALSE;
}
/**
* {@inheritdoc}
*/
public function label() {
return 'Label';
}
/**
* {@inheritdoc}
*/
public function access($op, $account = NULL) {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function getPropertyInfo($name = NULL) {
return array();
}
/**
* {@inheritdoc}
*/
public function &refPropertyInfo() {
return $this->getPropertyInfo();
}
/**
* {@inheritdoc}
*/
public function language($langcode = LANGUAGE_NONE) {}
/**
* {@inheritdoc}
*/
public function getPropertyLanguage() {
return (object) array('language' => LANGUAGE_NONE);
}
/**
* {@inheritdoc}
*/
public function get($name) {
return new MockEntityDrupalWrapper(NULL, NULL, $this->entity->{$name});
}
/**
* {@inheritdoc}
*/
public function getIterator() {
return array();
}
/**
* {@inheritdoc}
*/
public function getIdentifier() {
return 1;
}
/**
* {@inheritdoc}
*/
public function getBundle() {
$this->bundle;
}
/**
* {@inheritdoc}
*/
public function view($view_mode = 'full', $langcode = NULL, $page = NULL) {
return array();
}
/**
* {@inheritdoc}
*/
public function entityAccess($op, $account = NULL) {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function save() {}
/**
* {@inheritdoc}
*/
public function delete() {}
/**
* {@inheritdoc}
*/
public function entityInfo() {
return array();
}
/**
* {@inheritdoc}
*/
public function entityKey($name) {
return 'id';
}
/**
* Sub wrappers.
*
* @param $name
* The name of the requested property.
*
* @return \EntityMetadataWrapperInterface
* The wrapper.
*/
public function __get($name) {
return $this->get($name);
}
}
<?php
/**
* @file
* Contains \Drupal\typed_entity\TypedEntity\Tests\MockEntityDrupalWrapperInterface.
*/
namespace Drupal\typed_entity\TypedEntity\Tests;
use Drupal\typed_entity\Exception\TypedEntityException;
interface MockEntityDrupalWrapperInterface extends \EntityDrupalWrapperInterface {
/**
* Load a fixture from a file with a serialized entity.
*
* @param string $fixture_path
* The path to the file containing the serialized version of the entity. Or
* the object itself.
*
* @throws TypedEntityException
*/
public function loadFixture($fixture_path);
}
<?php
/**
* @file
* Contains \Drupal\typed_entity\TypedEntity\Tests\TypedEntityModules.
*/
namespace Drupal\typed_entity\TypedEntity\Tests;
use Drupal\xautoload\Tests\Example\AbstractExampleModules;
class TypedEntityModules extends AbstractExampleModules {
/**
* @return string[]
*/
public function getAvailableExtensions() {
return array_fill_keys(array(
'system',
'xautoload',
'typed_entity',
'typed_entity_example',
), 'module');
}
/**
* @return string[]
*/
public function getExampleClasses() {
return array(
'typed_entity' => array(
'\\Drupal\\typed_entity\\TypedEntity\\TypedEntity',
'\\Drupal\\typed_entity\\TypedEntity\\TypedEntityInterface',
'\\Drupal\\typed_entity\\TypedEntity\\TypedEntityManager',
'\\Drupal\\typed_entity\\TypedEntity\\TypedEntityManagerInterface',
'\\Drupal\\typed_entity\\TypedEntity\\Tests\\MockEntityDrupalWrapper',
'\\Drupal\\typed_entity\\TypedEntity\\Tests\\MockEntityDrupalWrapperInterface',
'\\Drupal\\typed_entity\\TypedEntity\\Tests\\TypedEntityModules',
'\\Drupal\\typed_entity\\Tests\\TypedEntityUnitTestCase',
),
'typed_entity_example' => array(
'\\Drupal\\typed_entity_example\\TypedEntity\\TypedNode',
'\\Drupal\\typed_entity_example\\TypedEntity\\TypedNodeInterface',
'\\Drupal\\typed_entity_example\\TypedEntity\\Node\\Article',
'\\Drupal\\typed_entity_example\\TypedEntity\\Node\\ArticleInterface',
'\\Drupal\\typed_entity_example\\TypedEntity\\Tests\\TypedNodeArticleUnitTest',
'\\Drupal\\typed_entity_example\\Tests\\TypedEntityExampleUnitTestCase',
'\\Drupal\\typed_entity_example\\Tests\\TypedEntityExampleWebTestCase',
),
);
}
/**
* Replicates drupal_parse_info_file(dirname($module->uri) . '/' . $module->name . '.info')
*
* @see drupal_parse_info_file()
*
* @param string $name
*
* @return array
* Parsed info file contents.
*/
public function drupalParseInfoFile($name) {
$info = array('core' => '7.x');
if (0 === strpos($name, 'typed_entity_example')) {
$info['dependencies'][] = 'xautoload';
$info['dependencies'][] = 'entity';
$info['dependencies'][] = 'typed_entity';
}
elseif (0 === strpos($name, 'typed_entity')) {
$info['dependencies'][] = 'xautoload';
$info['dependencies'][] = 'entity';
}
return $info;
}
}
......@@ -85,9 +85,9 @@ class TypedEntity implements TypedEntityInterface {
// This means that somehow we do not have neither entity nor entity ID.
throw new TypedEntityException('You need to provide the fully loaded entity or the entity ID.');
}
list($entity_id, , $bundle) = entity_extract_ids($this->getEntityType(), $this->entity);
$this->entityId = $entity_id;
$this->bundle = $bundle;
$this->entityId = $this
->getWrapper()
->getIdentifier();
return $this->entityId;
}
......@@ -104,8 +104,12 @@ class TypedEntity implements TypedEntityInterface {
// We do not have neither entity nor ID. We cannot load.
return NULL;
}
$entities = entity_load($this->getEntityType(), array($this->getEntityId()));
$this->entity = isset($entities[$this->getEntityId()]) ? $entities[$this->getEntityId()] : NULL;
$this->entity = xautoload()
->getServiceContainer()
->get('entity_wrapper')
->wrap($this->getEntityType(), $entity_id)
->value();
return $this->entity;
}
......@@ -124,9 +128,9 @@ class TypedEntity implements TypedEntityInterface {
return $this->bundle;
}
list($entity_id, , $bundle) = entity_extract_ids($this->getEntityType(), $this->getEntity());
$this->entityId = $entity_id;
$this->bundle = $bundle;
$this->bundle = $this
->getWrapper()
->getBundle();
return $this->bundle;
}
......@@ -137,7 +141,10 @@ class TypedEntity implements TypedEntityInterface {
if (isset($this->wrapper)) {
return $this->wrapper;
}
$this->wrapper = entity_metadata_wrapper($this->getEntityType(), $this->getEntity());
$this->wrapper = xautoload()
->getServiceContainer()
->get('entity_wrapper')
->wrap($this->getEntityType(), $this->getEntity());
return $this->wrapper;
}
......
......@@ -4,3 +4,12 @@
* @file
* Module implementation file.
*/
use Drupal\typed_entity\Entity\EntityWrapperService;
/**
* Implements hook_xautoload().
*/
function typed_entity_xautoload($adapter) {
xautoload()->getServiceContainer()->set('entity_wrapper', new EntityWrapperService());
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment