Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View file

@ -0,0 +1,568 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Access\AccessManagerTest.
*/
namespace Drupal\Tests\Core\Access;
use Drupal\Core\Access\AccessCheckInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessManagerInterface;
use Drupal\Core\Access\CheckProvider;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Access\AccessManager;
use Drupal\Core\Access\DefaultAccessCheck;
use Drupal\Tests\UnitTestCase;
use Drupal\router_test\Access\DefinedTestAccessCheck;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* @coversDefaultClass \Drupal\Core\Access\AccessManager
* @group Access
*/
class AccessManagerTest extends UnitTestCase {
/**
* The dependency injection container.
*
* @var \Symfony\Component\DependencyInjection\ContainerBuilder
*/
protected $container;
/**
* The collection of routes, which are tested.
*
* @var \Symfony\Component\Routing\RouteCollection
*/
protected $routeCollection;
/**
* The access manager to test.
*
* @var \Drupal\Core\Access\AccessManager
*/
protected $accessManager;
/**
* The route provider.
*
* @var \PHPUnit_Framework_MockObject_MockObject
*/
protected $routeProvider;
/**
* The parameter converter.
*
* @var \Drupal\Core\ParamConverter\ParamConverterManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $paramConverter;
/**
* The mocked account.
*
* @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $account;
/**
* The access arguments resolver.
*
* @var \Drupal\Core\Access\AccessArgumentsResolverFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $argumentsResolverFactory;
/**
* @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $currentUser;
/**
* @var \Drupal\Core\Access\CheckProvider
*/
protected $checkProvider;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->container = new ContainerBuilder();
$this->routeCollection = new RouteCollection();
$this->routeCollection->add('test_route_1', new Route('/test-route-1'));
$this->routeCollection->add('test_route_2', new Route('/test-route-2', array(), array('_access' => 'TRUE')));
$this->routeCollection->add('test_route_3', new Route('/test-route-3', array(), array('_access' => 'FALSE')));
$this->routeCollection->add('test_route_4', new Route('/test-route-4/{value}', array(), array('_access' => 'TRUE')));
$this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
$map = array();
foreach ($this->routeCollection->all() as $name => $route) {
$map[] = array($name, array(), $route);
}
$map[] = array('test_route_4', array('value' => 'example'), $this->routeCollection->get('test_route_4'));
$this->routeProvider->expects($this->any())
->method('getRouteByName')
->will($this->returnValueMap($map));
$map = array();
$map[] = array('test_route_1', array(), '/test-route-1');
$map[] = array('test_route_2', array(), '/test-route-2');
$map[] = array('test_route_3', array(), '/test-route-3');
$map[] = array('test_route_4', array('value' => 'example'), '/test-route-4/example');
$this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface');
$this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
$this->currentUser = $this->getMock('Drupal\Core\Session\AccountInterface');
$this->argumentsResolverFactory = $this->getMock('Drupal\Core\Access\AccessArgumentsResolverFactoryInterface');
$this->checkProvider = new CheckProvider();
$this->checkProvider->setContainer($this->container);
$this->accessManager = new AccessManager($this->routeProvider, $this->paramConverter, $this->argumentsResolverFactory, $this->currentUser, $this->checkProvider);
}
/**
* Tests \Drupal\Core\Access\AccessManager::setChecks().
*/
public function testSetChecks() {
// Check setChecks without any access checker defined yet.
$this->checkProvider->setChecks($this->routeCollection);
foreach ($this->routeCollection->all() as $route) {
$this->assertNull($route->getOption('_access_checks'));
}
$this->setupAccessChecker();
$this->checkProvider->setChecks($this->routeCollection);
$this->assertEquals($this->routeCollection->get('test_route_1')->getOption('_access_checks'), NULL);
$this->assertEquals($this->routeCollection->get('test_route_2')->getOption('_access_checks'), array('test_access_default'));
$this->assertEquals($this->routeCollection->get('test_route_3')->getOption('_access_checks'), array('test_access_default'));
}
/**
* Tests setChecks with a dynamic access checker.
*/
public function testSetChecksWithDynamicAccessChecker() {
// Setup the access manager.
$this->accessManager = new AccessManager($this->routeProvider, $this->paramConverter, $this->argumentsResolverFactory, $this->currentUser, $this->checkProvider);
// Setup the dynamic access checker.
$access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface');
$this->container->set('test_access', $access_check);
$this->checkProvider->addCheckService('test_access', 'access');
$route = new Route('/test-path', array(), array('_foo' => '1', '_bar' => '1'));
$route2 = new Route('/test-path', array(), array('_foo' => '1', '_bar' => '2'));
$collection = new RouteCollection();
$collection->add('test_route', $route);
$collection->add('test_route2', $route2);
$access_check->expects($this->exactly(2))
->method('applies')
->with($this->isInstanceOf('Symfony\Component\Routing\Route'))
->will($this->returnCallback(function (Route $route) {
return $route->getRequirement('_bar') == 2;
}));
$this->checkProvider->setChecks($collection);
$this->assertEmpty($route->getOption('_access_checks'));
$this->assertEquals(array('test_access'), $route2->getOption('_access_checks'));
}
/**
* Tests \Drupal\Core\Access\AccessManager::check().
*/
public function testCheck() {
$route_matches = [];
// Construct route match objects.
foreach ($this->routeCollection->all() as $route_name => $route) {
$route_matches[$route_name] = new RouteMatch($route_name, $route, [], []);
}
// Check route access without any access checker defined yet.
foreach ($route_matches as $route_match) {
$this->assertEquals(FALSE, $this->accessManager->check($route_match, $this->account));
$this->assertEquals(AccessResult::neutral(), $this->accessManager->check($route_match, $this->account, NULL, TRUE));
}
$this->setupAccessChecker();
// An access checker got setup, but the routes haven't been setup using
// setChecks.
foreach ($route_matches as $route_match) {
$this->assertEquals(FALSE, $this->accessManager->check($route_match, $this->account));
$this->assertEquals(AccessResult::neutral(), $this->accessManager->check($route_match, $this->account, NULL, TRUE));
}
// Now applicable access checks have been saved on each route object.
$this->checkProvider->setChecks($this->routeCollection);
$this->setupAccessArgumentsResolverFactory();
$this->assertEquals(FALSE, $this->accessManager->check($route_matches['test_route_1'], $this->account));
$this->assertEquals(TRUE, $this->accessManager->check($route_matches['test_route_2'], $this->account));
$this->assertEquals(FALSE, $this->accessManager->check($route_matches['test_route_3'], $this->account));
$this->assertEquals(TRUE, $this->accessManager->check($route_matches['test_route_4'], $this->account));
$this->assertEquals(AccessResult::neutral(), $this->accessManager->check($route_matches['test_route_1'], $this->account, NULL, TRUE));
$this->assertEquals(AccessResult::allowed(), $this->accessManager->check($route_matches['test_route_2'], $this->account, NULL, TRUE));
$this->assertEquals(AccessResult::forbidden(), $this->accessManager->check($route_matches['test_route_3'], $this->account, NULL, TRUE));
$this->assertEquals(AccessResult::allowed(), $this->accessManager->check($route_matches['test_route_4'], $this->account, NULL, TRUE));
}
/**
* Tests \Drupal\Core\Access\AccessManager::check() with no account specified.
*
* @covers ::check
*/
public function testCheckWithNullAccount() {
$this->setupAccessChecker();
$this->checkProvider->setChecks($this->routeCollection);
$route = $this->routeCollection->get('test_route_2');
$route_match = new RouteMatch('test_route_2', $route, [], []);
// Asserts that the current user is passed to the access arguments resolver
// factory.
$this->setupAccessArgumentsResolverFactory()
->with($route_match, $this->currentUser, NULL);
$this->assertTrue($this->accessManager->check($route_match));
}
/**
* Provides data for the conjunction test.
*
* @return array
* An array of data for check conjunctions.
*
* @see \Drupal\Tests\Core\Access\AccessManagerTest::testCheckConjunctions()
*/
public function providerTestCheckConjunctions() {
$access_allow = AccessResult::allowed();
$access_deny = AccessResult::neutral();
$access_kill = AccessResult::forbidden();
$access_configurations = array();
$access_configurations[] = array(
'name' => 'test_route_4',
'condition_one' => 'TRUE',
'condition_two' => 'FALSE',
'expected' => $access_kill,
);
$access_configurations[] = array(
'name' => 'test_route_5',
'condition_one' => 'TRUE',
'condition_two' => 'NULL',
'expected' => $access_deny,
);
$access_configurations[] = array(
'name' => 'test_route_6',
'condition_one' => 'FALSE',
'condition_two' => 'NULL',
'expected' => $access_kill,
);
$access_configurations[] = array(
'name' => 'test_route_7',
'condition_one' => 'TRUE',
'condition_two' => 'TRUE',
'expected' => $access_allow,
);
$access_configurations[] = array(
'name' => 'test_route_8',
'condition_one' => 'FALSE',
'condition_two' => 'FALSE',
'expected' => $access_kill,
);
$access_configurations[] = array(
'name' => 'test_route_9',
'condition_one' => 'NULL',
'condition_two' => 'NULL',
'expected' => $access_deny,
);
return $access_configurations;
}
/**
* Test \Drupal\Core\Access\AccessManager::check() with conjunctions.
*
* @dataProvider providerTestCheckConjunctions
*/
public function testCheckConjunctions($name, $condition_one, $condition_two, $expected_access) {
$this->setupAccessChecker();
$access_check = new DefinedTestAccessCheck();
$this->container->register('test_access_defined', $access_check);
$this->checkProvider->addCheckService('test_access_defined', 'access', array('_test_access'));
$route_collection = new RouteCollection();
// Setup a test route for each access configuration.
$requirements = array(
'_access' => $condition_one,
'_test_access' => $condition_two,
);
$route = new Route($name, array(), $requirements);
$route_collection->add($name, $route);
$this->checkProvider->setChecks($route_collection);
$this->setupAccessArgumentsResolverFactory();
$route_match = new RouteMatch($name, $route, array(), array());
$this->assertEquals($expected_access->isAllowed(), $this->accessManager->check($route_match, $this->account));
$this->assertEquals($expected_access, $this->accessManager->check($route_match, $this->account, NULL, TRUE));
}
/**
* Tests the checkNamedRoute method.
*
* @see \Drupal\Core\Access\AccessManager::checkNamedRoute()
*/
public function testCheckNamedRoute() {
$this->setupAccessChecker();
$this->checkProvider->setChecks($this->routeCollection);
$this->setupAccessArgumentsResolverFactory();
$this->paramConverter->expects($this->at(0))
->method('convert')
->with(array(RouteObjectInterface::ROUTE_NAME => 'test_route_2', RouteObjectInterface::ROUTE_OBJECT => $this->routeCollection->get('test_route_2')))
->will($this->returnValue(array()));
$this->paramConverter->expects($this->at(1))
->method('convert')
->with(array(RouteObjectInterface::ROUTE_NAME => 'test_route_2', RouteObjectInterface::ROUTE_OBJECT => $this->routeCollection->get('test_route_2')))
->will($this->returnValue(array()));
$this->paramConverter->expects($this->at(2))
->method('convert')
->with(array('value' => 'example', RouteObjectInterface::ROUTE_NAME => 'test_route_4', RouteObjectInterface::ROUTE_OBJECT => $this->routeCollection->get('test_route_4')))
->will($this->returnValue(array('value' => 'example')));
$this->paramConverter->expects($this->at(3))
->method('convert')
->with(array('value' => 'example', RouteObjectInterface::ROUTE_NAME => 'test_route_4', RouteObjectInterface::ROUTE_OBJECT => $this->routeCollection->get('test_route_4')))
->will($this->returnValue(array('value' => 'example')));
// Tests the access with routes with parameters without given request.
$this->assertEquals(TRUE, $this->accessManager->checkNamedRoute('test_route_2', array(), $this->account));
$this->assertEquals(AccessResult::allowed(), $this->accessManager->checkNamedRoute('test_route_2', array(), $this->account, TRUE));
$this->assertEquals(TRUE, $this->accessManager->checkNamedRoute('test_route_4', array('value' => 'example'), $this->account));
$this->assertEquals(AccessResult::allowed(), $this->accessManager->checkNamedRoute('test_route_4', array('value' => 'example'), $this->account, TRUE));
}
/**
* Tests the checkNamedRoute with upcasted values.
*
* @see \Drupal\Core\Access\AccessManager::checkNamedRoute()
*/
public function testCheckNamedRouteWithUpcastedValues() {
$this->routeCollection = new RouteCollection();
$route = new Route('/test-route-1/{value}', array(), array('_test_access' => 'TRUE'));
$this->routeCollection->add('test_route_1', $route);
$this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
$this->routeProvider->expects($this->any())
->method('getRouteByName')
->with('test_route_1', array('value' => 'example'))
->will($this->returnValue($route));
$map = array();
$map[] = array('test_route_1', array('value' => 'example'), '/test-route-1/example');
$this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface');
$this->paramConverter->expects($this->atLeastOnce())
->method('convert')
->with(array('value' => 'example', RouteObjectInterface::ROUTE_NAME => 'test_route_1', RouteObjectInterface::ROUTE_OBJECT => $route))
->will($this->returnValue(array('value' => 'upcasted_value')));
$this->setupAccessArgumentsResolverFactory($this->exactly(2))
->with($this->callback(function ($route_match) {
return $route_match->getParameters()->get('value') == 'upcasted_value';
}));
$this->accessManager = new AccessManager($this->routeProvider, $this->paramConverter, $this->argumentsResolverFactory, $this->currentUser, $this->checkProvider);
$access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface');
$access_check->expects($this->atLeastOnce())
->method('applies')
->will($this->returnValue(TRUE));
$access_check->expects($this->atLeastOnce())
->method('access')
->will($this->returnValue(AccessResult::forbidden()));
$this->container->set('test_access', $access_check);
$this->checkProvider->addCheckService('test_access', 'access');
$this->checkProvider->setChecks($this->routeCollection);
$this->assertEquals(FALSE, $this->accessManager->checkNamedRoute('test_route_1', array('value' => 'example'), $this->account));
$this->assertEquals(AccessResult::forbidden(), $this->accessManager->checkNamedRoute('test_route_1', array('value' => 'example'), $this->account, TRUE));
}
/**
* Tests the checkNamedRoute with default values.
*
* @covers ::checkNamedRoute
*/
public function testCheckNamedRouteWithDefaultValue() {
$this->routeCollection = new RouteCollection();
$route = new Route('/test-route-1/{value}', array('value' => 'example'), array('_test_access' => 'TRUE'));
$this->routeCollection->add('test_route_1', $route);
$this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
$this->routeProvider->expects($this->any())
->method('getRouteByName')
->with('test_route_1', array())
->will($this->returnValue($route));
$map = array();
$map[] = array('test_route_1', array('value' => 'example'), '/test-route-1/example');
$this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface');
$this->paramConverter->expects($this->atLeastOnce())
->method('convert')
->with(array('value' => 'example', RouteObjectInterface::ROUTE_NAME => 'test_route_1', RouteObjectInterface::ROUTE_OBJECT => $route))
->will($this->returnValue(array('value' => 'upcasted_value')));
$this->setupAccessArgumentsResolverFactory($this->exactly(2))
->with($this->callback(function ($route_match) {
return $route_match->getParameters()->get('value') == 'upcasted_value';
}));
$this->accessManager = new AccessManager($this->routeProvider, $this->paramConverter, $this->argumentsResolverFactory, $this->currentUser, $this->checkProvider);
$access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface');
$access_check->expects($this->atLeastOnce())
->method('applies')
->will($this->returnValue(TRUE));
$access_check->expects($this->atLeastOnce())
->method('access')
->will($this->returnValue(AccessResult::forbidden()));
$this->container->set('test_access', $access_check);
$this->checkProvider->addCheckService('test_access', 'access');
$this->checkProvider->setChecks($this->routeCollection);
$this->assertEquals(FALSE, $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account));
$this->assertEquals(AccessResult::forbidden(), $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account, TRUE));
}
/**
* Tests checkNamedRoute given an invalid/non existing route name.
*/
public function testCheckNamedRouteWithNonExistingRoute() {
$this->routeProvider->expects($this->any())
->method('getRouteByName')
->will($this->throwException(new RouteNotFoundException()));
$this->setupAccessChecker();
$this->assertEquals(FALSE, $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account), 'A non existing route lead to access.');
$this->assertEquals(AccessResult::forbidden()->addCacheTags(['config:core.extension']), $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account, TRUE), 'A non existing route lead to access.');
}
/**
* Tests that an access checker throws an exception for not allowed values.
*
* @dataProvider providerCheckException
*
* @expectedException \Drupal\Core\Access\AccessException
*/
public function testCheckException($return_value) {
$route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
// Setup a test route for each access configuration.
$requirements = array(
'_test_incorrect_value' => 'TRUE',
);
$options = array(
'_access_checks' => array(
'test_incorrect_value',
),
);
$route = new Route('', array(), $requirements, $options);
$route_provider->expects($this->any())
->method('getRouteByName')
->will($this->returnValue($route));
$this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface');
$this->paramConverter->expects($this->any())
->method('convert')
->will($this->returnValue(array()));
$this->setupAccessArgumentsResolverFactory();
$container = new ContainerBuilder();
// Register a service that will return an incorrect value.
$access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface');
$access_check->expects($this->any())
->method('access')
->will($this->returnValue($return_value));
$container->set('test_incorrect_value', $access_check);
$access_manager = new AccessManager($route_provider, $this->paramConverter, $this->argumentsResolverFactory, $this->currentUser, $this->checkProvider);
$this->checkProvider->setContainer($container);
$this->checkProvider->addCheckService('test_incorrect_value', 'access');
$access_manager->checkNamedRoute('test_incorrect_value', array(), $this->account);
}
/**
* Data provider for testCheckException.
*
* @return array
*/
public function providerCheckException() {
return array(
array(array(1)),
array('string'),
array(0),
array(1),
);
}
/**
* Adds a default access check service to the container and the access manager.
*/
protected function setupAccessChecker() {
$access_check = new DefaultAccessCheck();
$this->container->register('test_access_default', $access_check);
$this->checkProvider->addCheckService('test_access_default', 'access', array('_access'));
}
/**
* Add default expectations to the access arguments resolver factory.
*/
protected function setupAccessArgumentsResolverFactory($constraint = NULL) {
if (!isset($constraint)) {
$constraint = $this->any();
}
return $this->argumentsResolverFactory->expects($constraint)
->method('getArgumentsResolver')
->will($this->returnCallback(function ($route_match, $account) {
$resolver = $this->getMock('Drupal\Component\Utility\ArgumentsResolverInterface');
$resolver->expects($this->any())
->method('getArguments')
->will($this->returnCallback(function ($callable) use ($route_match) {
return array($route_match->getRouteObject());
}));
return $resolver;
}));
}
}
/**
* Defines an interface with a defined access() method for mocking.
*/
interface TestAccessCheckInterface extends AccessCheckInterface {
public function access();
}

View file

@ -0,0 +1,938 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Access\AccessResultTest.
*/
namespace Drupal\Tests\Core\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Access\AccessResultNeutral;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Access\AccessResult
* @group Access
*/
class AccessResultTest extends UnitTestCase {
protected function assertDefaultCacheability(AccessResult $access) {
$this->assertSame([], $access->getCacheContexts());
$this->assertSame([], $access->getCacheTags());
$this->assertSame(Cache::PERMANENT, $access->getCacheMaxAge());
}
/**
* Tests the construction of an AccessResult object.
*
* @covers ::__construct
* @covers ::neutral
*/
public function testConstruction() {
$verify = function (AccessResult $access) {
$this->assertFalse($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertTrue($access->isNeutral());
$this->assertDefaultCacheability($access);
};
// Verify the object when using the constructor.
$a = new AccessResultNeutral();
$verify($a);
// Verify the object when using the ::create() convenience method.
$b = AccessResult::neutral();
$verify($b);
$this->assertEquals($a, $b);
}
/**
* @covers ::allowed
* @covers ::isAllowed
* @covers ::isForbidden
* @covers ::isNeutral
*/
public function testAccessAllowed() {
$verify = function (AccessResult $access) {
$this->assertTrue($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
};
// Verify the object when using the ::allowed() convenience static method.
$b = AccessResult::allowed();
$verify($b);
}
/**
* @covers ::forbidden
* @covers ::isAllowed
* @covers ::isForbidden
* @covers ::isNeutral
*/
public function testAccessForbidden() {
$verify = function (AccessResult $access) {
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
};
// Verify the object when using the ::forbidden() convenience static method.
$b = AccessResult::forbidden();
$verify($b);
}
/**
* @covers ::allowedIf
* @covers ::isAllowed
* @covers ::isForbidden
* @covers ::isNeutral
*/
public function testAccessConditionallyAllowed() {
$verify = function (AccessResult $access, $allowed) {
$this->assertSame($allowed, $access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertSame(!$allowed, $access->isNeutral());
$this->assertDefaultCacheability($access);
};
$b1 = AccessResult::allowedIf(TRUE);
$verify($b1, TRUE);
$b2 = AccessResult::allowedIf(FALSE);
$verify($b2, FALSE);
}
/**
* @covers ::forbiddenIf
* @covers ::isAllowed
* @covers ::isForbidden
* @covers ::isNeutral
*/
public function testAccessConditionallyForbidden() {
$verify = function (AccessResult $access, $forbidden) {
$this->assertFalse($access->isAllowed());
$this->assertSame($forbidden, $access->isForbidden());
$this->assertSame(!$forbidden, $access->isNeutral());
$this->assertDefaultCacheability($access);
};
$b1 = AccessResult::forbiddenIf(TRUE);
$verify($b1, TRUE);
$b2 = AccessResult::forbiddenIf(FALSE);
$verify($b2, FALSE);
}
/**
* @covers ::andIf
*/
public function testAndIf() {
$neutral = AccessResult::neutral();
$allowed = AccessResult::allowed();
$forbidden = AccessResult::forbidden();
$unused_access_result_due_to_lazy_evaluation = $this->getMock('\Drupal\Core\Access\AccessResultInterface');
$unused_access_result_due_to_lazy_evaluation->expects($this->never())
->method($this->anything());
// ALLOWED && ALLOWED === ALLOWED.
$access = $allowed->andIf($allowed);
$this->assertTrue($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// ALLOWED && NEUTRAL === NEUTRAL.
$access = $allowed->andIf($neutral);
$this->assertFalse($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertTrue($access->isNeutral());
$this->assertDefaultCacheability($access);
// ALLOWED && FORBIDDEN === FORBIDDEN.
$access = $allowed->andIf($forbidden);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// NEUTRAL && ALLOW == NEUTRAL
$access = $neutral->andIf($allowed);
$this->assertFalse($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertTrue($access->isNeutral());
$this->assertDefaultCacheability($access);
// NEUTRAL && NEUTRAL === NEUTRAL.
$access = $neutral->andIf($neutral);
$this->assertFalse($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertTrue($access->isNeutral());
$this->assertDefaultCacheability($access);
// NEUTRAL && FORBIDDEN === FORBIDDEN.
$access = $neutral->andIf($forbidden);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// FORBIDDEN && ALLOWED = FORBIDDEN
$access = $forbidden->andif($allowed);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// FORBIDDEN && NEUTRAL = FORBIDDEN
$access = $forbidden->andif($neutral);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// FORBIDDEN && FORBIDDEN = FORBIDDEN
$access = $forbidden->andif($forbidden);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// FORBIDDEN && * === FORBIDDEN: lazy evaluation verification.
$access = $forbidden->andIf($unused_access_result_due_to_lazy_evaluation);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
}
/**
* @covers ::orIf
*/
public function testOrIf() {
$neutral = AccessResult::neutral();
$allowed = AccessResult::allowed();
$forbidden = AccessResult::forbidden();
$unused_access_result_due_to_lazy_evaluation = $this->getMock('\Drupal\Core\Access\AccessResultInterface');
$unused_access_result_due_to_lazy_evaluation->expects($this->never())
->method($this->anything());
// ALLOWED || ALLOWED === ALLOWED.
$access = $allowed->orIf($allowed);
$this->assertTrue($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// ALLOWED || NEUTRAL === ALLOWED.
$access = $allowed->orIf($neutral);
$this->assertTrue($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// ALLOWED || FORBIDDEN === FORBIDDEN.
$access = $allowed->orIf($forbidden);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// NEUTRAL || NEUTRAL === NEUTRAL.
$access = $neutral->orIf($neutral);
$this->assertFalse($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertTrue($access->isNeutral());
$this->assertDefaultCacheability($access);
// NEUTRAL || ALLOWED === ALLOWED.
$access = $neutral->orIf($allowed);
$this->assertTrue($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// NEUTRAL || FORBIDDEN === FORBIDDEN.
$access = $neutral->orIf($forbidden);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// FORBIDDEN || ALLOWED === FORBIDDEN.
$access = $forbidden->orIf($allowed);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// FORBIDDEN || NEUTRAL === FORBIDDEN.
$access = $forbidden->orIf($allowed);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// FORBIDDEN || FORBIDDEN === FORBIDDEN.
$access = $forbidden->orIf($allowed);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
// FORBIDDEN || * === FORBIDDEN.
$access = $forbidden->orIf($unused_access_result_due_to_lazy_evaluation);
$this->assertFalse($access->isAllowed());
$this->assertTrue($access->isForbidden());
$this->assertFalse($access->isNeutral());
$this->assertDefaultCacheability($access);
}
/**
* @covers ::setCacheMaxAge
* @covers ::getCacheMaxAge
*/
public function testCacheMaxAge() {
$this->assertSame(Cache::PERMANENT, AccessResult::neutral()->getCacheMaxAge());
$this->assertSame(1337, AccessResult::neutral()->setCacheMaxAge(1337)->getCacheMaxAge());
}
/**
* @covers ::addCacheContexts
* @covers ::resetCacheContexts
* @covers ::getCacheContexts
* @covers ::cachePerPermissions
* @covers ::cachePerUser
* @covers ::allowedIfHasPermission
*/
public function testCacheContexts() {
$verify = function (AccessResult $access, array $contexts) {
$this->assertFalse($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertTrue($access->isNeutral());
$this->assertSame(Cache::PERMANENT, $access->getCacheMaxAge());
$this->assertSame($contexts, $access->getCacheContexts());
$this->assertSame([], $access->getCacheTags());
};
$access = AccessResult::neutral()->addCacheContexts(['foo']);
$verify($access, ['foo']);
// Verify resetting works.
$access->resetCacheContexts();
$verify($access, []);
// Verify idempotency.
$access->addCacheContexts(['foo'])
->addCacheContexts(['foo']);
$verify($access, ['foo']);
// Verify same values in different call order yields the same result.
$access->resetCacheContexts()
->addCacheContexts(['foo'])
->addCacheContexts(['bar']);
$verify($access, ['bar', 'foo']);
$access->resetCacheContexts()
->addCacheContexts(['bar'])
->addCacheContexts(['foo']);
$verify($access, ['bar', 'foo']);
// ::cachePerPermissions() convenience method.
$contexts = array('user.permissions');
$a = AccessResult::neutral()->addCacheContexts($contexts);
$verify($a, $contexts);
$b = AccessResult::neutral()->cachePerPermissions();
$verify($b, $contexts);
$this->assertEquals($a, $b);
// ::cachePerUser() convenience method.
$contexts = array('user');
$a = AccessResult::neutral()->addCacheContexts($contexts);
$verify($a, $contexts);
$b = AccessResult::neutral()->cachePerUser();
$verify($b, $contexts);
$this->assertEquals($a, $b);
// Both.
$contexts = array('user', 'user.permissions');
$a = AccessResult::neutral()->addCacheContexts($contexts);
$verify($a, $contexts);
$b = AccessResult::neutral()->cachePerPermissions()->cachePerUser();
$verify($b, $contexts);
$c = AccessResult::neutral()->cachePerUser()->cachePerPermissions();
$verify($c, $contexts);
$this->assertEquals($a, $b);
$this->assertEquals($a, $c);
// ::allowIfHasPermission and ::allowedIfHasPermission convenience methods.
$account = $this->getMock('\Drupal\Core\Session\AccountInterface');
$account->expects($this->any())
->method('hasPermission')
->with('may herd llamas')
->will($this->returnValue(FALSE));
$contexts = array('user.permissions');
// Verify the object when using the ::allowedIfHasPermission() convenience
// static method.
$b = AccessResult::allowedIfHasPermission($account, 'may herd llamas');
$verify($b, $contexts);
}
/**
* @covers ::addCacheTags
* @covers ::resetCacheTags
* @covers ::getCacheTags
* @covers ::cacheUntilEntityChanges
* @covers ::cacheUntilConfigurationChanges
*/
public function testCacheTags() {
$verify = function (AccessResult $access, array $tags) {
$this->assertFalse($access->isAllowed());
$this->assertFalse($access->isForbidden());
$this->assertTrue($access->isNeutral());
$this->assertSame(Cache::PERMANENT, $access->getCacheMaxAge());
$this->assertSame([], $access->getCacheContexts());
$this->assertSame($tags, $access->getCacheTags());
};
$access = AccessResult::neutral()->addCacheTags(['foo:bar']);
$verify($access, ['foo:bar']);
// Verify resetting works.
$access->resetCacheTags();
$verify($access, []);
// Verify idempotency.
$access->addCacheTags(['foo:bar'])
->addCacheTags(['foo:bar']);
$verify($access, ['foo:bar']);
// Verify same values in different call order yields the same result.
$access->resetCacheTags()
->addCacheTags(['bar:baz'])
->addCacheTags(['bar:qux'])
->addCacheTags(['foo:bar'])
->addCacheTags(['foo:baz']);
$verify($access, ['bar:baz', 'bar:qux', 'foo:bar', 'foo:baz']);
$access->resetCacheTags()
->addCacheTags(['foo:bar'])
->addCacheTags(['bar:qux'])
->addCacheTags(['foo:baz'])
->addCacheTags(['bar:baz']);
$verify($access, ['bar:baz', 'bar:qux', 'foo:bar', 'foo:baz']);
// ::cacheUntilEntityChanges() convenience method.
$node = $this->getMock('\Drupal\node\NodeInterface');
$node->expects($this->any())
->method('getCacheTags')
->will($this->returnValue(array('node:20011988')));
$tags = array('node:20011988');
$a = AccessResult::neutral()->addCacheTags($tags);
$verify($a, $tags);
$b = AccessResult::neutral()->cacheUntilEntityChanges($node);
$verify($b, $tags);
$this->assertEquals($a, $b);
// ::cacheUntilConfigurationChanges() convenience method.
$configuration = $this->getMock('\Drupal\Core\Config\ConfigBase');
$configuration->expects($this->any())
->method('getCacheTags')
->will($this->returnValue(array('config:foo.bar.baz')));
$tags = array('config:foo.bar.baz');
$a = AccessResult::neutral()->addCacheTags($tags);
$verify($a, $tags);
$b = AccessResult::neutral()->cacheUntilConfigurationChanges($configuration);
$verify($b, $tags);
$this->assertEquals($a, $b);
}
/**
* @covers ::inheritCacheability
*/
public function testInheritCacheability() {
// andIf(); 1st has defaults, 2nd has custom tags, contexts and max-age.
$access = AccessResult::allowed();
$other = AccessResult::allowed()->setCacheMaxAge(1500)->cachePerPermissions()->addCacheTags(['node:20011988']);
$this->assertTrue($access->inheritCacheability($other) instanceof AccessResult);
$this->assertSame(['user.permissions'], $access->getCacheContexts());
$this->assertSame(['node:20011988'], $access->getCacheTags());
$this->assertSame(1500, $access->getCacheMaxAge());
// andIf(); 1st has custom tags, max-age, 2nd has custom contexts and max-age.
$access = AccessResult::allowed()->cachePerUser()->setCacheMaxAge(43200);
$other = AccessResult::forbidden()->addCacheTags(['node:14031991'])->setCacheMaxAge(86400);
$this->assertTrue($access->inheritCacheability($other) instanceof AccessResult);
$this->assertSame(['user'], $access->getCacheContexts());
$this->assertSame(['node:14031991'], $access->getCacheTags());
$this->assertSame(43200, $access->getCacheMaxAge());
}
/**
* Provides a list of access result pairs and operations to test.
*
* This tests the propagation of cacheability metadata. Rather than testing
* every single bit of cacheability metadata, which would lead to a mind-
* boggling number of permutations, in this test, we only consider the
* permutations of all pairs of the following set:
* - Allowed, implements CDI and is cacheable.
* - Allowed, implements CDI and is not cacheable.
* - Allowed, does not implement CDI (hence not cacheable).
* - Forbidden, implements CDI and is cacheable.
* - Forbidden, implements CDI and is not cacheable.
* - Forbidden, does not implement CDI (hence not cacheable).
* - Neutral, implements CDI and is cacheable.
* - Neutral, implements CDI and is not cacheable.
* - Neutral, does not implement CDI (hence not cacheable).
*
* (Where "CDI" is CacheableDependencyInterface.)
*
* This leads to 72 permutations (9!/(9-2)! = 9*8 = 72) per operation. There
* are two operations to test (AND and OR), so that leads to a grand total of
* 144 permutations, all of which are tested.
*
* There are two "contagious" patterns:
* - Any operation with a forbidden access result yields a forbidden result.
* This therefore also applies to the cacheability metadata associated with
* a forbidden result. This is the case for bullets 4, 5 and 6 in the set
* above.
* - Any operation yields an access result object that is of the same class
* (implementation) as the first operand. This is because operations are
* invoked on the first operand. Therefore, if the first implementation
* does not implement CacheableDependencyInterface, then the result won't
* either. This is the case for bullets 3, 6 and 9 in the set above.
*/
public function andOrCacheabilityPropagationProvider() {
// ct: cacheable=true, cf: cacheable=false, un: uncacheable.
// Note: the test cases that have a "un" access result as the first operand
// test UncacheableTestAccessResult, not AccessResult. However, we
// definitely want to verify that AccessResult's orIf() and andIf() methods
// work correctly when given an AccessResultInterface implementation that
// does not implement CacheableDependencyInterface, and we want to test the
// full gamut of permutations, so that's not a problem.
$allowed_ct = AccessResult::allowed();
$allowed_cf = AccessResult::allowed()->setCacheMaxAge(0);
$allowed_un = new UncacheableTestAccessResult('ALLOWED');
$forbidden_ct = AccessResult::forbidden();
$forbidden_cf = AccessResult::forbidden()->setCacheMaxAge(0);
$forbidden_un = new UncacheableTestAccessResult('FORBIDDEN');
$neutral_ct = AccessResult::neutral();
$neutral_cf = AccessResult::neutral()->setCacheMaxAge(0);
$neutral_un = new UncacheableTestAccessResult('NEUTRAL');
// Structure:
// - First column: first access result.
// - Second column: operator ('OR' or 'AND').
// - Third column: second access result.
// - Fourth column: whether result implements CacheableDependencyInterface
// - Fifth column: whether the result is cacheable (if column 4 is TRUE)
return [
// Allowed (ct) OR allowed (ct,cf,un).
[$allowed_ct, 'OR', $allowed_ct, TRUE, TRUE],
[$allowed_ct, 'OR', $allowed_cf, TRUE, TRUE],
[$allowed_ct, 'OR', $allowed_un, TRUE, TRUE],
// Allowed (cf) OR allowed (ct,cf,un).
[$allowed_cf, 'OR', $allowed_ct, TRUE, TRUE],
[$allowed_cf, 'OR', $allowed_cf, TRUE, FALSE],
[$allowed_cf, 'OR', $allowed_un, TRUE, FALSE],
// Allowed (un) OR allowed (ct,cf,un).
[$allowed_un, 'OR', $allowed_ct, FALSE, NULL],
[$allowed_un, 'OR', $allowed_cf, FALSE, NULL],
[$allowed_un, 'OR', $allowed_un, FALSE, NULL],
// Allowed (ct) OR forbidden (ct,cf,un).
[$allowed_ct, 'OR', $forbidden_ct, TRUE, TRUE],
[$allowed_ct, 'OR', $forbidden_cf, TRUE, FALSE],
[$allowed_ct, 'OR', $forbidden_un, TRUE, FALSE],
// Allowed (cf) OR forbidden (ct,cf,un).
[$allowed_cf, 'OR', $forbidden_ct, TRUE, TRUE],
[$allowed_cf, 'OR', $forbidden_cf, TRUE, FALSE],
[$allowed_cf, 'OR', $forbidden_un, TRUE, FALSE],
// Allowed (un) OR forbidden (ct,cf,un).
[$allowed_un, 'OR', $forbidden_ct, FALSE, NULL],
[$allowed_un, 'OR', $forbidden_cf, FALSE, NULL],
[$allowed_un, 'OR', $forbidden_un, FALSE, NULL],
// Allowed (ct) OR neutral (ct,cf,un).
[$allowed_ct, 'OR', $neutral_ct, TRUE, TRUE],
[$allowed_ct, 'OR', $neutral_cf, TRUE, TRUE],
[$allowed_ct, 'OR', $neutral_un, TRUE, TRUE],
// Allowed (cf) OR neutral (ct,cf,un).
[$allowed_cf, 'OR', $neutral_ct, TRUE, FALSE],
[$allowed_cf, 'OR', $neutral_cf, TRUE, FALSE],
[$allowed_cf, 'OR', $neutral_un, TRUE, FALSE],
// Allowed (un) OR neutral (ct,cf,un).
[$allowed_un, 'OR', $neutral_ct, FALSE, NULL],
[$allowed_un, 'OR', $neutral_cf, FALSE, NULL],
[$allowed_un, 'OR', $neutral_un, FALSE, NULL],
// Forbidden (ct) OR allowed (ct,cf,un).
[$forbidden_ct, 'OR', $allowed_ct, TRUE, TRUE],
[$forbidden_ct, 'OR', $allowed_cf, TRUE, TRUE],
[$forbidden_ct, 'OR', $allowed_un, TRUE, TRUE],
// Forbidden (cf) OR allowed (ct,cf,un).
[$forbidden_cf, 'OR', $allowed_ct, TRUE, FALSE],
[$forbidden_cf, 'OR', $allowed_cf, TRUE, FALSE],
[$forbidden_cf, 'OR', $allowed_un, TRUE, FALSE],
// Forbidden (un) OR allowed (ct,cf,un).
[$forbidden_un, 'OR', $allowed_ct, FALSE, NULL],
[$forbidden_un, 'OR', $allowed_cf, FALSE, NULL],
[$forbidden_un, 'OR', $allowed_un, FALSE, NULL],
// Forbidden (ct) OR neutral (ct,cf,un).
[$forbidden_ct, 'OR', $neutral_ct, TRUE, TRUE],
[$forbidden_ct, 'OR', $neutral_cf, TRUE, TRUE],
[$forbidden_ct, 'OR', $neutral_un, TRUE, TRUE],
// Forbidden (cf) OR neutral (ct,cf,un).
[$forbidden_cf, 'OR', $neutral_ct, TRUE, FALSE],
[$forbidden_cf, 'OR', $neutral_cf, TRUE, FALSE],
[$forbidden_cf, 'OR', $neutral_un, TRUE, FALSE],
// Forbidden (un) OR neutral (ct,cf,un).
[$forbidden_un, 'OR', $neutral_ct, FALSE, NULL],
[$forbidden_un, 'OR', $neutral_cf, FALSE, NULL],
[$forbidden_un, 'OR', $neutral_un, FALSE, NULL],
// Forbidden (ct) OR forbidden (ct,cf,un).
[$forbidden_ct, 'OR', $forbidden_ct, TRUE, TRUE],
[$forbidden_ct, 'OR', $forbidden_cf, TRUE, TRUE],
[$forbidden_ct, 'OR', $forbidden_un, TRUE, TRUE],
// Forbidden (cf) OR forbidden (ct,cf,un).
[$forbidden_cf, 'OR', $forbidden_ct, TRUE, TRUE],
[$forbidden_cf, 'OR', $forbidden_cf, TRUE, FALSE],
[$forbidden_cf, 'OR', $forbidden_un, TRUE, FALSE],
// Forbidden (un) OR forbidden (ct,cf,un).
[$forbidden_un, 'OR', $forbidden_ct, FALSE, NULL],
[$forbidden_un, 'OR', $forbidden_cf, FALSE, NULL],
[$forbidden_un, 'OR', $forbidden_un, FALSE, NULL],
// Neutral (ct) OR allowed (ct,cf,un).
[$neutral_ct, 'OR', $allowed_ct, TRUE, TRUE],
[$neutral_ct, 'OR', $allowed_cf, TRUE, FALSE],
[$neutral_ct, 'OR', $allowed_un, TRUE, FALSE],
// Neutral (cf) OR allowed (ct,cf,un).
[$neutral_cf, 'OR', $allowed_ct, TRUE, TRUE],
[$neutral_cf, 'OR', $allowed_cf, TRUE, FALSE],
[$neutral_cf, 'OR', $allowed_un, TRUE, FALSE],
// Neutral (un) OR allowed (ct,cf,un).
[$neutral_un, 'OR', $allowed_ct, FALSE, NULL],
[$neutral_un, 'OR', $allowed_cf, FALSE, NULL],
[$neutral_un, 'OR', $allowed_un, FALSE, NULL],
// Neutral (ct) OR neutral (ct,cf,un).
[$neutral_ct, 'OR', $neutral_ct, TRUE, TRUE],
[$neutral_ct, 'OR', $neutral_cf, TRUE, TRUE],
[$neutral_ct, 'OR', $neutral_un, TRUE, TRUE],
// Neutral (cf) OR neutral (ct,cf,un).
[$neutral_cf, 'OR', $neutral_ct, TRUE, TRUE],
[$neutral_cf, 'OR', $neutral_cf, TRUE, FALSE],
[$neutral_cf, 'OR', $neutral_un, TRUE, FALSE],
// Neutral (un) OR neutral (ct,cf,un).
[$neutral_un, 'OR', $neutral_ct, FALSE, NULL],
[$neutral_un, 'OR', $neutral_cf, FALSE, NULL],
[$neutral_un, 'OR', $neutral_un, FALSE, NULL],
// Neutral (ct) OR forbidden (ct,cf,un).
[$neutral_ct, 'OR', $forbidden_ct, TRUE, TRUE],
[$neutral_ct, 'OR', $forbidden_cf, TRUE, FALSE],
[$neutral_ct, 'OR', $forbidden_un, TRUE, FALSE],
// Neutral (cf) OR forbidden (ct,cf,un).
[$neutral_cf, 'OR', $forbidden_ct, TRUE, TRUE],
[$neutral_cf, 'OR', $forbidden_cf, TRUE, FALSE],
[$neutral_cf, 'OR', $forbidden_un, TRUE, FALSE],
// Neutral (un) OR forbidden (ct,cf,un).
[$neutral_un, 'OR', $forbidden_ct, FALSE, NULL],
[$neutral_un, 'OR', $forbidden_cf, FALSE, NULL],
[$neutral_un, 'OR', $forbidden_un, FALSE, NULL],
// Allowed (ct) AND allowed (ct,cf,un).
[$allowed_ct, 'AND', $allowed_ct, TRUE, TRUE],
[$allowed_ct, 'AND', $allowed_cf, TRUE, FALSE],
[$allowed_ct, 'AND', $allowed_un, TRUE, FALSE],
// Allowed (cf) AND allowed (ct,cf,un).
[$allowed_cf, 'AND', $allowed_ct, TRUE, FALSE],
[$allowed_cf, 'AND', $allowed_cf, TRUE, FALSE],
[$allowed_cf, 'AND', $allowed_un, TRUE, FALSE],
// Allowed (un) AND allowed (ct,cf,un).
[$allowed_un, 'AND', $allowed_ct, FALSE, NULL],
[$allowed_un, 'AND', $allowed_cf, FALSE, NULL],
[$allowed_un, 'AND', $allowed_un, FALSE, NULL],
// Allowed (ct) AND forbidden (ct,cf,un).
[$allowed_ct, 'AND', $forbidden_ct, TRUE, TRUE],
[$allowed_ct, 'AND', $forbidden_cf, TRUE, FALSE],
[$allowed_ct, 'AND', $forbidden_un, TRUE, FALSE],
// Allowed (cf) AND forbidden (ct,cf,un).
[$allowed_cf, 'AND', $forbidden_ct, TRUE, TRUE],
[$allowed_cf, 'AND', $forbidden_cf, TRUE, FALSE],
[$allowed_cf, 'AND', $forbidden_un, TRUE, FALSE],
// Allowed (un) AND forbidden (ct,cf,un).
[$allowed_un, 'AND', $forbidden_ct, FALSE, NULL],
[$allowed_un, 'AND', $forbidden_cf, FALSE, NULL],
[$allowed_un, 'AND', $forbidden_un, FALSE, NULL],
// Allowed (ct) AND neutral (ct,cf,un).
[$allowed_ct, 'AND', $neutral_ct, TRUE, TRUE],
[$allowed_ct, 'AND', $neutral_cf, TRUE, FALSE],
[$allowed_ct, 'AND', $neutral_un, TRUE, FALSE],
// Allowed (cf) AND neutral (ct,cf,un).
[$allowed_cf, 'AND', $neutral_ct, TRUE, FALSE],
[$allowed_cf, 'AND', $neutral_cf, TRUE, FALSE],
[$allowed_cf, 'AND', $neutral_un, TRUE, FALSE],
// Allowed (un) AND neutral (ct,cf,un).
[$allowed_un, 'AND', $neutral_ct, FALSE, NULL],
[$allowed_un, 'AND', $neutral_cf, FALSE, NULL],
[$allowed_un, 'AND', $neutral_un, FALSE, NULL],
// Forbidden (ct) AND allowed (ct,cf,un).
[$forbidden_ct, 'AND', $allowed_ct, TRUE, TRUE],
[$forbidden_ct, 'AND', $allowed_cf, TRUE, TRUE],
[$forbidden_ct, 'AND', $allowed_un, TRUE, TRUE],
// Forbidden (cf) AND allowed (ct,cf,un).
[$forbidden_cf, 'AND', $allowed_ct, TRUE, FALSE],
[$forbidden_cf, 'AND', $allowed_cf, TRUE, FALSE],
[$forbidden_cf, 'AND', $allowed_un, TRUE, FALSE],
// Forbidden (un) AND allowed (ct,cf,un).
[$forbidden_un, 'AND', $allowed_ct, FALSE, NULL],
[$forbidden_un, 'AND', $allowed_cf, FALSE, NULL],
[$forbidden_un, 'AND', $allowed_un, FALSE, NULL],
// Forbidden (ct) AND neutral (ct,cf,un).
[$forbidden_ct, 'AND', $neutral_ct, TRUE, TRUE],
[$forbidden_ct, 'AND', $neutral_cf, TRUE, TRUE],
[$forbidden_ct, 'AND', $neutral_un, TRUE, TRUE],
// Forbidden (cf) AND neutral (ct,cf,un).
[$forbidden_cf, 'AND', $neutral_ct, TRUE, FALSE],
[$forbidden_cf, 'AND', $neutral_cf, TRUE, FALSE],
[$forbidden_cf, 'AND', $neutral_un, TRUE, FALSE],
// Forbidden (un) AND neutral (ct,cf,un).
[$forbidden_un, 'AND', $neutral_ct, FALSE, NULL],
[$forbidden_un, 'AND', $neutral_cf, FALSE, NULL],
[$forbidden_un, 'AND', $neutral_un, FALSE, NULL],
// Forbidden (ct) AND forbidden (ct,cf,un).
[$forbidden_ct, 'AND', $forbidden_ct, TRUE, TRUE],
[$forbidden_ct, 'AND', $forbidden_cf, TRUE, TRUE],
[$forbidden_ct, 'AND', $forbidden_un, TRUE, TRUE],
// Forbidden (cf) AND forbidden (ct,cf,un).
[$forbidden_cf, 'AND', $forbidden_ct, TRUE, FALSE],
[$forbidden_cf, 'AND', $forbidden_cf, TRUE, FALSE],
[$forbidden_cf, 'AND', $forbidden_un, TRUE, FALSE],
// Forbidden (un) AND forbidden (ct,cf,un).
[$forbidden_un, 'AND', $forbidden_ct, FALSE, NULL],
[$forbidden_un, 'AND', $forbidden_cf, FALSE, NULL],
[$forbidden_un, 'AND', $forbidden_un, FALSE, NULL],
// Neutral (ct) AND allowed (ct,cf,un).
[$neutral_ct, 'AND', $allowed_ct, TRUE, TRUE],
[$neutral_ct, 'AND', $allowed_cf, TRUE, TRUE],
[$neutral_ct, 'AND', $allowed_un, TRUE, TRUE],
// Neutral (cf) AND allowed (ct,cf,un).
[$neutral_cf, 'AND', $allowed_ct, TRUE, FALSE],
[$neutral_cf, 'AND', $allowed_cf, TRUE, FALSE],
[$neutral_cf, 'AND', $allowed_un, TRUE, FALSE],
// Neutral (un) AND allowed (ct,cf,un).
[$neutral_un, 'AND', $allowed_ct, FALSE, NULL],
[$neutral_un, 'AND', $allowed_cf, FALSE, NULL],
[$neutral_un, 'AND', $allowed_un, FALSE, NULL],
// Neutral (ct) AND neutral (ct,cf,un).
[$neutral_ct, 'AND', $neutral_ct, TRUE, TRUE],
[$neutral_ct, 'AND', $neutral_cf, TRUE, TRUE],
[$neutral_ct, 'AND', $neutral_un, TRUE, TRUE],
// Neutral (cf) AND neutral (ct,cf,un).
[$neutral_cf, 'AND', $neutral_ct, TRUE, FALSE],
[$neutral_cf, 'AND', $neutral_cf, TRUE, FALSE],
[$neutral_cf, 'AND', $neutral_un, TRUE, FALSE],
// Neutral (un) AND neutral (ct,cf,un).
[$neutral_un, 'AND', $neutral_ct, FALSE, NULL],
[$neutral_un, 'AND', $neutral_cf, FALSE, NULL],
[$neutral_un, 'AND', $neutral_un, FALSE, NULL],
// Neutral (ct) AND forbidden (ct,cf,un).
[$neutral_ct, 'AND', $forbidden_ct, TRUE, TRUE],
[$neutral_ct, 'AND', $forbidden_cf, TRUE, FALSE],
[$neutral_ct, 'AND', $forbidden_un, TRUE, FALSE],
// Neutral (cf) AND forbidden (ct,cf,un).
[$neutral_cf, 'AND', $forbidden_ct, TRUE, TRUE],
[$neutral_cf, 'AND', $forbidden_cf, TRUE, FALSE],
[$neutral_cf, 'AND', $forbidden_un, TRUE, FALSE],
// Neutral (un) AND forbidden (ct,cf,un).
[$neutral_un, 'AND', $forbidden_ct, FALSE, NULL],
[$neutral_un, 'AND', $forbidden_cf, FALSE, NULL],
[$neutral_un, 'AND', $forbidden_un, FALSE, NULL],
];
}
/**
* @covers ::andIf
* @covers ::orIf
* @covers ::inheritCacheability
*
* @dataProvider andOrCacheabilityPropagationProvider
*/
public function testAndOrCacheabilityPropagation(AccessResultInterface $first, $op, AccessResultInterface $second, $implements_cacheable_dependency_interface, $is_cacheable) {
if ($op === 'OR') {
$result = $first->orIf($second);
}
else if ($op === 'AND') {
$result = $first->andIf($second);
}
else {
throw new \LogicException('Invalid operator specified');
}
if ($implements_cacheable_dependency_interface) {
$this->assertTrue($result instanceof CacheableDependencyInterface, 'Result is an instance of CacheableDependencyInterface.');
if ($result instanceof CacheableDependencyInterface) {
$this->assertSame($is_cacheable, $result->getCacheMaxAge() !== 0, 'getCacheMaxAge() matches expectations.');
}
}
else {
$this->assertFalse($result instanceof CacheableDependencyInterface, 'Result is not an instance of CacheableDependencyInterface.');
}
}
/**
* @covers ::orIf
*
* Tests the special case of ORing non-forbidden access results that are both
* cacheable but have different cacheability metadata.
* This is only the case for non-forbidden access results; we still abort the
* ORing process as soon as a forbidden access result is encountered. This is
* tested in ::testOrIf().
*/
public function testOrIfCacheabilityMerging() {
$merge_both_directions = function(AccessResult $a, AccessResult $b) {
// A globally cacheable access result.
$a->setCacheMaxAge(3600);
// Another access result that is cacheable per permissions.
$b->setCacheMaxAge(86400)->cachePerPermissions();
$r1 = $a->orIf($b);
$this->assertTrue($r1->getCacheMaxAge() === 3600);
$this->assertSame(['user.permissions'], $r1->getCacheContexts());
$r2 = $b->orIf($a);
$this->assertTrue($r2->getCacheMaxAge() === 3600);
$this->assertSame(['user.permissions'], $r2->getCacheContexts());
};
// Merge either direction, get the same result.
$merge_both_directions(AccessResult::allowed(), AccessResult::allowed());
$merge_both_directions(AccessResult::allowed(), AccessResult::neutral());
$merge_both_directions(AccessResult::neutral(), AccessResult::neutral());
$merge_both_directions(AccessResult::neutral(), AccessResult::allowed());
}
/**
* Tests allowedIfHasPermissions().
*
* @covers ::allowedIfHasPermissions
*
* @dataProvider providerTestAllowedIfHasPermissions
*/
public function testAllowedIfHasPermissions($permissions, $conjunction, $expected_access) {
$account = $this->getMock('\Drupal\Core\Session\AccountInterface');
$account->expects($this->any())
->method('hasPermission')
->willReturnMap([
['allowed', TRUE],
['denied', FALSE],
]);
$access_result = AccessResult::allowedIfHasPermissions($account, $permissions, $conjunction);
$this->assertEquals($expected_access, $access_result);
}
/**
* Provides data for the testAllowedIfHasPermissions() method.
*
* @return array
*/
public function providerTestAllowedIfHasPermissions() {
return [
[[], 'AND', AccessResult::allowedIf(FALSE)],
[[], 'OR', AccessResult::allowedIf(FALSE)],
[['allowed'], 'OR', AccessResult::allowedIf(TRUE)->addCacheContexts(['user.permissions'])],
[['allowed'], 'AND', AccessResult::allowedIf(TRUE)->addCacheContexts(['user.permissions'])],
[['denied'], 'OR', AccessResult::allowedIf(FALSE)->addCacheContexts(['user.permissions'])],
[['denied'], 'AND', AccessResult::allowedIf(FALSE)->addCacheContexts(['user.permissions'])],
[['allowed', 'denied'], 'OR', AccessResult::allowedIf(TRUE)->addCacheContexts(['user.permissions'])],
[['denied', 'allowed'], 'OR', AccessResult::allowedIf(TRUE)->addCacheContexts(['user.permissions'])],
[['allowed', 'denied', 'other'], 'OR', AccessResult::allowedIf(TRUE)->addCacheContexts(['user.permissions'])],
[['allowed', 'denied'], 'AND', AccessResult::allowedIf(FALSE)->addCacheContexts(['user.permissions'])],
];
}
}
class UncacheableTestAccessResult implements AccessResultInterface {
/**
* The access result value. 'ALLOWED', 'FORBIDDEN' or 'NEUTRAL'.
*
* @var string
*/
protected $value;
/**
* Constructs a new UncacheableTestAccessResult object.
*/
public function __construct($value) {
$this->value = $value;
}
/**
* {@inheritdoc}
*/
public function isAllowed() {
return $this->value === 'ALLOWED';
}
/**
* {@inheritdoc}
*/
public function isForbidden() {
return $this->value === 'FORBIDDEN';
}
/**
* {@inheritdoc}
*/
public function isNeutral() {
return $this->value === 'NEUTRAL';
}
/**
* {@inheritdoc}
*/
public function orIf(AccessResultInterface $other) {
if ($this->isForbidden() || $other->isForbidden()) {
return new static('FORBIDDEN');
}
elseif ($this->isAllowed() || $other->isAllowed()) {
return new static('ALLOWED');
}
else {
return new static('NEUTRAL');
}
}
/**
* {@inheritdoc}
*/
public function andIf(AccessResultInterface $other) {
if ($this->isForbidden() || $other->isForbidden()) {
return new static('FORBIDDEN');
}
elseif ($this->isAllowed() && $other->isAllowed()) {
return new static('ALLOWED');
}
else {
return new static('NEUTRAL');
}
}
}

View file

@ -0,0 +1,91 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Access\CsrfAccessCheckTest.
*/
namespace Drupal\Tests\Core\Access;
use Drupal\Core\Access\AccessResult;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
use Drupal\Core\Access\CsrfAccessCheck;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Access\CsrfAccessCheck
* @group Access
*/
class CsrfAccessCheckTest extends UnitTestCase {
/**
* The mock CSRF token generator.
*
* @var \Drupal\Core\Access\CsrfTokenGenerator|\PHPUnit_Framework_MockObject_MockObject
*/
protected $csrfToken;
/**
* The access checker.
*
* @var \Drupal\Core\Access\CsrfAccessCheck
*/
protected $accessCheck;
/**
* The mock route match.
*
* @var \Drupal\Core\RouteMatch\RouteMatchInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $routeMatch;
protected function setUp() {
$this->csrfToken = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator')
->disableOriginalConstructor()
->getMock();
$this->routeMatch = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$this->accessCheck = new CsrfAccessCheck($this->csrfToken);
}
/**
* Tests the access() method with a valid token.
*/
public function testAccessTokenPass() {
$this->csrfToken->expects($this->once())
->method('validate')
->with('test_query', 'test-path/42')
->will($this->returnValue(TRUE));
$this->routeMatch->expects($this->once())
->method('getRawParameters')
->will($this->returnValue(array('node' => 42)));
$route = new Route('/test-path/{node}', array(), array('_csrf_token' => 'TRUE'));
$request = Request::create('/test-path/42?token=test_query');
$this->assertEquals(AccessResult::allowed()->setCacheMaxAge(0), $this->accessCheck->access($route, $request, $this->routeMatch));
}
/**
* Tests the access() method with an invalid token.
*/
public function testAccessTokenFail() {
$this->csrfToken->expects($this->once())
->method('validate')
->with('test_query', 'test-path')
->will($this->returnValue(FALSE));
$this->routeMatch->expects($this->once())
->method('getRawParameters')
->will($this->returnValue(array()));
$route = new Route('/test-path', array(), array('_csrf_token' => 'TRUE'));
$request = Request::create('/test-path?token=test_query');
$this->assertEquals(AccessResult::forbidden()->setCacheMaxAge(0), $this->accessCheck->access($route, $request, $this->routeMatch));
}
}

View file

@ -0,0 +1,216 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Access\CsrfTokenGeneratorTest.
*/
namespace Drupal\Tests\Core\Access;
use Drupal\Core\Site\Settings;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Component\Utility\Crypt;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests the CsrfTokenGenerator class.
*
* @group Access
* @coversDefaultClass \Drupal\Core\Access\CsrfTokenGenerator
*/
class CsrfTokenGeneratorTest extends UnitTestCase {
/**
* The CSRF token generator.
*
* @var \Drupal\Core\Access\CsrfTokenGenerator
*/
protected $generator;
/**
* The mock private key instance.
*
* @var \Drupal\Core\PrivateKey|\PHPUnit_Framework_MockObject_MockObject
*/
protected $privateKey;
/**
* The mock session metadata bag.
*
* @var \Drupal\Core\Session\MetadataBag|\PHPUnit_Framework_MockObject_MockObject
*/
protected $sessionMetadata;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->privateKey = $this->getMockBuilder('Drupal\Core\PrivateKey')
->disableOriginalConstructor()
->setMethods(array('get'))
->getMock();
$this->sessionMetadata = $this->getMockBuilder('Drupal\Core\Session\MetadataBag')
->disableOriginalConstructor()
->getMock();
$settings = array(
'hash_salt' => $this->randomMachineName(),
);
new Settings($settings);
$this->generator = new CsrfTokenGenerator($this->privateKey, $this->sessionMetadata);
}
/**
* Set up default expectations on the mocks.
*/
protected function setupDefaultExpectations() {
$key = Crypt::randomBytesBase64();
$this->privateKey->expects($this->any())
->method('get')
->will($this->returnValue($key));
$seed = Crypt::randomBytesBase64();
$this->sessionMetadata->expects($this->any())
->method('getCsrfTokenSeed')
->will($this->returnValue($seed));
}
/**
* Tests CsrfTokenGenerator::get().
*
* @covers ::get
*/
public function testGet() {
$this->setupDefaultExpectations();
$this->assertInternalType('string', $this->generator->get());
$this->assertNotSame($this->generator->get(), $this->generator->get($this->randomMachineName()));
$this->assertNotSame($this->generator->get($this->randomMachineName()), $this->generator->get($this->randomMachineName()));
}
/**
* Tests that a new token seed is generated upon first use.
*
* @covers ::get
*/
public function testGenerateSeedOnGet() {
$key = Crypt::randomBytesBase64();
$this->privateKey->expects($this->any())
->method('get')
->will($this->returnValue($key));
$this->sessionMetadata->expects($this->once())
->method('getCsrfTokenSeed')
->will($this->returnValue(NULL));
$this->sessionMetadata->expects($this->once())
->method('setCsrfTokenSeed')
->with($this->isType('string'));
$this->assertInternalType('string', $this->generator->get());
}
/**
* Tests CsrfTokenGenerator::validate().
*
* @covers ::validate
*/
public function testValidate() {
$this->setupDefaultExpectations();
$token = $this->generator->get();
$this->assertTrue($this->generator->validate($token));
$this->assertFalse($this->generator->validate($token, 'foo'));
$token = $this->generator->get('bar');
$this->assertTrue($this->generator->validate($token, 'bar'));
}
/**
* Tests CsrfTokenGenerator::validate() with different parameter types.
*
* @param mixed $token
* The token to be validated.
* @param mixed $value
* (optional) An additional value to base the token on.
*
* @covers ::validate
* @dataProvider providerTestValidateParameterTypes
*/
public function testValidateParameterTypes($token, $value) {
$this->setupDefaultExpectations();
// The following check might throw PHP fatals and notices, so we disable
// error assertions.
set_error_handler(function () {return TRUE;});
$this->assertFalse($this->generator->validate($token, $value));
restore_error_handler();
}
/**
* Provides data for testValidateParameterTypes.
*
* @return array
* An array of data used by the test.
*/
public function providerTestValidateParameterTypes() {
return array(
array(array(), ''),
array(TRUE, 'foo'),
array(0, 'foo'),
);
}
/**
* Tests CsrfTokenGenerator::validate() with invalid parameter types.
*
* @param mixed $token
* The token to be validated.
* @param mixed $value
* (optional) An additional value to base the token on.
*
* @covers ::validate
* @dataProvider providerTestInvalidParameterTypes
* @expectedException InvalidArgumentException
*/
public function testInvalidParameterTypes($token, $value = '') {
$this->setupDefaultExpectations();
$this->generator->validate($token, $value);
}
/**
* Provides data for testInvalidParameterTypes.
*
* @return array
* An array of data used by the test.
*/
public function providerTestInvalidParameterTypes() {
return array(
array(NULL, new \stdClass()),
array(0, array()),
array('', array()),
array(array(), array()),
);
}
/**
* Tests the exception thrown when no 'hash_salt' is provided in settings.
*
* @covers ::get
* @expectedException \RuntimeException
*/
public function testGetWithNoHashSalt() {
// Update settings with no hash salt.
new Settings(array());
$generator = new CsrfTokenGenerator($this->privateKey, $this->sessionMetadata);
$generator->get();
}
}

View file

@ -0,0 +1,125 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Access\CustomAccessCheckTest.
*/
namespace Drupal\Tests\Core\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\CustomAccessCheck;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\Routing\Route;
/**
* @coversDefaultClass \Drupal\Core\Access\CustomAccessCheck
* @group Access
*/
class CustomAccessCheckTest extends UnitTestCase {
/**
* The access checker to test.
*
* @var \Drupal\Core\Access\CustomAccessCheck
*/
protected $accessChecker;
/**
* The mocked controller resolver.
*
* @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $controllerResolver;
/**
* The mocked arguments resolver.
*
* @var \Drupal\Core\Access\AccessArgumentsResolverFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $argumentsResolverFactory;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface');
$this->argumentsResolverFactory = $this->getMock('Drupal\Core\Access\AccessArgumentsResolverFactoryInterface');
$this->accessChecker = new CustomAccessCheck($this->controllerResolver, $this->argumentsResolverFactory);
}
/**
* Test the access method.
*/
public function testAccess() {
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$this->controllerResolver->expects($this->at(0))
->method('getControllerFromDefinition')
->with('\Drupal\Tests\Core\Access\TestController::accessDeny')
->will($this->returnValue(array(new TestController(), 'accessDeny')));
$resolver0 = $this->getMock('Drupal\Component\Utility\ArgumentsResolverInterface');
$resolver0->expects($this->once())
->method('getArguments')
->will($this->returnValue(array()));
$this->argumentsResolverFactory->expects($this->at(0))
->method('getArgumentsResolver')
->will($this->returnValue($resolver0));
$this->controllerResolver->expects($this->at(1))
->method('getControllerFromDefinition')
->with('\Drupal\Tests\Core\Access\TestController::accessAllow')
->will($this->returnValue(array(new TestController(), 'accessAllow')));
$resolver1 = $this->getMock('Drupal\Component\Utility\ArgumentsResolverInterface');
$resolver1->expects($this->once())
->method('getArguments')
->will($this->returnValue(array()));
$this->argumentsResolverFactory->expects($this->at(1))
->method('getArgumentsResolver')
->will($this->returnValue($resolver1));
$this->controllerResolver->expects($this->at(2))
->method('getControllerFromDefinition')
->with('\Drupal\Tests\Core\Access\TestController::accessParameter')
->will($this->returnValue(array(new TestController(), 'accessParameter')));
$resolver2 = $this->getMock('Drupal\Component\Utility\ArgumentsResolverInterface');
$resolver2->expects($this->once())
->method('getArguments')
->will($this->returnValue(array('parameter' => 'TRUE')));
$this->argumentsResolverFactory->expects($this->at(2))
->method('getArgumentsResolver')
->will($this->returnValue($resolver2));
$route = new Route('/test-route', array(), array('_custom_access' => '\Drupal\Tests\Core\Access\TestController::accessDeny'));
$account = $this->getMock('Drupal\Core\Session\AccountInterface');
$this->assertEquals(AccessResult::neutral(), $this->accessChecker->access($route, $route_match, $account));
$route = new Route('/test-route', array(), array('_custom_access' => '\Drupal\Tests\Core\Access\TestController::accessAllow'));
$this->assertEquals(AccessResult::allowed(), $this->accessChecker->access($route, $route_match, $account));
$route = new Route('/test-route', array('parameter' => 'TRUE'), array('_custom_access' => '\Drupal\Tests\Core\Access\TestController::accessParameter'));
$this->assertEquals(AccessResult::allowed(), $this->accessChecker->access($route, $route_match, $account));
}
}
class TestController {
public function accessAllow() {
return AccessResult::allowed();
}
public function accessDeny() {
return AccessResult::neutral();
}
public function accessParameter($parameter) {
return AccessResult::allowedIf($parameter == 'TRUE');
}
}

View file

@ -0,0 +1,62 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Access\DefaultAccessCheckTest.
*/
namespace Drupal\Tests\Core\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\DefaultAccessCheck;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* @coversDefaultClass \Drupal\Core\Access\DefaultAccessCheck
* @group Access
*/
class DefaultAccessCheckTest extends UnitTestCase {
/**
* The access checker to test.
*
* @var \Drupal\Core\Access\DefaultAccessCheck
*/
protected $accessChecker;
/**
* The mocked account.
*
* @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $account;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
$this->accessChecker = new DefaultAccessCheck();
}
/**
* Test the access method.
*/
public function testAccess() {
$request = new Request(array());
$route = new Route('/test-route', array(), array('_access' => 'NULL'));
$this->assertEquals(AccessResult::neutral(), $this->accessChecker->access($route, $request, $this->account));
$route = new Route('/test-route', array(), array('_access' => 'FALSE'));
$this->assertEquals(AccessResult::forbidden(), $this->accessChecker->access($route, $request, $this->account));
$route = new Route('/test-route', array(), array('_access' => 'TRUE'));
$this->assertEquals(AccessResult::allowed(), $this->accessChecker->access($route, $request, $this->account));
}
}

View file

@ -0,0 +1,120 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Access\RouteProcessorCsrfTest.
*/
namespace Drupal\Tests\Core\Access;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Access\RouteProcessorCsrf;
use Symfony\Component\Routing\Route;
/**
* @coversDefaultClass \Drupal\Core\Access\RouteProcessorCsrf
* @group Access
*/
class RouteProcessorCsrfTest extends UnitTestCase {
/**
* The mock CSRF token generator.
*
* @var \Drupal\Core\Access\CsrfTokenGenerator|\PHPUnit_Framework_MockObject_MockObject
*/
protected $csrfToken;
/**
* The route processor.
*
* @var \Drupal\Core\Access\RouteProcessorCsrf
*/
protected $processor;
protected function setUp() {
$this->csrfToken = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator')
->disableOriginalConstructor()
->getMock();
$this->processor = new RouteProcessorCsrf($this->csrfToken);
}
/**
* Tests the processOutbound() method with no _csrf_token route requirement.
*/
public function testProcessOutboundNoRequirement() {
$this->csrfToken->expects($this->never())
->method('get');
$route = new Route('/test-path');
$parameters = array();
$cacheable_metadata = new CacheableMetadata();
$this->processor->processOutbound('test', $route, $parameters, $cacheable_metadata);
// No parameters should be added to the parameters array.
$this->assertEmpty($parameters);
// Cacheability of routes without a _csrf_token route requirement is
// unaffected.
$this->assertEquals((new CacheableMetadata()), $cacheable_metadata);
}
/**
* Tests the processOutbound() method with a _csrf_token route requirement.
*/
public function testProcessOutbound() {
$this->csrfToken->expects($this->once())
->method('get')
// The leading '/' will be stripped from the path.
->with('test-path')
->will($this->returnValue('test_token'));
$route = new Route('/test-path', array(), array('_csrf_token' => 'TRUE'));
$parameters = array();
$cacheable_metadata = new CacheableMetadata();
$this->processor->processOutbound('test', $route, $parameters, $cacheable_metadata);
// 'token' should be added to the parameters array.
$this->assertArrayHasKey('token', $parameters);
$this->assertSame($parameters['token'], 'test_token');
// Cacheability of routes with a _csrf_token route requirement is max-age=0.
$this->assertEquals((new CacheableMetadata())->setCacheMaxAge(0), $cacheable_metadata);
}
/**
* Tests the processOutbound() method with a dynamic path and one replacement.
*/
public function testProcessOutboundDynamicOne() {
$this->csrfToken->expects($this->once())
->method('get')
->with('test-path/100')
->will($this->returnValue('test_token'));
$route = new Route('/test-path/{slug}', array(), array('_csrf_token' => 'TRUE'));
$parameters = array('slug' => 100);
$cacheable_metadata = new CacheableMetadata();
$this->processor->processOutbound('test', $route, $parameters, $cacheable_metadata);
// Cacheability of routes with a _csrf_token route requirement is max-age=0.
$this->assertEquals((new CacheableMetadata())->setCacheMaxAge(0), $cacheable_metadata);
}
/**
* Tests the processOutbound() method with two parameter replacements.
*/
public function testProcessOutboundDynamicTwo() {
$this->csrfToken->expects($this->once())
->method('get')
->with('100/test-path/test')
->will($this->returnValue('test_token'));
$route = new Route('{slug_1}/test-path/{slug_2}', array(), array('_csrf_token' => 'TRUE'));
$parameters = array('slug_1' => 100, 'slug_2' => 'test');
$cacheable_metadata = new CacheableMetadata();
$this->processor->processOutbound('test', $route, $parameters, $cacheable_metadata);
// Cacheability of routes with a _csrf_token route requirement is max-age=0.
$this->assertEquals((new CacheableMetadata())->setCacheMaxAge(0), $cacheable_metadata);
}
}

View file

@ -0,0 +1,437 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Ajax\AjaxCommandsTest.
*/
namespace Drupal\Tests\Core\Ajax;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Ajax\AddCssCommand;
use Drupal\Core\Ajax\AfterCommand;
use Drupal\Core\Ajax\AlertCommand;
use Drupal\Core\Ajax\AppendCommand;
use Drupal\Core\Ajax\BeforeCommand;
use Drupal\Core\Ajax\ChangedCommand;
use Drupal\Core\Ajax\CssCommand;
use Drupal\Core\Ajax\DataCommand;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\InsertCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Ajax\PrependCommand;
use Drupal\Core\Ajax\RemoveCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Ajax\RestripeCommand;
use Drupal\Core\Ajax\SettingsCommand;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\Core\Ajax\CloseModalDialogCommand;
use Drupal\Core\Ajax\SetDialogOptionCommand;
use Drupal\Core\Ajax\SetDialogTitleCommand;
use Drupal\Core\Ajax\RedirectCommand;
/**
* Test coverage for various classes in the \Drupal\Core\Ajax namespace.
*
* @group Ajax
*/
class AjaxCommandsTest extends UnitTestCase {
/**
* @covers \Drupal\Core\Ajax\AddCssCommand
*/
public function testAddCssCommand() {
$command = new AddCssCommand('p{ text-decoration:blink; }');
$expected = array(
'command' => 'add_css',
'data' => 'p{ text-decoration:blink; }',
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\AfterCommand
*/
public function testAfterCommand() {
$command = new AfterCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
$expected = array(
'command' => 'insert',
'method' => 'after',
'selector' => '#page-title',
'data' => '<p>New Text!</p>',
'settings' => array('my-setting' => 'setting'),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\AlertCommand
*/
public function testAlertCommand() {
$command = new AlertCommand('Set condition 1 throughout the ship!');
$expected = array(
'command' => 'alert',
'text' => 'Set condition 1 throughout the ship!',
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\AppendCommand
*/
public function testAppendCommand() {
$command = new AppendCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
$expected = array(
'command' => 'insert',
'method' => 'append',
'selector' => '#page-title',
'data' => '<p>New Text!</p>',
'settings' => array('my-setting' => 'setting'),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\BeforeCommand
*/
public function testBeforeCommand() {
$command = new BeforeCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
$expected = array(
'command' => 'insert',
'method' => 'before',
'selector' => '#page-title',
'data' => '<p>New Text!</p>',
'settings' => array('my-setting' => 'setting'),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\ChangedCommand
*/
public function testChangedCommand() {
$command = new ChangedCommand('#page-title', '#page-title-changed');
$expected = array(
'command' => 'changed',
'selector' => '#page-title',
'asterisk' => '#page-title-changed',
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\CssCommand
*/
public function testCssCommand() {
$command = new CssCommand('#page-title', array('text-decoration' => 'blink'));
$command->setProperty('font-size', '40px')->setProperty('font-weight', 'bold');
$expected = array(
'command' => 'css',
'selector' => '#page-title',
'argument' => array(
'text-decoration' => 'blink',
'font-size' => '40px',
'font-weight' => 'bold',
),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\DataCommand
*/
public function testDataCommand() {
$command = new DataCommand('#page-title', 'my-data', array('key' => 'value'));
$expected = array(
'command' => 'data',
'selector' => '#page-title',
'name' => 'my-data',
'value' => array('key' => 'value'),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\HtmlCommand
*/
public function testHtmlCommand() {
$command = new HtmlCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
$expected = array(
'command' => 'insert',
'method' => 'html',
'selector' => '#page-title',
'data' => '<p>New Text!</p>',
'settings' => array('my-setting' => 'setting'),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\InsertCommand
*/
public function testInsertCommand() {
$command = new InsertCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
$expected = array(
'command' => 'insert',
'method' => NULL,
'selector' => '#page-title',
'data' => '<p>New Text!</p>',
'settings' => array('my-setting' => 'setting'),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\InvokeCommand
*/
public function testInvokeCommand() {
$command = new InvokeCommand('#page-title', 'myMethod', array('var1', 'var2'));
$expected = array(
'command' => 'invoke',
'selector' => '#page-title',
'method' => 'myMethod',
'args' => array('var1', 'var2'),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\PrependCommand
*/
public function testPrependCommand() {
$command = new PrependCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
$expected = array(
'command' => 'insert',
'method' => 'prepend',
'selector' => '#page-title',
'data' => '<p>New Text!</p>',
'settings' => array('my-setting' => 'setting'),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\RemoveCommand
*/
public function testRemoveCommand() {
$command = new RemoveCommand('#page-title');
$expected = array(
'command' => 'remove',
'selector' => '#page-title',
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\ReplaceCommand
*/
public function testReplaceCommand() {
$command = new ReplaceCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
$expected = array(
'command' => 'insert',
'method' => 'replaceWith',
'selector' => '#page-title',
'data' => '<p>New Text!</p>',
'settings' => array('my-setting' => 'setting'),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\RestripeCommand
*/
public function testRestripeCommand() {
$command = new RestripeCommand('#page-title');
$expected = array(
'command' => 'restripe',
'selector' => '#page-title',
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\SettingsCommand
*/
public function testSettingsCommand() {
$command = new SettingsCommand(array('key' => 'value'), TRUE);
$expected = array(
'command' => 'settings',
'settings' => array('key' => 'value'),
'merge' => TRUE,
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\OpenDialogCommand
*/
public function testOpenDialogCommand() {
$command = $this->getMockBuilder('Drupal\Core\Ajax\OpenDialogCommand')
->setConstructorArgs(array(
'#some-dialog', 'Title', '<p>Text!</p>', array(
'url' => FALSE,
'width' => 500,
),
))
->setMethods(array('getRenderedContent'))
->getMock();
// This method calls the render service, which isn't available. We want it
// to do nothing so we mock it to return a known value.
$command->expects($this->once())
->method('getRenderedContent')
->willReturn('rendered content');
$expected = array(
'command' => 'openDialog',
'selector' => '#some-dialog',
'settings' => NULL,
'data' => 'rendered content',
'dialogOptions' => array(
'url' => FALSE,
'width' => 500,
'title' => 'Title',
'modal' => FALSE,
),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\OpenModalDialogCommand
*/
public function testOpenModalDialogCommand() {
$command = $this->getMockBuilder('Drupal\Core\Ajax\OpenModalDialogCommand')
->setConstructorArgs(array(
'Title', '<p>Text!</p>', array(
'url' => 'example',
'width' => 500,
),
))
->setMethods(array('getRenderedContent'))
->getMock();
// This method calls the render service, which isn't available. We want it
// to do nothing so we mock it to return a known value.
$command->expects($this->once())
->method('getRenderedContent')
->willReturn('rendered content');
$expected = array(
'command' => 'openDialog',
'selector' => '#drupal-modal',
'settings' => NULL,
'data' => 'rendered content',
'dialogOptions' => array(
'url' => 'example',
'width' => 500,
'title' => 'Title',
'modal' => TRUE,
),
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\CloseModalDialogCommand
*/
public function testCloseModalDialogCommand() {
$command = new CloseModalDialogCommand();
$expected = array(
'command' => 'closeDialog',
'selector' => '#drupal-modal',
'persist' => FALSE,
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\CloseDialogCommand
*/
public function testCloseDialogCommand() {
$command = new CloseDialogCommand('#some-dialog', TRUE);
$expected = array(
'command' => 'closeDialog',
'selector' => '#some-dialog',
'persist' => TRUE,
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\SetDialogOptionCommand
*/
public function testSetDialogOptionCommand() {
$command = new SetDialogOptionCommand('#some-dialog', 'width', '500');
$expected = array(
'command' => 'setDialogOption',
'selector' => '#some-dialog',
'optionName' => 'width',
'optionValue' => '500',
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\SetDialogTitleCommand
*/
public function testSetDialogTitleCommand() {
$command = new SetDialogTitleCommand('#some-dialog', 'Example');
$expected = array(
'command' => 'setDialogOption',
'selector' => '#some-dialog',
'optionName' => 'title',
'optionValue' => 'Example',
);
$this->assertEquals($expected, $command->render());
}
/**
* @covers \Drupal\Core\Ajax\RedirectCommand
*/
public function testRedirectCommand() {
$command = new RedirectCommand('http://example.com');
$expected = array(
'command' => 'redirect',
'url' => 'http://example.com',
);
$this->assertEquals($expected, $command->render());
}
}

View file

@ -0,0 +1,101 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Ajax\AjaxResponseTest.
*/
namespace Drupal\Tests\Core\Ajax;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
use Drupal\Core\Render\Element\Ajax;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* @coversDefaultClass \Drupal\Core\Ajax\AjaxResponse
* @group Ajax
*/
class AjaxResponseTest extends UnitTestCase {
/**
* The tested ajax response object.
*
* @var \Drupal\Core\Ajax\AjaxResponse
*/
protected $ajaxResponse;
protected function setUp() {
$this->ajaxResponse = new AjaxResponse();
}
/**
* Tests the add and getCommands method.
*
* @see \Drupal\Core\Ajax\AjaxResponse::addCommand()
* @see \Drupal\Core\Ajax\AjaxResponse::getCommands()
*/
public function testCommands() {
$command_one = $this->getMock('Drupal\Core\Ajax\CommandInterface');
$command_one->expects($this->once())
->method('render')
->will($this->returnValue(array('command' => 'one')));
$command_two = $this->getMock('Drupal\Core\Ajax\CommandInterface');
$command_two->expects($this->once())
->method('render')
->will($this->returnValue(array('command' => 'two')));
$command_three = $this->getMock('Drupal\Core\Ajax\CommandInterface');
$command_three->expects($this->once())
->method('render')
->will($this->returnValue(array('command' => 'three')));
$this->ajaxResponse->addCommand($command_one);
$this->ajaxResponse->addCommand($command_two);
$this->ajaxResponse->addCommand($command_three, TRUE);
// Ensure that the added commands are in the right order.
$commands =& $this->ajaxResponse->getCommands();
$this->assertSame($commands[1], array('command' => 'one'));
$this->assertSame($commands[2], array('command' => 'two'));
$this->assertSame($commands[0], array('command' => 'three'));
// Remove one and change one element from commands and ensure the reference
// worked as expected.
unset($commands[2]);
$commands[0]['class'] = 'test-class';
$commands = $this->ajaxResponse->getCommands();
$this->assertSame($commands[1], array('command' => 'one'));
$this->assertFalse(isset($commands[2]));
$this->assertSame($commands[0], array('command' => 'three', 'class' => 'test-class'));
}
/**
* Tests the support for IE specific headers in file uploads.
*
* @cover ::prepareResponse
*/
public function testPrepareResponseForIeFormRequestsWithFileUpload() {
$request = Request::create('/example', 'POST');
$request->headers->set('Accept', 'text/html');
$response = new AjaxResponse([]);
$response->headers->set('Content-Type', 'application/json; charset=utf-8');
$ajax_response_attachments_processor = $this->getMock('\Drupal\Core\Render\AttachmentsResponseProcessorInterface');
$subscriber = new AjaxResponseSubscriber($ajax_response_attachments_processor);
$event = new FilterResponseEvent(
$this->getMock('\Symfony\Component\HttpKernel\HttpKernelInterface'),
$request,
HttpKernelInterface::MASTER_REQUEST,
$response
);
$subscriber->onResponse($event);
$this->assertEquals('text/html; charset=utf-8', $response->headers->get('Content-Type'));
$this->assertEquals($response->getContent(), '<textarea>[]</textarea>');
}
}

View file

@ -0,0 +1,86 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Annotation\TranslationTest.
*/
namespace Drupal\Tests\Core\Annotation;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Annotation\Translation
* @group Annotation
*/
class TranslationTest extends UnitTestCase {
/**
* The translation manager used for testing.
*
* @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $translationManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->translationManager = $this->getStringTranslationStub();
}
/**
* @covers ::get
*
* @dataProvider providerTestGet
*/
public function testGet(array $values, $expected) {
$container = new ContainerBuilder();
$container->set('string_translation', $this->translationManager);
\Drupal::setContainer($container);
$arguments = isset($values['arguments']) ? $values['arguments'] : array();
$options = isset($values['context']) ? array(
'context' => $values['context'],
) : array();
$this->translationManager->expects($this->once())
->method('translate')
->with($values['value'], $arguments, $options);
$annotation = new Translation($values);
$this->assertSame($expected, (string) $annotation->get());
}
/**
* Provides data to self::testGet().
*/
public function providerTestGet() {
$data = array();
$data[] = array(
array(
'value' => 'Foo',
),
'Foo'
);
$random = $this->randomMachineName();
$random_html_entity = '&' . $random;
$data[] = array(
array(
'value' => 'Foo !bar @baz %qux',
'arguments' => array(
'!bar' => $random,
'@baz' => $random_html_entity,
'%qux' => $random_html_entity,
),
'context' => $this->randomMachineName(),
),
'Foo ' . $random . ' &amp;' . $random . ' <em class="placeholder">&amp;' . $random . '</em>',
);
return $data;
}
}

View file

@ -0,0 +1,178 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Asset\CssCollectionGrouperUnitTest.
*/
namespace Drupal\Tests\Core\Asset;
use Drupal\Core\Asset\CssCollectionGrouper;
use Drupal\Tests\UnitTestCase;
/**
* Tests the CSS asset collection grouper.
*
* @group Asset
*/
class CssCollectionGrouperUnitTest extends UnitTestCase {
/**
* A CSS asset grouper.
*
* @var \Drupal\Core\Asset\CssCollectionGrouper object.
*/
protected $grouper;
protected function setUp() {
parent::setUp();
$this->grouper = new CssCollectionGrouper();
}
/**
* Tests \Drupal\Core\Asset\CssCollectionGrouper.
*/
function testGrouper() {
$css_assets = array(
'system.base.css' => array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.012,
'media' => 'all',
'preprocess' => TRUE,
'data' => 'core/modules/system/system.base.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'system.base.css',
),
'system.theme.css' => array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.013,
'media' => 'all',
'preprocess' => TRUE,
'data' => 'core/modules/system/system.theme.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'system.theme.css',
),
'jquery.ui.core.css' => array(
'group' => -100,
'type' => 'file',
'weight' => 0.004,
'every_page' => FALSE,
'media' => 'all',
'preprocess' => TRUE,
'data' => 'core/misc/ui/themes/base/jquery.ui.core.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'jquery.ui.core.css',
),
'field.css' => array(
'every_page' => TRUE,
'group' => 0,
'type' => 'file',
'weight' => 0.011,
'media' => 'all',
'preprocess' => TRUE,
'data' => 'core/modules/field/theme/field.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'field.css',
),
'external.css' => array(
'every_page' => FALSE,
'group' => 0,
'type' => 'external',
'weight' => 0.009,
'media' => 'all',
'preprocess' => TRUE,
'data' => 'http://example.com/external.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'external.css',
),
'elements.css' => array(
'group' => 100,
'every_page' => TRUE,
'media' => 'all',
'type' => 'file',
'weight' => 0.001,
'preprocess' => TRUE,
'data' => 'core/themes/bartik/css/base/elements.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'elements.css',
),
'print.css' => array(
'group' => 100,
'every_page' => TRUE,
'media' => 'print',
'type' => 'file',
'weight' => 0.003,
'preprocess' => TRUE,
'data' => 'core/themes/bartik/css/print.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'print.css',
),
);
$groups = $this->grouper->group($css_assets);
$this->assertSame(count($groups), 6, "6 groups created.");
// Check group 1.
$this->assertSame($groups[0]['group'], -100);
$this->assertSame($groups[0]['every_page'], TRUE);
$this->assertSame($groups[0]['type'], 'file');
$this->assertSame($groups[0]['media'], 'all');
$this->assertSame($groups[0]['preprocess'], TRUE);
$this->assertSame(count($groups[0]['items']), 2);
$this->assertContains($css_assets['system.base.css'], $groups[0]['items']);
$this->assertContains($css_assets['system.theme.css'], $groups[0]['items']);
// Check group 2.
$this->assertSame($groups[1]['group'], -100);
$this->assertSame($groups[1]['every_page'], FALSE);
$this->assertSame($groups[1]['type'], 'file');
$this->assertSame($groups[1]['media'], 'all');
$this->assertSame($groups[1]['preprocess'], TRUE);
$this->assertSame(count($groups[1]['items']), 1);
$this->assertContains($css_assets['jquery.ui.core.css'], $groups[1]['items']);
// Check group 3.
$this->assertSame($groups[2]['group'], 0);
$this->assertSame($groups[2]['every_page'], TRUE);
$this->assertSame($groups[2]['type'], 'file');
$this->assertSame($groups[2]['media'], 'all');
$this->assertSame($groups[2]['preprocess'], TRUE);
$this->assertSame(count($groups[2]['items']), 1);
$this->assertContains($css_assets['field.css'], $groups[2]['items']);
// Check group 4.
$this->assertSame($groups[3]['group'], 0);
$this->assertSame($groups[3]['every_page'], FALSE);
$this->assertSame($groups[3]['type'], 'external');
$this->assertSame($groups[3]['media'], 'all');
$this->assertSame($groups[3]['preprocess'], TRUE);
$this->assertSame(count($groups[3]['items']), 1);
$this->assertContains($css_assets['external.css'], $groups[3]['items']);
// Check group 5.
$this->assertSame($groups[4]['group'], 100);
$this->assertSame($groups[4]['every_page'], TRUE);
$this->assertSame($groups[4]['type'], 'file');
$this->assertSame($groups[4]['media'], 'all');
$this->assertSame($groups[4]['preprocess'], TRUE);
$this->assertSame(count($groups[4]['items']), 1);
$this->assertContains($css_assets['elements.css'], $groups[4]['items']);
// Check group 6.
$this->assertSame($groups[5]['group'], 100);
$this->assertSame($groups[5]['every_page'], TRUE);
$this->assertSame($groups[5]['type'], 'file');
$this->assertSame($groups[5]['media'], 'print');
$this->assertSame($groups[5]['preprocess'], TRUE);
$this->assertSame(count($groups[5]['items']), 1);
$this->assertContains($css_assets['print.css'], $groups[5]['items']);
}
}

View file

@ -0,0 +1,507 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Asset\CssCollectionRendererUnitTest.
*/
namespace {
/**
* CssRenderer uses file_create_url(), which *is* available when using the
* Simpletest test runner, but not when using the PHPUnit test runner; hence
* this hack.
*/
if (!function_exists('file_create_url')) {
/**
* Temporary mock for file_create_url(), until that is moved into
* Component/Utility.
*/
function file_create_url($uri) {
return 'file_create_url:' . $uri;
}
}
}
namespace Drupal\Tests\Core\Asset {
use Drupal\Core\Asset\CssCollectionRenderer;
use Drupal\Tests\UnitTestCase;
/**
* Tests the CSS asset collection renderer.
*
* @group Asset
*/
class CssCollectionRendererUnitTest extends UnitTestCase {
/**
* A CSS asset renderer.
*
* @var \Drupal\Core\Asset\CssRenderer object.
*/
protected $renderer;
/**
* A valid file CSS asset group.
*
* @var array
*/
protected $fileCssGroup;
/**
* The state mock class.
*
* @var \Drupal\Core\State\StateInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $state;
protected function setUp() {
parent::setUp();
$this->state = $this->getMock('Drupal\Core\State\StateInterface');
$this->renderer = new CssCollectionRenderer($this->state);
$this->fileCssGroup = array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'media' => 'all',
'preprocess' => TRUE,
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'items' => array(
0 => array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.012,
'media' => 'all',
'preprocess' => TRUE,
'data' => 'tests/Drupal/Tests/Core/Asset/foo.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'foo.css',
),
1 => array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.013,
'media' => 'all',
'preprocess' => TRUE,
'data' => 'tests/Drupal/Tests/Core/Asset/bar.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'bar.css',
),
),
);
}
/**
* Provides data for the CSS asset rendering test.
*
* @see testRender
*/
function providerTestRender() {
$create_link_element = function($href, $media = 'all', $browsers = array()) {
return array(
'#type' => 'html_tag',
'#tag' => 'link',
'#attributes' => array(
'rel' => 'stylesheet',
'href' => $href,
'media' => $media,
),
'#browsers' => $browsers,
);
};
$create_style_element = function($value, $media, $browsers = array(), $wrap_in_cdata = FALSE) {
$style_element = array(
'#type' => 'html_tag',
'#tag' => 'style',
'#value' => $value,
'#attributes' => array(
'media' => $media
),
'#browsers' => $browsers,
);
if ($wrap_in_cdata) {
$style_element['#value_prefix'] = "\n/* <![CDATA[ */\n";
$style_element['#value_suffix'] = "\n/* ]]> */\n";
}
return $style_element;
};
$create_file_css_asset = function($data, $media = 'all', $preprocess = TRUE) {
return array('group' => 0, 'every_page' => FALSE, 'type' => 'file', 'media' => $media, 'preprocess' => $preprocess, 'data' => $data, 'browsers' => array());
};
return array(
// Single external CSS asset.
0 => array(
// CSS assets.
array(
0 => array('group' => 0, 'every_page' => TRUE, 'type' => 'external', 'media' => 'all', 'preprocess' => TRUE, 'data' => 'http://example.com/popular.js', 'browsers' => array()),
),
// Render elements.
array(
0 => $create_link_element('http://example.com/popular.js', 'all'),
),
),
// Single file CSS asset.
2 => array(
array(
0 => array('group' => 0, 'every_page' => TRUE, 'type' => 'file', 'media' => 'all', 'preprocess' => TRUE, 'data' => 'public://css/file-every_page-all', 'browsers' => array()),
),
array(
0 => $create_link_element(file_create_url('public://css/file-every_page-all') . '?0', 'all'),
),
),
// 31 file CSS assets: expect 31 link elements.
3 => array(
array(
0 => $create_file_css_asset('public://css/1.css'),
1 => $create_file_css_asset('public://css/2.css'),
2 => $create_file_css_asset('public://css/3.css'),
3 => $create_file_css_asset('public://css/4.css'),
4 => $create_file_css_asset('public://css/5.css'),
5 => $create_file_css_asset('public://css/6.css'),
6 => $create_file_css_asset('public://css/7.css'),
7 => $create_file_css_asset('public://css/8.css'),
8 => $create_file_css_asset('public://css/9.css'),
9 => $create_file_css_asset('public://css/10.css'),
10 => $create_file_css_asset('public://css/11.css'),
11 => $create_file_css_asset('public://css/12.css'),
12 => $create_file_css_asset('public://css/13.css'),
13 => $create_file_css_asset('public://css/14.css'),
14 => $create_file_css_asset('public://css/15.css'),
15 => $create_file_css_asset('public://css/16.css'),
16 => $create_file_css_asset('public://css/17.css'),
17 => $create_file_css_asset('public://css/18.css'),
18 => $create_file_css_asset('public://css/19.css'),
19 => $create_file_css_asset('public://css/20.css'),
20 => $create_file_css_asset('public://css/21.css'),
21 => $create_file_css_asset('public://css/22.css'),
22 => $create_file_css_asset('public://css/23.css'),
23 => $create_file_css_asset('public://css/24.css'),
24 => $create_file_css_asset('public://css/25.css'),
25 => $create_file_css_asset('public://css/26.css'),
26 => $create_file_css_asset('public://css/27.css'),
27 => $create_file_css_asset('public://css/28.css'),
28 => $create_file_css_asset('public://css/29.css'),
29 => $create_file_css_asset('public://css/30.css'),
30 => $create_file_css_asset('public://css/31.css'),
),
array(
0 => $create_link_element(file_create_url('public://css/1.css') . '?0'),
1 => $create_link_element(file_create_url('public://css/2.css') . '?0'),
2 => $create_link_element(file_create_url('public://css/3.css') . '?0'),
3 => $create_link_element(file_create_url('public://css/4.css') . '?0'),
4 => $create_link_element(file_create_url('public://css/5.css') . '?0'),
5 => $create_link_element(file_create_url('public://css/6.css') . '?0'),
6 => $create_link_element(file_create_url('public://css/7.css') . '?0'),
7 => $create_link_element(file_create_url('public://css/8.css') . '?0'),
8 => $create_link_element(file_create_url('public://css/9.css') . '?0'),
9 => $create_link_element(file_create_url('public://css/10.css') . '?0'),
10 => $create_link_element(file_create_url('public://css/11.css') . '?0'),
11 => $create_link_element(file_create_url('public://css/12.css') . '?0'),
12 => $create_link_element(file_create_url('public://css/13.css') . '?0'),
13 => $create_link_element(file_create_url('public://css/14.css') . '?0'),
14 => $create_link_element(file_create_url('public://css/15.css') . '?0'),
15 => $create_link_element(file_create_url('public://css/16.css') . '?0'),
16 => $create_link_element(file_create_url('public://css/17.css') . '?0'),
17 => $create_link_element(file_create_url('public://css/18.css') . '?0'),
18 => $create_link_element(file_create_url('public://css/19.css') . '?0'),
19 => $create_link_element(file_create_url('public://css/20.css') . '?0'),
20 => $create_link_element(file_create_url('public://css/21.css') . '?0'),
21 => $create_link_element(file_create_url('public://css/22.css') . '?0'),
22 => $create_link_element(file_create_url('public://css/23.css') . '?0'),
23 => $create_link_element(file_create_url('public://css/24.css') . '?0'),
24 => $create_link_element(file_create_url('public://css/25.css') . '?0'),
25 => $create_link_element(file_create_url('public://css/26.css') . '?0'),
26 => $create_link_element(file_create_url('public://css/27.css') . '?0'),
27 => $create_link_element(file_create_url('public://css/28.css') . '?0'),
28 => $create_link_element(file_create_url('public://css/29.css') . '?0'),
29 => $create_link_element(file_create_url('public://css/30.css') . '?0'),
30 => $create_link_element(file_create_url('public://css/31.css') . '?0'),
),
),
// 32 file CSS assets with the same properties: expect 2 style elements.
4 => array(
array(
0 => $create_file_css_asset('public://css/1.css'),
1 => $create_file_css_asset('public://css/2.css'),
2 => $create_file_css_asset('public://css/3.css'),
3 => $create_file_css_asset('public://css/4.css'),
4 => $create_file_css_asset('public://css/5.css'),
5 => $create_file_css_asset('public://css/6.css'),
6 => $create_file_css_asset('public://css/7.css'),
7 => $create_file_css_asset('public://css/8.css'),
8 => $create_file_css_asset('public://css/9.css'),
9 => $create_file_css_asset('public://css/10.css'),
10 => $create_file_css_asset('public://css/11.css'),
11 => $create_file_css_asset('public://css/12.css'),
12 => $create_file_css_asset('public://css/13.css'),
13 => $create_file_css_asset('public://css/14.css'),
14 => $create_file_css_asset('public://css/15.css'),
15 => $create_file_css_asset('public://css/16.css'),
16 => $create_file_css_asset('public://css/17.css'),
17 => $create_file_css_asset('public://css/18.css'),
18 => $create_file_css_asset('public://css/19.css'),
19 => $create_file_css_asset('public://css/20.css'),
20 => $create_file_css_asset('public://css/21.css'),
21 => $create_file_css_asset('public://css/22.css'),
22 => $create_file_css_asset('public://css/23.css'),
23 => $create_file_css_asset('public://css/24.css'),
24 => $create_file_css_asset('public://css/25.css'),
25 => $create_file_css_asset('public://css/26.css'),
26 => $create_file_css_asset('public://css/27.css'),
27 => $create_file_css_asset('public://css/28.css'),
28 => $create_file_css_asset('public://css/29.css'),
29 => $create_file_css_asset('public://css/30.css'),
30 => $create_file_css_asset('public://css/31.css'),
31 => $create_file_css_asset('public://css/32.css'),
),
array(
0 => $create_style_element('
@import url("' . file_create_url('public://css/1.css') . '?0");
@import url("' . file_create_url('public://css/2.css') . '?0");
@import url("' . file_create_url('public://css/3.css') . '?0");
@import url("' . file_create_url('public://css/4.css') . '?0");
@import url("' . file_create_url('public://css/5.css') . '?0");
@import url("' . file_create_url('public://css/6.css') . '?0");
@import url("' . file_create_url('public://css/7.css') . '?0");
@import url("' . file_create_url('public://css/8.css') . '?0");
@import url("' . file_create_url('public://css/9.css') . '?0");
@import url("' . file_create_url('public://css/10.css') . '?0");
@import url("' . file_create_url('public://css/11.css') . '?0");
@import url("' . file_create_url('public://css/12.css') . '?0");
@import url("' . file_create_url('public://css/13.css') . '?0");
@import url("' . file_create_url('public://css/14.css') . '?0");
@import url("' . file_create_url('public://css/15.css') . '?0");
@import url("' . file_create_url('public://css/16.css') . '?0");
@import url("' . file_create_url('public://css/17.css') . '?0");
@import url("' . file_create_url('public://css/18.css') . '?0");
@import url("' . file_create_url('public://css/19.css') . '?0");
@import url("' . file_create_url('public://css/20.css') . '?0");
@import url("' . file_create_url('public://css/21.css') . '?0");
@import url("' . file_create_url('public://css/22.css') . '?0");
@import url("' . file_create_url('public://css/23.css') . '?0");
@import url("' . file_create_url('public://css/24.css') . '?0");
@import url("' . file_create_url('public://css/25.css') . '?0");
@import url("' . file_create_url('public://css/26.css') . '?0");
@import url("' . file_create_url('public://css/27.css') . '?0");
@import url("' . file_create_url('public://css/28.css') . '?0");
@import url("' . file_create_url('public://css/29.css') . '?0");
@import url("' . file_create_url('public://css/30.css') . '?0");
@import url("' . file_create_url('public://css/31.css') . '?0");
', 'all'),
1 => $create_style_element('
@import url("' . file_create_url('public://css/32.css') . '?0");
', 'all'),
),
),
// 32 file CSS assets with the same properties, except for the 10th and
// 20th files, they have different 'media' properties. Expect 5 style
// elements.
5 => array(
array(
0 => $create_file_css_asset('public://css/1.css'),
1 => $create_file_css_asset('public://css/2.css'),
2 => $create_file_css_asset('public://css/3.css'),
3 => $create_file_css_asset('public://css/4.css'),
4 => $create_file_css_asset('public://css/5.css'),
5 => $create_file_css_asset('public://css/6.css'),
6 => $create_file_css_asset('public://css/7.css'),
7 => $create_file_css_asset('public://css/8.css'),
8 => $create_file_css_asset('public://css/9.css'),
9 => $create_file_css_asset('public://css/10.css', 'screen'),
10 => $create_file_css_asset('public://css/11.css'),
11 => $create_file_css_asset('public://css/12.css'),
12 => $create_file_css_asset('public://css/13.css'),
13 => $create_file_css_asset('public://css/14.css'),
14 => $create_file_css_asset('public://css/15.css'),
15 => $create_file_css_asset('public://css/16.css'),
16 => $create_file_css_asset('public://css/17.css'),
17 => $create_file_css_asset('public://css/18.css'),
18 => $create_file_css_asset('public://css/19.css'),
19 => $create_file_css_asset('public://css/20.css', 'print'),
20 => $create_file_css_asset('public://css/21.css'),
21 => $create_file_css_asset('public://css/22.css'),
22 => $create_file_css_asset('public://css/23.css'),
23 => $create_file_css_asset('public://css/24.css'),
24 => $create_file_css_asset('public://css/25.css'),
25 => $create_file_css_asset('public://css/26.css'),
26 => $create_file_css_asset('public://css/27.css'),
27 => $create_file_css_asset('public://css/28.css'),
28 => $create_file_css_asset('public://css/29.css'),
29 => $create_file_css_asset('public://css/30.css'),
30 => $create_file_css_asset('public://css/31.css'),
31 => $create_file_css_asset('public://css/32.css'),
),
array(
0 => $create_style_element('
@import url("' . file_create_url('public://css/1.css') . '?0");
@import url("' . file_create_url('public://css/2.css') . '?0");
@import url("' . file_create_url('public://css/3.css') . '?0");
@import url("' . file_create_url('public://css/4.css') . '?0");
@import url("' . file_create_url('public://css/5.css') . '?0");
@import url("' . file_create_url('public://css/6.css') . '?0");
@import url("' . file_create_url('public://css/7.css') . '?0");
@import url("' . file_create_url('public://css/8.css') . '?0");
@import url("' . file_create_url('public://css/9.css') . '?0");
', 'all'),
1 => $create_style_element('
@import url("' . file_create_url('public://css/10.css') . '?0");
', 'screen'),
2 => $create_style_element('
@import url("' . file_create_url('public://css/11.css') . '?0");
@import url("' . file_create_url('public://css/12.css') . '?0");
@import url("' . file_create_url('public://css/13.css') . '?0");
@import url("' . file_create_url('public://css/14.css') . '?0");
@import url("' . file_create_url('public://css/15.css') . '?0");
@import url("' . file_create_url('public://css/16.css') . '?0");
@import url("' . file_create_url('public://css/17.css') . '?0");
@import url("' . file_create_url('public://css/18.css') . '?0");
@import url("' . file_create_url('public://css/19.css') . '?0");
', 'all'),
3 => $create_style_element('
@import url("' . file_create_url('public://css/20.css') . '?0");
', 'print'),
4 => $create_style_element('
@import url("' . file_create_url('public://css/21.css') . '?0");
@import url("' . file_create_url('public://css/22.css') . '?0");
@import url("' . file_create_url('public://css/23.css') . '?0");
@import url("' . file_create_url('public://css/24.css') . '?0");
@import url("' . file_create_url('public://css/25.css') . '?0");
@import url("' . file_create_url('public://css/26.css') . '?0");
@import url("' . file_create_url('public://css/27.css') . '?0");
@import url("' . file_create_url('public://css/28.css') . '?0");
@import url("' . file_create_url('public://css/29.css') . '?0");
@import url("' . file_create_url('public://css/30.css') . '?0");
@import url("' . file_create_url('public://css/31.css') . '?0");
@import url("' . file_create_url('public://css/32.css') . '?0");
', 'all'),
),
),
// 32 file CSS assets with the same properties, except for the 15th, which
// has 'preprocess' = FALSE. Expect 1 link element and 2 style elements.
6 => array(
array(
0 => $create_file_css_asset('public://css/1.css'),
1 => $create_file_css_asset('public://css/2.css'),
2 => $create_file_css_asset('public://css/3.css'),
3 => $create_file_css_asset('public://css/4.css'),
4 => $create_file_css_asset('public://css/5.css'),
5 => $create_file_css_asset('public://css/6.css'),
6 => $create_file_css_asset('public://css/7.css'),
7 => $create_file_css_asset('public://css/8.css'),
8 => $create_file_css_asset('public://css/9.css'),
9 => $create_file_css_asset('public://css/10.css'),
10 => $create_file_css_asset('public://css/11.css'),
11 => $create_file_css_asset('public://css/12.css'),
12 => $create_file_css_asset('public://css/13.css'),
13 => $create_file_css_asset('public://css/14.css'),
14 => $create_file_css_asset('public://css/15.css', 'all', FALSE),
15 => $create_file_css_asset('public://css/16.css'),
16 => $create_file_css_asset('public://css/17.css'),
17 => $create_file_css_asset('public://css/18.css'),
18 => $create_file_css_asset('public://css/19.css'),
19 => $create_file_css_asset('public://css/20.css'),
20 => $create_file_css_asset('public://css/21.css'),
21 => $create_file_css_asset('public://css/22.css'),
22 => $create_file_css_asset('public://css/23.css'),
23 => $create_file_css_asset('public://css/24.css'),
24 => $create_file_css_asset('public://css/25.css'),
25 => $create_file_css_asset('public://css/26.css'),
26 => $create_file_css_asset('public://css/27.css'),
27 => $create_file_css_asset('public://css/28.css'),
28 => $create_file_css_asset('public://css/29.css'),
29 => $create_file_css_asset('public://css/30.css'),
30 => $create_file_css_asset('public://css/31.css'),
31 => $create_file_css_asset('public://css/32.css'),
),
array(
0 => $create_style_element('
@import url("' . file_create_url('public://css/1.css') . '?0");
@import url("' . file_create_url('public://css/2.css') . '?0");
@import url("' . file_create_url('public://css/3.css') . '?0");
@import url("' . file_create_url('public://css/4.css') . '?0");
@import url("' . file_create_url('public://css/5.css') . '?0");
@import url("' . file_create_url('public://css/6.css') . '?0");
@import url("' . file_create_url('public://css/7.css') . '?0");
@import url("' . file_create_url('public://css/8.css') . '?0");
@import url("' . file_create_url('public://css/9.css') . '?0");
@import url("' . file_create_url('public://css/10.css') . '?0");
@import url("' . file_create_url('public://css/11.css') . '?0");
@import url("' . file_create_url('public://css/12.css') . '?0");
@import url("' . file_create_url('public://css/13.css') . '?0");
@import url("' . file_create_url('public://css/14.css') . '?0");
', 'all'),
1 => $create_link_element(file_create_url('public://css/15.css') . '?0'),
2 => $create_style_element('
@import url("' . file_create_url('public://css/16.css') . '?0");
@import url("' . file_create_url('public://css/17.css') . '?0");
@import url("' . file_create_url('public://css/18.css') . '?0");
@import url("' . file_create_url('public://css/19.css') . '?0");
@import url("' . file_create_url('public://css/20.css') . '?0");
@import url("' . file_create_url('public://css/21.css') . '?0");
@import url("' . file_create_url('public://css/22.css') . '?0");
@import url("' . file_create_url('public://css/23.css') . '?0");
@import url("' . file_create_url('public://css/24.css') . '?0");
@import url("' . file_create_url('public://css/25.css') . '?0");
@import url("' . file_create_url('public://css/26.css') . '?0");
@import url("' . file_create_url('public://css/27.css') . '?0");
@import url("' . file_create_url('public://css/28.css') . '?0");
@import url("' . file_create_url('public://css/29.css') . '?0");
@import url("' . file_create_url('public://css/30.css') . '?0");
@import url("' . file_create_url('public://css/31.css') . '?0");
@import url("' . file_create_url('public://css/32.css') . '?0");
', 'all'),
),
),
);
}
/**
* Tests CSS asset rendering.
*
* @dataProvider providerTestRender
*/
function testRender(array $css_assets, array $render_elements) {
$this->state->expects($this->once())
->method('get')
->with('system.css_js_query_string')
->will($this->returnValue(NULL));
$this->assertSame($render_elements, $this->renderer->render($css_assets));
}
/**
* Tests a CSS asset group with the invalid 'type' => 'internal'.
*/
function testRenderInvalidType() {
$this->state->expects($this->once())
->method('get')
->with('system.css_js_query_string')
->will($this->returnValue(NULL));
$this->setExpectedException('Exception', 'Invalid CSS asset type.');
$css_group = array(
'group' => 0,
'every_page' => TRUE,
'type' => 'internal',
'media' => 'all',
'preprocess' => TRUE,
'browsers' => array(),
'data' => 'http://example.com/popular.js'
);
$this->renderer->render($css_group);
}
}
}

View file

@ -0,0 +1,284 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Asset\CssOptimizerUnitTest.
*/
namespace {
/**
* CssOptimizer uses file_create_url(), which *is* available when using the
* Simpletest test runner, but not when using the PHPUnit test runner; hence
* this hack.
*/
if (!function_exists('file_create_url')) {
/**
* Temporary mock for file_create_url(), until that is moved into
* Component/Utility.
*/
function file_create_url($uri) {
return 'file_create_url:' . $uri;
}
}
if (!function_exists('file_uri_scheme')) {
function file_uri_scheme($uri) {
return FALSE;
}
}
}
namespace Drupal\Tests\Core\Asset {
use Drupal\Core\Asset\CssOptimizer;
use Drupal\Tests\UnitTestCase;
/**
* Tests the CSS asset optimizer.
*
* @group Asset
*/
class CssOptimizerUnitTest extends UnitTestCase {
/**
* A CSS asset optimizer.
*
* @var \Drupal\Core\Asset\CssOptimizer object.
*/
protected $optimizer;
protected function setUp() {
parent::setUp();
$this->optimizer = new CssOptimizer();
}
/**
* Provides data for the CSS asset optimizing test.
*/
function providerTestOptimize() {
$path = dirname(__FILE__) . '/css_test_files/';
return array(
// File. Tests:
// - Stripped comments and white-space.
// - Retain white-space in selectors. (https://www.drupal.org/node/472820)
// - Retain pseudo-selectors. (https://www.drupal.org/node/460448)
0 => array(
array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.012,
'media' => 'all',
'preprocess' => TRUE,
'data' => $path . 'css_input_without_import.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'css_input_without_import.css',
),
file_get_contents($path . 'css_input_without_import.css.optimized.css'),
),
// File. Tests:
// - Proper URLs in imported files. (https://www.drupal.org/node/265719)
// - A background image with relative paths, which must be rewritten.
// - The rewritten background image path must also be passed through
// file_create_url(). (https://www.drupal.org/node/1961340)
// - Imported files that are external (protocol-relative URL or not)
// should not be expanded. (https://www.drupal.org/node/2014851)
1 => array(
array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.013,
'media' => 'all',
'preprocess' => TRUE,
'data' => $path . 'css_input_with_import.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'css_input_with_import.css',
),
str_replace('url(images/icon.png)', 'url(' . file_create_url($path . 'images/icon.png') . ')', file_get_contents($path . 'css_input_with_import.css.optimized.css')),
),
// File. Tests:
// - Retain comment hacks.
2 => array(
array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.013,
'media' => 'all',
'preprocess' => TRUE,
'data' => $path . 'comment_hacks.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'comment_hacks.css',
),
file_get_contents($path . 'comment_hacks.css.optimized.css'),
),
// File in subfolder. Tests:
// - CSS import path is properly interpreted.
// (https://www.drupal.org/node/1198904)
// - Don't adjust data URIs (https://www.drupal.org/node/2142441)
5 => array(
array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.013,
'media' => 'all',
'preprocess' => TRUE,
'data' => $path . 'css_subfolder/css_input_with_import.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'css_input_with_import.css',
),
str_replace('url(../images/icon.png)', 'url(' . file_create_url($path . 'images/icon.png') . ')', file_get_contents($path . 'css_subfolder/css_input_with_import.css.optimized.css')),
),
// File. Tests:
// - Any @charaset declaration at the beginning of a file should be
// removed without breaking subsequent CSS.
6 => array(
array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.013,
'media' => 'all',
'preprocess' => TRUE,
'data' => $path . 'charset_sameline.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'charset_sameline.css',
),
file_get_contents($path . 'charset.css.optimized.css'),
),
7 => array(
array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.013,
'media' => 'all',
'preprocess' => TRUE,
'data' => $path . 'charset_newline.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'charset_newline.css',
),
file_get_contents($path . 'charset.css.optimized.css'),
),
6 => array(
array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.013,
'media' => 'all',
'preprocess' => TRUE,
'data' => $path . 'css_input_with_bom.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'css_input_with_bom.css',
),
'.byte-order-mark-test{content:"☃";}'. "\n",
),
7 => array(
array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.013,
'media' => 'all',
'preprocess' => TRUE,
'data' => $path . 'css_input_with_charset.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'css_input_with_charset.css',
),
'.charset-test{content:"€";}' . "\n",
),
8 => array(
array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.013,
'media' => 'all',
'preprocess' => TRUE,
'data' => $path . 'css_input_with_bom_and_charset.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'css_input_with_bom_and_charset.css',
),
'.byte-order-mark-charset-test{content:"☃";}' . "\n",
),
9 => array(
array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.013,
'media' => 'all',
'preprocess' => TRUE,
'data' => $path . 'css_input_with_utf16_bom.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'css_input_with_utf16_bom.css',
),
'.utf16-byte-order-mark-test{content:"☃";}' . "\n",
),
);
}
/**
* Tests optimizing a CSS asset group containing 'type' => 'file'.
*
* @dataProvider providerTestOptimize
*/
function testOptimize($css_asset, $expected) {
$this->assertEquals($expected, $this->optimizer->optimize($css_asset), 'Group of file CSS assets optimized correctly.');
}
/**
* Tests a file CSS asset with preprocessing disabled.
*/
function testTypeFilePreprocessingDisabled() {
$this->setExpectedException('Exception', 'Only file CSS assets with preprocessing enabled can be optimized.');
$css_asset = array(
'group' => -100,
'every_page' => TRUE,
'type' => 'file',
'weight' => 0.012,
'media' => 'all',
// Preprocessing disabled.
'preprocess' => FALSE,
'data' => 'tests/Drupal/Tests/Core/Asset/foo.css',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
'basename' => 'foo.css',
);
$this->optimizer->optimize($css_asset);
}
/**
* Tests a CSS asset with 'type' => 'external'.
*/
function testTypeExternal() {
$this->setExpectedException('Exception', 'Only file or inline CSS assets can be optimized.');
$css_asset = array(
'group' => -100,
'every_page' => TRUE,
// Type external.
'type' => 'external',
'weight' => 0.012,
'media' => 'all',
'preprocess' => TRUE,
'data' => 'http://example.com/foo.js',
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
);
$this->optimizer->optimize($css_asset);
}
}
}

View file

@ -0,0 +1,131 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Asset\JsOptimizerUnitTest.
*/
namespace Drupal\Tests\Core\Asset;
use Drupal\Core\Asset\JsOptimizer;
use Drupal\Tests\UnitTestCase;
/**
* Tests the JS asset optimizer.
*
* @group Asset
*/
class JsOptimizerUnitTest extends UnitTestCase {
/**
* A JS asset optimizer.
*
* @var \Drupal\Core\Asset\JsOptimizer object.
*/
protected $optimizer;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->optimizer = new JsOptimizer();
}
/**
* Provides data for the JS asset cleaning test.
*
* @see \Drupal\Core\Asset\JsOptimizer::clean().
*
* @returns array
* An array of test data.
*/
function providerTestClean() {
$path = dirname(__FILE__) . '/js_test_files/';
return array(
// File. Tests:
// - Stripped sourceMappingURL with comment # syntax.
0 => array(
file_get_contents($path . 'source_mapping_url.min.js'),
file_get_contents($path . 'source_mapping_url.min.js.optimized.js'),
),
// File. Tests:
// - Stripped sourceMappingURL with comment @ syntax.
1 => array(
file_get_contents($path . 'source_mapping_url_old.min.js'),
file_get_contents($path . 'source_mapping_url_old.min.js.optimized.js'),
),
// File. Tests:
// - Stripped sourceURL with comment # syntax.
2 => array(
file_get_contents($path . 'source_url.min.js'),
file_get_contents($path . 'source_url.min.js.optimized.js'),
),
// File. Tests:
// - Stripped sourceURL with comment @ syntax.
3 => array(
file_get_contents($path . 'source_url_old.min.js'),
file_get_contents($path . 'source_url_old.min.js.optimized.js'),
),
);
}
/**
* Tests cleaning of a JS asset group containing 'type' => 'file'.
*
* @dataProvider providerTestClean
*/
function testClean($js_asset, $expected) {
$this->assertEquals($expected, $this->optimizer->clean($js_asset));
}
/**
* Provides data for the JS asset optimize test.
*
* @see \Drupal\Core\Asset\JsOptimizer::optimize().
*
* @returns array
* An array of test data.
*/
function providerTestOptimize() {
$path = dirname(__FILE__) . '/js_test_files/';
return array(
0 => array(
array(
'type' => 'file',
'preprocess' => TRUE,
'data' => $path . 'utf8_bom.js',
),
file_get_contents($path . 'utf8_bom.js.optimized.js'),
),
1 => array(
array(
'type' => 'file',
'preprocess' => TRUE,
'data' => $path . 'utf16_bom.js',
),
file_get_contents($path . 'utf16_bom.js.optimized.js'),
),
2 => array(
array(
'type' => 'file',
'preprocess' => TRUE,
'data' => $path . 'latin_9.js',
'attributes' => array('charset' => 'ISO-8859-15'),
),
file_get_contents($path . 'latin_9.js.optimized.js'),
),
);
}
/**
* Tests cleaning of a JS asset group containing 'type' => 'file'.
*
* @dataProvider providerTestOptimize
*/
function testOptimize($js_asset, $expected) {
$this->assertEquals($expected, $this->optimizer->optimize($js_asset));
}
}

View file

@ -0,0 +1,177 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Asset\LibraryDependencyResolverTest.
*/
namespace Drupal\Tests\Core\Asset;
use Drupal\Core\Asset\LibraryDependencyResolver;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Asset\LibraryDependencyResolver
* @group Asset
*/
class LibraryDependencyResolverTest extends UnitTestCase {
/**
* The tested library dependency resolver.
*
* @var \Drupal\Core\Asset\LibraryDependencyResolver
*/
protected $libraryDependencyResolver;
/**
* The mocked library discovery service.
*
* @var \Drupal\Core\Asset\LibraryDiscoveryInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $libraryDiscovery;
/**
* The mocked module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $moduleHandler;
/**
* Test library data.
*
* @var array
*/
protected $libraryData = array(
'no_deps_a' => ['js' => [], 'css' => []],
'no_deps_b' => ['js' => [], 'css' => []],
'no_deps_c' => ['js' => [], 'css' => []],
'deps_a' => ['js' => [], 'css' => [], 'dependencies' => ['test/no_deps_a']],
'deps_b' => ['js' => [], 'css' => [], 'dependencies' => ['test/no_deps_a', 'test/no_deps_b']],
'deps_c' => ['js' => [], 'css' => [], 'dependencies' => ['test/no_deps_b', 'test/no_deps_a']],
'nested_deps_a' => ['js' => [], 'css' => [], 'dependencies' => ['test/deps_a']],
'nested_deps_b' => ['js' => [], 'css' => [], 'dependencies' => ['test/nested_deps_a']],
'nested_deps_c' => ['js' => [], 'css' => [], 'dependencies' => ['test/nested_deps_b']],
);
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->libraryDiscovery = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscovery')
->disableOriginalConstructor()
->setMethods(['getLibrariesByExtension'])
->getMock();
$this->libraryDiscovery->expects($this->any())
->method('getLibrariesByExtension')
->with('test')
->will($this->returnValue($this->libraryData));
$this->libraryDependencyResolver= new LibraryDependencyResolver($this->libraryDiscovery);
}
/**
* Provides test data for ::testGetLibrariesWithDependencies().
*/
public function providerTestGetLibrariesWithDependencies() {
return [
// Empty list of libraries.
[[], []],
// Without dependencies.
[['test/no_deps_a'], ['test/no_deps_a']],
[['test/no_deps_a', 'test/no_deps_b'], ['test/no_deps_a', 'test/no_deps_b']],
[['test/no_deps_b', 'test/no_deps_a'], ['test/no_deps_b', 'test/no_deps_a']],
// Single-level (direct) dependencies.
[['test/deps_a'], ['test/no_deps_a', 'test/deps_a']],
[['test/deps_b'], ['test/no_deps_a', 'test/no_deps_b', 'test/deps_b']],
[['test/deps_c'], ['test/no_deps_b', 'test/no_deps_a', 'test/deps_c']],
[['test/deps_a', 'test/deps_b'], ['test/no_deps_a', 'test/deps_a', 'test/no_deps_b', 'test/deps_b']],
[['test/deps_a', 'test/deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/no_deps_b', 'test/deps_c']],
[['test/deps_a', 'test/deps_b', 'test/deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/no_deps_b', 'test/deps_b', 'test/deps_c']],
[['test/deps_b', 'test/deps_a'], ['test/no_deps_a', 'test/no_deps_b', 'test/deps_b', 'test/deps_a']],
[['test/deps_b', 'test/deps_c'], ['test/no_deps_a', 'test/no_deps_b', 'test/deps_b', 'test/deps_c']],
[['test/deps_c', 'test/deps_b'], ['test/no_deps_b', 'test/no_deps_a', 'test/deps_c', 'test/deps_b']],
// Multi-level (indirect) dependencies.
[['test/nested_deps_a'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a']],
[['test/nested_deps_b'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b']],
[['test/nested_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
[['test/nested_deps_a', 'test/nested_deps_b'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b']],
[['test/nested_deps_b', 'test/nested_deps_a'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b']],
[['test/nested_deps_a', 'test/nested_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
[['test/nested_deps_b', 'test/nested_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
[['test/nested_deps_c', 'test/nested_deps_a'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
[['test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
[['test/nested_deps_a', 'test/nested_deps_c', 'test/nested_deps_b'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
[['test/nested_deps_b', 'test/nested_deps_a', 'test/nested_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
[['test/nested_deps_b', 'test/nested_deps_c', 'test/nested_deps_a'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
[['test/nested_deps_c', 'test/nested_deps_a', 'test/nested_deps_b'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
[['test/nested_deps_c', 'test/nested_deps_b', 'test/nested_deps_a'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
// Complex dependencies, combining the above, with many intersections.
[['test/deps_c', 'test/nested_deps_b'], ['test/no_deps_b', 'test/no_deps_a', 'test/deps_c', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b']],
[['test/no_deps_a', 'test/deps_c', 'test/nested_deps_b'], ['test/no_deps_a', 'test/no_deps_b', 'test/deps_c', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b']],
[['test/nested_deps_b', 'test/deps_c', 'test/no_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/no_deps_b', 'test/deps_c', 'test/no_deps_c']],
];
}
/**
* @covers ::getLibrariesWithDependencies
*
* @dataProvider providerTestGetLibrariesWithDependencies
*/
public function testGetLibrariesWithDependencies(array $libraries, array $expected) {
$this->assertEquals($expected, $this->libraryDependencyResolver->getLibrariesWithDependencies($libraries));
}
/**
* Provides test data for ::testGetMinimalRepresentativeSubset().
*/
public function providerTestGetMinimalRepresentativeSubset() {
return [
// Empty list of libraries.
[[], []],
// Without dependencies.
[['test/no_deps_a'], ['test/no_deps_a']],
[['test/no_deps_a', 'test/no_deps_b'], ['test/no_deps_a', 'test/no_deps_b']],
[['test/no_deps_b', 'test/no_deps_a'], ['test/no_deps_b', 'test/no_deps_a']],
// Single-level (direct) dependencies.
[['test/deps_a'], ['test/deps_a']],
[['test/deps_b'], ['test/deps_b']],
[['test/deps_c'], ['test/deps_c']],
[['test/deps_a', 'test/deps_b'], ['test/deps_a', 'test/deps_b']],
[['test/deps_a', 'test/deps_c'], ['test/deps_a', 'test/deps_c']],
[['test/deps_a', 'test/deps_b', 'test/deps_c'], ['test/deps_a', 'test/deps_b', 'test/deps_c']],
[['test/deps_b', 'test/deps_a'], ['test/deps_b', 'test/deps_a']],
[['test/deps_b', 'test/deps_c'], ['test/deps_b', 'test/deps_c']],
[['test/deps_c', 'test/deps_b'], ['test/deps_c', 'test/deps_b']],
// Multi-level (indirect) dependencies.
[['test/nested_deps_a'], ['test/nested_deps_a']],
[['test/nested_deps_b'], ['test/nested_deps_b']],
[['test/nested_deps_c'], ['test/nested_deps_c']],
[['test/nested_deps_a', 'test/nested_deps_b'], ['test/nested_deps_b']],
[['test/nested_deps_b', 'test/nested_deps_a'], ['test/nested_deps_b']],
[['test/nested_deps_a', 'test/nested_deps_c'], ['test/nested_deps_c']],
[['test/nested_deps_b', 'test/nested_deps_c'], ['test/nested_deps_c']],
[['test/nested_deps_c', 'test/nested_deps_a'], ['test/nested_deps_c']],
[['test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c'], ['test/nested_deps_c']],
[['test/nested_deps_a', 'test/nested_deps_c', 'test/nested_deps_b'], ['test/nested_deps_c']],
[['test/nested_deps_b', 'test/nested_deps_a', 'test/nested_deps_c'], ['test/nested_deps_c']],
[['test/nested_deps_b', 'test/nested_deps_c', 'test/nested_deps_a'], ['test/nested_deps_c']],
[['test/nested_deps_c', 'test/nested_deps_a', 'test/nested_deps_b'], ['test/nested_deps_c']],
[['test/nested_deps_c', 'test/nested_deps_b', 'test/nested_deps_a'], ['test/nested_deps_c']],
// Complex dependencies, combining the above, with many intersections.
[['test/deps_c', 'test/nested_deps_b'], ['test/deps_c', 'test/nested_deps_b']],
[['test/no_deps_a', 'test/deps_c', 'test/nested_deps_b'], ['test/deps_c', 'test/nested_deps_b']],
[['test/nested_deps_b', 'test/deps_c', 'test/no_deps_c'], ['test/nested_deps_b', 'test/deps_c', 'test/no_deps_c']],
];
}
/**
* @covers ::getMinimalRepresentativeSubset
*
* @dataProvider providerTestGetMinimalRepresentativeSubset
*/
public function testGetMinimalRepresentativeSubset(array $libraries, array $expected) {
$this->assertEquals($expected, $this->libraryDependencyResolver->getMinimalRepresentativeSubset($libraries));
}
}

View file

@ -0,0 +1,158 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Asset\LibraryDiscoveryCollectorTest.
*/
namespace Drupal\Tests\Core\Asset;
use Drupal\Core\Asset\LibraryDiscoveryCollector;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Asset\LibraryDiscoveryCollector
* @group Asset
*/
class LibraryDiscoveryCollectorTest extends UnitTestCase {
/**
* The mock cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cache;
/**
* The mock lock backend.
*
* @var \Drupal\Core\Lock\LockBackendInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $lock;
/**
* The mock library discovery parser.
*
* @var \Drupal\Core\Asset\LibraryDiscoveryParser|\PHPUnit_Framework_MockObject_MockObject
*/
protected $libraryDiscoveryParser;
/**
* The library discovery collector under test.
*
* @var \Drupal\Core\Asset\LibraryDiscoveryCollector
*/
protected $libraryDiscoveryCollector;
/**
* The mocked theme manager.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $themeManager;
/**
* Test library data.
*
* @var array
*/
protected $libraryData = array(
'test_1' => array(
'js' => array(),
'css' => array(),
),
'test_2' => array(
'js' => array(),
'css' => array(),
),
);
protected $activeTheme;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface');
$this->themeManager = $this->getMockBuilder('Drupal\Core\Theme\ThemeManagerInterface')
->disableOriginalConstructor()
->getMock();
$this->libraryDiscoveryParser = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscoveryParser')
->disableOriginalConstructor()
->getMock();
}
/**
* Tests the resolve cache miss function.
*
* @covers ::resolveCacheMiss
*/
public function testResolveCacheMiss() {
$this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme')
->disableOriginalConstructor()
->getMock();
$this->themeManager->expects($this->once())
->method('getActiveTheme')
->willReturn($this->activeTheme);
$this->activeTheme->expects($this->once())
->method('getName')
->willReturn('kitten_theme');
$this->libraryDiscoveryCollector = new LibraryDiscoveryCollector($this->cache, $this->lock, $this->libraryDiscoveryParser, $this->themeManager);
$this->libraryDiscoveryParser->expects($this->once())
->method('buildByExtension')
->with('test')
->willReturn($this->libraryData);
$this->assertSame($this->libraryData, $this->libraryDiscoveryCollector->get('test'));
$this->assertSame($this->libraryData, $this->libraryDiscoveryCollector->get('test'));
}
/**
* Tests the destruct method.
*
* @covers ::destruct
*/
public function testDestruct() {
$this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme')
->disableOriginalConstructor()
->getMock();
$this->themeManager->expects($this->once())
->method('getActiveTheme')
->willReturn($this->activeTheme);
$this->activeTheme->expects($this->once())
->method('getName')
->willReturn('kitten_theme');
$this->libraryDiscoveryCollector = new LibraryDiscoveryCollector($this->cache, $this->lock, $this->libraryDiscoveryParser, $this->themeManager);
$this->libraryDiscoveryParser->expects($this->once())
->method('buildByExtension')
->with('test')
->willReturn($this->libraryData);
$lock_key = 'library_info:kitten_theme:Drupal\Core\Cache\CacheCollector';
$this->lock->expects($this->once())
->method('acquire')
->with($lock_key)
->will($this->returnValue(TRUE));
$this->cache->expects($this->exactly(2))
->method('get')
->with('library_info:kitten_theme')
->willReturn(FALSE);
$this->cache->expects($this->once())
->method('set')
->with('library_info:kitten_theme', array('test' => $this->libraryData), Cache::PERMANENT, ['library_info']);
$this->lock->expects($this->once())
->method('release')
->with($lock_key);
// This should get data and persist the key.
$this->libraryDiscoveryCollector->get('test');
$this->libraryDiscoveryCollector->destruct();
}
}

View file

@ -0,0 +1,568 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Asset\LibraryDiscoveryParserTest.
*/
namespace Drupal\Tests\Core\Asset;
use Drupal\Core\Asset\LibraryDiscoveryParser;
use Drupal\Tests\UnitTestCase;
if (!defined('CSS_AGGREGATE_DEFAULT')) {
define('CSS_AGGREGATE_DEFAULT', 0);
define('CSS_AGGREGATE_THEME', 100);
define('CSS_BASE', -200);
define('CSS_LAYOUT', -100);
define('CSS_COMPONENT', 0);
define('CSS_STATE', 100);
define('CSS_THEME', 200);
define('JS_SETTING', -200);
define('JS_LIBRARY', -100);
define('JS_DEFAULT', 0);
define('JS_THEME', 100);
}
/**
* @coversDefaultClass \Drupal\Core\Asset\LibraryDiscoveryParser
* @group Asset
*/
class LibraryDiscoveryParserTest extends UnitTestCase {
/**
* The tested library discovery parser service.
*
* @var \Drupal\Core\Asset\LibraryDiscoveryParser|\Drupal\Tests\Core\Asset\TestLibraryDiscoveryParser
*/
protected $libraryDiscoveryParser;
/**
* The mocked cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cache;
/**
* The mocked module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $moduleHandler;
/**
* The mocked theme manager.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $themeManager;
/**
* The mocked lock backend.
*
* @var \Drupal\Core\Lock\LockBackendInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $lock;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
$this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
$this->libraryDiscoveryParser = new TestLibraryDiscoveryParser($this->root, $this->moduleHandler, $this->themeManager);
}
/**
* Tests that basic functionality works for getLibraryByName.
*
* @covers ::buildByExtension
*/
public function testBuildByExtensionSimple() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('example_module')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'example_module', $path);
$libraries = $this->libraryDiscoveryParser->buildByExtension('example_module', 'example');
$library = $libraries['example'];
$this->assertCount(0, $library['js']);
$this->assertCount(1, $library['css']);
$this->assertCount(0, $library['dependencies']);
$this->assertEquals($path . '/css/example.css', $library['css'][0]['data']);
// Ensures that VERSION is replaced by the current core version.
$this->assertEquals(\Drupal::VERSION, $library['version']);
}
/**
* Tests that a theme can be used instead of a module.
*
* @covers ::buildByExtension
*/
public function testBuildByExtensionWithTheme() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('example_theme')
->will($this->returnValue(FALSE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('theme', 'example_theme', $path);
$libraries = $this->libraryDiscoveryParser->buildByExtension('example_theme');
$library = $libraries['example'];
$this->assertCount(0, $library['js']);
$this->assertCount(1, $library['css']);
$this->assertCount(0, $library['dependencies']);
$this->assertEquals($path . '/css/example.css', $library['css'][0]['data']);
}
/**
* Tests that a module with a missing library file results in FALSE.
*
* @covers ::buildByExtension
*/
public function testBuildByExtensionWithMissingLibraryFile() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('example_module')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files_not_existing';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'example_module', $path);
$this->assertSame($this->libraryDiscoveryParser->buildByExtension('example_module'), array());
}
/**
* Tests that an exception is thrown when a libraries file couldn't be parsed.
*
* @expectedException \Drupal\Core\Asset\Exception\InvalidLibraryFileException
*
* @covers ::buildByExtension
*/
public function testInvalidLibrariesFile() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('invalid_file')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'invalid_file', $path);
$this->libraryDiscoveryParser->buildByExtension('invalid_file');
}
/**
* Tests that an exception is thrown when no CSS/JS/setting is specified.
*
* @expectedException \Drupal\Core\Asset\Exception\IncompleteLibraryDefinitionException
* @expectedExceptionMessage Incomplete library definition for definition 'example' in extension 'example_module_missing_information'
*
* @covers ::buildByExtension
*/
public function testBuildByExtensionWithMissingInformation() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('example_module_missing_information')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'example_module_missing_information', $path);
$this->libraryDiscoveryParser->buildByExtension('example_module_missing_information');
}
/**
* Tests the version property, and how it propagates to the contained assets.
*
* @covers ::buildByExtension
*/
public function testVersion() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('versions')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'versions', $path);
$libraries = $this->libraryDiscoveryParser->buildByExtension('versions');
$this->assertFalse(array_key_exists('version', $libraries['versionless']));
$this->assertEquals(-1, $libraries['versionless']['css'][0]['version']);
$this->assertEquals(-1, $libraries['versionless']['js'][0]['version']);
$this->assertEquals('9.8.7.6', $libraries['versioned']['version']);
$this->assertEquals('9.8.7.6', $libraries['versioned']['css'][0]['version']);
$this->assertEquals('9.8.7.6', $libraries['versioned']['js'][0]['version']);
$this->assertEquals(\Drupal::VERSION, $libraries['core-versioned']['version']);
$this->assertEquals(\Drupal::VERSION, $libraries['core-versioned']['css'][0]['version']);
$this->assertEquals(\Drupal::VERSION, $libraries['core-versioned']['js'][0]['version']);
}
/**
* Tests the version property with ISO dates.
*
* We want to make sure that versions defined in the YAML file are the same
* versions that are parsed.
*
* For example, ISO dates are converted into UNIX time by the YAML parser.
*
* @covers ::buildByExtension
*/
public function testNonStringVersion() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('versions')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'versions', $path);
$libraries = $this->libraryDiscoveryParser->buildByExtension('versions');
// As an example, we defined an ISO date in the YAML file and the YAML
// parser converts it into a UNIX timestamp.
$this->assertNotEquals('2014-12-13', $libraries['invalid-version']['version']);
// An example of an ISO date as a string which parses correctly.
$this->assertEquals('2014-12-13', $libraries['valid-version']['version']);
}
/**
* Tests that the version property of external libraries is handled.
*
* @covers ::buildByExtension
*/
public function testExternalLibraries() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('external')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'external', $path);
$libraries = $this->libraryDiscoveryParser->buildByExtension('external');
$library = $libraries['example_external'];
$this->assertEquals('http://example.com/css/example_external.css', $library['css'][0]['data']);
$this->assertEquals('http://example.com/example_external.js', $library['js'][0]['data']);
$this->assertEquals('3.14', $library['version']);
}
/**
* Ensures that CSS weights are taken into account properly.
*
* @covers ::buildByExtension
*/
public function testDefaultCssWeights() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('css_weights')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'css_weights', $path);
$libraries = $this->libraryDiscoveryParser->buildByExtension('css_weights');
$library = $libraries['example'];
$css = $library['css'];
$this->assertCount(10, $css);
// The following default weights are tested:
// - CSS_BASE: -200
// - CSS_LAYOUT: -100
// - CSS_COMPONENT: 0
// - CSS_STATE: 100
// - CSS_THEME: 200
$this->assertEquals(200, $css[0]['weight']);
$this->assertEquals(200 + 29, $css[1]['weight']);
$this->assertEquals(-200, $css[2]['weight']);
$this->assertEquals(-200 + 97, $css[3]['weight']);
$this->assertEquals(-100, $css[4]['weight']);
$this->assertEquals(-100 + 92, $css[5]['weight']);
$this->assertEquals(0, $css[6]['weight']);
$this->assertEquals(45, $css[7]['weight']);
$this->assertEquals(100, $css[8]['weight']);
$this->assertEquals(100 + 8, $css[9]['weight']);
}
/**
* Ensures that you cannot provide positive weights for JavaScript libraries.
*
* @expectedException \UnexpectedValueException
*
* @covers ::buildByExtension
*/
public function testJsWithPositiveWeight() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('js_positive_weight')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'js_positive_weight', $path);
$this->libraryDiscoveryParser->buildByExtension('js_positive_weight');
}
/**
* Tests a library with CSS/JavaScript and a setting.
*
* @covers ::buildByExtension
*/
public function testLibraryWithCssJsSetting() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('css_js_settings')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'css_js_settings', $path);
$libraries = $this->libraryDiscoveryParser->buildByExtension('css_js_settings');
$library = $libraries['example'];
// Ensures that the group and type are set automatically.
$this->assertEquals(-100, $library['js'][0]['group']);
$this->assertEquals('file', $library['js'][0]['type']);
$this->assertEquals($path . '/js/example.js', $library['js'][0]['data']);
$this->assertEquals(0, $library['css'][0]['group']);
$this->assertEquals('file', $library['css'][0]['type']);
$this->assertEquals($path . '/css/base.css', $library['css'][0]['data']);
$this->assertEquals(array('key' => 'value'), $library['drupalSettings']);
}
/**
* Tests a library with dependencies.
*
* @covers ::buildByExtension
*/
public function testLibraryWithDependencies() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('dependencies')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'dependencies', $path);
$libraries = $this->libraryDiscoveryParser->buildByExtension('dependencies');
$library = $libraries['example'];
$this->assertCount(2, $library['dependencies']);
$this->assertEquals('external/example_external', $library['dependencies'][0]);
$this->assertEquals('example_module/example', $library['dependencies'][1]);
}
/**
* Tests a library with a couple of data formats like full URL.
*
* @covers ::buildByExtension
*/
public function testLibraryWithDataTypes() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('data_types')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'data_types', $path);
$this->libraryDiscoveryParser->setFileValidUri('public://test.css', TRUE);
$this->libraryDiscoveryParser->setFileValidUri('public://test2.css', FALSE);
$libraries = $this->libraryDiscoveryParser->buildByExtension('data_types');
$library = $libraries['example'];
$this->assertCount(5, $library['css']);
$this->assertEquals('external', $library['css'][0]['type']);
$this->assertEquals('http://example.com/test.css', $library['css'][0]['data']);
$this->assertEquals('file', $library['css'][1]['type']);
$this->assertEquals('tmp/test.css', $library['css'][1]['data']);
$this->assertEquals('external', $library['css'][2]['type']);
$this->assertEquals('//cdn.com/test.css', $library['css'][2]['data']);
$this->assertEquals('file', $library['css'][3]['type']);
$this->assertEquals('public://test.css', $library['css'][3]['data']);
}
/**
* Tests a library with JavaScript-specific flags.
*
* @covers ::buildByExtension
*/
public function testLibraryWithJavaScript() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('js')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'js', $path);
$libraries = $this->libraryDiscoveryParser->buildByExtension('js');
$library = $libraries['example'];
$this->assertCount(2, $library['js']);
$this->assertEquals(FALSE, $library['js'][0]['minified']);
$this->assertEquals(TRUE, $library['js'][1]['minified']);
}
/**
* Tests that an exception is thrown when license is missing when 3rd party.
*
* @expectedException \Drupal\Core\Asset\Exception\LibraryDefinitionMissingLicenseException
* @expectedExceptionMessage Missing license information in library definition for definition 'no-license-info-but-remote' extension 'licenses_missing_information': it has a remote, but no license.
*
* @covers ::buildByExtension
*/
public function testLibraryThirdPartyWithMissingLicense() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('licenses_missing_information')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'licenses_missing_information', $path);
$this->libraryDiscoveryParser->buildByExtension('licenses_missing_information');
}
/**
* Tests a library with various licenses, some GPL-compatible, some not.
*
* @covers ::buildByExtension
*/
public function testLibraryWithLicenses() {
$this->moduleHandler->expects($this->atLeastOnce())
->method('moduleExists')
->with('licenses')
->will($this->returnValue(TRUE));
$path = __DIR__ . '/library_test_files';
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'licenses', $path);
$libraries = $this->libraryDiscoveryParser->buildByExtension('licenses');
// For libraries without license info, the default license is applied.
$library = $libraries['no-license-info'];
$this->assertCount(1, $library['css']);
$this->assertCount(1, $library['js']);
$this->assertTrue(isset($library['license']));
$default_license = array(
'name' => 'GNU-GPL-2.0-or-later',
'url' => 'https://www.drupal.org/licensing/faq',
'gpl-compatible' => TRUE,
);
$this->assertEquals($library['license'], $default_license);
// GPL2-licensed libraries.
$library = $libraries['gpl2'];
$this->assertCount(1, $library['css']);
$this->assertCount(1, $library['js']);
$expected_license = array(
'name' => 'gpl2',
'url' => 'https://url-to-gpl2-license',
'gpl-compatible' => TRUE,
);
$this->assertEquals($library['license'], $expected_license);
// MIT-licensed libraries.
$library = $libraries['mit'];
$this->assertCount(1, $library['css']);
$this->assertCount(1, $library['js']);
$expected_license = array(
'name' => 'MIT',
'url' => 'https://url-to-mit-license',
'gpl-compatible' => TRUE,
);
$this->assertEquals($library['license'], $expected_license);
// Libraries in the Public Domain.
$library = $libraries['public-domain'];
$this->assertCount(1, $library['css']);
$this->assertCount(1, $library['js']);
$expected_license = array(
'name' => 'Public Domain',
'url' => 'https://url-to-public-domain-license',
'gpl-compatible' => TRUE,
);
$this->assertEquals($library['license'], $expected_license);
// Apache-licensed libraries.
$library = $libraries['apache'];
$this->assertCount(1, $library['css']);
$this->assertCount(1, $library['js']);
$expected_license = array(
'name' => 'apache',
'url' => 'https://url-to-apache-license',
'gpl-compatible' => FALSE,
);
$this->assertEquals($library['license'], $expected_license);
// Copyrighted libraries.
$library = $libraries['copyright'];
$this->assertCount(1, $library['css']);
$this->assertCount(1, $library['js']);
$expected_license = array(
'name' => '© Some company',
'gpl-compatible' => FALSE,
);
$this->assertEquals($library['license'], $expected_license);
}
}
/**
* Wraps the tested class to mock the external dependencies.
*/
class TestLibraryDiscoveryParser extends LibraryDiscoveryParser {
protected $paths;
protected $validUris;
protected function drupalGetPath($type, $name) {
return isset($this->paths[$type][$name]) ? $this->paths[$type][$name] : NULL;
}
public function setPaths($type, $name, $path) {
$this->paths[$type][$name] = $path;
}
protected function fileValidUri($source) {
return isset($this->validUris[$source]) ? $this->validUris[$source] : FALSE;
}
public function setFileValidUri($source, $valid) {
$this->validUris[$source] = $valid;
}
}

View file

@ -0,0 +1,92 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Asset\LibraryDiscoveryTest.
*/
namespace Drupal\Tests\Core\Asset;
use Drupal\Core\Asset\LibraryDiscovery;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Asset\LibraryDiscovery
* @group Asset
*/
class LibraryDiscoveryTest extends UnitTestCase {
/**
* The tested library discovery service.
*
* @var \Drupal\Core\Asset\LibraryDiscovery
*/
protected $libraryDiscovery;
/**
* The mocked library discovery cache collector.
*
* @var \Drupal\Core\Cache\CacheCollectorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $libraryDiscoveryCollector;
/**
* The cache tags invalidator.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cacheTagsInvalidator;
/**
* Test library data.
*
* @var array
*/
protected $libraryData = [
'test_1' => [
'js' => [],
'css' => [
'foo.css' => [],
],
],
'test_2' => [
'js' => [
'bar.js' => [],
],
'css' => [],
],
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
$this->libraryDiscoveryCollector = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscoveryCollector')
->disableOriginalConstructor()
->getMock();
$this->libraryDiscovery = new LibraryDiscovery($this->libraryDiscoveryCollector, $this->cacheTagsInvalidator);
}
/**
* @covers ::getLibrariesByExtension
*/
public function testGetLibrariesByExtension() {
$this->libraryDiscoveryCollector->expects($this->once())
->method('get')
->with('test')
->willReturn($this->libraryData);
$this->libraryDiscovery->getLibrariesbyExtension('test');
// Verify that subsequent calls don't trigger hook_library_info_alter()
// and hook_js_settings_alter() invocations, nor do they talk to the
// collector again. This ensures that the alterations made by
// hook_library_info_alter() and hook_js_settings_alter() implementations
// are statically cached, as desired.
$this->libraryDiscovery->getLibraryByName('test', 'test_1');
$this->libraryDiscovery->getLibrariesbyExtension('test');
}
}

View file

@ -0,0 +1 @@
@charset "UTF-8";html{font-family:"sans-serif";}

View file

@ -0,0 +1 @@
html{font-family:"sans-serif";}

View file

@ -0,0 +1,2 @@
@charset "UTF-8";
html{font-family:"sans-serif";}

View file

@ -0,0 +1 @@
@charset "UTF-8";html{font-family:"sans-serif";}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
.test1{display:block;}html .clear-block{height:1%;}.clear-block{display:block;font:italic bold 12px/30px Georgia,serif;}.test2{display:block;}.bkslshv1{background-color:#c00;}.test3{display:block;}.test4{display:block;}.comment-in-double-quotes:before{content:"/* ";}.this_rule_must_stay{color:#f00;background-color:#fff;}.comment-in-double-quotes:after{content:" */";}.comment-in-single-quotes:before{content:'/*';}.this_rule_must_stay{color:#f00;background-color:#fff;}.comment-in-single-quotes:after{content:'*/';}.comment-in-mixed-quotes:before{content:'"/*"';}.this_rule_must_stay{color:#f00;background-color:#fff;}.comment-in-mixed-quotes:after{content:"'*/'";}.comment-in-quotes-with-escaped:before{content:'/* \" \' */';}.this_rule_must_stay{color:#f00;background-color:#fff;}.comment-in-quotes-with-escaped:after{content:"*/ \" \ '";}

View file

@ -0,0 +1,3 @@
.byte-order-mark-test {
content: "☃";
}

View file

@ -0,0 +1,4 @@
@charset "utf-8";
.byte-order-mark-charset-test {
content: "☃";
}

View file

@ -0,0 +1,4 @@
@charset "iso-8859-15";
.charset-test {
content: "¤";
}

View file

@ -0,0 +1,32 @@
@import "import1.css";
@import "import2.css";
@import url("http://example.com/style.css");
@import url("//example.com/style.css");
body {
margin: 0;
padding: 0;
background: #edf5fa;
font: 76%/170% Verdana, sans-serif;
color: #494949;
}
.this .is .a .test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
.this
.is
.a
.test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
textarea, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}

View file

@ -0,0 +1,6 @@
ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
p,select{font:1em/160% Verdana,sans-serif;color:#494949;}
@import url("http://example.com/style.css");@import url("//example.com/style.css");body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this
.is
.a
.test{font:1em/100% Verdana,sans-serif;color:#494949;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;}

View file

@ -0,0 +1,65 @@
/**
* @file Basic css that does not use import
*/
body {
margin: 0;
padding: 0;
background: #edf5fa;
font: 76%/170% Verdana, sans-serif;
color: #494949;
}
.this .is .a .test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
/**
* CSS spec says that all whitespace is valid whitespace, so this selector
* should be just as good as the one above.
*/
.this
.is
.a
.test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
some :pseudo .thing {
border-radius: 3px;
}
::-moz-selection {
background: #000;
color:#fff;
}
::selection {
background: #000;
color: #fff;
}
@media print {
* {
background: #000 !important;
color: #fff !important;
}
@page {
margin: 0.5cm;
}
}
@media screen and (max-device-width: 480px) {
background: #000;
color: #fff;
}
textarea, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}

View file

@ -0,0 +1,4 @@
body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this
.is
.a
.test{font:1em/100% Verdana,sans-serif;color:#494949;}some :pseudo .thing{border-radius:3px;}::-moz-selection{background:#000;color:#fff;}::selection{background:#000;color:#fff;}@media print{*{background:#000 !important;color:#fff !important;}@page{margin:0.5cm;}}@media screen and (max-device-width:480px){background:#000;color:#fff;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;}

View file

@ -0,0 +1,29 @@
@import "../import1.css";
@import "../import2.css";
body {
margin: 0;
padding: 0;
background: #edf5fa;
font: 76%/170% Verdana, sans-serif;
color: #494949;
}
.this .is .a .test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
.this
.is
.a
.test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
textarea, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}

View file

@ -0,0 +1,6 @@
ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(../images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
p,select{font:1em/160% Verdana,sans-serif;color:#494949;}
body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this
.is
.a
.test{font:1em/100% Verdana,sans-serif;color:#494949;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;}

View file

@ -0,0 +1,20 @@
ul, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}
.ui-icon{background-image: url(images/icon.png);}
/* Test data URI images with different quote styles. */
.data .double-quote {
/* http://stackoverflow.com/a/13139830/11023 */
background-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
}
.data .single-quote {
background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');
}
.data .no-quote {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);
}

View file

@ -0,0 +1,5 @@
p, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}

View file

@ -0,0 +1 @@
var latin9Char = '¤';

View file

@ -0,0 +1 @@
var latin9Char = '€';

View file

@ -0,0 +1,2 @@
(function($) { "use strict"; })
//# sourceMappingURL=source_mapping_url.min.map

View file

@ -0,0 +1 @@
(function($) { "use strict"; })

View file

@ -0,0 +1,2 @@
(function($) { "use strict"; })
//@ sourceMappingURL=source_mapping_url.min.map

View file

@ -0,0 +1 @@
(function($) { "use strict"; })

View file

@ -0,0 +1,2 @@
(function($) { "use strict"; })
//# sourceURL=source_mapping_url.js

View file

@ -0,0 +1 @@
(function($) { "use strict"; })

View file

@ -0,0 +1,2 @@
(function($) { "use strict"; })
//@ sourceURL=source_mapping_url.js

View file

@ -0,0 +1 @@
(function($) { "use strict"; })

View file

@ -0,0 +1 @@
var utf8BOM = '☃';

View file

@ -0,0 +1 @@
var utf8BOM = '☃';

View file

@ -0,0 +1 @@
var utf8BOM = '☃';

View file

@ -0,0 +1,8 @@
example:
css:
base:
css/base.css: {}
js:
js/example.js: {}
drupalSettings:
key: value

View file

@ -0,0 +1,22 @@
example:
css:
theme:
css/theme__no_weight.css: {}
css/theme__weight.css:
weight: 29
base:
css/base__no_weight.css: {}
css/base__weight.css:
weight: 97
layout:
css/layout__no_weight.css: {}
css/layout__weight.css:
weight: 92
component:
css/component__no_weight.css: {}
css/component__weight.css:
weight: 45
state:
css/state__no_weight.css: {}
css/state__weight.css:
weight: 8

View file

@ -0,0 +1,13 @@
example:
css:
theme:
# External URL.
'http://example.com/test.css':
type: external
# Absolute path.
/tmp/test.css: {}
# Protocol free.
//cdn.com/test.css: {}
# Stream wrapper URI.
'public://test.css': {}
'example://test2.css': {}

View file

@ -0,0 +1,6 @@
example:
css:
css/example.js: {}
dependencies:
- external/example_external
- example_module/example

View file

@ -0,0 +1,5 @@
example:
version: VERSION
css:
theme:
css/example.css: {}

View file

@ -0,0 +1,2 @@
example:
version: VERSION

View file

@ -0,0 +1,4 @@
example:
css:
theme:
css/example.css: {}

View file

@ -0,0 +1,7 @@
example_external:
version: v3.14
js:
http://example.com/example_external.js: { type: external }
css:
theme:
http://example.com/css/example_external.css: { type: external }

View file

@ -0,0 +1,3 @@
example:
key1: value1
key2: value2

View file

@ -0,0 +1,4 @@
example:
js:
js/unminified.js: {}
js/minified.js: { minified: true }

View file

@ -0,0 +1,4 @@
example:
js:
js/positive_weight.js:
weight: 10

View file

@ -0,0 +1,72 @@
# No license information: should default to GPL 2.0 or later..
no-license-info:
version: 1.0
js:
no-license-info.js: {}
css:
base:
no-license-info.css: {}
# A library with all assets licensed under the GPL-compatible GPL 2 license.
gpl2:
version: 1.0
license:
name: gpl2
url: https://url-to-gpl2-license
gpl-compatible: true
js:
gpl2.js: {}
css:
base:
gpl2.css: {}
# A library with all assets licensed under the GPL-compatible MIT license.
mit:
version: 1.0
license:
name: MIT
url: https://url-to-mit-license
gpl-compatible: true
js:
mit.js: {}
css:
base:
mit.css: {}
# A library with all assets licensed under the GPL-compatible Public Domain.
public-domain:
version: 1.0
license:
name: Public Domain
url: https://url-to-public-domain-license
gpl-compatible: true
js:
public-domain.js: {}
css:
base:
public-domain.css: {}
# A library with all assets licensed under the GPL-incompatible Apache license.
apache:
version: 1.0
license:
name: apache
url: https://url-to-apache-license
gpl-compatible: false
js:
apache.js: {}
css:
base:
apache.css: {}
# A library with all assets licensed under the GPL-incompatible copyright.
copyright:
version: 1.0
license:
name: © Some company
gpl-compatible: false
js:
copyright.js: {}
css:
base:
copyright.css: {}

View file

@ -0,0 +1,9 @@
# No license information for a 3rd party library (it has the 'remote' key).
no-license-info-but-remote:
version: 1.0
remote: https://url-to-remote
js:
no-license-info-but-remote.js: {}
css:
base:
no-license-info-but-remote.css: {}

View file

@ -0,0 +1,32 @@
versionless:
css:
theme:
versionless.css: {}
js:
versionless.js: {}
versioned:
version: "9.8.7.6"
css:
theme:
versioned.css: {}
js:
versioned.js: {}
core-versioned:
version: VERSION
css:
theme:
core-versioned.css: {}
js:
core-versioned.js: {}
invalid-version:
version: 2014-12-13
js:
versioned.js: {}
valid-version:
version: "2014-12-13"
js:
versioned.js: {}

View file

@ -0,0 +1,88 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Authentication\AuthenticationManagerTest.
*/
namespace Drupal\Tests\Core\Authentication;
use Drupal\Core\Authentication\AuthenticationManager;
use Drupal\Core\Authentication\AuthenticationProviderFilterInterface;
use Drupal\Core\Authentication\AuthenticationProviderInterface;
use Drupal\Tests\UnitTestCase;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* @coversDefaultClass \Drupal\Core\Authentication\AuthenticationManager
* @group Authentication
*/
class AuthenticationManagerTest extends UnitTestCase {
/**
* @covers ::defaultFilter
* @covers ::applyFilter
*
* @dataProvider providerTestDefaultFilter
*/
public function testDefaultFilter($applies, $has_route, $auth_option, $provider_id, $global) {
$authentication_manager = new AuthenticationManager();
$auth_provider = $this->getMock('Drupal\Core\Authentication\AuthenticationProviderInterface');
$authentication_manager->addProvider($auth_provider, $provider_id, 0, $global);
$request = new Request();
if ($has_route) {
$route = new Route('/example');
if ($auth_option) {
$route->setOption('_auth', $auth_option);
}
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $route);
}
$this->assertSame($applies, $authentication_manager->appliesToRoutedRequest($request, FALSE));
}
/**
* @covers ::applyFilter
*/
public function testApplyFilterWithFilterprovider() {
$authentication_manager = new AuthenticationManager();
$auth_provider = $this->getMock('Drupal\Tests\Core\Authentication\TestAuthenticationProviderInterface');
$authentication_manager->addProvider($auth_provider, 'filtered', 0);
$auth_provider->expects($this->once())
->method('appliesToRoutedRequest')
->willReturn(TRUE);
$request = new Request();
$this->assertTrue($authentication_manager->appliesToRoutedRequest($request, FALSE));
}
/**
* Provides data to self::testDefaultFilter().
*/
public function providerTestDefaultFilter() {
$data = [];
// No route, cookie is global, should apply.
$data[] = [TRUE, FALSE, [], 'cookie', TRUE];
// No route, cookie is not global, should not apply.
$data[] = [FALSE, FALSE, [], 'cookie', FALSE];
// Route, no _auth, cookie is global, should apply.
$data[] = [TRUE, TRUE, [], 'cookie', TRUE];
// Route, no _auth, cookie is not global, should not apply.
$data[] = [FALSE, TRUE, [], 'cookie', FALSE];
// Route, with _auth and non-matching provider, should not apply.
$data[] = [FALSE, TRUE, ['basic_auth'], 'cookie', TRUE];
// Route, with _auth and matching provider should not apply.
$data[] = [TRUE, TRUE, ['basic_auth'], 'basic_auth', TRUE];
return $data;
}
}
/**
* Helper interface to mock two interfaces at once.
*/
interface TestAuthenticationProviderInterface extends AuthenticationProviderFilterInterface, AuthenticationProviderInterface {}

View file

@ -0,0 +1,88 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Batch\PercentagesTest.
*/
namespace Drupal\Tests\Core\Batch;
use Drupal\Core\Batch\Percentage;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Batch\Percentage
* @group Batch
*
* Tests the Batch helper object to make sure that the rounding works properly
* in all cases.
*/
class PercentagesTest extends UnitTestCase {
protected $testCases = array();
/**
* @dataProvider providerTestPercentages
* @covers ::format
*/
public function testPercentages($total, $current, $expected_result) {
$actual_result = Percentage::format($total, $current);
$this->assertEquals($actual_result, $expected_result, sprintf('The expected the batch api percentage at the state %s/%s is %s%% and got %s%%.', $current, $total, $expected_result, $actual_result));
}
/**
* Provide data for batch unit tests.
*
* @return array
* An array of data used by the test.
*/
public function providerTestPercentages() {
// Set up an array of test cases.
return array(
// array(total, current, expected).
// 1/2 is 50%.
array(2, 1, '50'),
// Though we should never encounter a case where the current set is set
// 0, if we did, we should get 0%.
array(3, 0, '0'),
// 1/3 is closer to 33% than to 34%.
array(3, 1, '33'),
// 2/3 is closer to 67% than to 66%.
array(3, 2, '67'),
// 1/199 should round up to 1%.
array(199, 1, '1'),
// 198/199 should round down to 99%.
array(199, 198, '99'),
// 199/200 would have rounded up to 100%, which would give the false
// impression of being finished, so we add another digit and should get
// 99.5%.
array(200, 199, '99.5'),
// The same logic holds for 1/200: we should get 0.5%.
array(200, 1, '0.5'),
// Numbers that come out evenly, such as 50/200, should be forced to have
// extra digits for consistency.
array(200, 50, '25.0'),
// Regardless of number of digits we're using, 100% should always just be
// 100%.
array(200, 200, '100'),
// 1998/1999 should similarly round down to 99.9%.
array(1999, 1998, '99.9'),
// 1999/2000 should add another digit and go to 99.95%.
array(2000, 1999, '99.95'),
// 19999/20000 should add yet another digit and go to 99.995%.
array(20000, 19999, '99.995'),
// The next five test cases simulate a batch with a single operation
// ('total' equals 1) that takes several steps to complete. Within the
// operation, we imagine that there are 501 items to process, and 100 are
// completed during each step. The percentages we get back should be
// rounded the usual way for the first few passes (i.e., 20%, 40%, etc.),
// but for the last pass through, when 500 out of 501 items have been
// processed, we do not want to round up to 100%, since that would
// erroneously indicate that the processing is complete.
array('total' => 1, 'current' => 100/501, '20'),
array('total' => 1, 'current' => 200/501, '40'),
array('total' => 1, 'current' => 300/501, '60'),
array('total' => 1, 'current' => 400/501, '80'),
array('total' => 1, 'current' => 500/501, '99.8'),
);
}
}

View file

@ -0,0 +1,50 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Block\BlockBaseTest.
*/
namespace Drupal\Tests\Core\Block;
use Drupal\block_test\Plugin\Block\TestBlockInstantiation;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Block\BlockBase
* @group block
*/
class BlockBaseTest extends UnitTestCase {
/**
* Tests the machine name suggestion.
*
* @see \Drupal\Core\Block\BlockBase::getMachineNameSuggestion().
*/
public function testGetMachineNameSuggestion() {
$module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
$transliteration = $this->getMockBuilder('Drupal\Core\Transliteration\PhpTransliteration')
->setConstructorArgs(array(NULL, $module_handler))
->setMethods(array('readLanguageOverrides'))
->getMock();
$config = array();
$definition = array(
'admin_label' => 'Admin label',
'provider' => 'block_test',
);
$block_base = new TestBlockInstantiation($config, 'test_block_instantiation', $definition);
$block_base->setTransliteration($transliteration);
$this->assertEquals('adminlabel', $block_base->getMachineNameSuggestion());
// Test with more unicodes.
$definition = array(
'admin_label' => 'über åwesome',
'provider' => 'block_test',
);
$block_base = new TestBlockInstantiation($config, 'test_block_instantiation', $definition);
$block_base->setTransliteration($transliteration);
$this->assertEquals('uberawesome', $block_base->getMachineNameSuggestion());
}
}

View file

@ -0,0 +1,158 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Breadcrumb\BreadcrumbManagerTest.
*/
namespace Drupal\Tests\Core\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbManager;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Breadcrumb\BreadcrumbManager
* @group Breadcrumb
*/
class BreadcrumbManagerTest extends UnitTestCase {
/**
* The tested breadcrumb manager.
*
* @var \Drupal\Core\Breadcrumb\BreadcrumbManager
*/
protected $breadcrumbManager;
/**
* The mocked module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $moduleHandler;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
$this->breadcrumbManager = new BreadcrumbManager($this->moduleHandler);
}
/**
* Tests the breadcrumb manager without any set breadcrumb.
*/
public function testBuildWithoutBuilder() {
$result = $this->breadcrumbManager->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals(array(), $result);
}
/**
* Tests the build method with a single breadcrumb builder.
*/
public function testBuildWithSingleBuilder() {
$builder = $this->getMock('Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface');
$breadcrumb = array('<a href="/example">Test</a>');
$builder->expects($this->once())
->method('applies')
->will($this->returnValue(TRUE));
$builder->expects($this->once())
->method('build')
->will($this->returnValue($breadcrumb));
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$this->moduleHandler->expects($this->once())
->method('alter')
->with('system_breadcrumb', $breadcrumb, $route_match, array('builder' => $builder));
$this->breadcrumbManager->addBuilder($builder, 0);
$result = $this->breadcrumbManager->build($route_match);
$this->assertEquals($breadcrumb, $result);
}
/**
* Tests multiple breadcrumb builder with different priority.
*/
public function testBuildWithMultipleApplyingBuilders() {
$builder1 = $this->getMock('Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface');
$builder1->expects($this->never())
->method('applies');
$builder1->expects($this->never())
->method('build');
$builder2 = $this->getMock('Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface');
$breadcrumb2 = array('<a href="/example2">Test2</a>');
$builder2->expects($this->once())
->method('applies')
->will($this->returnValue(TRUE));
$builder2->expects($this->once())
->method('build')
->will($this->returnValue($breadcrumb2));
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$this->moduleHandler->expects($this->once())
->method('alter')
->with('system_breadcrumb', $breadcrumb2, $route_match, array('builder' => $builder2));
$this->breadcrumbManager->addBuilder($builder1, 0);
$this->breadcrumbManager->addBuilder($builder2, 10);
$result = $this->breadcrumbManager->build($route_match);
$this->assertEquals($breadcrumb2, $result);
}
/**
* Tests multiple breadcrumb builders of which one returns NULL.
*/
public function testBuildWithOneNotApplyingBuilders() {
$builder1 = $this->getMock('Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface');
$builder1->expects($this->once())
->method('applies')
->will($this->returnValue(FALSE));
$builder1->expects($this->never())
->method('build');
$builder2 = $this->getMock('Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface');
$breadcrumb2 = array('<a href="/example2">Test2</a>');
$builder2->expects($this->once())
->method('applies')
->will($this->returnValue(TRUE));
$builder2->expects($this->once())
->method('build')
->will($this->returnValue($breadcrumb2));
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$this->moduleHandler->expects($this->once())
->method('alter')
->with('system_breadcrumb', $breadcrumb2, $route_match, array('builder' => $builder2));
$this->breadcrumbManager->addBuilder($builder1, 10);
$this->breadcrumbManager->addBuilder($builder2, 0);
$result = $this->breadcrumbManager->build($route_match);
$this->assertEquals($breadcrumb2, $result);
}
/**
* Tests a breadcrumb builder with a bad return value.
*
* @expectedException \UnexpectedValueException
*/
public function testBuildWithInvalidBreadcrumbResult() {
$builder = $this->getMock('Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface');
$builder->expects($this->once())
->method('applies')
->will($this->returnValue(TRUE));
$builder->expects($this->once())
->method('build')
->will($this->returnValue('invalid_result'));
$this->breadcrumbManager->addBuilder($builder, 0);
$this->breadcrumbManager->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
}
}

View file

@ -0,0 +1,305 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Cache\BackendChainImplementationUnitTest.
*/
namespace Drupal\Tests\Core\Cache;
use Drupal\Core\Cache\BackendChain;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\MemoryBackend;
use Drupal\Tests\UnitTestCase;
/**
* Unit test of backend chain implementation specifics.
*
* @group Cache
*/
class BackendChainImplementationUnitTest extends UnitTestCase {
/**
* Chain that will be heavily tested.
*
* @var \Drupal\Core\Cache\BackendChain
*/
protected $chain;
/**
* First backend in the chain.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $firstBackend;
/**
* Second backend in the chain.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $secondBackend;
/**
* Third backend in the chain.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $thirdBackend;
protected function setUp() {
parent::setUp();
// Set up three memory backends to be used in the chain.
$this->firstBackend = new MemoryBackend('foo');
$this->secondBackend = new MemoryBackend('bar');
$this->thirdBackend = new MemoryBackend('baz');
// Set an initial fixed dataset for all testing. The next three data
// collections will test two edge cases (last backend has the data, and
// first backend has the data) and will test a normal use case (middle
// backend has the data). We should have a complete unit test with those.
// Note that in all cases, when the same key is set on more than one
// backend, the values are voluntarily different, this ensures in which
// backend we actually fetched the key when doing get calls.
// Set a key present on all backends (for delete).
$this->firstBackend->set('t123', 1231);
$this->secondBackend->set('t123', 1232);
$this->thirdBackend->set('t123', 1233);
// Set a key present on the second and the third (for get), those two will
// be different, this will ensure from where we get the key.
$this->secondBackend->set('t23', 232);
$this->thirdBackend->set('t23', 233);
// Set a key on only the third, we will ensure propagation using this one.
$this->thirdBackend->set('t3', 33);
// Create the chain.
$this->chain = new BackendChain('foobarbaz');
$this->chain
->appendBackend($this->firstBackend)
->appendBackend($this->secondBackend)
->appendBackend($this->thirdBackend);
}
/**
* Test the get feature.
*/
public function testGet() {
$cached = $this->chain->get('t123');
$this->assertNotSame(FALSE, $cached, 'Got key that is on all backends');
$this->assertSame(1231, $cached->data, 'Got the key from the backend 1');
$cached = $this->chain->get('t23');
$this->assertNotSame(FALSE, $cached, 'Got key that is on backends 2 and 3');
$this->assertSame(232, $cached->data, 'Got the key from the backend 2');
$cached = $this->chain->get('t3');
$this->assertNotSame(FALSE, $cached, 'Got key that is on the backend 3');
$this->assertSame(33, $cached->data, 'Got the key from the backend 3');
}
/**
* Test the get multiple feature.
*/
public function testGetMultiple() {
$cids = array('t123', 't23', 't3', 't4');
$ret = $this->chain->getMultiple($cids);
$this->assertSame($ret['t123']->data, 1231, 'Got key 123 and value is from the first backend');
$this->assertSame($ret['t23']->data, 232, 'Got key 23 and value is from the second backend');
$this->assertSame($ret['t3']->data, 33, 'Got key 3 and value is from the third backend');
$this->assertFalse(array_key_exists('t4', $ret), "Didn't get the nonexistent key");
$this->assertFalse(in_array('t123', $cids), "Existing key 123 has been removed from &\$cids");
$this->assertFalse(in_array('t23', $cids), "Existing key 23 has been removed from &\$cids");
$this->assertFalse(in_array('t3', $cids), "Existing key 3 has been removed from &\$cids");
$this->assertTrue(in_array('t4', $cids), "Non existing key 4 is still in &\$cids");
}
/**
* Test that set will propagate.
*/
public function testSet() {
$this->chain->set('test', 123);
$cached = $this->firstBackend->get('test');
$this->assertNotSame(FALSE, $cached, 'Test key is in the first backend');
$this->assertSame(123, $cached->data, 'Test key has the right value');
$cached = $this->secondBackend->get('test');
$this->assertNotSame(FALSE, $cached, 'Test key is in the second backend');
$this->assertSame(123, $cached->data, 'Test key has the right value');
$cached = $this->thirdBackend->get('test');
$this->assertNotSame(FALSE, $cached, 'Test key is in the third backend');
$this->assertSame(123, $cached->data, 'Test key has the right value');
}
/**
* Test that delete will propagate.
*/
public function testDelete() {
$this->chain->set('test', 5);
$cached = $this->firstBackend->get('test');
$this->assertNotSame(FALSE, $cached, 'Test key has been added to the first backend');
$cached = $this->secondBackend->get('test');
$this->assertNotSame(FALSE, $cached, 'Test key has been added to the first backend');
$cached = $this->thirdBackend->get('test');
$this->assertNotSame(FALSE, $cached, 'Test key has been added to the first backend');
$this->chain->delete('test');
$cached = $this->firstBackend->get('test');
$this->assertSame(FALSE, $cached, 'Test key is removed from the first backend');
$cached = $this->secondBackend->get('test');
$this->assertSame(FALSE, $cached, 'Test key is removed from the second backend');
$cached = $this->thirdBackend->get('test');
$this->assertSame(FALSE, $cached, 'Test key is removed from the third backend');
}
/**
* Ensure get values propagation to previous backends.
*/
public function testGetHasPropagated() {
$this->chain->get('t23');
$cached = $this->firstBackend->get('t23');
$this->assertNotSame(FALSE, $cached, 'Test 2 has been propagated to the first backend');
$this->chain->get('t3');
$cached = $this->firstBackend->get('t3');
$this->assertNotSame(FALSE, $cached, 'Test 3 has been propagated to the first backend');
$cached = $this->secondBackend->get('t3');
$this->assertNotSame(FALSE, $cached, 'Test 3 has been propagated to the second backend');
}
/**
* Ensure get multiple values propagation to previous backends.
*/
public function testGetMultipleHasPropagated() {
$cids = array('t3', 't23');
$this->chain->getMultiple($cids);
$cached = $this->firstBackend->get('t3');
$this->assertNotSame(FALSE, $cached, 'Test 3 has been propagated to the first backend');
$this->assertSame(33, $cached->data, 'And value has been kept');
$cached = $this->secondBackend->get('t3');
$this->assertNotSame(FALSE, $cached, 'Test 3 has been propagated to the second backend');
$this->assertSame(33, $cached->data, 'And value has been kept');
$cached = $this->firstBackend->get('t23');
$this->assertNotSame(FALSE, $cached, 'Test 2 has been propagated to the first backend');
$this->assertSame(232, $cached->data, 'And value has been kept');
}
/**
* Test that the delete all operation is propagated to all backends in the chain.
*/
public function testDeleteAllPropagation() {
// Set both expiring and permanent keys.
$this->chain->set('test1', 1, Cache::PERMANENT);
$this->chain->set('test2', 3, time() + 1000);
$this->chain->deleteAll();
$this->assertFalse($this->firstBackend->get('test1'), 'First key has been deleted in first backend.');
$this->assertFalse($this->firstBackend->get('test2'), 'Second key has been deleted in first backend.');
$this->assertFalse($this->secondBackend->get('test1'), 'First key has been deleted in second backend.');
$this->assertFalse($this->secondBackend->get('test2'), 'Second key has been deleted in second backend.');
$this->assertFalse($this->thirdBackend->get('test1'), 'First key has been deleted in third backend.');
$this->assertFalse($this->thirdBackend->get('test2'), 'Second key has been deleted in third backend.');
}
/**
* Test that the delete tags operation is propagated to all backends
* in the chain.
*/
public function testDeleteTagsPropagation() {
// Create two cache entries with the same tag and tag value.
$this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag:2'));
$this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag:2'));
$this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1')
&& $this->firstBackend->get('test_cid_clear2')
&& $this->secondBackend->get('test_cid_clear1')
&& $this->secondBackend->get('test_cid_clear2')
&& $this->thirdBackend->get('test_cid_clear1')
&& $this->thirdBackend->get('test_cid_clear2'),
'Two cache items were created in all backends.');
// Invalidate test_tag of value 1. This should invalidate both entries.
$this->chain->invalidateTags(array('test_tag:2'));
$this->assertSame(FALSE, $this->firstBackend->get('test_cid_clear1')
&& $this->firstBackend->get('test_cid_clear2')
&& $this->secondBackend->get('test_cid_clear1')
&& $this->secondBackend->get('test_cid_clear2')
&& $this->thirdBackend->get('test_cid_clear1')
&& $this->thirdBackend->get('test_cid_clear2'),
'Two caches removed from all backends after clearing a cache tag.');
// Create two cache entries with the same tag and an array tag value.
$this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag:1'));
$this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag:1'));
$this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1')
&& $this->firstBackend->get('test_cid_clear2')
&& $this->secondBackend->get('test_cid_clear1')
&& $this->secondBackend->get('test_cid_clear2')
&& $this->thirdBackend->get('test_cid_clear1')
&& $this->thirdBackend->get('test_cid_clear2'),
'Two cache items were created in all backends.');
// Invalidate test_tag of value 1. This should invalidate both entries.
$this->chain->invalidateTags(array('test_tag:1'));
$this->assertSame(FALSE, $this->firstBackend->get('test_cid_clear1')
&& $this->firstBackend->get('test_cid_clear2')
&& $this->secondBackend->get('test_cid_clear1')
&& $this->secondBackend->get('test_cid_clear2')
&& $this->thirdBackend->get('test_cid_clear1')
&& $this->thirdBackend->get('test_cid_clear2'),
'Two caches removed from all backends after clearing a cache tag.');
// Create three cache entries with a mix of tags and tag values.
$this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag:1'));
$this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag:2'));
$this->chain->set('test_cid_clear3', 'foo', Cache::PERMANENT, array('test_tag_foo:3'));
$this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1')
&& $this->firstBackend->get('test_cid_clear2')
&& $this->firstBackend->get('test_cid_clear3')
&& $this->secondBackend->get('test_cid_clear1')
&& $this->secondBackend->get('test_cid_clear2')
&& $this->secondBackend->get('test_cid_clear3')
&& $this->thirdBackend->get('test_cid_clear1')
&& $this->thirdBackend->get('test_cid_clear2')
&& $this->thirdBackend->get('test_cid_clear3'),
'Three cached items were created in all backends.');
$this->chain->invalidateTags(array('test_tag_foo:3'));
$this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1')
&& $this->firstBackend->get('test_cid_clear2')
&& $this->secondBackend->get('test_cid_clear1')
&& $this->secondBackend->get('test_cid_clear2')
&& $this->thirdBackend->get('test_cid_clear1')
&& $this->thirdBackend->get('test_cid_clear2'),
'Cached items not matching the tag were not cleared from any of the backends.');
$this->assertSame(FALSE, $this->firstBackend->get('test_cid_clear3')
&& $this->secondBackend->get('test_cid_clear3')
&& $this->thirdBackend->get('test_cid_clear3'),
'Cached item matching the tag was removed from all backends.');
}
/**
* Test that removing bin propagates to all backends.
*/
public function testRemoveBin() {
$chain = new BackendChain('foo');
for ($i = 0; $i < 3; $i++) {
$backend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$backend->expects($this->once())->method('removeBin');
$chain->appendBackend($backend);
}
$chain->removeBin();
}
}

View file

@ -0,0 +1,72 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Cache\CacheCollectorHelper.
*/
namespace Drupal\Tests\Core\Cache;
use Drupal\Core\Cache\CacheCollector;
/**
* Helper class to test the cache collector.
*/
class CacheCollectorHelper extends CacheCollector {
/**
* Contains data to return on a cache miss.
* @var array
*/
protected $cacheMissData = array();
/**
* Number of calls to \Drupal\Core\Cache\CacheCollector::resolveCacheMiss().
*
* @var int
*/
protected $cacheMisses = 0;
/**
* {@inheritdoc}
*/
public function set($key, $value) {
parent::set($key, $value);
$this->persist($key);
}
/**
* {@inheritdoc}
*/
public function resolveCacheMiss($key) {
$this->cacheMisses++;
if (isset($this->cacheMissData[$key])) {
$this->storage[$key] = $this->cacheMissData[$key];
$this->persist($key);
return $this->cacheMissData[$key];
}
}
/**
* Sets data to return from a cache miss resolve.
*
* @param string $key
* The key being looked for.
* @param mixed $value
* The value to return.
*/
public function setCacheMissData($key, $value) {
$this->cacheMissData[$key] = $value;
}
/**
* Returns the number of cache misses.
*
* @return int
* Number of calls to the resolve cache miss method.
*/
public function getCacheMisses() {
return $this->cacheMisses;
}
}

View file

@ -0,0 +1,422 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Cache\CacheCollectorTest.
*/
namespace Drupal\Tests\Core\Cache;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Cache\CacheCollector
* @group Cache
*/
class CacheCollectorTest extends UnitTestCase {
/**
* The cache backend that should be used.
*
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cacheBackend;
/**
* The cache tags invalidator.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cacheTagsInvalidator;
/**
* The lock backend that should be used.
*
* @var \PHPUnit_Framework_MockObject_MockObject
*/
protected $lock;
/**
* The cache id used for the test.
*
* @var string
*/
protected $cid;
/**
* Cache collector implementation to test.
*
* @var \Drupal\Tests\Core\Cache\CacheCollectorHelper
*/
protected $collector;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
$this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface');
$this->cid = $this->randomMachineName();
$this->collector = new CacheCollectorHelper($this->cid, $this->cacheBackend, $this->lock);
$this->getContainerWithCacheTagsInvalidator($this->cacheTagsInvalidator);
}
/**
* Tests the resolve cache miss function.
*/
public function testResolveCacheMiss() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
$this->collector->setCacheMissData($key, $value);
$this->assertEquals($value, $this->collector->get($key));
}
/**
* Tests setting and getting values when the cache is empty.
*/
public function testSetAndGet() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
$this->assertNull($this->collector->get($key));
$this->collector->set($key, $value);
$this->assertTrue($this->collector->has($key));
$this->assertEquals($value, $this->collector->get($key));
}
/**
* Makes sure that NULL is a valid value and is collected.
*/
public function testSetAndGetNull() {
$key = $this->randomMachineName();
$value = NULL;
$this->cacheBackend->expects($this->once())
->method('invalidate')
->with($this->cid);
$this->collector->set($key, $value);
$this->assertTrue($this->collector->has($key));
$this->assertEquals($value, $this->collector->get($key));
// Ensure that getting a value that isn't set does not mark it as
// existent.
$non_existing_key = $this->randomMachineName(7);
$this->collector->get($non_existing_key);
$this->assertFalse($this->collector->has($non_existing_key));
}
/**
* Tests returning value from the collected cache.
*/
public function testGetFromCache() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
$cache = (object) array(
'data' => array($key => $value),
'created' => (int) $_SERVER['REQUEST_TIME'],
);
$this->cacheBackend->expects($this->once())
->method('get')
->with($this->cid)
->will($this->returnValue($cache));
$this->assertTrue($this->collector->has($key));
$this->assertEquals($value, $this->collector->get($key));
$this->assertEquals(0, $this->collector->getCacheMisses());
}
/**
* Tests setting and deleting values.
*/
public function testDelete() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
$this->assertNull($this->collector->get($key));
$this->collector->set($key, $value);
$this->assertTrue($this->collector->has($key));
$this->assertEquals($value, $this->collector->get($key));
$this->cacheBackend->expects($this->once())
->method('invalidate')
->with($this->cid);
$this->collector->delete($key);
$this->assertFalse($this->collector->has($key));
$this->assertEquals(NULL, $this->collector->get($key));
}
/**
* Tests updating the cache when no changes were made.
*/
public function testUpdateCacheNoChanges() {
$this->lock->expects($this->never())
->method('acquire');
$this->cacheBackend->expects($this->never())
->method('set');
// Destruct the object to trigger the update data process.
$this->collector->destruct();
}
/**
* Tests updating the cache after a set.
*/
public function testUpdateCache() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
$this->collector->setCacheMissData($key, $value);
$this->collector->get($key);
// Set up mock objects for the expected calls, first a lock acquire, then
// cache get to look for conflicting cache entries, then a cache set and
// finally the lock is released again.
$this->lock->expects($this->once())
->method('acquire')
->with($this->cid . ':Drupal\Core\Cache\CacheCollector')
->will($this->returnValue(TRUE));
$this->cacheBackend->expects($this->once())
->method('get')
->with($this->cid, FALSE);
$this->cacheBackend->expects($this->once())
->method('set')
->with($this->cid, array($key => $value), Cache::PERMANENT, array());
$this->lock->expects($this->once())
->method('release')
->with($this->cid . ':Drupal\Core\Cache\CacheCollector');
// Destruct the object to trigger the update data process.
$this->collector->destruct();
}
/**
* Tests updating the cache when the lock acquire fails.
*/
public function testUpdateCacheLockFail() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
$this->collector->setCacheMissData($key, $value);
$this->collector->get($key);
// The lock acquire returns false, so the method should abort.
$this->lock->expects($this->once())
->method('acquire')
->with($this->cid . ':Drupal\Core\Cache\CacheCollector')
->will($this->returnValue(FALSE));
$this->cacheBackend->expects($this->never())
->method('set');
// Destruct the object to trigger the update data process.
$this->collector->destruct();
}
/**
* Tests updating the cache when there is a conflict after cache invalidation.
*/
public function testUpdateCacheInvalidatedConflict() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
$cache = (object) array(
'data' => array($key => $value),
'created' => (int) $_SERVER['REQUEST_TIME'],
);
$this->cacheBackend->expects($this->at(0))
->method('get')
->with($this->cid)
->will($this->returnValue($cache));
$this->cacheBackend->expects($this->at(1))
->method('invalidate')
->with($this->cid);
$this->collector->set($key, 'new value');
// Set up mock objects for the expected calls, first a lock acquire, then
// cache get to look for conflicting cache entries, which does find
// and then it deletes the cache and aborts.
$this->lock->expects($this->once())
->method('acquire')
->with($this->cid . ':Drupal\Core\Cache\CacheCollector')
->will($this->returnValue(TRUE));
$cache = (object) array(
'data' => array($key => $value),
'created' => (int) $_SERVER['REQUEST_TIME'] + 1,
);
$this->cacheBackend->expects($this->at(0))
->method('get')
->with($this->cid)
->will($this->returnValue($cache));
$this->cacheBackend->expects($this->once())
->method('delete')
->with($this->cid);
$this->lock->expects($this->once())
->method('release')
->with($this->cid . ':Drupal\Core\Cache\CacheCollector');
// Destruct the object to trigger the update data process.
$this->collector->destruct();
}
/**
* Tests updating the cache when a different request
*/
public function testUpdateCacheMerge() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
$this->collector->setCacheMissData($key, $value);
$this->collector->get($key);
// Set up mock objects for the expected calls, first a lock acquire, then
// cache get to look for existing cache entries, which does find
// and then it merges them.
$this->lock->expects($this->once())
->method('acquire')
->with($this->cid . ':Drupal\Core\Cache\CacheCollector')
->will($this->returnValue(TRUE));
$cache = (object) array(
'data' => array('other key' => 'other value'),
'created' => (int) $_SERVER['REQUEST_TIME'] + 1,
);
$this->cacheBackend->expects($this->at(0))
->method('get')
->with($this->cid)
->will($this->returnValue($cache));
$this->cacheBackend->expects($this->once())
->method('set')
->with($this->cid, array('other key' => 'other value', $key => $value), Cache::PERMANENT, array());
$this->lock->expects($this->once())
->method('release')
->with($this->cid . ':Drupal\Core\Cache\CacheCollector');
// Destruct the object to trigger the update data process.
$this->collector->destruct();
}
/**
* Tests updating the cache after a delete.
*/
public function testUpdateCacheDelete() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
$cache = (object) array(
'data' => array($key => $value),
'created' => (int) $_SERVER['REQUEST_TIME'],
);
$this->cacheBackend->expects($this->at(0))
->method('get')
->with($this->cid)
->will($this->returnValue($cache));
$this->collector->delete($key);
// Set up mock objects for the expected calls, first a lock acquire, then
// cache get to look for conflicting cache entries, then a cache set and
// finally the lock is released again.
$this->lock->expects($this->once())
->method('acquire')
->with($this->cid . ':Drupal\Core\Cache\CacheCollector')
->will($this->returnValue(TRUE));
// The second argument is set to TRUE because we triggered a cache
// invalidation.
$this->cacheBackend->expects($this->at(0))
->method('get')
->with($this->cid, TRUE);
$this->cacheBackend->expects($this->once())
->method('set')
->with($this->cid, array(), Cache::PERMANENT, array());
$this->lock->expects($this->once())
->method('release')
->with($this->cid . ':Drupal\Core\Cache\CacheCollector');
// Destruct the object to trigger the update data process.
$this->collector->destruct();
}
/**
* Tests a reset of the cache collector.
*/
public function testUpdateCacheReset() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
// Set the data and request it.
$this->collector->setCacheMissData($key, $value);
$this->assertEquals($value, $this->collector->get($key));
$this->assertEquals($value, $this->collector->get($key));
// Should have been added to the storage and only be requested once.
$this->assertEquals(1, $this->collector->getCacheMisses());
// Reset the collected cache, should call it again.
$this->collector->reset();
$this->assertEquals($value, $this->collector->get($key));
$this->assertEquals(2, $this->collector->getCacheMisses());
}
/**
* Tests a clear of the cache collector.
*/
public function testUpdateCacheClear() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
// Set the data and request it.
$this->collector->setCacheMissData($key, $value);
$this->assertEquals($value, $this->collector->get($key));
$this->assertEquals($value, $this->collector->get($key));
// Should have been added to the storage and only be requested once.
$this->assertEquals(1, $this->collector->getCacheMisses());
// Clear the collected cache, should call it again.
$this->cacheBackend->expects($this->once())
->method('delete')
->with($this->cid);
$this->cacheTagsInvalidator->expects($this->never())
->method('invalidateTags');
$this->collector->clear();
$this->assertEquals($value, $this->collector->get($key));
$this->assertEquals(2, $this->collector->getCacheMisses());
}
/**
* Tests a clear of the cache collector using tags.
*/
public function testUpdateCacheClearTags() {
$key = $this->randomMachineName();
$value = $this->randomMachineName();
$tags = array($this->randomMachineName());
$this->collector = new CacheCollectorHelper($this->cid, $this->cacheBackend, $this->lock, $tags);
// Set the data and request it.
$this->collector->setCacheMissData($key, $value);
$this->assertEquals($value, $this->collector->get($key));
$this->assertEquals($value, $this->collector->get($key));
// Should have been added to the storage and only be requested once.
$this->assertEquals(1, $this->collector->getCacheMisses());
// Clear the collected cache using the tags, should call it again.
$this->cacheBackend->expects($this->never())
->method('delete');
$this->cacheTagsInvalidator->expects($this->once())
->method('invalidateTags')
->with($tags);
$this->collector->clear();
$this->assertEquals($value, $this->collector->get($key));
$this->assertEquals(2, $this->collector->getCacheMisses());
}
}

View file

@ -0,0 +1,109 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Cache\CacheFactoryTest.
*/
namespace Drupal\Tests\Core\Cache;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Cache\CacheFactory;
use Drupal\Core\Site\Settings;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Cache\CacheFactory
* @group Cache
*/
class CacheFactoryTest extends UnitTestCase {
/**
* Test that the cache factory falls back to the built-in default service.
*
* @covers ::__construct
* @covers ::get
*/
public function testCacheFactoryWithDefaultSettings() {
$settings = new Settings(array());
$cache_factory = new CacheFactory($settings);
$container = new ContainerBuilder();
$cache_factory->setContainer($container);
$builtin_default_backend_factory = $this->getMock('\Drupal\Core\Cache\CacheFactoryInterface');
$container->set('cache.backend.database', $builtin_default_backend_factory);
$render_bin = $this->getMock('\Drupal\Core\Cache\CacheBackendInterface');
$builtin_default_backend_factory->expects($this->once())
->method('get')
->with('render')
->will($this->returnValue($render_bin));
$actual_bin = $cache_factory->get('render');
$this->assertSame($render_bin, $actual_bin);
}
/**
* Test that the cache factory falls back to customized default service.
*
* @covers ::__construct
* @covers ::get
*/
public function testCacheFactoryWithCustomizedDefaultBackend() {
$settings = new Settings(array(
'cache' => array(
'default' => 'cache.backend.custom',
),
));
$cache_factory = new CacheFactory($settings);
$container = new ContainerBuilder();
$cache_factory->setContainer($container);
$custom_default_backend_factory = $this->getMock('\Drupal\Core\Cache\CacheFactoryInterface');
$container->set('cache.backend.custom', $custom_default_backend_factory);
$render_bin = $this->getMock('\Drupal\Core\Cache\CacheBackendInterface');
$custom_default_backend_factory->expects($this->once())
->method('get')
->with('render')
->will($this->returnValue($render_bin));
$actual_bin = $cache_factory->get('render');
$this->assertSame($render_bin, $actual_bin);
}
/**
* Test that the cache factory picks the correct per-bin service.
*
* @covers ::__construct
* @covers ::get
*/
public function testCacheFactoryWithSpecifiedPerBinBackend() {
$settings = new Settings(array(
'cache' => array(
'bins' => array(
'render' => 'cache.backend.custom',
),
),
));
$cache_factory = new CacheFactory($settings);
$container = new ContainerBuilder();
$cache_factory->setContainer($container);
$custom_render_backend_factory = $this->getMock('\Drupal\Core\Cache\CacheFactoryInterface');
$container->set('cache.backend.custom', $custom_render_backend_factory);
$render_bin = $this->getMock('\Drupal\Core\Cache\CacheBackendInterface');
$custom_render_backend_factory->expects($this->once())
->method('get')
->with('render')
->will($this->returnValue($render_bin));
$actual_bin = $cache_factory->get('render');
$this->assertSame($render_bin, $actual_bin);
}
}

View file

@ -0,0 +1,67 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Cache\CacheTagsInvalidatorTest.
*/
namespace Drupal\Tests\Core\Cache;
use Drupal\Core\Cache\CacheTagsInvalidator;
use Drupal\Core\DependencyInjection\Container;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Cache\CacheTagsInvalidator
* @group Cache
*/
class CacheTagsInvalidatorTest extends UnitTestCase {
/**
* @covers ::invalidateTags
*
* @expectedException \LogicException
* @expectedExceptionMessage Cache tags must be strings, array given.
*/
public function testInvalidateTagsWithInvalidTags() {
$cache_tags_invalidator = new CacheTagsInvalidator();
$cache_tags_invalidator->invalidateTags(['node' => [2, 3, 5, 8, 13]]);
}
/**
* @covers ::invalidateTags
* @covers ::addInvalidator
*/
public function testInvalidateTags() {
$cache_tags_invalidator = new CacheTagsInvalidator();
// This does not actually implement,
// \Drupal\Cache\Cache\CacheBackendInterface but we can not mock from two
// interfaces, we would need a test class for that.
$invalidator_cache_bin = $this->getMock('\Drupal\Core\Cache\CacheTagsInvalidator');
$invalidator_cache_bin->expects($this->once())
->method('invalidateTags')
->with(array('node:1'));
// We do not have to define that invalidateTags() is never called as the
// interface does not define that method, trying to call it would result in
// a fatal error.
$non_invalidator_cache_bin = $this->getMock('\Drupal\Core\Cache\CacheBackendInterface');
$container = new Container();
$container->set('cache.invalidator_cache_bin', $invalidator_cache_bin);
$container->set('cache.non_invalidator_cache_bin', $non_invalidator_cache_bin);
$container->setParameter('cache_bins', array('cache.invalidator_cache_bin' => 'invalidator_cache_bin', 'cache.non_invalidator_cache_bin' => 'non_invalidator_cache_bin'));
$cache_tags_invalidator->setContainer($container);
$invalidator = $this->getMock('\Drupal\Core\Cache\CacheTagsInvalidator');
$invalidator->expects($this->once())
->method('invalidateTags')
->with(array('node:1'));
$cache_tags_invalidator->addInvalidator($invalidator);
$cache_tags_invalidator->invalidateTags(array('node:1'));
}
}

View file

@ -0,0 +1,156 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Cache\CacheTest.
*/
namespace Drupal\Tests\Core\Cache;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @coversDefaultClass \Drupal\Core\Cache\Cache
* @group Cache
*/
class CacheTest extends UnitTestCase {
/**
* Provides a list of cache tags arrays.
*
* @return array
*/
public function validateTagsProvider() {
return [
[[], FALSE],
[['foo'], FALSE],
[['foo', 'bar'], FALSE],
[['foo', 'bar', 'llama:2001988', 'baz', 'llama:14031991'], FALSE],
// Invalid.
[[FALSE], 'Cache tags must be strings, boolean given.'],
[[TRUE], 'Cache tags must be strings, boolean given.'],
[['foo', FALSE], 'Cache tags must be strings, boolean given.'],
[[NULL], 'Cache tags must be strings, NULL given.'],
[['foo', NULL], 'Cache tags must be strings, NULL given.'],
[[1337], 'Cache tags must be strings, integer given.'],
[['foo', 1337], 'Cache tags must be strings, integer given.'],
[[3.14], 'Cache tags must be strings, double given.'],
[['foo', 3.14], 'Cache tags must be strings, double given.'],
[[[]], 'Cache tags must be strings, array given.'],
[['foo', []], 'Cache tags must be strings, array given.'],
[['foo', ['bar']], 'Cache tags must be strings, array given.'],
[[new \stdClass()], 'Cache tags must be strings, object given.'],
[['foo', new \stdClass()], 'Cache tags must be strings, object given.'],
];
}
/**
* @covers ::validateTags
*
* @dataProvider validateTagsProvider
*/
public function testValidateTags(array $tags, $expected_exception_message) {
if ($expected_exception_message !== FALSE) {
$this->setExpectedException('LogicException', $expected_exception_message);
}
// If it doesn't throw an exception, validateTags() returns NULL.
$this->assertNull(Cache::validateTags($tags));
}
/**
* Provides a list of pairs of cache tags arrays to be merged.
*
* @return array
*/
public function mergeTagsProvider() {
return [
[[], [], []],
[['bar'], ['foo'], ['bar', 'foo']],
[['foo'], ['bar'], ['bar', 'foo']],
[['foo'], ['bar', 'foo'], ['bar', 'foo']],
[['foo'], ['foo', 'bar'], ['bar', 'foo']],
[['bar', 'foo'], ['foo', 'bar'], ['bar', 'foo']],
[['foo', 'bar'], ['foo', 'bar'], ['bar', 'foo']],
];
}
/**
* @covers ::mergeTags
*
* @dataProvider mergeTagsProvider
*/
public function testMergeTags(array $a, array $b, array $expected) {
$this->assertEquals($expected, Cache::mergeTags($a, $b));
}
/**
* Provides a list of pairs of cache tags arrays to be merged.
*
* @return array
*/
public function mergeMaxAgesProvider() {
return [
[Cache::PERMANENT, Cache::PERMANENT, Cache::PERMANENT],
[60, 60, 60],
[0, 0, 0],
[60, 0, 0],
[0, 60, 0],
[Cache::PERMANENT, 0, 0],
[0, Cache::PERMANENT, 0],
[Cache::PERMANENT, 60, 60],
[60, Cache::PERMANENT, 60],
];
}
/**
* @covers ::mergeMaxAges
*
* @dataProvider mergeMaxAgesProvider
*/
public function testMergeMaxAges($a, $b, $expected) {
$this->assertSame($expected, Cache::mergeMaxAges($a, $b));
}
/**
* Provides a list of pairs of (prefix, suffixes) to build cache tags from.
*
* @return array
*/
public function buildTagsProvider() {
return [
['node', [1], ['node:1']],
['node', [1, 2, 3], ['node:1', 'node:2', 'node:3']],
['node', [3, 2, 1], ['node:3', 'node:2', 'node:1']],
['node', [NULL], ['node:']],
['node', [TRUE, FALSE], ['node:1', 'node:']],
['node', ['a', 'z', 'b'], ['node:a', 'node:z', 'node:b']],
// No suffixes, no cache tags.
['', [], []],
['node', [], []],
// Only suffix values matter, not keys: verify that expectation.
['node', [5 => 145, 4545 => 3], ['node:145', 'node:3']],
['node', [5 => TRUE], ['node:1']],
['node', [5 => NULL], ['node:']],
['node', ['a' => NULL], ['node:']],
['node', ['a' => TRUE], ['node:1']],
// Test the $glue parameter.
['config:system.menu', ['menu_name'], ['config:system.menu.menu_name'], '.'],
];
}
/**
* @covers ::buildTags
*
* @dataProvider buildTagsProvider
*/
public function testBuildTags($prefix, array $suffixes, array $expected, $glue = ':') {
$this->assertEquals($expected, Cache::buildTags($prefix, $suffixes, $glue));
}
}

View file

@ -0,0 +1,187 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Cache\CacheableMetadataTest.
*/
namespace Drupal\Tests\Core\Cache;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Tests\Core\Render\TestCacheableDependency;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Render\Element;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @coversDefaultClass \Drupal\Core\Cache\CacheableMetadata
* @group Cache
*/
class CacheableMetadataTest extends UnitTestCase {
/**
* @covers ::merge
* @dataProvider providerTestMerge
*
* This only tests at a high level, because it reuses existing logic. Detailed
* tests exist for the existing logic:
*
* @see \Drupal\Tests\Core\Cache\CacheTest::testMergeTags()
* @see \Drupal\Tests\Core\Cache\CacheTest::testMergeMaxAges()
* @see \Drupal\Tests\Core\Cache\CacheContextsTest
*/
public function testMerge(CacheableMetadata $a, CacheableMetadata $b, CacheableMetadata $expected) {
$cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
->disableOriginalConstructor()
->getMock();
$container = new ContainerBuilder();
$container->set('cache_contexts_manager', $cache_contexts_manager);
\Drupal::setContainer($container);
$this->assertEquals($expected, $a->merge($b));
}
/**
* Provides test data for testMerge().
*
* @return array
*/
public function providerTestMerge() {
return [
// All empty.
[(new CacheableMetadata()), (new CacheableMetadata()), (new CacheableMetadata())],
// Cache contexts.
[(new CacheableMetadata())->setCacheContexts(['foo']), (new CacheableMetadata())->setCacheContexts(['bar']), (new CacheableMetadata())->setCacheContexts(['bar', 'foo'])],
// Cache tags.
[(new CacheableMetadata())->setCacheTags(['foo']), (new CacheableMetadata())->setCacheTags(['bar']), (new CacheableMetadata())->setCacheTags(['bar', 'foo'])],
// Cache max-ages.
[(new CacheableMetadata())->setCacheMaxAge(60), (new CacheableMetadata())->setCacheMaxAge(Cache::PERMANENT), (new CacheableMetadata())->setCacheMaxAge(60)],
];
}
/**
* This delegates to Cache::mergeTags(), so just a basic test.
*
* @covers ::addCacheTags
*/
public function testAddCacheTags() {
$metadata = new CacheableMetadata();
$add_expected = [
[ [], [] ],
[ ['foo:bar'], ['foo:bar'] ],
[ ['foo:baz'], ['foo:bar', 'foo:baz'] ],
[ ['axx:first', 'foo:baz'], ['axx:first', 'foo:bar', 'foo:baz'] ],
[ [], ['axx:first', 'foo:bar', 'foo:baz'] ],
[ ['axx:first'], ['axx:first', 'foo:bar', 'foo:baz'] ],
];
foreach ($add_expected as $data) {
list($add, $expected) = $data;
$metadata->addCacheTags($add);
$this->assertEquals($expected, $metadata->getCacheTags());
}
}
/**
* Test valid and invalid values as max age.
*
* @covers ::setCacheMaxAge
* @dataProvider providerSetCacheMaxAge
*/
public function testSetCacheMaxAge($data, $expect_exception) {
$metadata = new CacheableMetadata();
if ($expect_exception) {
$this->setExpectedException('\InvalidArgumentException');
}
$metadata->setCacheMaxAge($data);
$this->assertEquals($data, $metadata->getCacheMaxAge());
}
/**
* Data provider for testSetCacheMaxAge.
*/
public function providerSetCacheMaxAge() {
return [
[0 , FALSE],
['http', TRUE],
['0', TRUE],
[new \stdClass(), TRUE],
[300, FALSE],
[[], TRUE],
[8.0, TRUE]
];
}
/**
* @covers ::createFromRenderArray
* @dataProvider providerTestCreateFromRenderArray
*/
public function testCreateFromRenderArray(array $render_array, CacheableMetadata $expected) {
$this->assertEquals($expected, CacheableMetadata::createFromRenderArray($render_array));
}
/**
* Provides test data for createFromRenderArray().
*
* @return array
*/
public function providerTestCreateFromRenderArray() {
$data = [];
$empty_metadata = new CacheableMetadata();
$nonempty_metadata = new CacheableMetadata();
$nonempty_metadata->setCacheContexts(['qux'])
->setCacheTags(['foo:bar']);
$empty_render_array = [];
$nonempty_render_array = [
'#cache' => [
'contexts' => ['qux'],
'tags' => ['foo:bar'],
'max-age' => Cache::PERMANENT,
],
];
$data[] = [$empty_render_array, $empty_metadata];
$data[] = [$nonempty_render_array, $nonempty_metadata];
return $data;
}
/**
* @covers ::createFromObject
* @dataProvider providerTestCreateFromObject
*/
public function testCreateFromObject($object, CacheableMetadata $expected) {
$this->assertEquals($expected, CacheableMetadata::createFromObject($object));
}
/**
* Provides test data for createFromObject().
*
* @return array
*/
public function providerTestCreateFromObject() {
$data = [];
$empty_metadata = new CacheableMetadata();
$nonempty_metadata = new CacheableMetadata();
$nonempty_metadata->setCacheContexts(['qux'])
->setCacheTags(['foo:bar'])
->setCacheMaxAge(600);
$uncacheable_metadata = new CacheableMetadata();
$uncacheable_metadata->setCacheMaxAge(0);
$empty_cacheable_object = new TestCacheableDependency([], [], Cache::PERMANENT);
$nonempty_cacheable_object = new TestCacheableDependency(['qux'], ['foo:bar'], 600);
$uncacheable_object = new \stdClass();
$data[] = [$empty_cacheable_object, $empty_metadata];
$data[] = [$nonempty_cacheable_object, $nonempty_metadata];
$data[] = [$uncacheable_object, $uncacheable_metadata];
return $data;
}
}

View file

@ -0,0 +1,116 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Cache\ChainedFastBackendTest.
*/
namespace Drupal\Tests\Core\Cache;
use Drupal\Core\Cache\ChainedFastBackend;
use Drupal\Core\Cache\MemoryBackend;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Cache\ChainedFastBackend
* @group Cache
*/
class ChainedFastBackendTest extends UnitTestCase {
/**
* The consistent cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $consistentCache;
/**
* The fast cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $fastCache;
/**
* The cache bin.
*
* @var string
*/
protected $bin;
/**
* Tests a get() on the fast backend, with no hit on the consistent backend.
*/
public function testGetDoesntHitConsistentBackend() {
$consistent_cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$timestamp_cid = ChainedFastBackend::LAST_WRITE_TIMESTAMP_PREFIX . 'cache_foo';
// Use the request time because that is what we will be comparing against.
$timestamp_item = (object) array('cid' => $timestamp_cid, 'data' => (int) $_SERVER['REQUEST_TIME'] - 60);
$consistent_cache->expects($this->once())
->method('get')->with($timestamp_cid)
->will($this->returnValue($timestamp_item));
$consistent_cache->expects($this->never())
->method('getMultiple');
$fast_cache = new MemoryBackend('foo');
$fast_cache->set('foo', 'baz');
$chained_fast_backend = new ChainedFastBackend(
$consistent_cache,
$fast_cache,
'foo'
);
$this->assertEquals('baz', $chained_fast_backend->get('foo')->data);
}
/**
* Tests a fast cache miss gets data from the consistent cache backend.
*/
public function testFallThroughToConsistentCache() {
$timestamp_item = (object) array(
'cid' => ChainedFastBackend::LAST_WRITE_TIMESTAMP_PREFIX . 'cache_foo',
'data' => time() + 60, // Time travel is easy.
);
$cache_item = (object) array(
'cid' => 'foo',
'data' => 'baz',
'created' => time(),
'expire' => time() + 3600,
'tags' => ['tag'],
);
$consistent_cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$fast_cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
// We should get a call for the timestamp on the consistent backend.
$consistent_cache->expects($this->once())
->method('get')
->with($timestamp_item->cid)
->will($this->returnValue($timestamp_item));
// We should get a call for the cache item on the consistent backend.
$consistent_cache->expects($this->once())
->method('getMultiple')
->with(array($cache_item->cid))
->will($this->returnValue(array($cache_item->cid => $cache_item)));
// We should get a call for the cache item on the fast backend.
$fast_cache->expects($this->once())
->method('getMultiple')
->with(array($cache_item->cid))
->will($this->returnValue(array($cache_item->cid => $cache_item)));
// We should get a call to set the cache item on the fast backend.
$fast_cache->expects($this->once())
->method('set')
->with($cache_item->cid, $cache_item->data, $cache_item->expire);
$chained_fast_backend = new ChainedFastBackend(
$consistent_cache,
$fast_cache,
'foo'
);
$this->assertEquals('baz', $chained_fast_backend->get('foo')->data);
}
}

View file

@ -0,0 +1,261 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Cache\Context\CacheContextsManagerTest.
*/
namespace Drupal\Tests\Core\Cache\Context;
use Drupal\Core\Cache\Context\CacheContextsManager;
use Drupal\Core\Cache\Context\CacheContextInterface;
use Drupal\Core\Cache\Context\CalculatedCacheContextInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\Container;
/**
* @coversDefaultClass \Drupal\Core\Cache\Context\CacheContextsManager
* @group Cache
*/
class CacheContextsManagerTest extends UnitTestCase {
/**
* @covers ::optimizeTokens
*
* @dataProvider providerTestOptimizeTokens
*/
public function testOptimizeTokens(array $context_tokens, array $optimized_context_tokens) {
$container = $this->getMockBuilder('Drupal\Core\DependencyInjection\Container')
->disableOriginalConstructor()
->getMock();
$container->expects($this->any())
->method('get')
->will($this->returnValueMap([
['a', Container::EXCEPTION_ON_INVALID_REFERENCE, new FooCacheContext()],
['a.b', Container::EXCEPTION_ON_INVALID_REFERENCE, new FooCacheContext()],
['a.b.c', Container::EXCEPTION_ON_INVALID_REFERENCE, new BazCacheContext()],
['x', Container::EXCEPTION_ON_INVALID_REFERENCE, new BazCacheContext()],
]));
$cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
$this->assertSame($optimized_context_tokens, $cache_contexts_manager->optimizeTokens($context_tokens));
}
/**
* Provides a list of context token sets.
*/
public function providerTestOptimizeTokens() {
return [
[['a', 'x'], ['a', 'x']],
[['a.b', 'x'], ['a.b', 'x']],
// Direct ancestor, single-level hierarchy.
[['a', 'a.b'], ['a']],
[['a.b', 'a'], ['a']],
// Direct ancestor, multi-level hierarchy.
[['a.b', 'a.b.c'], ['a.b']],
[['a.b.c', 'a.b'], ['a.b']],
// Indirect ancestor.
[['a', 'a.b.c'], ['a']],
[['a.b.c', 'a'], ['a']],
// Direct & indirect ancestors.
[['a', 'a.b', 'a.b.c'], ['a']],
[['a', 'a.b.c', 'a.b'], ['a']],
[['a.b', 'a', 'a.b.c'], ['a']],
[['a.b', 'a.b.c', 'a'], ['a']],
[['a.b.c', 'a.b', 'a'], ['a']],
[['a.b.c', 'a', 'a.b'], ['a']],
// Using parameters.
[['a', 'a.b.c:foo'], ['a']],
[['a.b.c:foo', 'a'], ['a']],
[['a.b.c:foo', 'a.b.c'], ['a.b.c']],
];
}
/**
* @covers ::convertTokensToKeys
*/
public function testConvertTokensToKeys() {
$container = $this->getMockContainer();
$cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
$new_keys = $cache_contexts_manager->convertTokensToKeys([
'foo',
'baz:parameterA',
'baz:parameterB',
]);
$expected = [
'baz.cnenzrgreN',
'baz.cnenzrgreO',
'bar',
];
$this->assertEquals($expected, $new_keys);
}
/**
* @covers ::convertTokensToKeys
*
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage "non-cache-context" is not a valid cache context ID.
*/
public function testInvalidContext() {
$container = $this->getMockContainer();
$cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
$cache_contexts_manager->convertTokensToKeys(["non-cache-context"]);
}
/**
* @covers ::convertTokensToKeys
*
* @expectedException \Exception
*
* @dataProvider providerTestInvalidCalculatedContext
*/
public function testInvalidCalculatedContext($context_token) {
$container = $this->getMockContainer();
$cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
$cache_contexts_manager->convertTokensToKeys([$context_token]);
}
/**
* Provides a list of invalid 'baz' cache contexts: the parameter is missing.
*/
public function providerTestInvalidCalculatedContext() {
return [
['baz'],
['baz:'],
];
}
public function testAvailableContextStrings() {
$cache_contexts_manager = new CacheContextsManager($this->getMockContainer(), $this->getContextsFixture());
$contexts = $cache_contexts_manager->getAll();
$this->assertEquals(array("foo", "baz"), $contexts);
}
public function testAvailableContextLabels() {
$container = $this->getMockContainer();
$cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
$labels = $cache_contexts_manager->getLabels();
$expected = array("foo" => "Foo");
$this->assertEquals($expected, $labels);
}
protected function getContextsFixture() {
return array('foo', 'baz');
}
protected function getMockContainer() {
$container = $this->getMockBuilder('Drupal\Core\DependencyInjection\Container')
->disableOriginalConstructor()
->getMock();
$container->expects($this->any())
->method('get')
->will($this->returnValueMap([
['cache_context.foo', Container::EXCEPTION_ON_INVALID_REFERENCE, new FooCacheContext()],
['cache_context.baz', Container::EXCEPTION_ON_INVALID_REFERENCE, new BazCacheContext()],
]));
return $container;
}
/**
* Provides a list of cache context token arrays.
*
* @return array
*/
public function validateTokensProvider() {
return [
[[], FALSE],
[['foo'], FALSE],
[['foo', 'foo.bar'], FALSE],
[['foo', 'baz:llama'], FALSE],
// Invalid.
[[FALSE], 'Cache contexts must be strings, boolean given.'],
[[TRUE], 'Cache contexts must be strings, boolean given.'],
[['foo', FALSE], 'Cache contexts must be strings, boolean given.'],
[[NULL], 'Cache contexts must be strings, NULL given.'],
[['foo', NULL], 'Cache contexts must be strings, NULL given.'],
[[1337], 'Cache contexts must be strings, integer given.'],
[['foo', 1337], 'Cache contexts must be strings, integer given.'],
[[3.14], 'Cache contexts must be strings, double given.'],
[['foo', 3.14], 'Cache contexts must be strings, double given.'],
[[[]], 'Cache contexts must be strings, array given.'],
[['foo', []], 'Cache contexts must be strings, array given.'],
[['foo', ['bar']], 'Cache contexts must be strings, array given.'],
[[new \stdClass()], 'Cache contexts must be strings, object given.'],
[['foo', new \stdClass()], 'Cache contexts must be strings, object given.'],
// Non-existing.
[['foo.bar', 'qux'], '"qux" is not a valid cache context ID.'],
[['qux', 'baz'], '"qux" is not a valid cache context ID.'],
];
}
/**
* @covers ::validateTokens
*
* @dataProvider validateTokensProvider
*/
public function testValidateContexts(array $contexts, $expected_exception_message) {
$container = new ContainerBuilder();
$cache_contexts_manager = new CacheContextsManager($container, ['foo', 'foo.bar', 'baz']);
if ($expected_exception_message !== FALSE) {
$this->setExpectedException('LogicException', $expected_exception_message);
}
// If it doesn't throw an exception, validateTokens() returns NULL.
$this->assertNull($cache_contexts_manager->validateTokens($contexts));
}
}
/**
* Fake cache context class.
*/
class FooCacheContext implements CacheContextInterface {
/**
* {@inheritdoc}
*/
public static function getLabel() {
return 'Foo';
}
/**
* {@inheritdoc}
*/
public function getContext() {
return 'bar';
}
}
/**
* Fake calculated cache context class.
*/
class BazCacheContext implements CalculatedCacheContextInterface {
/**
* {@inheritdoc}
*/
public static function getLabel() {
return 'Baz';
}
/**
* {@inheritdoc}
*/
public function getContext($parameter = NULL) {
if (!is_string($parameter) || strlen($parameter) === 0) {
throw new \Exception();
}
return 'baz.' . str_rot13($parameter);
}
}

View file

@ -0,0 +1,32 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Cache\NullBackendTest.
*/
namespace Drupal\Tests\Core\Cache;
use Drupal\Core\Cache\NullBackend;
use Drupal\Tests\UnitTestCase;
/**
* Tests the cache NullBackend.
*
* @group Cache
*/
class NullBackendTest extends UnitTestCase {
/**
* Tests that the NullBackend does not actually store variables.
*/
function testNullBackend() {
$null_cache = new NullBackend('test');
$key = $this->randomMachineName();
$value = $this->randomMachineName();
$null_cache->set($key, $value);
$this->assertFalse($null_cache->get($key));
}
}

View file

@ -0,0 +1,74 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Common\AttributesTest.
*/
namespace Drupal\Tests\Core\Common;
use Drupal\Core\Template\Attribute;
use Drupal\Tests\UnitTestCase;
/**
* Tests the Drupal\Core\Template\Attribute functionality.
*
* @group Common
*/
class AttributesTest extends UnitTestCase {
/**
* Provides data for the Attribute test.
*
* @return array
*/
public function providerTestAttributeData() {
return array(
// Verify that special characters are HTML encoded.
array(array('&"\'<>' => 'value'), ' &amp;&quot;&#039;&lt;&gt;="value"', 'HTML encode attribute names.'),
array(array('title' => '&"\'<>'), ' title="&amp;&quot;&#039;&lt;&gt;"', 'HTML encode attribute values.'),
// Verify multi-value attributes are concatenated with spaces.
array(array('class' => array('first', 'last')), ' class="first last"', 'Concatenate multi-value attributes.'),
// Verify boolean attribute values are rendered correctly.
array(array('disabled' => TRUE), ' disabled', 'Boolean attribute is rendered.'),
array(array('disabled' => FALSE), '', 'Boolean attribute is not rendered.'),
// Verify empty attribute values are rendered.
array(array('alt' => ''), ' alt=""', 'Empty attribute value #1.'),
array(array('alt' => NULL), '', 'Null attribute value #2.'),
// Verify multiple attributes are rendered.
array(
array(
'id' => 'id-test',
'class' => array('first', 'last'),
'alt' => 'Alternate',
),
' id="id-test" class="first last" alt="Alternate"',
'Multiple attributes.'
),
// Verify empty attributes array is rendered.
array(array(), '', 'Empty attributes array.'),
);
}
/**
* Tests casting an Attribute object to a string.
*
* @see \Drupal\Core\Template\Attribute::__toString()
*
* @dataProvider providerTestAttributeData
*/
function testDrupalAttributes($attributes, $expected, $message) {
$this->assertSame($expected, (string) new Attribute($attributes), $message);
}
/**
* Test attribute iteration
*/
public function testAttributeIteration() {
$attribute = new Attribute(array('key1' => 'value1'));
foreach ($attribute as $value) {
$this->assertSame((string) $value, 'value1', 'Iterate over attribute.');
}
}
}

View file

@ -0,0 +1,77 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Common\DiffArrayTest.
*/
namespace Drupal\Tests\Core\Common;
use Drupal\Component\Utility\DiffArray;
use Drupal\Tests\UnitTestCase;
/**
* Tests the DiffArray helper class.
*
* @group Common
*/
class DiffArrayTest extends UnitTestCase {
/**
* Array to use for testing.
*
* @var array
*/
protected $array1;
/**
* Array to use for testing.
*
* @var array
*/
protected $array2;
protected function setUp() {
parent::setUp();
$this->array1 = array(
'same' => 'yes',
'different' => 'no',
'array_empty_diff' => array(),
'null' => NULL,
'int_diff' => 1,
'array_diff' => array('same' => 'same', 'array' => array('same' => 'same')),
'array_compared_to_string' => array('value'),
'string_compared_to_array' => 'value',
'new' => 'new',
);
$this->array2 = array(
'same' => 'yes',
'different' => 'yes',
'array_empty_diff' => array(),
'null' => NULL,
'int_diff' => '1',
'array_diff' => array('same' => 'different', 'array' => array('same' => 'same')),
'array_compared_to_string' => 'value',
'string_compared_to_array' => array('value'),
);
}
/**
* Tests DiffArray::diffAssocRecursive().
*/
public function testDiffAssocRecursive() {
$expected = array(
'different' => 'no',
'int_diff' => 1,
// The 'array' key should not be returned, as it's the same.
'array_diff' => array('same' => 'same'),
'array_compared_to_string' => array('value'),
'string_compared_to_array' => 'value',
'new' => 'new',
);
$this->assertSame(DiffArray::diffAssocRecursive($this->array1, $this->array2), $expected);
}
}

View file

@ -0,0 +1,64 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Common\TagsTest.
*/
namespace Drupal\Tests\Core\Common;
use Drupal\Component\Utility\Tags;
use Drupal\Tests\UnitTestCase;
/**
* Tests explosion and implosion of autocomplete tags.
*
* @group Common
*/
class TagsTest extends UnitTestCase {
protected $validTags = array(
'Drupal' => 'Drupal',
'Drupal with some spaces' => 'Drupal with some spaces',
'"Legendary Drupal mascot of doom: ""Druplicon"""' => 'Legendary Drupal mascot of doom: "Druplicon"',
'"Drupal, although it rhymes with sloopal, is as awesome as a troopal!"' => 'Drupal, although it rhymes with sloopal, is as awesome as a troopal!',
);
/**
* Explodes a series of tags.
*/
public function explodeTags() {
$string = implode(', ', array_keys($this->validTags));
$tags = Tags::explode($string);
$this->assertTags($tags);
}
/**
* Implodes a series of tags.
*/
public function testImplodeTags() {
$tags = array_values($this->validTags);
// Let's explode and implode to our heart's content.
for ($i = 0; $i < 10; $i++) {
$string = Tags::implode($tags);
$tags = Tags::explode($string);
}
$this->assertTags($tags);
}
/**
* Helper function: asserts that the ending array of tags is what we wanted.
*/
protected function assertTags($tags) {
$original = $this->validTags;
foreach ($tags as $tag) {
$key = array_search($tag, $original);
$this->assertTrue((bool) $key, $tag, sprintf('Make sure tag %s shows up in the final tags array (originally %s)', $tag, $key));
unset($original[$key]);
}
foreach ($original as $leftover) {
$this->fail(sprintf('Leftover tag %s was left over.', $leftover));
}
}
}

View file

@ -0,0 +1,92 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Condition\ConditionAccessResolverTraitTest.
*/
namespace Drupal\Tests\Core\Condition;
use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Core\Condition\ConditionAccessResolverTrait;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Condition\ConditionAccessResolverTrait
* @group Condition
*/
class ConditionAccessResolverTraitTest extends UnitTestCase {
/**
* Tests the resolveConditions() method.
*
* @covers ::resolveConditions
*
* @dataProvider providerTestResolveConditions
*/
public function testResolveConditions($conditions, $logic, $expected) {
$trait_object = new TestConditionAccessResolverTrait();
$this->assertEquals($expected, $trait_object->resolveConditions($conditions, $logic));
}
public function providerTestResolveConditions() {
$data = array();
$condition_true = $this->getMock('Drupal\Core\Condition\ConditionInterface');
$condition_true->expects($this->any())
->method('execute')
->will($this->returnValue(TRUE));
$condition_false = $this->getMock('Drupal\Core\Condition\ConditionInterface');
$condition_false->expects($this->any())
->method('execute')
->will($this->returnValue(FALSE));
$condition_exception = $this->getMock('Drupal\Core\Condition\ConditionInterface');
$condition_exception->expects($this->any())
->method('execute')
->will($this->throwException(new ContextException()));
$conditions = array();
$data[] = array($conditions, 'and', TRUE);
$data[] = array($conditions, 'or', FALSE);
$conditions = array($condition_false);
$data[] = array($conditions, 'or', FALSE);
$data[] = array($conditions, 'and', FALSE);
$conditions = array($condition_true);
$data[] = array($conditions, 'or', TRUE);
$data[] = array($conditions, 'and', TRUE);
$conditions = array($condition_true, $condition_false);
$data[] = array($conditions, 'or', TRUE);
$data[] = array($conditions, 'and', FALSE);
$conditions = array($condition_exception);
$data[] = array($conditions, 'or', FALSE);
$data[] = array($conditions, 'and', FALSE);
$conditions = array($condition_true, $condition_exception);
$data[] = array($conditions, 'or', TRUE);
$data[] = array($conditions, 'and', FALSE);
$conditions = array($condition_exception, $condition_true);
$data[] = array($conditions, 'or', TRUE);
$data[] = array($conditions, 'and', FALSE);
$conditions = array($condition_false, $condition_exception);
$data[] = array($conditions, 'or', FALSE);
$data[] = array($conditions, 'and', FALSE);
$conditions = array($condition_exception, $condition_false);
$data[] = array($conditions, 'or', FALSE);
$data[] = array($conditions, 'and', FALSE);
return $data;
}
}
class TestConditionAccessResolverTrait {
use \Drupal\Core\Condition\ConditionAccessResolverTrait {
resolveConditions as public;
}
}

View file

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\CachedStorageTest.
*/
namespace Drupal\Tests\Core\Config;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Config\CachedStorage;
use Drupal\Core\Cache\NullBackend;
/**
* Tests the interaction of cache and file storage in CachedStorage.
*
* @group Config
*/
class CachedStorageTest extends UnitTestCase {
/**
* @var \Drupal\Core\Cache\CacheFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cacheFactory;
/**
* Test listAll static cache.
*/
public function testListAllStaticCache() {
$prefix = __FUNCTION__;
$storage = $this->getMock('Drupal\Core\Config\StorageInterface');
$response = array("$prefix." . $this->randomMachineName(), "$prefix." . $this->randomMachineName());
$storage->expects($this->once())
->method('listAll')
->with($prefix)
->will($this->returnValue($response));
$cache = new NullBackend(__FUNCTION__);
$cachedStorage = new CachedStorage($storage, $cache);
$this->assertEquals($response, $cachedStorage->listAll($prefix));
$this->assertEquals($response, $cachedStorage->listAll($prefix));
}
}

View file

@ -0,0 +1,97 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\ConfigFactoryTest.
*/
namespace Drupal\Tests\Core\Config;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @group Config
* @coversDefaultClass \Drupal\Core\Config\ConfigFactory
*/
class ConfigFactoryTest extends UnitTestCase {
/**
* Config factory under test.
*
* @var \Drupal\Core\Config\ConfigFactory
*/
protected $configFactory;
/**
* Storage.
*
* @var \Drupal\Core\Config\StorageInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $storage;
/**
* Event Dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $eventDispatcher;
/**
* Typed Config.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $typedConfig;
/**
* The mocked cache tags invalidator.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cacheTagsInvalidator;
/**
* {@inheritdoc}
*/
public function setUp() {
$this->storage = $this->getMock('Drupal\Core\Config\StorageInterface');
$this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$this->typedConfig = $this->getMock('\Drupal\Core\Config\TypedConfigManagerInterface');
$this->configFactory = new ConfigFactory($this->storage, $this->eventDispatcher, $this->typedConfig);
$this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
$container = new ContainerBuilder();
$container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
\Drupal::setContainer($container);
}
/**
* @covers ::rename
*/
public function testRename() {
$old = new Config($this->randomMachineName(), $this->storage, $this->eventDispatcher, $this->typedConfig);
$new = new Config($this->randomMachineName(), $this->storage, $this->eventDispatcher, $this->typedConfig);
$this->storage->expects($this->exactly(2))
->method('readMultiple')
->willReturnMap([
[[$old->getName()], $old->getRawData()],
[[$new->getName()], $new->getRawData()],
]);
$this->cacheTagsInvalidator->expects($this->once())
->method('invalidateTags')
->with($old->getCacheTags());
$this->storage->expects($this->once())
->method('rename')
->with($old->getName(), $new->getName());
$this->configFactory->rename($old->getName(), $new->getName());
}
}

View file

@ -0,0 +1,538 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\ConfigTest.
*/
namespace Drupal\Tests\Core\Config;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Config\Config;
use Drupal\Component\Utility\SafeMarkup;
/**
* Tests the Config.
*
* @coversDefaultClass \Drupal\Core\Config\Config
*
* @group Config
*
* @see \Drupal\Core\Config\Config
*/
class ConfigTest extends UnitTestCase {
/**
* Config.
*
* @var \Drupal\Core\Config\Config
*/
protected $config;
/**
* Storage.
*
* @var \Drupal\Core\Config\StorageInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $storage;
/**
* Event Dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $eventDispatcher;
/**
* Typed Config.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $typedConfig;
/**
* The mocked cache tags invalidator.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cacheTagsInvalidator;
public function setUp() {
$this->storage = $this->getMock('Drupal\Core\Config\StorageInterface');
$this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$this->typedConfig = $this->getMock('\Drupal\Core\Config\TypedConfigManagerInterface');
$this->config = new Config('config.test', $this->storage, $this->eventDispatcher, $this->typedConfig);
$this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
$container = new ContainerBuilder();
$container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
\Drupal::setContainer($container);
}
/**
* @covers ::setName
* @dataProvider setNameProvider
*/
public function testSetName($name) {
// Set the name.
$this->config->setName($name);
// Check that the name has been set correctly.
$this->assertEquals($name, $this->config->getName());
// Check that the name validates.
// Should throw \Drupal\Core\Config\ConfigNameException if invalid.
$this->config->validateName($name);
}
/**
* Provides config names to test.
*
* @see \Drupal\Tests\Core\Config\ConfigTest::testSetName()
*/
public function setNameProvider() {
return array(
// Valid name with dot.
array(
'test.name',
),
// Maximum length.
array(
'test.' . str_repeat('a', Config::MAX_NAME_LENGTH - 5),
),
);
}
/**
* @covers ::isNew
*/
public function testIsNew() {
// Config should be new by default.
$this->assertTrue($this->config->isNew());
// Config is no longer new once saved.
$this->config->save();
$this->assertFalse($this->config->isNew());
}
/**
* @covers ::setData
* @dataProvider nestedDataProvider
*/
public function testSetData($data) {
$this->config->setData($data);
$this->assertEquals($data, $this->config->getRawData());
$this->assertConfigDataEquals($data);
}
/**
* @covers ::save
* @dataProvider nestedDataProvider
*/
public function testSaveNew($data) {
$this->cacheTagsInvalidator->expects($this->never())
->method('invalidateTags');
// Set initial data.
$this->config->setData($data);
// Check that original data has not been set yet.
foreach ($data as $key => $value) {
$this->assertNull($this->config->getOriginal($key, FALSE));
}
// Save so that the original data is set.
$config = $this->config->save();
// Check that returned $config is instance of Config.
$this->assertInstanceOf('\Drupal\Core\Config\Config', $config);
// Check that the original data it saved.
$this->assertOriginalConfigDataEquals($data, TRUE);
}
/**
* @covers ::save
* @dataProvider nestedDataProvider
*/
public function testSaveExisting($data) {
$this->cacheTagsInvalidator->expects($this->once())
->method('invalidateTags')
->with(['config:config.test']);
// Set initial data.
$this->config->setData($data);
$this->config->save();
// Update.
$new_data = $data;
$new_data['a']['d'] = 2;
$this->config->setData($new_data);
$this->config->save();
$this->assertOriginalConfigDataEquals($new_data, TRUE);
}
/**
* @covers ::setModuleOverride
* @covers ::setSettingsOverride
* @covers ::getOriginal
* @dataProvider overrideDataProvider
*/
public function testOverrideData($data, $module_data, $setting_data) {
// Set initial data.
$this->config->setData($data);
// Check original data was set correctly.
$this->assertConfigDataEquals($data);
// Save so that the original data is stored.
$this->config->save();
// Set module override data and check value before and after save.
$this->config->setModuleOverride($module_data);
$this->assertConfigDataEquals($module_data);
$this->config->save();
$this->assertConfigDataEquals($module_data);
// Set setting override data and check value before and after save.
$this->config->setSettingsOverride($setting_data);
$this->assertConfigDataEquals($setting_data);
$this->config->save();
$this->assertConfigDataEquals($setting_data);
// Set module overrides again to ensure override order is correct.
$this->config->setModuleOverride($module_data);
// Setting data should be overriding module data.
$this->assertConfigDataEquals($setting_data);
$this->config->save();
$this->assertConfigDataEquals($setting_data);
// Check original data has not changed.
$this->assertOriginalConfigDataEquals($data, FALSE);
// Check setting overrides are returned with $apply_overrides = TRUE.
$this->assertOriginalConfigDataEquals($setting_data, TRUE);
// Check that $apply_overrides defaults to TRUE.
foreach ($setting_data as $key => $value) {
$config_value = $this->config->getOriginal($key);
$this->assertEquals($value, $config_value);
}
}
/**
* @covers ::set
* @dataProvider nestedDataProvider
*/
public function testSetValue($data) {
foreach ($data as $key => $value) {
$this->config->set($key, $value);
}
$this->assertConfigDataEquals($data);
}
/**
* @covers ::set
* @expectedException \Drupal\Core\Config\ConfigValueException
*/
public function testSetValidation() {
$this->config->set('testData', array('dot.key' => 1));
}
/**
* @covers ::set
* @expectedException PHPUnit_Framework_Error_Warning
*/
public function testSetIllegalOffsetValue() {
// Set a single value.
$this->config->set('testData', 1);
// Attempt to treat the single value as a nested item.
$this->config->set('testData.illegalOffset', 1);
}
/**
* @covers ::initWithData
* @dataProvider nestedDataProvider
*/
public function testInitWithData($data) {
$config = $this->config->initWithData($data);
// Should return the Config object.
$this->assertInstanceOf('\Drupal\Core\Config\Config', $config);
// Check config is not new.
$this->assertEquals(FALSE, $this->config->isNew());
// Check that data value was set correctly.
$this->assertConfigDataEquals($data);
// Check that original data was set.
$this->assertOriginalConfigDataEquals($data, TRUE);
// Check without applying overrides.
$this->assertOriginalConfigDataEquals($data, FALSE);
}
/**
* @covers ::clear
* @dataProvider simpleDataProvider
*/
public function testClear($data) {
foreach ($data as $key => $value) {
// Check that values are cleared.
$this->config->set($key, $value);
$this->assertEquals($value, $this->config->get($key));
$this->config->clear($key);
$this->assertNull($this->config->get($key));
}
}
/**
* @covers ::clear
* @dataProvider nestedDataProvider
*/
public function testNestedClear($data) {
foreach ($data as $key => $value) {
// Check that values are cleared.
$this->config->set($key, $value);
// Check each nested value.
foreach ($value as $nested_key => $nested_value) {
$full_nested_key = $key . '.' . $nested_key;
$this->assertEquals($nested_value, $this->config->get($full_nested_key));
$this->config->clear($full_nested_key);
$this->assertNull($this->config->get($full_nested_key));
}
}
}
/**
* @covers ::delete
* @dataProvider overrideDataProvider
*/
public function testDelete($data, $module_data) {
$this->cacheTagsInvalidator->expects($this->once())
->method('invalidateTags')
->with(['config:config.test']);
// Set initial data.
foreach ($data as $key => $value) {
$this->config->set($key, $value);
}
// Set overrides.
$this->config->setModuleOverride($module_data);
// Save.
$this->config->save();
// Check that original data is still correct.
$this->assertOriginalConfigDataEquals($data, FALSE);
// Check overrides have been set.
$this->assertConfigDataEquals($module_data);
$this->assertOriginalConfigDataEquals($module_data, TRUE);
// Check that config is new.
$this->assertFalse($this->config->isNew());
// Delete.
$this->config->delete();
// Check object properties have been reset.
$this->assertTrue($this->config->isNew());
foreach ($data as $key => $value) {
$this->assertEmpty($this->config->getOriginal($key, FALSE));
}
// Check that overrides have persisted.
foreach ($module_data as $key => $value) {
$this->assertConfigDataEquals($module_data);
$this->assertOriginalConfigDataEquals($module_data, TRUE);
}
}
/**
* @covers ::merge
* @dataProvider mergeDataProvider
*/
public function testMerge($data, $data_to_merge, $merged_data) {
// Set initial data.
$this->config->setData($data);
// Data to merge.
$this->config->merge($data_to_merge);
// Check that data has merged correctly.
$this->assertEquals($merged_data, $this->config->getRawData());
}
/**
* Provides data to test merges.
*
* @see \Drupal\Tests\Core\Config\ConfigTest::testMerge()
*/
public function mergeDataProvider() {
return array(
array(
// Data.
array('a' => 1, 'b' => 2, 'c' => array('d' => 3)),
// Data to merge.
array('a' => 2, 'e' => 4, 'c' => array('f' => 5)),
// Data merged.
array('a' => 2, 'b' => 2, 'c' => array('d' => 3, 'f' => 5), 'e' => 4),
),
);
}
/**
* @covers ::validateName
* @expectedException \Drupal\Core\Config\ConfigNameException
* @dataProvider validateNameProvider
*/
public function testValidateNameException($name, $exception_message) {
$this->setExpectedException('\Drupal\Core\Config\ConfigNameException', $exception_message);
$this->config->validateName($name);
}
/**
* @covers ::getCacheTags
*/
public function testGetCacheTags() {
$this->assertSame(['config:' . $this->config->getName()], $this->config->getCacheTags());
}
/**
* Provides data to test name validation.
*
* @see \Drupal\Tests\Core\Config\ConfigTest::testValidateNameException()
*/
public function validateNameProvider() {
$return = array(
// Name missing namespace (dot).
array(
'MissingNamespace',
SafeMarkup::format('Missing namespace in Config object name MissingNamespace.', array(
'@name' => 'MissingNamespace',
)),
),
// Exceeds length (max length plus an extra dot).
array(
str_repeat('a', Config::MAX_NAME_LENGTH) . ".",
SafeMarkup::format('Config object name @name exceeds maximum allowed length of @length characters.', array(
'@name' => str_repeat('a', Config::MAX_NAME_LENGTH) . ".",
'@length' => Config::MAX_NAME_LENGTH,
)),
),
);
// Name must not contain : ? * < > " ' / \
foreach (array(':', '?', '*', '<', '>', '"', "'", '/', '\\') as $char) {
$name = 'name.' . $char;
$return[] = array(
$name,
SafeMarkup::format('Invalid character in Config object name @name.', array(
'@name' => $name,
)),
);
}
return $return;
}
/**
* Provides override data.
*
* @see \Drupal\Tests\Core\Config\ConfigTest::testOverrideData()
* @see \Drupal\Tests\Core\Config\ConfigTest::testDelete()
*/
public function overrideDataProvider() {
return array(
array(
// Original data.
array(
'a' => 'originalValue',
),
// Module overrides.
array(
'a' => 'moduleValue',
),
// Setting overrides.
array(
'a' => 'settingValue',
),
),
);
}
/**
* Provides simple test data.
*
* @see \Drupal\Tests\Core\Config\ConfigTest::testClear()
*/
public function simpleDataProvider() {
return array(
array(
array(
'a' => '1',
'b' => '2',
'c' => '3',
),
),
);
}
/**
* Provides nested test data.
*
* @see \Drupal\Tests\Core\Config\ConfigTest::testSetData()
* @see \Drupal\Tests\Core\Config\ConfigTest::testSave()
* @see \Drupal\Tests\Core\Config\ConfigTest::testSetValue()
* @see \Drupal\Tests\Core\Config\ConfigTest::testInitWithData()
* @see \Drupal\Tests\Core\Config\ConfigTest::testNestedClear()
*/
public function nestedDataProvider() {
return array(
array(
array(
'a' => array(
'd' => 1,
),
'b' => array(
'e' => 2,
),
'c' => array(
'f' => 3,
),
),
),
);
}
/**
* Asserts all config data equals $data provided.
*
* @param array $data
* Config data to be checked.
*/
public function assertConfigDataEquals($data) {
foreach ($data as $key => $value) {
$this->assertEquals($value, $this->config->get($key));
}
}
/**
* Asserts all original config data equals $data provided.
*
* @param array $data
* Config data to be checked.
* @param bool $apply_overrides
* Apply any overrides to the original data.
*/
public function assertOriginalConfigDataEquals($data, $apply_overrides) {
foreach ($data as $key => $value) {
$config_value = $this->config->getOriginal($key, $apply_overrides);
$this->assertEquals($value, $config_value);
}
}
}

View file

@ -0,0 +1,40 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\Entity\ConfigDependencyManagerTest.
*/
namespace Drupal\Tests\Core\Config\Entity;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Config\Entity\ConfigDependencyManager;
/**
* Tests the ConfigDependencyManager class.
*
* @group Config
*/
class ConfigDependencyManagerTest extends UnitTestCase {
public function testNoConfiguration() {
$dep_manger = new ConfigDependencyManager();
$this->assertEmpty($dep_manger->getDependentEntities('config', 'config_test.dynamic.entity_id:745b0ce0-aece-42dd-a800-ade5b8455e84'));
}
public function testNoConfigEntities() {
$dep_manger = new ConfigDependencyManager();
$dep_manger->setData(array(
'simple.config' => array(
'key' => 'value',
),
));
$this->assertEmpty($dep_manger->getDependentEntities('config', 'config_test.dynamic.entity_id:745b0ce0-aece-42dd-a800-ade5b8455e84'));
// Configuration is always dependent on its provider.
$dependencies = $dep_manger->getDependentEntities('module', 'simple');
$this->assertArrayHasKey('simple.config', $dependencies);
$this->assertCount(1, $dependencies);
}
}

View file

@ -0,0 +1,563 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\Entity\ConfigEntityBaseUnitTest.
*/
namespace Drupal\Tests\Core\Config\Entity;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Language\Language;
use Drupal\Tests\Core\Plugin\Fixtures\TestConfigurablePlugin;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Config\Entity\ConfigEntityBase
* @group Config
*/
class ConfigEntityBaseUnitTest extends UnitTestCase {
/**
* The entity under test.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityBase|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entity;
/**
* The entity type used for testing.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityType;
/**
* The entity manager used for testing.
*
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityManager;
/**
* The ID of the type of the entity under test.
*
* @var string
*/
protected $entityTypeId;
/**
* The UUID generator used for testing.
*
* @var \Drupal\Component\Uuid\UuidInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $uuid;
/**
* The provider of the entity type.
*
* @var string
*/
protected $provider;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $languageManager;
/**
* The entity ID.
*
* @var string
*/
protected $id;
/**
* The mocked cache backend.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cacheTagsInvalidator;
/**
* The mocked typed config manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $typedConfigManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->id = $this->randomMachineName();
$values = array(
'id' => $this->id,
'langcode' => 'en',
'uuid' => '3bb9ee60-bea5-4622-b89b-a63319d10b3a',
);
$this->entityTypeId = $this->randomMachineName();
$this->provider = $this->randomMachineName();
$this->entityType = $this->getMock('\Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
$this->entityType->expects($this->any())
->method('getProvider')
->will($this->returnValue($this->provider));
$this->entityType->expects($this->any())
->method('getConfigPrefix')
->willReturn('test_provider.' . $this->entityTypeId);
$this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
$this->entityManager->expects($this->any())
->method('getDefinition')
->with($this->entityTypeId)
->will($this->returnValue($this->entityType));
$this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface');
$this->languageManager = $this->getMock('\Drupal\Core\Language\LanguageManagerInterface');
$this->languageManager->expects($this->any())
->method('getLanguage')
->with('en')
->will($this->returnValue(new Language(array('id' => 'en'))));
$this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
$this->typedConfigManager = $this->getMock('Drupal\Core\Config\TypedConfigManagerInterface');
$container = new ContainerBuilder();
$container->set('entity.manager', $this->entityManager);
$container->set('uuid', $this->uuid);
$container->set('language_manager', $this->languageManager);
$container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
$container->set('config.typed', $this->typedConfigManager);
\Drupal::setContainer($container);
$this->entity = $this->getMockForAbstractClass('\Drupal\Core\Config\Entity\ConfigEntityBase', array($values, $this->entityTypeId));
}
/**
* @covers ::calculateDependencies
*/
public function testCalculateDependencies() {
// Calculating dependencies will reset the dependencies array.
$this->entity->set('dependencies', array('module' => array('node')));
$this->assertEmpty($this->entity->calculateDependencies());
// Calculating dependencies will reset the dependencies array using enforced
// dependencies.
$this->entity->set('dependencies', array('module' => array('node'), 'enforced' => array('module' => 'views')));
$dependencies = $this->entity->calculateDependencies();
$this->assertContains('views', $dependencies['module']);
$this->assertNotContains('node', $dependencies['module']);
$this->assertContains('views', $dependencies['enforced']['module']);
}
/**
* @covers ::preSave
*/
public function testPreSaveDuringSync() {
$query = $this->getMock('\Drupal\Core\Entity\Query\QueryInterface');
$storage = $this->getMock('\Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
$query->expects($this->any())
->method('execute')
->will($this->returnValue(array()));
$query->expects($this->any())
->method('condition')
->will($this->returnValue($query));
$storage->expects($this->any())
->method('getQuery')
->will($this->returnValue($query));
$storage->expects($this->any())
->method('loadUnchanged')
->will($this->returnValue($this->entity));
// Saving an entity will not reset the dependencies array during config
// synchronization.
$this->entity->set('dependencies', array('module' => array('node')));
$this->entity->preSave($storage);
$this->assertEmpty($this->entity->getDependencies());
$this->entity->setSyncing(TRUE);
$this->entity->set('dependencies', array('module' => array('node')));
$this->entity->preSave($storage);
$dependencies = $this->entity->getDependencies();
$this->assertContains('node', $dependencies['module']);
}
/**
* @covers ::addDependency
*/
public function testAddDependency() {
$method = new \ReflectionMethod('\Drupal\Core\Config\Entity\ConfigEntityBase', 'addDependency');
$method->setAccessible(TRUE);
$method->invoke($this->entity, 'module', $this->provider);
$method->invoke($this->entity, 'module', 'core');
$method->invoke($this->entity, 'module', 'node');
$dependencies = $this->entity->getDependencies();
$this->assertNotContains($this->provider, $dependencies['module']);
$this->assertNotContains('core', $dependencies['module']);
$this->assertContains('node', $dependencies['module']);
// Test sorting of dependencies.
$method->invoke($this->entity, 'module', 'action');
$dependencies = $this->entity->getDependencies();
$this->assertEquals(array('action', 'node'), $dependencies['module']);
// Test sorting of dependency types.
$method->invoke($this->entity, 'entity', 'system.action.id');
$dependencies = $this->entity->getDependencies();
$this->assertEquals(array('entity', 'module'), array_keys($dependencies));
}
/**
* @covers ::calculateDependencies
*
* @dataProvider providerCalculateDependenciesWithPluginCollections
*/
public function testCalculateDependenciesWithPluginCollections($definition, $expected_dependencies) {
$values = array();
$this->entity = $this->getMockBuilder('\Drupal\Tests\Core\Config\Entity\Fixtures\ConfigEntityBaseWithPluginCollections')
->setConstructorArgs(array($values, $this->entityTypeId))
->setMethods(array('getPluginCollections'))
->getMock();
// Create a configurable plugin that would add a dependency.
$instance_id = $this->randomMachineName();
$instance = new TestConfigurablePlugin(array(), $instance_id, $definition);
// Create a plugin collection to contain the instance.
$pluginCollection = $this->getMockBuilder('\Drupal\Core\Plugin\DefaultLazyPluginCollection')
->disableOriginalConstructor()
->setMethods(array('get'))
->getMock();
$pluginCollection->expects($this->atLeastOnce())
->method('get')
->with($instance_id)
->will($this->returnValue($instance));
$pluginCollection->addInstanceId($instance_id);
// Return the mocked plugin collection.
$this->entity->expects($this->once())
->method('getPluginCollections')
->will($this->returnValue(array($pluginCollection)));
$this->assertEquals($expected_dependencies, $this->entity->calculateDependencies());
}
/**
* Data provider for testCalculateDependenciesWithPluginCollections.
*
* @return array
*/
public function providerCalculateDependenciesWithPluginCollections() {
// Start with 'a' so that order of the dependency array is fixed.
$instance_dependency_1 = 'a' . $this->randomMachineName(10);
$instance_dependency_2 = 'a' . $this->randomMachineName(11);
return array(
// Tests that the plugin provider is a module dependency.
array(
array('provider' => 'test'),
array('module' => array('test')),
),
// Tests that a plugin that is provided by the same module as the config
// entity is not added to the dependencies array.
array(
array('provider' => $this->provider),
array('module' => array(NULL)),
),
// Tests that a config entity that has a plugin which provides config
// dependencies in its definition has them.
array(
array(
'provider' => 'test',
'config_dependencies' => array(
'config' => array($instance_dependency_1),
'module' => array($instance_dependency_2),
)
),
array(
'config' => array($instance_dependency_1),
'module' => array($instance_dependency_2, 'test')
)
)
);
}
/**
* @covers ::calculateDependencies
* @covers ::onDependencyRemoval
*/
public function testCalculateDependenciesWithThirdPartySettings() {
$this->entity = $this->getMockForAbstractClass('\Drupal\Core\Config\Entity\ConfigEntityBase', array(array(), $this->entityTypeId));
$this->entity->setThirdPartySetting('test_provider', 'test', 'test');
$this->entity->setThirdPartySetting('test_provider2', 'test', 'test');
$this->entity->setThirdPartySetting($this->provider, 'test', 'test');
$this->assertEquals(array('test_provider', 'test_provider2'), $this->entity->calculateDependencies()['module']);
$changed = $this->entity->onDependencyRemoval(['module' => ['test_provider2']]);
$this->assertTrue($changed, 'Calling onDependencyRemoval with an existing third party dependency provider returns TRUE.');
$changed = $this->entity->onDependencyRemoval(['module' => ['test_provider3']]);
$this->assertFalse($changed, 'Calling onDependencyRemoval with a non-existing third party dependency provider returns FALSE.');
$this->assertEquals(array('test_provider'), $this->entity->calculateDependencies()['module']);
}
/**
* @covers ::setOriginalId
* @covers ::getOriginalId
*/
public function testGetOriginalId() {
$new_id = $this->randomMachineName();
$this->entity->set('id', $new_id);
$this->assertSame($this->id, $this->entity->getOriginalId());
$this->assertSame($this->entity, $this->entity->setOriginalId($new_id));
$this->assertSame($new_id, $this->entity->getOriginalId());
// Check that setOriginalId() does not change the entity "isNew" status.
$this->assertFalse($this->entity->isNew());
$this->entity->setOriginalId($this->randomMachineName());
$this->assertFalse($this->entity->isNew());
$this->entity->enforceIsNew();
$this->assertTrue($this->entity->isNew());
$this->entity->setOriginalId($this->randomMachineName());
$this->assertTrue($this->entity->isNew());
}
/**
* @covers ::isNew
*/
public function testIsNew() {
$this->assertFalse($this->entity->isNew());
$this->assertSame($this->entity, $this->entity->enforceIsNew());
$this->assertTrue($this->entity->isNew());
$this->entity->enforceIsNew(FALSE);
$this->assertFalse($this->entity->isNew());
}
/**
* @covers ::set
* @covers ::get
*/
public function testGet() {
$name = 'id';
$value = $this->randomMachineName();
$this->assertSame($this->id, $this->entity->get($name));
$this->assertSame($this->entity, $this->entity->set($name, $value));
$this->assertSame($value, $this->entity->get($name));
}
/**
* @covers ::setStatus
* @covers ::status
*/
public function testSetStatus() {
$this->assertTrue($this->entity->status());
$this->assertSame($this->entity, $this->entity->setStatus(FALSE));
$this->assertFalse($this->entity->status());
$this->entity->setStatus(TRUE);
$this->assertTrue($this->entity->status());
}
/**
* @covers ::enable
* @depends testSetStatus
*/
public function testEnable() {
$this->entity->setStatus(FALSE);
$this->assertSame($this->entity, $this->entity->enable());
$this->assertTrue($this->entity->status());
}
/**
* @covers ::disable
* @depends testSetStatus
*/
public function testDisable() {
$this->cacheTagsInvalidator->expects($this->once())
->method('invalidateTags')
->with(array('config:test_provider.' . $this->entityTypeId . '.' . $this->id));
$this->entity->setStatus(TRUE);
$this->assertSame($this->entity, $this->entity->disable());
$this->assertFalse($this->entity->status());
}
/**
* @covers ::setSyncing
* @covers ::isSyncing
*/
public function testIsSyncing() {
$this->assertFalse($this->entity->isSyncing());
$this->assertSame($this->entity, $this->entity->setSyncing(TRUE));
$this->assertTrue($this->entity->isSyncing());
$this->entity->setSyncing(FALSE);
$this->assertFalse($this->entity->isSyncing());
}
/**
* @covers ::createDuplicate
*/
public function testCreateDuplicate() {
$this->entityType->expects($this->at(0))
->method('getKey')
->with('id')
->will($this->returnValue('id'));
$this->entityType->expects($this->at(1))
->method('hasKey')
->with('uuid')
->will($this->returnValue(TRUE));
$this->entityType->expects($this->at(2))
->method('getKey')
->with('uuid')
->will($this->returnValue('uuid'));
$new_uuid = '8607ef21-42bc-4913-978f-8c06207b0395';
$this->uuid->expects($this->once())
->method('generate')
->will($this->returnValue($new_uuid));
$duplicate = $this->entity->createDuplicate();
$this->assertInstanceOf('\Drupal\Core\Entity\Entity', $duplicate);
$this->assertNotSame($this->entity, $duplicate);
$this->assertFalse($this->entity->isNew());
$this->assertTrue($duplicate->isNew());
$this->assertNull($duplicate->id());
$this->assertNull($duplicate->getOriginalId());
$this->assertNotEquals($this->entity->uuid(), $duplicate->uuid());
$this->assertSame($new_uuid, $duplicate->uuid());
}
/**
* @covers ::sort
*/
public function testSort() {
$this->entityManager->expects($this->any())
->method('getDefinition')
->with($this->entityTypeId)
->will($this->returnValue(array(
'entity_keys' => array(
'label' => 'label',
),
)));
$entity_a = $this->entity;
$entity_a->label = 'foo';
$entity_b = clone $this->entity;
$entity_b->label = 'bar';
$list = array($entity_a, $entity_b);
// Suppress errors because of https://bugs.php.net/bug.php?id=50688.
@usort($list, '\Drupal\Core\Config\Entity\ConfigEntityBase::sort');
$this->assertSame($entity_b, $list[0]);
$entity_a->weight = 0;
$entity_b->weight = 1;
// Suppress errors because of https://bugs.php.net/bug.php?id=50688.
@usort($list, array($entity_a, 'sort'));
$this->assertSame($entity_a, $list[0]);
}
/**
* @covers ::toArray
*/
public function testToArray() {
$this->typedConfigManager->expects($this->never())
->method('getDefinition');
$this->entityType->expects($this->any())
->method('getPropertiesToExport')
->willReturn(['id' => 'configId', 'dependencies' => 'dependencies']);
$properties = $this->entity->toArray();
$this->assertInternalType('array', $properties);
$this->assertEquals(array('configId' => $this->entity->id(), 'dependencies' => array()), $properties);
}
/**
* @covers ::toArray
*/
public function testToArrayIdKey() {
$entity = $this->getMockForAbstractClass('\Drupal\Core\Config\Entity\ConfigEntityBase', [[], $this->entityTypeId], '', TRUE, TRUE, TRUE, ['id', 'get']);
$entity->expects($this->atLeastOnce())
->method('id')
->willReturn($this->id);
$entity->expects($this->once())
->method('get')
->with('dependencies')
->willReturn([]);
$this->typedConfigManager->expects($this->never())
->method('getDefinition');
$this->entityType->expects($this->any())
->method('getPropertiesToExport')
->willReturn(['id' => 'configId', 'dependencies' => 'dependencies']);
$this->entityType->expects($this->once())
->method('getKey')
->with('id')
->willReturn('id');
$properties = $entity->toArray();
$this->assertInternalType('array', $properties);
$this->assertEquals(['configId' => $entity->id(), 'dependencies' => []], $properties);
}
/**
* @covers ::toArray
*/
public function testToArraySchemaFallback() {
$this->typedConfigManager->expects($this->once())
->method('getDefinition')
->will($this->returnValue(array('mapping' => array('id' => '', 'dependencies' => ''))));
$this->entityType->expects($this->any())
->method('getPropertiesToExport')
->willReturn([]);
$properties = $this->entity->toArray();
$this->assertInternalType('array', $properties);
$this->assertEquals(array('id' => $this->entity->id(), 'dependencies' => array()), $properties);
}
/**
* @covers ::toArray
*
* @expectedException \Drupal\Core\Config\Schema\SchemaIncompleteException
*/
public function testToArrayFallback() {
$this->entityType->expects($this->any())
->method('getPropertiesToExport')
->willReturn([]);
$this->entity->toArray();
}
/**
* @covers ::getThirdPartySetting
* @covers ::setThirdPartySetting
* @covers ::getThirdPartySettings
* @covers ::unsetThirdPartySetting
* @covers ::getThirdPartyProviders
*/
public function testThirdPartySettings() {
$key = 'test';
$third_party = 'test_provider';
$value = $this->getRandomGenerator()->string();
// Test getThirdPartySetting() with no settings.
$this->assertEquals($value, $this->entity->getThirdPartySetting($third_party, $key, $value));
$this->assertNull($this->entity->getThirdPartySetting($third_party, $key));
// Test setThirdPartySetting().
$this->entity->setThirdPartySetting($third_party, $key, $value);
$this->assertEquals($value, $this->entity->getThirdPartySetting($third_party, $key));
$this->assertEquals($value, $this->entity->getThirdPartySetting($third_party, $key, $this->randomGenerator->string()));
// Test getThirdPartySettings().
$this->entity->setThirdPartySetting($third_party, 'test2', 'value2');
$this->assertEquals(array($key => $value, 'test2' => 'value2'), $this->entity->getThirdPartySettings($third_party));
// Test getThirdPartyProviders().
$this->entity->setThirdPartySetting('test_provider2', $key, $value);
$this->assertEquals(array($third_party, 'test_provider2'), $this->entity->getThirdPartyProviders());
// Test unsetThirdPartyProviders().
$this->entity->unsetThirdPartySetting('test_provider2', $key);
$this->assertEquals(array($third_party), $this->entity->getThirdPartyProviders());
}
}

View file

@ -0,0 +1,57 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\Entity\ConfigEntityDependencyTest.
*/
namespace Drupal\Tests\Core\Config\Entity;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Config\Entity\ConfigEntityDependency;
/**
* Tests the ConfigEntityDependency class.
*
* @group Config
*/
class ConfigEntityDependencyTest extends UnitTestCase {
public function testEmptyDependencies() {
$dep = new ConfigEntityDependency('config_test.dynamic.entity_id', array());
$this->assertEquals('config_test.dynamic.entity_id', $dep->getConfigDependencyName());
$this->assertEquals(array(), $dep->getDependencies('theme'));
$this->assertEquals(array(), $dep->getDependencies('config'));
$this->assertEquals(array('config_test'), $dep->getDependencies('module'));
$this->assertTrue($dep->hasDependency('module', 'config_test'));
$this->assertFalse($dep->hasDependency('module', 'views'));
}
public function testWithDependencies() {
$values = array(
'uuid' => '60db47f4-54fb-4c86-a439-5769fbda4bd1',
'dependencies' => array(
'module' => array(
'node',
'views'
),
'config' => array(
'config_test.dynamic.entity_id:745b0ce0-aece-42dd-a800-ade5b8455e84',
),
),
);
$dep = new ConfigEntityDependency('config_test.dynamic.entity_id', $values);
$this->assertEquals(array(), $dep->getDependencies('theme'));
$this->assertEquals(array('config_test.dynamic.entity_id:745b0ce0-aece-42dd-a800-ade5b8455e84'), $dep->getDependencies('config'));
$this->assertEquals(array('node', 'views', 'config_test'), $dep->getDependencies('module'));
$this->assertTrue($dep->hasDependency('module', 'config_test'));
$this->assertTrue($dep->hasDependency('module', 'views'));
$this->assertTrue($dep->hasDependency('module', 'node'));
$this->assertFalse($dep->hasDependency('module', 'block'));
$this->assertTrue($dep->hasDependency('config', 'config_test.dynamic.entity_id:745b0ce0-aece-42dd-a800-ade5b8455e84'));
$this->assertFalse($dep->hasDependency('config', 'config_test.dynamic.another_id:7dfa5cb7-2248-4d52-8c00-cd8e02d1e78e'));
}
}

View file

@ -0,0 +1,882 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\Entity\ConfigEntityStorageTest.
*/
namespace Drupal\Tests\Core\Config\Entity {
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Language\Language;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @coversDefaultClass \Drupal\Core\Config\Entity\ConfigEntityStorage
* @group Config
*/
class ConfigEntityStorageTest extends UnitTestCase {
/**
* The entity type.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityType;
/**
* The type ID of the entity under test.
*
* @var string
*/
protected $entityTypeId;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $moduleHandler;
/**
* The UUID service.
*
* @var \Drupal\Component\Uuid\UuidInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $uuidService;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $languageManager;
/**
* The config storage.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityStorage
*/
protected $entityStorage;
/**
* The config factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $configFactory;
/**
* The entity query.
*
* @var \Drupal\Core\Entity\Query\QueryInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityQuery;
/**
* The entity manager used for testing.
*
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityManager;
/**
* The mocked cache backend.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cacheTagsInvalidator;
/**
* The mocked typed config manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $typedConfigManager;
/**
* The configuration manager.
*
* @var \Drupal\Core\Config\ConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $configManager;
/**
* {@inheritdoc}
*
* @covers ::__construct
*/
protected function setUp() {
parent::setUp();
$this->entityType = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
$this->entityTypeId = 'test_entity_type';
$this->entityType->expects($this->any())
->method('getKey')
->will($this->returnValueMap(array(
array('id', 'id'),
array('uuid', 'uuid'),
array('langcode', 'langcode'),
)));
$this->entityType->expects($this->any())
->method('id')
->will($this->returnValue($this->entityTypeId));
$this->entityType->expects($this->any())
->method('getConfigPrefix')
->will($this->returnValue('the_config_prefix'));
$this->entityType->expects($this->any())
->method('getClass')
->will($this->returnValue(get_class($this->getMockEntity())));
$this->entityType->expects($this->any())
->method('getListCacheTags')
->willReturn(array('test_entity_type_list'));
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
$this->uuidService = $this->getMock('Drupal\Component\Uuid\UuidInterface');
$this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
$this->languageManager->expects($this->any())
->method('getCurrentLanguage')
->willReturn(new Language(array('id' => 'hu')));
$this->configFactory = $this->getMock('Drupal\Core\Config\ConfigFactoryInterface');
$this->entityQuery = $this->getMock('Drupal\Core\Entity\Query\QueryInterface');
$this->entityStorage = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigEntityStorage')
->setConstructorArgs(array($this->entityType, $this->configFactory, $this->uuidService, $this->languageManager))
->setMethods(array('getQuery'))
->getMock();
$this->entityStorage->expects($this->any())
->method('getQuery')
->will($this->returnValue($this->entityQuery));
$this->entityStorage->setModuleHandler($this->moduleHandler);
$this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
$this->entityManager->expects($this->any())
->method('getDefinition')
->with('test_entity_type')
->will($this->returnValue($this->entityType));
$this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
$this->typedConfigManager = $this->getMock('Drupal\Core\Config\TypedConfigManagerInterface');
$this->typedConfigManager->expects($this->any())
->method('getDefinition')
->will($this->returnValue(array('mapping' => array('id' => '', 'uuid' => '', 'dependencies' => ''))));
$this->configManager = $this->getMock('Drupal\Core\Config\ConfigManagerInterface');
$container = new ContainerBuilder();
$container->set('entity.manager', $this->entityManager);
$container->set('config.typed', $this->typedConfigManager);
$container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
$container->set('config.manager', $this->configManager);
$container->set('language_manager', $this->languageManager);
\Drupal::setContainer($container);
}
/**
* @covers ::create
* @covers ::doCreate
*/
public function testCreateWithPredefinedUuid() {
$this->cacheTagsInvalidator->expects($this->never())
->method('invalidateTags');
$this->moduleHandler->expects($this->at(0))
->method('invokeAll')
->with('test_entity_type_create');
$this->moduleHandler->expects($this->at(1))
->method('invokeAll')
->with('entity_create');
$this->uuidService->expects($this->never())
->method('generate');
$entity = $this->entityStorage->create(array('id' => 'foo', 'uuid' => 'baz'));
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
$this->assertSame('foo', $entity->id());
$this->assertSame('baz', $entity->uuid());
}
/**
* @covers ::create
* @covers ::doCreate
*
* @return \Drupal\Core\Entity\EntityInterface
*/
public function testCreate() {
$this->cacheTagsInvalidator->expects($this->never())
->method('invalidateTags');
$this->moduleHandler->expects($this->at(0))
->method('invokeAll')
->with('test_entity_type_create');
$this->moduleHandler->expects($this->at(1))
->method('invokeAll')
->with('entity_create');
$this->uuidService->expects($this->once())
->method('generate')
->will($this->returnValue('bar'));
$entity = $this->entityStorage->create(array('id' => 'foo'));
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
$this->assertSame('foo', $entity->id());
$this->assertSame('bar', $entity->uuid());
return $entity;
}
/**
* @covers ::create
* @covers ::doCreate
*/
public function testCreateWithCurrentLanguage() {
$this->languageManager->expects($this->any())
->method('getLanguage')
->with('hu')
->willReturn(new Language(array('id' => 'hu')));
$entity = $this->entityStorage->create(array('id' => 'foo'));
$this->assertSame('hu', $entity->language()->getId());
}
/**
* @covers ::create
* @covers ::doCreate
*/
public function testCreateWithExplicitLanguage() {
$this->languageManager->expects($this->any())
->method('getLanguage')
->with('en')
->willReturn(new Language(array('id' => 'en')));
$entity = $this->entityStorage->create(array('id' => 'foo', 'langcode' => 'en'));
$this->assertSame('en', $entity->language()->getId());
}
/**
* @covers ::save
* @covers ::doSave
*
* @param \Drupal\Core\Entity\EntityInterface $entity
*
* @return \Drupal\Core\Entity\EntityInterface
*
* @depends testCreate
*/
public function testSaveInsert(EntityInterface $entity) {
$config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$config_object->expects($this->atLeastOnce())
->method('isNew')
->will($this->returnValue(TRUE));
$config_object->expects($this->exactly(1))
->method('setData');
$config_object->expects($this->once())
->method('save');
$config_object->expects($this->atLeastOnce())
->method('get')
->willReturn([]);
$this->cacheTagsInvalidator->expects($this->once())
->method('invalidateTags')
->with(array(
$this->entityTypeId . '_list', // List cache tag.
));
$this->configFactory->expects($this->exactly(1))
->method('get')
->with('the_config_prefix.foo')
->will($this->returnValue($config_object));
$this->configFactory->expects($this->exactly(1))
->method('getEditable')
->with('the_config_prefix.foo')
->will($this->returnValue($config_object));
$this->moduleHandler->expects($this->at(0))
->method('invokeAll')
->with('test_entity_type_presave');
$this->moduleHandler->expects($this->at(1))
->method('invokeAll')
->with('entity_presave');
$this->moduleHandler->expects($this->at(2))
->method('invokeAll')
->with('test_entity_type_insert');
$this->moduleHandler->expects($this->at(3))
->method('invokeAll')
->with('entity_insert');
$this->entityQuery->expects($this->once())
->method('condition')
->with('uuid', 'bar')
->will($this->returnSelf());
$this->entityQuery->expects($this->once())
->method('execute')
->will($this->returnValue(array()));
$return = $this->entityStorage->save($entity);
$this->assertSame(SAVED_NEW, $return);
return $entity;
}
/**
* @covers ::save
* @covers ::doSave
*
* @param \Drupal\Core\Entity\EntityInterface $entity
*
* @return \Drupal\Core\Entity\EntityInterface
*
* @depends testSaveInsert
*/
public function testSaveUpdate(EntityInterface $entity) {
$config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$config_object->expects($this->atLeastOnce())
->method('isNew')
->will($this->returnValue(FALSE));
$config_object->expects($this->exactly(1))
->method('setData');
$config_object->expects($this->once())
->method('save');
$config_object->expects($this->atLeastOnce())
->method('get')
->willReturn([]);
$this->cacheTagsInvalidator->expects($this->once())
->method('invalidateTags')
->with(array(
// List cache tag only; the own cache tag is invalidated by the config
// system.
$this->entityTypeId . '_list',
));
$this->configFactory->expects($this->exactly(2))
->method('loadMultiple')
->with(array('the_config_prefix.foo'))
->will($this->returnValue(array()));
$this->configFactory->expects($this->exactly(1))
->method('get')
->with('the_config_prefix.foo')
->will($this->returnValue($config_object));
$this->configFactory->expects($this->exactly(1))
->method('getEditable')
->with('the_config_prefix.foo')
->will($this->returnValue($config_object));
$this->moduleHandler->expects($this->at(0))
->method('invokeAll')
->with('test_entity_type_presave');
$this->moduleHandler->expects($this->at(1))
->method('invokeAll')
->with('entity_presave');
$this->moduleHandler->expects($this->at(2))
->method('invokeAll')
->with('test_entity_type_update');
$this->moduleHandler->expects($this->at(3))
->method('invokeAll')
->with('entity_update');
$this->entityQuery->expects($this->once())
->method('condition')
->with('uuid', 'bar')
->will($this->returnSelf());
$this->entityQuery->expects($this->once())
->method('execute')
->will($this->returnValue(array($entity->id())));
$return = $this->entityStorage->save($entity);
$this->assertSame(SAVED_UPDATED, $return);
return $entity;
}
/**
* @covers ::save
* @covers ::doSave
*
* @depends testSaveInsert
*/
public function testSaveRename(ConfigEntityInterface $entity) {
$config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$config_object->expects($this->atLeastOnce())
->method('isNew')
->will($this->returnValue(FALSE));
$config_object->expects($this->exactly(1))
->method('setData');
$config_object->expects($this->once())
->method('save');
$config_object->expects($this->atLeastOnce())
->method('get')
->willReturn([]);
$this->cacheTagsInvalidator->expects($this->once())
->method('invalidateTags')
->with(array(
// List cache tag only; the own cache tag is invalidated by the config
// system.
$this->entityTypeId . '_list',
));
$this->configFactory->expects($this->once())
->method('rename')
->willReturn($this->configFactory);
$this->configFactory->expects($this->exactly(1))
->method('getEditable')
->with('the_config_prefix.bar')
->will($this->returnValue($config_object));
$this->configFactory->expects($this->exactly(2))
->method('loadMultiple')
->with(array('the_config_prefix.foo'))
->will($this->returnValue(array()));
$this->configFactory->expects($this->once())
->method('get')
->with('the_config_prefix.foo')
->will($this->returnValue($config_object));
// Performing a rename does not change the original ID until saving.
$this->assertSame('foo', $entity->getOriginalId());
$entity->set('id', 'bar');
$this->assertSame('foo', $entity->getOriginalId());
$this->entityQuery->expects($this->once())
->method('condition')
->with('uuid', 'bar')
->will($this->returnSelf());
$this->entityQuery->expects($this->once())
->method('execute')
->will($this->returnValue(array($entity->id())));
$return = $this->entityStorage->save($entity);
$this->assertSame(SAVED_UPDATED, $return);
$this->assertSame('bar', $entity->getOriginalId());
}
/**
* @covers ::save
*
* @expectedException \Drupal\Core\Entity\EntityMalformedException
* @expectedExceptionMessage The entity does not have an ID.
*/
public function testSaveInvalid() {
$this->cacheTagsInvalidator->expects($this->never())
->method('invalidateTags');
$entity = $this->getMockEntity();
$this->entityStorage->save($entity);
}
/**
* @covers ::save
* @covers ::doSave
*
* @expectedException \Drupal\Core\Entity\EntityStorageException
*/
public function testSaveDuplicate() {
$config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$config_object->expects($this->atLeastOnce())
->method('isNew')
->will($this->returnValue(FALSE));
$config_object->expects($this->never())
->method('set');
$config_object->expects($this->never())
->method('save');
$this->cacheTagsInvalidator->expects($this->never())
->method('invalidateTags');
$this->configFactory->expects($this->once())
->method('get')
->with('the_config_prefix.foo')
->will($this->returnValue($config_object));
$entity = $this->getMockEntity(array('id' => 'foo'));
$entity->enforceIsNew();
$this->entityStorage->save($entity);
}
/**
* @covers ::save
* @covers ::doSave
*
* @expectedException \Drupal\Core\Config\ConfigDuplicateUUIDException
* @expectedExceptionMessage when this UUID is already used for
*/
public function testSaveMismatch() {
$config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$config_object->expects($this->atLeastOnce())
->method('isNew')
->will($this->returnValue(TRUE));
$config_object->expects($this->never())
->method('save');
$this->cacheTagsInvalidator->expects($this->never())
->method('invalidateTags');
$this->configFactory->expects($this->once())
->method('get')
->with('the_config_prefix.foo')
->will($this->returnValue($config_object));
$this->entityQuery->expects($this->once())
->method('condition')
->will($this->returnSelf());
$this->entityQuery->expects($this->once())
->method('execute')
->will($this->returnValue(array('baz')));
$entity = $this->getMockEntity(array('id' => 'foo'));
$this->entityStorage->save($entity);
}
/**
* @covers ::save
* @covers ::doSave
*/
public function testSaveNoMismatch() {
$config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$config_object->expects($this->atLeastOnce())
->method('isNew')
->will($this->returnValue(TRUE));
$config_object->expects($this->once())
->method('save');
$config_object->expects($this->atLeastOnce())
->method('get')
->willReturn([]);
$this->cacheTagsInvalidator->expects($this->once())
->method('invalidateTags')
->with(array(
$this->entityTypeId . '_list', // List cache tag.
));
$this->configFactory->expects($this->once())
->method('get')
->with('the_config_prefix.baz')
->will($this->returnValue($config_object));
$this->configFactory->expects($this->once())
->method('rename')
->willReturn($this->configFactory);
$this->configFactory->expects($this->exactly(1))
->method('getEditable')
->with('the_config_prefix.foo')
->will($this->returnValue($config_object));
$this->entityQuery->expects($this->once())
->method('condition')
->will($this->returnSelf());
$this->entityQuery->expects($this->once())
->method('execute')
->will($this->returnValue(array('baz')));
$entity = $this->getMockEntity(array('id' => 'foo'));
$entity->setOriginalId('baz');
$entity->enforceIsNew();
$this->entityStorage->save($entity);
}
/**
* @covers ::save
* @covers ::doSave
*
* @expectedException \Drupal\Core\Config\ConfigDuplicateUUIDException
* @expectedExceptionMessage when this entity already exists with UUID
*/
public function testSaveChangedUuid() {
$config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$config_object->expects($this->atLeastOnce())
->method('isNew')
->will($this->returnValue(FALSE));
$config_object->expects($this->never())
->method('save');
$config_object->expects($this->exactly(2))
->method('get')
->will($this->returnValueMap(array(
array('', array('id' => 'foo')),
array('id', 'foo'),
)));
$this->cacheTagsInvalidator->expects($this->never())
->method('invalidateTags');
$this->configFactory->expects($this->at(1))
->method('loadMultiple')
->with(array('the_config_prefix.foo'))
->will($this->returnValue(array()));
$this->configFactory->expects($this->at(2))
->method('loadMultiple')
->with(array('the_config_prefix.foo'))
->will($this->returnValue(array($config_object)));
$this->configFactory->expects($this->once())
->method('get')
->with('the_config_prefix.foo')
->will($this->returnValue($config_object));
$this->configFactory->expects($this->never())
->method('rename')
->will($this->returnValue($config_object));
$this->moduleHandler->expects($this->exactly(2))
->method('getImplementations')
->will($this->returnValue(array()));
$this->entityQuery->expects($this->once())
->method('condition')
->will($this->returnSelf());
$this->entityQuery->expects($this->once())
->method('execute')
->will($this->returnValue(array('foo')));
$entity = $this->getMockEntity(array('id' => 'foo'));
$entity->set('uuid', 'baz');
$this->entityStorage->save($entity);
}
/**
* @covers ::load
* @covers ::postLoad
* @covers ::mapFromStorageRecords
* @covers ::doLoadMultiple
*/
public function testLoad() {
$config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$config_object->expects($this->exactly(2))
->method('get')
->will($this->returnValueMap(array(
array('', array('id' => 'foo')),
array('id', 'foo'),
)));
$this->configFactory->expects($this->once())
->method('loadMultiple')
->with(array('the_config_prefix.foo'))
->will($this->returnValue(array($config_object)));
$this->moduleHandler->expects($this->exactly(2))
->method('getImplementations')
->will($this->returnValue(array()));
$entity = $this->entityStorage->load('foo');
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
$this->assertSame('foo', $entity->id());
}
/**
* @covers ::loadMultiple
* @covers ::postLoad
* @covers ::mapFromStorageRecords
* @covers ::doLoadMultiple
*/
public function testLoadMultipleAll() {
$foo_config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$foo_config_object->expects($this->exactly(2))
->method('get')
->will($this->returnValueMap(array(
array('', array('id' => 'foo')),
array('id', 'foo'),
)));
$bar_config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$bar_config_object->expects($this->exactly(2))
->method('get')
->will($this->returnValueMap(array(
array('', array('id' => 'bar')),
array('id', 'bar'),
)));
$this->configFactory->expects($this->once())
->method('listAll')
->with('the_config_prefix.')
->will($this->returnValue(array('the_config_prefix.foo' , 'the_config_prefix.bar')));
$this->configFactory->expects($this->once())
->method('loadMultiple')
->with(array('the_config_prefix.foo' , 'the_config_prefix.bar'))
->will($this->returnValue(array($foo_config_object, $bar_config_object)));
$this->moduleHandler->expects($this->exactly(2))
->method('getImplementations')
->will($this->returnValue(array()));
$entities = $this->entityStorage->loadMultiple();
$expected['foo'] = 'foo';
$expected['bar'] = 'bar';
foreach ($entities as $id => $entity) {
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
$this->assertSame($id, $entity->id());
$this->assertSame($expected[$id], $entity->id());
}
}
/**
* @covers ::loadMultiple
* @covers ::postLoad
* @covers ::mapFromStorageRecords
* @covers ::doLoadMultiple
*/
public function testLoadMultipleIds() {
$config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$config_object->expects($this->exactly(2))
->method('get')
->will($this->returnValueMap(array(
array('', array('id' => 'foo')),
array('id', 'foo'),
)));
$this->configFactory->expects($this->once())
->method('loadMultiple')
->with(array('the_config_prefix.foo'))
->will($this->returnValue(array($config_object)));
$this->moduleHandler->expects($this->exactly(2))
->method('getImplementations')
->will($this->returnValue(array()));
$entities = $this->entityStorage->loadMultiple(array('foo'));
foreach ($entities as $id => $entity) {
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
$this->assertSame($id, $entity->id());
}
}
/**
* @covers ::loadRevision
*/
public function testLoadRevision() {
$this->assertSame(NULL, $this->entityStorage->loadRevision(1));
}
/**
* @covers ::deleteRevision
*/
public function testDeleteRevision() {
$this->cacheTagsInvalidator->expects($this->never())
->method('invalidateTags');
$this->assertSame(NULL, $this->entityStorage->deleteRevision(1));
}
/**
* @covers ::delete
* @covers ::doDelete
*/
public function testDelete() {
// Dependencies are tested in \Drupal\config\Tests\ConfigDependencyTest.
$this->configManager->expects($this->any())
->method('getConfigEntitiesToChangeOnDependencyRemoval')
->willReturn(['update' => [], 'delete' => [], 'unchanged' => []]);
$entities = array();
$configs = array();
$config_map = array();
foreach (array('foo', 'bar') as $id) {
$entity = $this->getMockEntity(array('id' => $id));
$entities[] = $entity;
$config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->getMock();
$config_object->expects($this->once())
->method('delete');
$configs[] = $config_object;
$config_map[] = array("the_config_prefix.$id", $config_object);
}
$this->cacheTagsInvalidator->expects($this->once())
->method('invalidateTags')
->with(array(
// List cache tag only; the own cache tag is invalidated by the config
// system.
$this->entityTypeId . '_list',
));
$this->configFactory->expects($this->exactly(2))
->method('getEditable')
->will($this->returnValueMap($config_map));
$this->moduleHandler->expects($this->at(0))
->method('invokeAll')
->with('test_entity_type_predelete');
$this->moduleHandler->expects($this->at(1))
->method('invokeAll')
->with('entity_predelete');
$this->moduleHandler->expects($this->at(2))
->method('invokeAll')
->with('test_entity_type_predelete');
$this->moduleHandler->expects($this->at(3))
->method('invokeAll')
->with('entity_predelete');
$this->moduleHandler->expects($this->at(4))
->method('invokeAll')
->with('test_entity_type_delete');
$this->moduleHandler->expects($this->at(5))
->method('invokeAll')
->with('entity_delete');
$this->moduleHandler->expects($this->at(6))
->method('invokeAll')
->with('test_entity_type_delete');
$this->moduleHandler->expects($this->at(7))
->method('invokeAll')
->with('entity_delete');
$this->entityStorage->delete($entities);
}
/**
* @covers ::delete
* @covers ::doDelete
*/
public function testDeleteNothing() {
$this->moduleHandler->expects($this->never())
->method($this->anything());
$this->configFactory->expects($this->never())
->method('get');
$this->cacheTagsInvalidator->expects($this->never())
->method('invalidateTags');
$this->entityStorage->delete(array());
}
/**
* Creates an entity with specific methods mocked.
*
* @param array $values
* (optional) Values to pass to the constructor.
* @param array $methods
* (optional) The methods to mock.
*
* @return \Drupal\Core\Entity\EntityInterface|\PHPUnit_Framework_MockObject_MockObject
*/
public function getMockEntity(array $values = array(), $methods = array()) {
return $this->getMockForAbstractClass('Drupal\Core\Config\Entity\ConfigEntityBase', array($values, 'test_entity_type'), '', TRUE, TRUE, TRUE, $methods);
}
}
}
namespace {
if (!defined('SAVED_NEW')) {
define('SAVED_NEW', 1);
}
if (!defined('SAVED_UPDATED')) {
define('SAVED_UPDATED', 2);
}
}

View file

@ -0,0 +1,190 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\Entity\ConfigEntityTypeTest.
*/
namespace Drupal\Tests\Core\Config\Entity;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Config\Entity\ConfigEntityType;
use Drupal\Component\Utility\SafeMarkup;
/**
* @coversDefaultClass \Drupal\Core\Config\Entity\ConfigEntityType
* @group Config
*/
class ConfigEntityTypeTest extends UnitTestCase {
/**
* Sets up a ConfigEntityType object for a given set of values.
*
* @param array $definition
* An array of values to use for the ConfigEntityType.
*
* @return \Drupal\Core\Config\Entity\ConfigEntityTypeInterface
*/
protected function setUpConfigEntityType($definition) {
if (!isset($definition['id'])) {
$definition += array(
'id' => 'example_config_entity_type',
);
}
return new ConfigEntityType($definition);
}
/**
* Tests that we get an exception when the length of the config prefix that is
* returned by getConfigPrefix() exceeds the maximum defined prefix length.
*
* @covers ::getConfigPrefix
*/
public function testConfigPrefixLengthExceeds() {
$message_text = 'The configuration file name prefix @config_prefix exceeds the maximum character limit of @max_char.';
// A provider length of 24 and config_prefix length of 59 (+1 for the .)
// results in a config length of 84, which is too long.
$definition = array(
'provider' => $this->randomMachineName(24),
'config_prefix' => $this->randomMachineName(59),
);
$config_entity = $this->setUpConfigEntityType($definition);
$this->setExpectedException('\Drupal\Core\Config\ConfigPrefixLengthException', SafeMarkup::format($message_text, array(
'@config_prefix' => $definition['provider'] . '.' . $definition['config_prefix'],
'@max_char' => ConfigEntityType::PREFIX_LENGTH,
)));
$this->assertEmpty($config_entity->getConfigPrefix());
}
/**
* Tests that a valid config prefix returned by getConfigPrefix()
* does not throw an exception and is formatted as expected.
*
* @covers ::getConfigPrefix
*/
public function testConfigPrefixLengthValid() {
// A provider length of 24 and config_prefix length of 58 (+1 for the .)
// results in a config length of 83, which is right at the limit.
$definition = array(
'provider' => $this->randomMachineName(24),
'config_prefix' => $this->randomMachineName(58),
);
$config_entity = $this->setUpConfigEntityType($definition);
$expected_prefix = $definition['provider'] . '.' . $definition['config_prefix'];
$this->assertEquals($expected_prefix, $config_entity->getConfigPrefix());
}
/**
* @covers ::__construct
*/
public function testConstruct() {
$config_entity = new ConfigEntityType([
'id' => 'example_config_entity_type',
]);
$this->assertEquals('Drupal\Core\Config\Entity\ConfigEntityStorage', $config_entity->getStorageClass());
}
/**
* @covers ::__construct
*
* @expectedException \Drupal\Core\Config\Entity\Exception\ConfigEntityStorageClassException
* @expectedExceptionMessage \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage is not \Drupal\Core\Config\Entity\ConfigEntityStorage or it does not extend it
*/
public function testConstructBadStorage() {
new ConfigEntityType([
'id' => 'example_config_entity_type',
'handlers' => ['storage' => '\Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage']
]);
}
/**
* @covers ::setStorageClass
*
* @expectedException \Drupal\Core\Config\Entity\Exception\ConfigEntityStorageClassException
* @expectedExceptionMessage \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage is not \Drupal\Core\Config\Entity\ConfigEntityStorage or it does not extend it
*/
public function testSetStorageClass() {
$config_entity = $this->setUpConfigEntityType([]);
$config_entity->setStorageClass('\Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage');
}
/**
* Tests the getConfigPrefix() method.
*
* @dataProvider providerTestGetConfigPrefix
*
* @covers ::getConfigPrefix
*/
public function testGetConfigPrefix($definition, $expected) {
$entity_type = $this->setUpConfigEntityType($definition);
$this->assertSame($expected, $entity_type->getConfigPrefix());
}
/**
* Provides test data.
*/
public function providerTestGetConfigPrefix() {
return array(
array(array('provider' => 'node', 'id' => 'node_type', 'config_prefix' => 'type'), 'node.type'),
array(array('provider' => 'views', 'id' => 'view'), 'views.view'),
);
}
/**
* @covers ::getPropertiesToExport
*
* @dataProvider providerGetPropertiesToExport
*/
public function testGetPropertiesToExport($definition, $expected) {
$entity_type = $this->setUpConfigEntityType($definition);
$properties_to_export = $entity_type->getPropertiesToExport();
$this->assertSame($expected, $properties_to_export);
// Ensure the method is idempotent.
$properties_to_export = $entity_type->getPropertiesToExport();
$this->assertSame($expected, $properties_to_export);
}
public function providerGetPropertiesToExport() {
$data = [];
$data[] = [
[],
NULL,
];
$data[] = [
[
'config_export' => [
'id',
'custom_property' => 'customProperty',
],
],
[
'uuid' => 'uuid',
'langcode' => 'langcode',
'status' => 'status',
'dependencies' => 'dependencies',
'third_party_settings' => 'third_party_settings',
'id' => 'id',
'custom_property' => 'customProperty',
],
];
$data[] = [
[
'config_export' => [
'id',
],
'mergedConfigExport' => [
'random_key' => 'random_key',
],
],
[
'random_key' => 'random_key',
],
];
return $data;
}
}

View file

@ -0,0 +1,74 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\Entity\EntityDisplayBaseTest.
*/
namespace Drupal\Tests\Core\Config\Entity;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Entity\EntityDisplayBase
*
* @group Config
*/
class EntityDisplayBaseTest extends UnitTestCase {
/**
* @covers ::getTargetEntityTypeId
*/
public function testGetTargetEntityTypeId() {
$mock = $this->getMockForAbstractClass('\Drupal\Core\Entity\EntityDisplayBase', [], '', FALSE);
$reflection = new \ReflectionProperty($mock, 'targetEntityType');
$reflection->setAccessible(TRUE);
$reflection->setValue($mock, 'test');
$this->assertEquals('test', $mock->getTargetEntityTypeId());
}
/**
* @covers ::getMode
*/
public function testGetMode() {
$mock = $this->getMockForAbstractClass('\Drupal\Core\Entity\EntityDisplayBase', [], '', FALSE);
$reflection = new \ReflectionProperty($mock, 'mode');
$reflection->setAccessible(TRUE);
$reflection->setValue($mock, 'test');
$this->assertEquals('test', $mock->getMode());
}
/**
* @covers ::getOriginalMode
*/
public function testGetOriginalMode() {
$mock = $this->getMockForAbstractClass('\Drupal\Core\Entity\EntityDisplayBase', [], '', FALSE);
$reflection = new \ReflectionProperty($mock, 'originalMode');
$reflection->setAccessible(TRUE);
$reflection->setValue($mock, 'test');
$this->assertEquals('test', $mock->getOriginalMode());
}
/**
* @covers ::getTargetBundle
*/
public function testGetTargetBundle() {
$mock = $this->getMockForAbstractClass('\Drupal\Core\Entity\EntityDisplayBase', [], '', FALSE);
$reflection = new \ReflectionProperty($mock, 'bundle');
$reflection->setAccessible(TRUE);
$reflection->setValue($mock, 'test');
$this->assertEquals('test', $mock->getTargetBundle());
}
/**
* @covers ::setTargetBundle
*/
public function testSetTargetBundle() {
$mock = $this->getMockForAbstractClass('\Drupal\Core\Entity\EntityDisplayBase', [], '', FALSE);
$reflection = new \ReflectionProperty($mock, 'bundle');
$reflection->setAccessible(TRUE);
$mock->setTargetBundle('test');
$this->assertEquals('test', $reflection->getValue($mock));
}
}

View file

@ -0,0 +1,161 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\Entity\EntityDisplayModeBaseUnitTest.
*/
namespace Drupal\Tests\Core\Config\Entity;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Entity\EntityDisplayModeBase
* @group Config
*/
class EntityDisplayModeBaseUnitTest extends UnitTestCase {
/**
* The entity under test.
*
* @var \Drupal\Core\Entity\EntityDisplayModeBase|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entity;
/**
* The entity type used for testing.
*
* @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityInfo;
/**
* The entity manager used for testing.
*
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityManager;
/**
* The ID of the type of the entity under test.
*
* @var string
*/
protected $entityType;
/**
* The UUID generator used for testing.
*
* @var \Drupal\Component\Uuid\UuidInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $uuid;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->entityType = $this->randomMachineName();
$this->entityInfo = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
$this->entityInfo->expects($this->any())
->method('getProvider')
->will($this->returnValue('entity'));
$this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
$this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface');
$container = new ContainerBuilder();
$container->set('entity.manager', $this->entityManager);
$container->set('uuid', $this->uuid);
\Drupal::setContainer($container);
}
/**
* @covers ::calculateDependencies
*/
public function testCalculateDependencies() {
$target_entity_type_id = $this->randomMachineName(16);
$target_entity_type = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
$target_entity_type->expects($this->any())
->method('getProvider')
->will($this->returnValue('test_module'));
$values = array('targetEntityType' => $target_entity_type_id);
$this->entityManager->expects($this->at(0))
->method('getDefinition')
->with($target_entity_type_id)
->will($this->returnValue($target_entity_type));
$this->entityManager->expects($this->at(1))
->method('getDefinition')
->with($this->entityType)
->will($this->returnValue($this->entityInfo));
$this->entity = $this->getMockBuilder('\Drupal\Core\Entity\EntityDisplayModeBase')
->setConstructorArgs(array($values, $this->entityType))
->setMethods(array('getFilterFormat'))
->getMock();
$dependencies = $this->entity->calculateDependencies();
$this->assertContains('test_module', $dependencies['module']);
}
/**
* @covers ::setTargetType
*/
public function testSetTargetType() {
// Generate mock.
$mock = $this->getMock(
'Drupal\Core\Entity\EntityDisplayModeBase',
NULL,
array(array('something' => 'nothing'), 'test_type')
);
// Some test values.
$bad_target = 'uninitialized';
$target = 'test_target_type';
// Gain access to the protected property.
$property = new \ReflectionProperty($mock, 'targetEntityType');
$property->setAccessible(TRUE);
// Set the property to a known state.
$property->setValue($mock, $bad_target);
// Set the target type.
$mock->setTargetType($target);
// Test the outcome.
$this->assertNotEquals($bad_target, $property->getValue($mock));
$this->assertEquals($target, $property->getValue($mock));
}
/**
* @covers ::getTargetType
*/
public function testGetTargetType() {
// Generate mock.
$mock = $this->getMock(
'Drupal\Core\Entity\EntityDisplayModeBase',
NULL,
array(array('something' => 'nothing'), 'test_type')
);
// A test value.
$target = 'test_target_type';
// Gain access to the protected property.
$property = new \ReflectionProperty($mock, 'targetEntityType');
$property->setAccessible(TRUE);
// Set the property to a known state.
$property->setValue($mock, $target);
// Get the target type.
$value = $mock->getTargetType($target);
// Test the outcome.
$this->assertEquals($value, $property->getValue($mock));
}
}

View file

@ -0,0 +1,20 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\Entity\Fixtures\ConfigEntityBaseWithPluginCollections.
*/
namespace Drupal\Tests\Core\Config\Entity\Fixtures;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
/**
* Enables testing of dependency calculation.
*
* @see \Drupal\Tests\Core\Config\Entity\ConfigEntityBaseUnitTest::testCalculateDependenciesWithPluginCollections()
* @see \Drupal\Core\Config\Entity\ConfigEntityBase::calculateDependencies()
*/
abstract class ConfigEntityBaseWithPluginCollections extends ConfigEntityBase implements EntityWithPluginCollectionInterface {
}

View file

@ -0,0 +1,138 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\Entity\Query\QueryFactoryTest.
*/
namespace Drupal\Tests\Core\Config\Entity\Query;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\Entity\Query\QueryFactory;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Config\Entity\Query\QueryFactory
* @group Config
*/
class QueryFactoryTest extends UnitTestCase {
/**
* @covers ::getKeys
* @covers ::getValues
*
* @dataProvider providerTestGetKeys
*/
public function testGetKeys(array $expected, $key, Config $config) {
$config_factory = $this->getMock('Drupal\Core\Config\ConfigFactoryInterface');
$key_value_factory = $this->getMock('Drupal\Core\KeyValueStore\KeyValueFactoryInterface');
$config_manager = $this->getMock('Drupal\Core\Config\ConfigManagerInterface');
$config_entity_type = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
$query_factory = new QueryFactory($config_factory, $key_value_factory, $config_manager);
$method = new \ReflectionMethod($query_factory, 'getKeys');
$method->setAccessible(TRUE);
$actual = $method->invoke($query_factory, $config, $key, 'get', $config_entity_type);
$this->assertEquals($expected, $actual);
}
public function providerTestGetKeys() {
$tests = [];
$tests[] = [
['uuid:abc'],
'uuid',
$this->getConfigObject('test')->set('uuid', 'abc')
];
// Tests a lookup being set to a top level key when sub-keys exist.
$tests[] = [
[],
'uuid',
$this->getConfigObject('test')->set('uuid.blah', 'abc')
];
// Tests a non existent key.
$tests[] = [
[],
'uuid',
$this->getConfigObject('test')
];
// Tests a non existent sub key.
$tests[] = [
[],
'uuid.blah',
$this->getConfigObject('test')->set('uuid', 'abc')
];
// Tests a existent sub key.
$tests[] = [
['uuid.blah:abc'],
'uuid.blah',
$this->getConfigObject('test')->set('uuid.blah', 'abc')
];
// One wildcard.
$tests[] = [
['test.*.value:a', 'test.*.value:b'],
'test.*.value',
$this->getConfigObject('test')->set('test.a.value', 'a')->set('test.b.value', 'b')
];
// Three wildcards.
$tests[] = [
['test.*.sub2.*.sub4.*.value:aaa', 'test.*.sub2.*.sub4.*.value:aab', 'test.*.sub2.*.sub4.*.value:bab'],
'test.*.sub2.*.sub4.*.value',
$this->getConfigObject('test')
->set('test.a.sub2.a.sub4.a.value', 'aaa')
->set('test.a.sub2.a.sub4.b.value', 'aab')
->set('test.b.sub2.a.sub4.b.value', 'bab')
];
// Three wildcards in a row.
$tests[] = [
['test.*.*.*.value:abc', 'test.*.*.*.value:abd'],
'test.*.*.*.value',
$this->getConfigObject('test')->set('test.a.b.c.value', 'abc')->set('test.a.b.d.value', 'abd')
];
return $tests;
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage test_config_entity_type lookup key test.* ends with a wildcard this can not be used as a lookup
*/
public function testGetKeysWildCardEnd() {
$config_factory = $this->getMock('Drupal\Core\Config\ConfigFactoryInterface');
$key_value_factory = $this->getMock('Drupal\Core\KeyValueStore\KeyValueFactoryInterface');
$config_manager = $this->getMock('Drupal\Core\Config\ConfigManagerInterface');
$config_entity_type = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
$config_entity_type->expects($this->atLeastOnce())
->method('id')
->willReturn('test_config_entity_type');
$query_factory = new QueryFactory($config_factory, $key_value_factory, $config_manager);
$method = new \ReflectionMethod($query_factory, 'getKeys');
$method->setAccessible(TRUE);
$method->invoke($query_factory, $this->getConfigObject('test'), 'test.*', 'get', $config_entity_type);
}
/**
* Gets a test configuration object.
*
* @param string $name
* The config name.
*
* @return \Drupal\Core\Config\Config|\PHPUnit_Framework_MockObject_MockObject
* The test configuration object.
*/
protected function getConfigObject($name) {
$config = $this->getMockBuilder('Drupal\Core\Config\Config')
->disableOriginalConstructor()
->setMethods(['save', 'delete'])
->getMock();
return $config->setName($name);
}
}

View file

@ -0,0 +1,70 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\ImmutableConfigTest.
*/
namespace Drupal\Tests\Core\Config;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Config\ImmutableConfig
* @group Config
*/
class ImmutableConfigTest extends UnitTestCase {
/**
* The immutable config object under test.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
protected function setUp() {
parent::setUp();
$storage = $this->getMock('Drupal\Core\Config\StorageInterface');
$event_dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$typed_config = $this->getMock('Drupal\Core\Config\TypedConfigManagerInterface');
$this->config = new ImmutableConfig('test', $storage, $event_dispatcher, $typed_config);
}
/**
* @covers ::set
* @expectedException \Drupal\Core\Config\ImmutableConfigException
* @expectedExceptionMessage Can not set values on immutable configuration test:name. Use \Drupal\Core\Config\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object
*/
public function testSet() {
$this->config->set('name', 'value');
}
/**
* @covers ::clear
* @expectedException \Drupal\Core\Config\ImmutableConfigException
* @expectedExceptionMessage Can not clear name key in immutable configuration test. Use \Drupal\Core\Config\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object
*/
public function testClear() {
$this->config->clear('name');
}
/**
* @covers ::save
* @expectedException \Drupal\Core\Config\ImmutableConfigException
* @expectedExceptionMessage Can not save immutable configuration test. Use \Drupal\Core\Config\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object
*/
public function testSave() {
$this->config->save();
}
/**
* @covers ::delete
* @expectedException \Drupal\Core\Config\ImmutableConfigException
* @expectedExceptionMessage Can not delete immutable configuration test. Use \Drupal\Core\Config\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object
*/
public function testDelete() {
$this->config->delete();
}
}

View file

@ -0,0 +1,252 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\StorageComparerTest.
*/
namespace Drupal\Tests\Core\Config;
use Drupal\Component\Uuid\Php;
use Drupal\Core\Config\StorageComparer;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Config\StorageComparer
* @group Config
*/
class StorageComparerTest extends UnitTestCase {
/**
* @var \Drupal\Core\Config\StorageInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $sourceStorage;
/**
* @var \Drupal\Core\Config\StorageInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $targetStorage;
/**
* @var \Drupal\Core\Config\ConfigManager|\PHPUnit_Framework_MockObject_MockObject
*/
protected $configManager;
/**
* The storage comparer to test.
*
* @var \Drupal\Core\Config\StorageComparer
*/
protected $storageComparer;
/**
* An array of test configuration data keyed by configuration name.
*
* @var array
*/
protected $configData;
protected function setUp() {
$this->sourceStorage = $this->getMock('Drupal\Core\Config\StorageInterface');
$this->targetStorage = $this->getMock('Drupal\Core\Config\StorageInterface');
$this->configManager = $this->getMock('Drupal\Core\Config\ConfigManagerInterface');
$this->storageComparer = new StorageComparer($this->sourceStorage, $this->targetStorage, $this->configManager);
}
protected function getConfigData() {
$uuid = new Php();
// Mock data using minimal data to use ConfigDependencyManger.
$this->configData = array(
// Simple config that controls configuration sync.
'system.site' => array(
'title' => 'Drupal',
'uuid' => $uuid->generate(),
),
// Config entity which requires another config entity.
'field.field.node.article.body' => array(
'id' => 'node.article.body',
'uuid' => $uuid->generate(),
'dependencies' => array(
'config' => array(
'field.storage.node.body'
),
),
),
// Config entity which is required by another config entity.
'field.storage.node.body' => array(
'id' => 'node.body',
'uuid' => $uuid->generate(),
'dependencies' => array(
'module' => array(
'text',
),
),
),
// Config entity not which has no dependencies on configuration.
'views.view.test_view' => array(
'id' => 'test_view',
'uuid' => $uuid->generate(),
'dependencies' => array(
'module' => array(
'node',
),
),
),
// Simple config.
'system.performance' => array(
'stale_file_threshold' => 2592000
),
);
return $this->configData;
}
/**
* @covers ::createChangelist
*/
public function testCreateChangelistNoChange() {
$config_data = $this->getConfigData();
$config_files = array_keys($config_data);
$this->sourceStorage->expects($this->once())
->method('listAll')
->will($this->returnValue($config_files));
$this->targetStorage->expects($this->once())
->method('listAll')
->will($this->returnValue($config_files));
$this->sourceStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($config_data));
$this->targetStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($config_data));
$this->sourceStorage->expects($this->once())
->method('getAllCollectionNames')
->will($this->returnValue(array()));
$this->targetStorage->expects($this->once())
->method('getAllCollectionNames')
->will($this->returnValue(array()));
$this->storageComparer->createChangelist();
$this->assertEmpty($this->storageComparer->getChangelist('create'));
$this->assertEmpty($this->storageComparer->getChangelist('delete'));
$this->assertEmpty($this->storageComparer->getChangelist('update'));
}
/**
* @covers ::createChangelist
*/
public function testCreateChangelistCreate() {
$target_data = $source_data = $this->getConfigData();
unset($target_data['field.storage.node.body']);
unset($target_data['field.field.node.article.body']);
unset($target_data['views.view.test_view']);
$this->sourceStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($source_data)));
$this->targetStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($target_data)));
$this->sourceStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($source_data));
$this->targetStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($target_data));
$this->sourceStorage->expects($this->once())
->method('getAllCollectionNames')
->will($this->returnValue(array()));
$this->targetStorage->expects($this->once())
->method('getAllCollectionNames')
->will($this->returnValue(array()));
$this->storageComparer->createChangelist();
$expected = array(
'field.storage.node.body',
'views.view.test_view',
'field.field.node.article.body',
);
$this->assertEquals($expected, $this->storageComparer->getChangelist('create'));
$this->assertEmpty($this->storageComparer->getChangelist('delete'));
$this->assertEmpty($this->storageComparer->getChangelist('update'));
}
/**
* @covers ::createChangelist
*/
public function testCreateChangelistDelete() {
$target_data = $source_data = $this->getConfigData();
unset($source_data['field.storage.node.body']);
unset($source_data['field.field.node.article.body']);
unset($source_data['views.view.test_view']);
$this->sourceStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($source_data)));
$this->targetStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($target_data)));
$this->sourceStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($source_data));
$this->targetStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($target_data));
$this->sourceStorage->expects($this->once())
->method('getAllCollectionNames')
->will($this->returnValue(array()));
$this->targetStorage->expects($this->once())
->method('getAllCollectionNames')
->will($this->returnValue(array()));
$this->storageComparer->createChangelist();
$expected = array(
'field.field.node.article.body',
'views.view.test_view',
'field.storage.node.body',
);
$this->assertEquals($expected, $this->storageComparer->getChangelist('delete'));
$this->assertEmpty($this->storageComparer->getChangelist('create'));
$this->assertEmpty($this->storageComparer->getChangelist('update'));
}
/**
* @covers ::createChangelist
*/
public function testCreateChangelistUpdate() {
$target_data = $source_data = $this->getConfigData();
$source_data['system.site']['title'] = 'Drupal New!';
$source_data['field.field.node.article.body']['new_config_key'] = 'new data';
$source_data['field.storage.node.body']['new_config_key'] = 'new data';
$this->sourceStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($source_data)));
$this->targetStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($target_data)));
$this->sourceStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($source_data));
$this->targetStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($target_data));
$this->sourceStorage->expects($this->once())
->method('getAllCollectionNames')
->will($this->returnValue(array()));
$this->targetStorage->expects($this->once())
->method('getAllCollectionNames')
->will($this->returnValue(array()));
$this->storageComparer->createChangelist();
$expected = array(
'field.storage.node.body',
'system.site',
'field.field.node.article.body',
);
$this->assertEquals($expected, $this->storageComparer->getChangelist('update'));
$this->assertEmpty($this->storageComparer->getChangelist('create'));
$this->assertEmpty($this->storageComparer->getChangelist('delete'));
}
}

View file

@ -0,0 +1,79 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\ContentNegotiationTest.
*/
namespace Drupal\Tests\Core;
use Drupal\Core\ContentNegotiation;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
/**
* @coversDefaultClass \Drupal\Core\ContentNegotiation
* @group ContentNegotiation
*/
class ContentNegotiationTest extends UnitTestCase {
/**
* @var \Drupal\Core\ContentNegotiation
*/
protected $contentNegotiation;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->contentNegotiation = new ContentNegotiation;
}
/**
* Tests the getContentType() method with AJAX iframe upload.
*
* @covers ::getContentType
*/
public function testAjaxIframeUpload() {
$request = new Request();
$request->attributes->set('ajax_iframe_upload', '1');
$this->assertSame('iframeupload', $this->contentNegotiation->getContentType($request));
}
/**
* Tests the specifying a format via query parameters gets used.
*/
public function testFormatViaQueryParameter() {
$request = new Request();
$request->query->set('_format', 'bob');
$this->assertSame('bob', $this->contentNegotiation->getContentType($request));
}
/**
* Tests the getContentType() method when no priority format is found.
*
* @covers ::getContentType
*/
public function testUnknowContentTypeReturnsHtmlByDefault() {
$request = new Request();
$this->assertSame('html', $this->contentNegotiation->getContentType($request));
}
/**
* Tests the getContentType() method when no priority format is found but it's an AJAX request.
*
* @covers ::getContentType
*/
public function testUnknowContentTypeButAjaxRequest() {
$request = new Request();
$request->headers->set('X-Requested-With', 'XMLHttpRequest');
$this->assertSame('ajax', $this->contentNegotiation->getContentType($request));
}
}

View file

@ -0,0 +1,101 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Controller\AjaxRendererTest.
*/
namespace Drupal\Tests\Core\Controller;
use Drupal\Core\Render\MainContent\AjaxRenderer;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @coversDefaultClass \Drupal\Core\Render\MainContent\AjaxRenderer
* @group Ajax
*/
class AjaxRendererTest extends UnitTestCase {
/**
* The tested ajax controller.
*
* @var \Drupal\Core\Render\MainContent\AjaxRenderer
*/
protected $ajaxRenderer;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $renderer;
/**
* {@inheritdoc}
*/
protected function setUp() {
$element_info_manager = $this->getMock('Drupal\Core\Render\ElementInfoManagerInterface');
$element_info_manager->expects($this->any())
->method('getInfo')
->with('ajax')
->willReturn([
'#header' => TRUE,
'#commands' => array(),
'#error' => NULL,
]);
$this->ajaxRenderer = new TestAjaxRenderer($element_info_manager);
$this->renderer = $this->getMockBuilder('Drupal\Core\Render\Renderer')
->disableOriginalConstructor()
->setMethods(NULL)
->getMock();
$container = new ContainerBuilder();
$container->set('renderer', $this->renderer);
\Drupal::setContainer($container);
}
/**
* Tests the content method.
*
* @covers ::renderResponse
*/
public function testRenderWithFragmentObject() {
$main_content = ['#markup' => 'example content'];
$request = new Request();
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
/** @var \Drupal\Core\Ajax\AjaxResponse $result */
$result = $this->ajaxRenderer->renderResponse($main_content, $request, $route_match);
$this->assertInstanceOf('Drupal\Core\Ajax\AjaxResponse', $result);
$commands = $result->getCommands();
$this->assertEquals('insert', $commands[0]['command']);
$this->assertEquals('example content', $commands[0]['data']);
$this->assertEquals('insert', $commands[1]['command']);
$this->assertEquals('status_messages', $commands[1]['data']);
}
}
class TestAjaxRenderer extends AjaxRenderer {
/**
* {@inheritdoc}
*/
protected function drupalRenderRoot(&$elements, $is_root_call = FALSE) {
$elements += ['#attached' => []];
if (isset($elements['#markup'])) {
return $elements['#markup'];
}
elseif (isset($elements['#type'])) {
return $elements['#type'];
}
else {
return 'Markup';
}
}
}

View file

@ -0,0 +1,61 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Controller\ControllerBaseTest.
*/
namespace Drupal\Tests\Core\Controller;
use Drupal\Tests\UnitTestCase;
/**
* Tests that the base controller class.
*
* @group Controller
*/
class ControllerBaseTest extends UnitTestCase {
/**
* The tested controller base class.
*
* @var \Drupal\Core\Controller\ControllerBase|\PHPUnit_Framework_MockObject_MockObject
*/
protected $controllerBase;
protected function setUp() {
$this->controllerBase = $this->getMockForAbstractClass('Drupal\Core\Controller\ControllerBase');
}
/**
* Tests the config method.
*/
public function testGetConfig() {
$config_factory = $this->getConfigFactoryStub(array(
'config_name' => array(
'key' => 'value',
),
'config_name2' => array(
'key2' => 'value2',
),
));
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$container->expects($this->once())
->method('get')
->with('config.factory')
->will($this->returnValue($config_factory));
\Drupal::setContainer($container);
$config_method = new \ReflectionMethod('Drupal\Core\Controller\ControllerBase', 'config');
$config_method->setAccessible(TRUE);
// Call config twice to ensure that the container is just called once.
$config = $config_method->invoke($this->controllerBase, 'config_name');
$this->assertEquals('value', $config->get('key'));
$config = $config_method->invoke($this->controllerBase, 'config_name2');
$this->assertEquals('value2', $config->get('key2'));
}
}

View file

@ -0,0 +1,255 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Controller\ControllerResolverTest.
*/
namespace Drupal\Tests\Core\Controller;
use Drupal\Core\Controller\ControllerResolver;
use Drupal\Core\DependencyInjection\ClassResolver;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
/**
* @coversDefaultClass \Drupal\Core\Controller\ControllerResolver
* @group Controller
*/
class ControllerResolverTest extends UnitTestCase {
/**
* The tested controller resolver.
*
* @var \Drupal\Core\Controller\ControllerResolver
*/
public $controllerResolver;
/**
* The container.
*
* @var \Symfony\Component\DependencyInjection\ContainerBuilder
*/
protected $container;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->container = new ContainerBuilder();
$class_resolver = new ClassResolver();
$class_resolver->setContainer($this->container);
$this->controllerResolver = new ControllerResolver($class_resolver);
}
/**
* Tests getArguments().
*
* Ensure that doGetArguments uses converted arguments if available.
*
* @see \Drupal\Core\Controller\ControllerResolver::getArguments()
* @see \Drupal\Core\Controller\ControllerResolver::doGetArguments()
*/
public function testGetArguments() {
$controller = function(EntityInterface $entity, $user, RouteMatchInterface $route_match) {
};
$mock_entity = $this->getMockBuilder('Drupal\Core\Entity\Entity')
->disableOriginalConstructor()
->getMock();
$mock_account = $this->getMock('Drupal\Core\Session\AccountInterface');
$request = new Request(array(), array(), array(
'entity' => $mock_entity,
'user' => $mock_account,
'_raw_variables' => new ParameterBag(array('entity' => 1, 'user' => 1)),
));
$arguments = $this->controllerResolver->getArguments($request, $controller);
$this->assertEquals($mock_entity, $arguments[0]);
$this->assertEquals($mock_account, $arguments[1]);
$this->assertEquals(RouteMatch::createFromRequest($request), $arguments[2], 'Ensure that the route match object is passed along as well');
}
/**
* Tests createController().
*
* @dataProvider providerTestCreateController
*/
public function testCreateController($controller, $class, $output) {
$this->container->set('some_service', new MockController());
$result = $this->controllerResolver->getControllerFromDefinition($controller);
$this->assertCallableController($result, $class, $output);
}
/**
* Provides test data for testCreateController().
*/
public function providerTestCreateController() {
return array(
// Tests class::method.
array('Drupal\Tests\Core\Controller\MockController::getResult', 'Drupal\Tests\Core\Controller\MockController', 'This is a regular controller.'),
// Tests service:method.
array('some_service:getResult', 'Drupal\Tests\Core\Controller\MockController', 'This is a regular controller.'),
// Tests a class with injection.
array('Drupal\Tests\Core\Controller\MockContainerInjection::getResult', 'Drupal\Tests\Core\Controller\MockContainerInjection', 'This used injection.'),
// Tests a ContainerAware class.
array('Drupal\Tests\Core\Controller\MockContainerAware::getResult', 'Drupal\Tests\Core\Controller\MockContainerAware', 'This is container aware.'),
);
}
/**
* Tests createController() with a non-existent class.
*
* @expectedException \InvalidArgumentException
*/
public function testCreateControllerNonExistentClass() {
$this->controllerResolver->getControllerFromDefinition('Class::method');
}
/**
* Tests createController() with an invalid name.
*
* @expectedException \LogicException
*/
public function testCreateControllerInvalidName() {
$this->controllerResolver->getControllerFromDefinition('ClassWithoutMethod');
}
/**
* Tests getController().
*
* @dataProvider providerTestGetController
*/
public function testGetController($attributes, $class, $output = NULL) {
$request = new Request(array(), array(), $attributes);
$result = $this->controllerResolver->getController($request);
if ($class) {
$this->assertCallableController($result, $class, $output);
}
else {
$this->assertSame(FALSE, $result);
}
}
/**
* Provides test data for testGetController().
*/
public function providerTestGetController() {
return array(
// Tests passing a controller via the request.
array(array('_controller' => 'Drupal\Tests\Core\Controller\MockContainerAware::getResult'), 'Drupal\Tests\Core\Controller\MockContainerAware', 'This is container aware.'),
// Tests a request with no controller specified.
array(array(), FALSE)
);
}
/**
* Tests getControllerFromDefinition().
*
* @dataProvider providerTestGetControllerFromDefinition
*/
public function testGetControllerFromDefinition($definition, $output) {
$controller = $this->controllerResolver->getControllerFromDefinition($definition);
$this->assertCallableController($controller, NULL, $output);
}
/**
* Provides test data for testGetControllerFromDefinition().
*/
public function providerTestGetControllerFromDefinition() {
return array(
// Tests a method on an object.
array(array(new MockController(), 'getResult'), 'This is a regular controller.'),
// Tests a function.
array('phpversion', phpversion()),
// Tests an object using __invoke().
array(new MockInvokeController(), 'This used __invoke().'),
// Tests a class using __invoke().
array('Drupal\Tests\Core\Controller\MockInvokeController', 'This used __invoke().'),
);
}
/**
* Tests getControllerFromDefinition() without a callable.
*
* @expectedException \InvalidArgumentException
*/
public function testGetControllerFromDefinitionNotCallable() {
$this->controllerResolver->getControllerFromDefinition('Drupal\Tests\Core\Controller\MockController::bananas');
}
/**
* Asserts that the controller is callable and produces the correct output.
*
* @param callable $controller
* A callable controller.
* @param string|null $class
* Either the name of the class the controller represents, or NULL if it is
* not an object.
* @param mixed $output
* The output expected for this controller.
*/
protected function assertCallableController($controller, $class, $output) {
if ($class) {
$this->assertTrue(is_object($controller[0]));
$this->assertInstanceOf($class, $controller[0]);
}
$this->assertTrue(is_callable($controller));
$this->assertSame($output, call_user_func($controller));
}
/**
* Tests getArguments with a route match and a request.
*
* @covers ::getArguments
* @covers ::doGetArguments
*/
public function testGetArgumentsWithRouteMatchAndRequest() {
$request = Request::create('/test');
$mock_controller = new MockController();
$arguments = $this->controllerResolver->getArguments($request, [$mock_controller, 'getControllerWithRequestAndRouteMatch']);
$this->assertEquals([RouteMatch::createFromRequest($request), $request], $arguments);
}
}
class MockController {
public function getResult() {
return 'This is a regular controller.';
}
public function getControllerWithRequestAndRouteMatch(RouteMatchInterface $route_match, Request $request) {
return 'this is another example controller';
}
}
class MockContainerInjection implements ContainerInjectionInterface {
protected $result;
public function __construct($result) {
$this->result = $result;
}
public static function create(ContainerInterface $container) {
return new static('This used injection.');
}
public function getResult() {
return $this->result;
}
}
class MockContainerAware extends ContainerAware {
public function getResult() {
return 'This is container aware.';
}
}
class MockInvokeController {
public function __invoke() {
return 'This used __invoke().';
}
}

Some files were not shown because too many files have changed in this diff Show more