ed59f2f0ffecc2906725e5fa4627ca6836e6cf69
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / Routing / RouteProviderTest.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\KernelTests\Core\Routing\RouteProviderTest.
6  */
7
8 namespace Drupal\KernelTests\Core\Routing;
9
10 use Drupal\Component\Utility\Unicode;
11 use Drupal\Core\Cache\MemoryBackend;
12 use Drupal\Core\Database\Database;
13 use Drupal\Core\DependencyInjection\ContainerBuilder;
14 use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
15 use Drupal\Core\Lock\NullLockBackend;
16 use Drupal\Core\Path\CurrentPathStack;
17 use Drupal\Core\Routing\MatcherDumper;
18 use Drupal\Core\Routing\RouteProvider;
19 use Drupal\Core\State\State;
20 use Drupal\KernelTests\KernelTestBase;
21 use Drupal\Tests\Core\Routing\RoutingFixtures;
22 use Symfony\Component\HttpFoundation\Request;
23 use Symfony\Component\HttpFoundation\RequestStack;
24 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
25 use Symfony\Component\Routing\Exception\RouteNotFoundException;
26 use Symfony\Component\Routing\Route;
27 use Symfony\Component\Routing\RouteCollection;
28
29 /**
30  * Confirm that the default route provider is working correctly.
31  *
32  * @group Routing
33  */
34 class RouteProviderTest extends KernelTestBase {
35
36   /**
37    * Modules to enable.
38    */
39   public static $modules = ['url_alter_test', 'system'];
40
41   /**
42    * A collection of shared fixture data for tests.
43    *
44    * @var RoutingFixtures
45    */
46   protected $fixtures;
47
48   /**
49    * The state.
50    *
51    * @var \Drupal\Core\State\StateInterface
52    */
53   protected $state;
54
55   /**
56    * The current path.
57    *
58    * @var \Drupal\Core\Path\CurrentPathStack
59    */
60   protected $currentPath;
61
62   /**
63    * The cache backend.
64    *
65    * @var \Drupal\Core\Cache\MemoryBackend
66    */
67   protected $cache;
68
69   /**
70    * The inbound path processor.
71    *
72    * @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface
73    */
74   protected $pathProcessor;
75
76   /**
77    * The cache tags invalidator.
78    *
79    * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
80    */
81   protected $cacheTagsInvalidator;
82
83   protected function setUp() {
84     parent::setUp();
85     $this->fixtures = new RoutingFixtures();
86     $this->state = new State(new KeyValueMemoryFactory(), new MemoryBackend('test'), new NullLockBackend());
87     $this->currentPath = new CurrentPathStack(new RequestStack());
88     $this->cache = new MemoryBackend();
89     $this->pathProcessor = \Drupal::service('path_processor_manager');
90     $this->cacheTagsInvalidator = \Drupal::service('cache_tags.invalidator');
91   }
92
93   /**
94    * {@inheritdoc}
95    */
96   public function register(ContainerBuilder $container) {
97     parent::register($container);
98
99     // Readd the incoming path alias for these tests.
100     if ($container->hasDefinition('path_processor_alias')) {
101       $definition = $container->getDefinition('path_processor_alias');
102       $definition->addTag('path_processor_inbound');
103     }
104   }
105
106   protected function tearDown() {
107     $this->fixtures->dropTables(Database::getConnection());
108
109     parent::tearDown();
110   }
111
112   /**
113    * Confirms that the correct candidate outlines are generated.
114    */
115   public function testCandidateOutlines() {
116
117     $connection = Database::getConnection();
118     $provider = new TestRouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
119
120     $parts = ['node', '5', 'edit'];
121
122     $candidates = $provider->getCandidateOutlines($parts);
123
124     $candidates = array_flip($candidates);
125
126     $this->assertTrue(count($candidates) == 7, 'Correct number of candidates found');
127     $this->assertTrue(array_key_exists('/node/5/edit', $candidates), 'First candidate found.');
128     $this->assertTrue(array_key_exists('/node/5/%', $candidates), 'Second candidate found.');
129     $this->assertTrue(array_key_exists('/node/%/edit', $candidates), 'Third candidate found.');
130     $this->assertTrue(array_key_exists('/node/%/%', $candidates), 'Fourth candidate found.');
131     $this->assertTrue(array_key_exists('/node/5', $candidates), 'Fifth candidate found.');
132     $this->assertTrue(array_key_exists('/node/%', $candidates), 'Sixth candidate found.');
133     $this->assertTrue(array_key_exists('/node', $candidates), 'Seventh candidate found.');
134   }
135
136   /**
137    * Don't fail when given an empty path.
138    */
139   public function testEmptyPathCandidatesOutlines() {
140     $provider = new TestRouteProvider(Database::getConnection(), $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
141     $candidates = $provider->getCandidateOutlines([]);
142     $this->assertEqual(count($candidates), 0, 'Empty parts should return no candidates.');
143   }
144
145   /**
146    * Confirms that we can find routes with the exact incoming path.
147    */
148   public function testExactPathMatch() {
149     $connection = Database::getConnection();
150     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
151
152     $this->fixtures->createTables($connection);
153
154     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
155     $dumper->addRoutes($this->fixtures->sampleRouteCollection());
156     $dumper->dump();
157
158     $path = '/path/one';
159
160     $request = Request::create($path, 'GET');
161
162     $routes = $provider->getRouteCollectionForRequest($request);
163
164     foreach ($routes as $route) {
165       $this->assertEqual($route->getPath(), $path, 'Found path has correct pattern');
166     }
167   }
168
169   /**
170    * Confirms that we can find routes whose pattern would match the request.
171    */
172   public function testOutlinePathMatch() {
173     $connection = Database::getConnection();
174     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
175
176     $this->fixtures->createTables($connection);
177
178     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
179     $dumper->addRoutes($this->fixtures->complexRouteCollection());
180     $dumper->dump();
181
182     $path = '/path/1/one';
183
184     $request = Request::create($path, 'GET');
185
186     $routes = $provider->getRouteCollectionForRequest($request);
187
188     // All of the matching paths have the correct pattern.
189     foreach ($routes as $route) {
190       $this->assertEqual($route->compile()->getPatternOutline(), '/path/%/one', 'Found path has correct pattern');
191     }
192
193     $this->assertEqual(count($routes), 2, 'The correct number of routes was found.');
194     $this->assertNotNull($routes->get('route_a'), 'The first matching route was found.');
195     $this->assertNotNull($routes->get('route_b'), 'The second matching route was not found.');
196   }
197
198   /**
199    * Data provider for testMixedCasePaths()
200    */
201   public function providerMixedCaseRoutePaths() {
202     return [
203       ['/path/one', 'route_a'],
204       ['/path/two', NULL],
205       ['/PATH/one', 'route_a'],
206       ['/path/2/one', 'route_b', 'PUT'],
207       ['/paTH/3/one', 'route_b', 'PUT'],
208       // There should be no lower case of a Hebrew letter.
209       ['/somewhere/4/over/the/קainbow', 'route_c'],
210       ['/Somewhere/5/over/the/קainboW', 'route_c'],
211       ['/another/llama/aboUT/22', 'route_d'],
212       ['/another/llama/about/22', 'route_d'],
213       ['/place/meΦω', 'route_e', 'HEAD'],
214       ['/place/meφΩ', 'route_e', 'HEAD'],
215     ];
216   }
217
218   /**
219    * Confirms that we find routes using a case-insensitive path match.
220    *
221    * @dataProvider providerMixedCaseRoutePaths
222    */
223   public function testMixedCasePaths($path, $expected_route_name, $method = 'GET') {
224     // The case-insensitive behavior for higher UTF-8 characters depends on
225     // \Drupal\Component\Utility\Unicode::strtolower() using mb_strtolower()
226     // but kernel tests do not currently run the check that enables it.
227     // @todo remove this when https://www.drupal.org/node/2849669 is fixed.
228     Unicode::check();
229
230     $connection = Database::getConnection();
231     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
232
233     $this->fixtures->createTables($connection);
234
235     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
236     $dumper->addRoutes($this->fixtures->mixedCaseRouteCollection());
237     $dumper->dump();
238
239     $request = Request::create($path, $method);
240
241     $routes = $provider->getRouteCollectionForRequest($request);
242
243     if ($expected_route_name) {
244       $this->assertEquals(1, count($routes), 'The correct number of routes was found.');
245       $this->assertNotNull($routes->get($expected_route_name), 'The first matching route was found.');
246     }
247     else {
248       $this->assertEquals(0, count($routes), 'No routes matched.');
249     }
250   }
251
252   /**
253    * Data provider for testMixedCasePaths()
254    */
255   public function providerDuplicateRoutePaths() {
256     // When matching routes with the same fit the route with the lowest-sorting
257     // name should end up first in the resulting route collection.
258     return [
259       ['/path/one', 3, 'route_a'],
260       ['/PATH/one', 3, 'route_a'],
261       ['/path/two', 1, 'route_d'],
262       ['/PATH/three', 0],
263       ['/place/meΦω', 2, 'route_e'],
264       ['/placE/meφΩ', 2, 'route_e'],
265     ];
266   }
267
268   /**
269    * Confirms that we find all routes with the same path.
270    *
271    * @dataProvider providerDuplicateRoutePaths
272    */
273   public function testDuplicateRoutePaths($path, $number, $expected_route_name = NULL) {
274
275     // The case-insensitive behavior for higher UTF-8 characters depends on
276     // \Drupal\Component\Utility\Unicode::strtolower() using mb_strtolower()
277     // but kernel tests do not currently run the check that enables it.
278     // @todo remove this when https://www.drupal.org/node/2849669 is fixed.
279     Unicode::check();
280
281     $connection = Database::getConnection();
282     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
283
284     $this->fixtures->createTables($connection);
285
286     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
287     $dumper->addRoutes($this->fixtures->duplicatePathsRouteCollection());
288     $dumper->dump();
289
290     $request = Request::create($path);
291     $routes = $provider->getRouteCollectionForRequest($request);
292     $this->assertEquals($number, count($routes), 'The correct number of routes was found.');
293     if ($expected_route_name) {
294       $route_name = key(current($routes));
295       $this->assertEquals($expected_route_name, $route_name, 'The expected route name was found.');
296     }
297   }
298
299   /**
300    * Confirms that a trailing slash on the request does not result in a 404.
301    */
302   public function testOutlinePathMatchTrailingSlash() {
303     $connection = Database::getConnection();
304     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
305
306     $this->fixtures->createTables($connection);
307
308     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
309     $dumper->addRoutes($this->fixtures->complexRouteCollection());
310     $dumper->dump();
311
312     $path = '/path/1/one/';
313
314     $request = Request::create($path, 'GET');
315
316     $routes = $provider->getRouteCollectionForRequest($request);
317
318     // All of the matching paths have the correct pattern.
319     foreach ($routes as $route) {
320       $this->assertEqual($route->compile()->getPatternOutline(), '/path/%/one', 'Found path has correct pattern');
321     }
322
323     $this->assertEqual(count($routes), 2, 'The correct number of routes was found.');
324     $this->assertNotNull($routes->get('route_a'), 'The first matching route was found.');
325     $this->assertNotNull($routes->get('route_b'), 'The second matching route was not found.');
326   }
327
328   /**
329    * Confirms that we can find routes whose pattern would match the request.
330    */
331   public function testOutlinePathMatchDefaults() {
332     $connection = Database::getConnection();
333     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
334
335     $this->fixtures->createTables($connection);
336
337     $collection = new RouteCollection();
338     $collection->add('poink', new Route('/some/path/{value}', [
339       'value' => 'poink',
340     ]));
341
342     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
343     $dumper->addRoutes($collection);
344     $dumper->dump();
345
346     $path = '/some/path';
347
348     $request = Request::create($path, 'GET');
349
350     try {
351       $routes = $provider->getRouteCollectionForRequest($request);
352
353       // All of the matching paths have the correct pattern.
354       foreach ($routes as $route) {
355         $this->assertEqual($route->compile()->getPatternOutline(), '/some/path', 'Found path has correct pattern');
356       }
357
358       $this->assertEqual(count($routes), 1, 'The correct number of routes was found.');
359       $this->assertNotNull($routes->get('poink'), 'The first matching route was found.');
360     }
361     catch (ResourceNotFoundException $e) {
362       $this->fail('No matching route found with default argument value.');
363     }
364   }
365
366   /**
367    * Confirms that we can find routes whose pattern would match the request.
368    */
369   public function testOutlinePathMatchDefaultsCollision() {
370     $connection = Database::getConnection();
371     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
372
373     $this->fixtures->createTables($connection);
374
375     $collection = new RouteCollection();
376     $collection->add('poink', new Route('/some/path/{value}', [
377       'value' => 'poink',
378     ]));
379     $collection->add('narf', new Route('/some/path/here'));
380
381     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
382     $dumper->addRoutes($collection);
383     $dumper->dump();
384
385     $path = '/some/path';
386
387     $request = Request::create($path, 'GET');
388
389     try {
390       $routes = $provider->getRouteCollectionForRequest($request);
391
392       // All of the matching paths have the correct pattern.
393       foreach ($routes as $route) {
394         $this->assertEqual($route->compile()->getPatternOutline(), '/some/path', 'Found path has correct pattern');
395       }
396
397       $this->assertEqual(count($routes), 1, 'The correct number of routes was found.');
398       $this->assertNotNull($routes->get('poink'), 'The first matching route was found.');
399     }
400     catch (ResourceNotFoundException $e) {
401       $this->fail('No matching route found with default argument value.');
402     }
403   }
404
405   /**
406    * Confirms that we can find routes whose pattern would match the request.
407    */
408   public function testOutlinePathMatchDefaultsCollision2() {
409     $connection = Database::getConnection();
410     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
411
412     $this->fixtures->createTables($connection);
413
414     $collection = new RouteCollection();
415     $collection->add('poink', new Route('/some/path/{value}', [
416       'value' => 'poink',
417     ]));
418     $collection->add('narf', new Route('/some/path/here'));
419     $collection->add('eep', new Route('/something/completely/different'));
420
421     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
422     $dumper->addRoutes($collection);
423     $dumper->dump();
424
425     $path = '/some/path/here';
426
427     $request = Request::create($path, 'GET');
428
429     try {
430       $routes = $provider->getRouteCollectionForRequest($request);
431       $routes_array = $routes->all();
432
433       $this->assertEqual(count($routes), 2, 'The correct number of routes was found.');
434       $this->assertEqual(['narf', 'poink'], array_keys($routes_array), 'Ensure the fitness was taken into account.');
435       $this->assertNotNull($routes->get('narf'), 'The first matching route was found.');
436       $this->assertNotNull($routes->get('poink'), 'The second matching route was found.');
437       $this->assertNull($routes->get('eep'), 'Non-matching route was not found.');
438     }
439     catch (ResourceNotFoundException $e) {
440       $this->fail('No matching route found with default argument value.');
441     }
442   }
443
444   /**
445    * Confirms that we can find multiple routes that match the request equally.
446    */
447   public function testOutlinePathMatchDefaultsCollision3() {
448     $connection = Database::getConnection();
449     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
450
451     $this->fixtures->createTables($connection);
452
453     $collection = new RouteCollection();
454     $collection->add('poink', new Route('/some/{value}/path'));
455     // Add a second route matching the same path pattern.
456     $collection->add('poink2', new Route('/some/{object}/path'));
457     $collection->add('narf', new Route('/some/here/path'));
458     $collection->add('eep', new Route('/something/completely/different'));
459
460     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
461     $dumper->addRoutes($collection);
462     $dumper->dump();
463
464     $path = '/some/over-there/path';
465
466     $request = Request::create($path, 'GET');
467
468     try {
469       $routes = $provider->getRouteCollectionForRequest($request);
470       $routes_array = $routes->all();
471
472       $this->assertEqual(count($routes), 2, 'The correct number of routes was found.');
473       $this->assertEqual(['poink', 'poink2'], array_keys($routes_array), 'Ensure the fitness and name were taken into account in the sort.');
474       $this->assertNotNull($routes->get('poink'), 'The first matching route was found.');
475       $this->assertNotNull($routes->get('poink2'), 'The second matching route was found.');
476       $this->assertNull($routes->get('eep'), 'Non-matching route was not found.');
477     }
478     catch (ResourceNotFoundException $e) {
479       $this->fail('No matching route found with default argument value.');
480     }
481   }
482
483   /**
484    * Tests a route with a 0 as value.
485    */
486   public function testOutlinePathMatchZero() {
487     $connection = Database::getConnection();
488     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
489
490     $this->fixtures->createTables($connection);
491
492     $collection = new RouteCollection();
493     $collection->add('poink', new Route('/some/path/{value}'));
494
495     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
496     $dumper->addRoutes($collection);
497     $dumper->dump();
498
499     $path = '/some/path/0';
500
501     $request = Request::create($path, 'GET');
502
503     try {
504       $routes = $provider->getRouteCollectionForRequest($request);
505
506       // All of the matching paths have the correct pattern.
507       foreach ($routes as $route) {
508         $this->assertEqual($route->compile()->getPatternOutline(), '/some/path/%', 'Found path has correct pattern');
509       }
510
511       $this->assertEqual(count($routes), 1, 'The correct number of routes was found.');
512     }
513     catch (ResourceNotFoundException $e) {
514       $this->fail('No matchout route found with 0 as argument value');
515     }
516   }
517
518   /**
519    * Confirms that an exception is thrown when no matching path is found.
520    */
521   public function testOutlinePathNoMatch() {
522     $connection = Database::getConnection();
523     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
524
525     $this->fixtures->createTables($connection);
526
527     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
528     $dumper->addRoutes($this->fixtures->complexRouteCollection());
529     $dumper->dump();
530
531     $path = '/no/such/path';
532
533     $request = Request::create($path, 'GET');
534
535
536     $routes = $provider->getRoutesByPattern($path);
537     $this->assertFalse(count($routes), 'No path found with this pattern.');
538
539     $collection = $provider->getRouteCollectionForRequest($request);
540     $this->assertTrue(count($collection) == 0, 'Empty route collection found with this pattern.');
541   }
542
543   /**
544    * Tests that route caching works.
545    */
546   public function testRouteCaching() {
547     $connection = Database::getConnection();
548     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
549
550     $this->fixtures->createTables($connection);
551
552     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
553     $dumper->addRoutes($this->fixtures->sampleRouteCollection());
554     $dumper->addRoutes($this->fixtures->complexRouteCollection());
555     $dumper->dump();
556
557     // A simple path.
558     $path = '/path/add/one';
559     $request = Request::create($path, 'GET');
560     $provider->getRouteCollectionForRequest($request);
561
562     $cache = $this->cache->get('route:/path/add/one:');
563     $this->assertEqual('/path/add/one', $cache->data['path']);
564     $this->assertEqual([], $cache->data['query']);
565     $this->assertEqual(3, count($cache->data['routes']));
566
567     // A path with query parameters.
568     $path = '/path/add/one?foo=bar';
569     $request = Request::create($path, 'GET');
570     $provider->getRouteCollectionForRequest($request);
571
572     $cache = $this->cache->get('route:/path/add/one:foo=bar');
573     $this->assertEqual('/path/add/one', $cache->data['path']);
574     $this->assertEqual(['foo' => 'bar'], $cache->data['query']);
575     $this->assertEqual(3, count($cache->data['routes']));
576
577     // A path with placeholders.
578     $path = '/path/1/one';
579     $request = Request::create($path, 'GET');
580     $provider->getRouteCollectionForRequest($request);
581
582     $cache = $this->cache->get('route:/path/1/one:');
583     $this->assertEqual('/path/1/one', $cache->data['path']);
584     $this->assertEqual([], $cache->data['query']);
585     $this->assertEqual(2, count($cache->data['routes']));
586
587     // A path with a path alias.
588     /** @var \Drupal\Core\Path\AliasStorageInterface $path_storage */
589     $path_storage = \Drupal::service('path.alias_storage');
590     $path_storage->save('/path/add/one', '/path/add-one');
591     /** @var \Drupal\Core\Path\AliasManagerInterface $alias_manager */
592     $alias_manager = \Drupal::service('path.alias_manager');
593     $alias_manager->cacheClear();
594
595     $path = '/path/add-one';
596     $request = Request::create($path, 'GET');
597     $provider->getRouteCollectionForRequest($request);
598
599     $cache = $this->cache->get('route:/path/add-one:');
600     $this->assertEqual('/path/add/one', $cache->data['path']);
601     $this->assertEqual([], $cache->data['query']);
602     $this->assertEqual(3, count($cache->data['routes']));
603   }
604
605   /**
606    * Test RouteProvider::getRouteByName() and RouteProvider::getRoutesByNames().
607    */
608   public function testRouteByName() {
609     $connection = Database::getConnection();
610     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
611
612     $this->fixtures->createTables($connection);
613
614     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
615     $dumper->addRoutes($this->fixtures->sampleRouteCollection());
616     $dumper->dump();
617
618     $route = $provider->getRouteByName('route_a');
619     $this->assertEqual($route->getPath(), '/path/one', 'The right route pattern was found.');
620     $this->assertEqual($route->getMethods(), ['GET'], 'The right route method was found.');
621     $route = $provider->getRouteByName('route_b');
622     $this->assertEqual($route->getPath(), '/path/one', 'The right route pattern was found.');
623     $this->assertEqual($route->getMethods(), ['PUT'], 'The right route method was found.');
624
625     $exception_thrown = FALSE;
626     try {
627       $provider->getRouteByName('invalid_name');
628     }
629     catch (RouteNotFoundException $e) {
630       $exception_thrown = TRUE;
631     }
632     $this->assertTrue($exception_thrown, 'Random route was not found.');
633
634     $routes = $provider->getRoutesByNames(['route_c', 'route_d', $this->randomMachineName()]);
635     $this->assertEqual(count($routes), 2, 'Only two valid routes found.');
636     $this->assertEqual($routes['route_c']->getPath(), '/path/two');
637     $this->assertEqual($routes['route_d']->getPath(), '/path/three');
638   }
639
640   /**
641    * Ensures that the routing system is capable of extreme long patterns.
642    */
643   public function testGetRoutesByPatternWithLongPatterns() {
644     $connection = Database::getConnection();
645     $provider = new TestRouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
646
647     $this->fixtures->createTables($connection);
648     // This pattern has only 3 parts, so we will get candidates, but no routes,
649     // even though we have not dumped the routes yet.
650     $shortest = '/test/1/test2';
651     $result = $provider->getRoutesByPattern($shortest);
652     $this->assertEqual($result->count(), 0);
653     $candidates = $provider->getCandidateOutlines(explode('/', trim($shortest, '/')));
654     $this->assertEqual(count($candidates), 7);
655     // A longer patten is not found and returns no candidates
656     $path_to_test = '/test/1/test2/2/test3/3/4/5/6/test4';
657     $result = $provider->getRoutesByPattern($path_to_test);
658     $this->assertEqual($result->count(), 0);
659     $candidates = $provider->getCandidateOutlines(explode('/', trim($path_to_test, '/')));
660     $this->assertEqual(count($candidates), 0);
661
662     // Add a matching route and dump it.
663     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
664     $collection = new RouteCollection();
665     $collection->add('long_pattern', new Route('/test/{v1}/test2/{v2}/test3/{v3}/{v4}/{v5}/{v6}/test4'));
666     $dumper->addRoutes($collection);
667     $dumper->dump();
668
669     $result = $provider->getRoutesByPattern($path_to_test);
670     $this->assertEqual($result->count(), 1);
671     // We can't compare the values of the routes directly, nor use
672     // spl_object_hash() because they are separate instances.
673     $this->assertEqual(serialize($result->get('long_pattern')), serialize($collection->get('long_pattern')), 'The right route was found.');
674     // We now have a single candidate outline.
675     $candidates = $provider->getCandidateOutlines(explode('/', trim($path_to_test, '/')));
676     $this->assertEqual(count($candidates), 1);
677     // Longer and shorter patterns are not found. Both are longer than 3, so
678     // we should not have any candidates either. The fact that we do not
679     // get any candidates for a longer path is a security feature.
680     $longer = '/test/1/test2/2/test3/3/4/5/6/test4/trailing/more/parts';
681     $result = $provider->getRoutesByPattern($longer);
682     $this->assertEqual($result->count(), 0);
683     $candidates = $provider->getCandidateOutlines(explode('/', trim($longer, '/')));
684     $this->assertEqual(count($candidates), 1);
685     $shorter = '/test/1/test2/2/test3';
686     $result = $provider->getRoutesByPattern($shorter);
687     $this->assertEqual($result->count(), 0);
688     $candidates = $provider->getCandidateOutlines(explode('/', trim($shorter, '/')));
689     $this->assertEqual(count($candidates), 0);
690     // This pattern has only 3 parts, so we will get candidates, but no routes.
691     // This result is unchanged by running the dumper.
692     $result = $provider->getRoutesByPattern($shortest);
693     $this->assertEqual($result->count(), 0);
694     $candidates = $provider->getCandidateOutlines(explode('/', trim($shortest, '/')));
695     $this->assertEqual(count($candidates), 7);
696   }
697
698   /**
699    * Tests getRoutesPaged().
700    */
701   public function testGetRoutesPaged() {
702     $connection = Database::getConnection();
703     $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
704
705     $this->fixtures->createTables($connection);
706     $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
707     $dumper->addRoutes($this->fixtures->sampleRouteCollection());
708     $dumper->dump();
709
710     $fixture_routes = $this->fixtures->staticSampleRouteCollection();
711
712     // Query all the routes.
713     $routes = $provider->getRoutesPaged(0);
714     $this->assertEqual(array_keys($routes), array_keys($fixture_routes));
715
716     // Query non routes.
717     $routes = $provider->getRoutesPaged(0, 0);
718     $this->assertEqual(array_keys($routes), []);
719
720     // Query a limited sets of routes.
721     $routes = $provider->getRoutesPaged(1, 2);
722     $this->assertEqual(array_keys($routes), array_slice(array_keys($fixture_routes), 1, 2));
723   }
724
725 }
726
727 class TestRouteProvider extends RouteProvider {
728
729   public function getCandidateOutlines(array $parts) {
730     return parent::getCandidateOutlines($parts);
731   }
732
733 }