From b15aed5760094e33ed5ed4f806721bb090dbb30e Mon Sep 17 00:00:00 2001 From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org> Date: Wed, 6 Nov 2013 19:58:22 +0000 Subject: [PATCH] Issue #1859684 by dawehner, effulgentsia: Expand routeBuilder unit test. --- .../lib/Drupal/Core/Routing/MatcherDumper.php | 6 +- .../Core/Routing/MatcherDumperInterface.php | 26 ++ core/lib/Drupal/Core/Routing/RouteBuilder.php | 37 ++- .../system/Tests/Routing/RoutingFixtures.php | 38 +++ .../Tests/Core/Routing/RouteBuilderTest.php | 243 ++++++++++++++++++ 5 files changed, 339 insertions(+), 11 deletions(-) create mode 100644 core/lib/Drupal/Core/Routing/MatcherDumperInterface.php create mode 100644 core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php diff --git a/core/lib/Drupal/Core/Routing/MatcherDumper.php b/core/lib/Drupal/Core/Routing/MatcherDumper.php index 72c0bee65017..a1bfa3ec6e59 100644 --- a/core/lib/Drupal/Core/Routing/MatcherDumper.php +++ b/core/lib/Drupal/Core/Routing/MatcherDumper.php @@ -7,7 +7,6 @@ namespace Drupal\Core\Routing; -use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface; use Symfony\Component\Routing\RouteCollection; use Drupal\Core\Database\Connection; @@ -59,10 +58,7 @@ public function __construct(Connection $connection, $table = 'router') { } /** - * Adds additional routes to be dumped. - * - * @param Symfony\Component\Routing\RouteCollection $routes - * A collection of routes to add to this dumper. + * {@inheritdoc} */ public function addRoutes(RouteCollection $routes) { if (empty($this->routes)) { diff --git a/core/lib/Drupal/Core/Routing/MatcherDumperInterface.php b/core/lib/Drupal/Core/Routing/MatcherDumperInterface.php new file mode 100644 index 000000000000..d4a464f0ee02 --- /dev/null +++ b/core/lib/Drupal/Core/Routing/MatcherDumperInterface.php @@ -0,0 +1,26 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Routing\MatcherDumperInterface. + */ + +namespace Drupal\Core\Routing; + +use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface as SymfonyMatcherDumperInterface; +use Symfony\Component\Routing\RouteCollection; + +/** + * Extends the symfony matcher dumper interface with a addRoutes method. + */ +interface MatcherDumperInterface extends SymfonyMatcherDumperInterface { + + /** + * Adds additional routes to be dumped. + * + * @param \Symfony\Component\Routing\RouteCollection $routes + * A collection of routes to add to this dumper. + */ + public function addRoutes(RouteCollection $routes); + +} diff --git a/core/lib/Drupal/Core/Routing/RouteBuilder.php b/core/lib/Drupal/Core/Routing/RouteBuilder.php index 7e5ebef5d6d5..45e102445883 100644 --- a/core/lib/Drupal/Core/Routing/RouteBuilder.php +++ b/core/lib/Drupal/Core/Routing/RouteBuilder.php @@ -8,7 +8,6 @@ namespace Drupal\Core\Routing; use Drupal\Component\Discovery\YamlDiscovery; -use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Yaml\Parser; use Symfony\Component\Routing\RouteCollection; @@ -47,7 +46,14 @@ class RouteBuilder { protected $dispatcher; /** - * The extension handler for retieving the list of enabled modules. + * The yaml discovery used to find all the .routing.yml files. + * + * @var \Drupal\Component\Discovery\YamlDiscovery + */ + protected $yamlDiscovery; + + /** + * The module handler. * * @var \Drupal\Core\Extension\ModuleHandlerInterface */ @@ -56,12 +62,14 @@ class RouteBuilder { /** * Construcs the RouteBuilder using the passed MatcherDumperInterface. * - * @param \Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface $dumper + * @param \Drupal\Core\Routing\MatcherDumperInterface $dumper * The matcher dumper used to store the route information. * @param \Drupal\Core\Lock\LockBackendInterface $lock * The lock backend. - * @param \Symfony\Component\EventDispatcherEventDispatcherInterface + * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher * The event dispatcher to notify of routes. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. */ public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher, ModuleHandlerInterface $module_handler) { $this->dumper = $dumper; @@ -72,6 +80,9 @@ public function __construct(MatcherDumperInterface $dumper, LockBackendInterface /** * Rebuilds the route info and dumps to dumper. + * + * @return bool + * Returns TRUE if the rebuild succeeds, FALSE otherwise. */ public function rebuild() { if (!$this->lock->acquire('router_rebuild')) { @@ -79,10 +90,10 @@ public function rebuild() { // We choose to block here since otherwise the routes might not be // available, resulting in a 404. $this->lock->wait('router_rebuild'); - return; + return FALSE; } - $yaml_discovery = new YamlDiscovery('routing', $this->moduleHandler->getModuleDirectories()); + $yaml_discovery = $this->getYamlDiscovery(); foreach ($yaml_discovery->findAll() as $module => $routes) { $collection = new RouteCollection(); @@ -111,6 +122,20 @@ public function rebuild() { $this->dumper->dump(array('route_set' => 'dynamic_routes')); $this->lock->release('router_rebuild'); + return TRUE; + } + + /** + * Returns the YAML discovery for getting all the .routing.yml files. + * + * @return \Drupal\Component\Discovery\YamlDiscovery + * The yaml discovery. + */ + protected function getYamlDiscovery() { + if (!isset($this->yamlDiscovery)) { + $this->yamlDiscovery = new YamlDiscovery('routing', $this->moduleHandler->getModuleDirectories()); + } + return $this->yamlDiscovery; } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/RoutingFixtures.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RoutingFixtures.php index 06f095300097..99513ed8d7d1 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/RoutingFixtures.php +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RoutingFixtures.php @@ -43,6 +43,44 @@ public function dropTables(Connection $connection) { } } + /** + * Returns a static version of the routes. + */ + public function staticSampleRouteCollection() { + $routes = array(); + $routes['route_a'] = array( + 'path' => '/path/one', + 'requirements' => array( + '_method' => 'GET', + ), + ); + $routes['route_b'] = array( + 'path' => '/path/one', + 'requirements' => array( + '_method' => 'PUT', + ), + ); + $routes['route_c'] = array( + 'path' => '/path/two', + 'requirements' => array( + '_method' => 'GET', + '_format' => 'json' + ), + ); + $routes['route_d'] = array( + 'path' => '/path/three', + ); + $routes['route_e'] = array( + 'path' => '/path/two', + 'requirements' => array( + '_method' => 'GET|HEAD', + '_format' => 'html' + ), + ); + + return $routes; + } + /** * Returns a standard set of routes for testing. * diff --git a/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php b/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php new file mode 100644 index 000000000000..7da52c00e908 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php @@ -0,0 +1,243 @@ +<?php + +/** + * @file + * Contains \Drupal\Tests\Core\Routing\RouteBuilderTest. + */ + +namespace Drupal\Tests\Core\Routing; + +use Drupal\Component\Discovery\YamlDiscovery; +use Drupal\Core\Routing\RouteBuilder; +use Drupal\Core\Routing\RouteBuildEvent; +use Drupal\Core\Routing\RoutingEvents; +use Drupal\system\Tests\Routing\RoutingFixtures; +use Drupal\Tests\UnitTestCase; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * Tests the route builder. + * + * @group Drupal + * @group Routing + * + * @see \Drupal\Core\Routing\RouteBuilder + */ +class RouteBuilderTest extends UnitTestCase { + + /** + * The actual tested route builder. + * + * @var \Drupal\Core\Routing\RouteBuilder + */ + protected $routeBuilder; + + /** + * The mocked matcher dumper. + * + * @var \Drupal\Core\Routing\MatcherDumperInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $dumper; + + /** + * The mocked lock backend. + * + * @var \Drupal\Core\Lock\LockBackendInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $lock; + + /** + * The mocked event dispatcher. + * + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $dispatcher; + + /** + * The mocked yaml discovery. + * + * @var \Drupal\Component\Discovery\YamlDiscovery|\PHPUnit_Framework_MockObject_MockObject + */ + protected $yamlDiscovery; + + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + public static function getInfo() { + return array( + 'name' => 'Route Builder', + 'description' => 'Tests the route builder.', + 'group' => 'Routing', + ); + } + + protected function setUp() { + $this->dumper = $this->getMock('Drupal\Core\Routing\MatcherDumperInterface'); + $this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface'); + $this->dispatcher = $this->getMock('\Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); + $this->yamlDiscovery = $this->getMockBuilder('\Drupal\Component\Discovery\YamlDiscovery') + ->disableOriginalConstructor() + ->getMock(); + + $this->routeBuilder = new TestRouteBuilder($this->dumper, $this->lock, $this->dispatcher, $this->moduleHandler); + $this->routeBuilder->setYamlDiscovery($this->yamlDiscovery); + } + + /** + * Tests that the route rebuilding both locks and unlocks. + */ + public function testRebuildLockingUnlocking() { + $this->lock->expects($this->once()) + ->method('acquire') + ->with('router_rebuild') + ->will($this->returnValue(TRUE)); + + $this->lock->expects($this->once()) + ->method('release') + ->with('router_rebuild'); + + $this->yamlDiscovery->expects($this->any()) + ->method('findAll') + ->will($this->returnValue(array())); + + $this->assertTrue($this->routeBuilder->rebuild()); + } + + /** + * Tests route rebuilding with a blocking lock. + */ + public function testRebuildBlockingLock() { + $this->lock->expects($this->once()) + ->method('acquire') + ->with('router_rebuild') + ->will($this->returnValue(FALSE)); + + $this->lock->expects($this->once()) + ->method('wait') + ->with('router_rebuild'); + + $this->lock->expects($this->never()) + ->method('release'); + + $this->yamlDiscovery->expects($this->never()) + ->method('findAll'); + + $this->assertFalse($this->routeBuilder->rebuild()); + } + + /** + * Tests that provided routes by a module is put into the dumper/dispatcher. + * + * @see \Drupal\Core\Routing\RouteBuilder::rebuild() + */ + public function testRebuildWithStaticModuleRoutes() { + $this->lock->expects($this->once()) + ->method('acquire') + ->with('router_rebuild') + ->will($this->returnValue(TRUE)); + + $routing_fixtures = new RoutingFixtures(); + $routes = $routing_fixtures->staticSampleRouteCollection(); + + $this->yamlDiscovery->expects($this->once()) + ->method('findAll') + ->will($this->returnValue(array('test_module' => $routes))); + + // Ensure that the dispatch events for altering are fired. + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with($this->equalTo(RoutingEvents::ALTER), $this->isInstanceOf('Drupal\Core\Routing\RouteBuildEvent')); + + $empty_collection = new RouteCollection(); + $route_build_event = new RouteBuildEvent($empty_collection, 'dynamic_routes'); + + // Ensure that the dynamic routes events are fired. + $this->dispatcher->expects($this->at(1)) + ->method('dispatch') + ->with(RoutingEvents::DYNAMIC, $route_build_event); + + $this->dispatcher->expects($this->at(2)) + ->method('dispatch') + ->with(RoutingEvents::ALTER, $route_build_event); + + // Ensure that the routes are set to the dumper and dumped. + $this->dumper->expects($this->at(0)) + ->method('addRoutes') + ->with($routing_fixtures->sampleRouteCollection()); + $this->dumper->expects($this->at(1)) + ->method('dump') + ->with(array('route_set' => 'test_module')); + $this->dumper->expects($this->at(2)) + ->method('addRoutes') + ->with($empty_collection); + $this->dumper->expects($this->at(3)) + ->method('dump') + ->with(array('route_set' => 'dynamic_routes')); + + + $this->assertTrue($this->routeBuilder->rebuild()); + } + + /** + * Tests the rebuild with some dynamic routes. + * + * @see \Drupal\Core\Routing\RouteBuilder::rebuild() + */ + public function testRebuildWithDynamicRoutes() { + $this->lock->expects($this->once()) + ->method('acquire') + ->with('router_rebuild') + ->will($this->returnValue(TRUE)); + + $this->yamlDiscovery->expects($this->once()) + ->method('findAll') + ->will($this->returnValue(array())); + + $route_collection_filled = new RouteCollection(); + $route_collection_filled->add('test_route', new Route('/test-route')); + + $this->dispatcher->expects($this->at(0)) + ->method('dispatch') + ->with($this->equalTo(RoutingEvents::DYNAMIC)) + ->will($this->returnCallback(function ($object, RouteBuildEvent $event) { + $event->getRouteCollection()->add('test_route', new Route('/test-route')); + })); + + $this->dispatcher->expects($this->at(1)) + ->method('dispatch') + ->with($this->equalTo(RoutingEvents::ALTER)); + + $this->dumper->expects($this->once()) + ->method('addRoutes') + ->with($route_collection_filled); + $this->dumper->expects($this->once()) + ->method('dump') + ->with(array('route_set' => 'dynamic_routes')); + + $this->assertTrue($this->routeBuilder->rebuild()); + } + +} + +/** + * Extends the core route builder with a setter method for the yaml discovery. + */ +class TestRouteBuilder extends RouteBuilder { + + /** + * Sets the yaml discovery. + * + * @param \Drupal\Component\Discovery\YamlDiscovery $yaml_discovery + * The yaml discovery to set. + */ + public function setYamlDiscovery(YamlDiscovery $yaml_discovery) { + $this->yamlDiscovery = $yaml_discovery; + } + +} -- GitLab