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