4 * This file is part of the Symfony CMF package.
6 * (c) 2011-2015 Symfony CMF
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Cmf\Component\Routing\Tests\Routing;
14 use Symfony\Cmf\Component\Routing\VersatileGeneratorInterface;
15 use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
16 use Symfony\Component\Routing\Exception\MethodNotAllowedException;
17 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
18 use Symfony\Component\Routing\Exception\RouteNotFoundException;
19 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
20 use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
21 use Symfony\Component\Routing\RequestContext;
22 use Symfony\Component\Routing\RouteCollection;
23 use Symfony\Component\HttpFoundation\Request;
24 use Symfony\Cmf\Component\Routing\ChainRouter;
25 use Symfony\Cmf\Component\Routing\Test\CmfUnitTestCase;
26 use Symfony\Component\Routing\RouterInterface;
28 class ChainRouterTest extends CmfUnitTestCase
35 * @var RequestContext|\PHPUnit_Framework_MockObject_MockObject
39 public function setUp()
41 $this->router = new ChainRouter($this->getMock('Psr\Log\LoggerInterface'));
42 $this->context = $this->getMock('Symfony\Component\Routing\RequestContext');
45 public function testPriority()
47 $this->assertEquals(array(), $this->router->all());
49 list($low, $high) = $this->createRouterMocks();
51 $this->router->add($low, 10);
52 $this->router->add($high, 100);
54 $this->assertEquals(array(
57 ), $this->router->all());
60 public function testHasRouters()
62 $this->assertEquals(array(), $this->router->all());
63 $this->assertFalse($this->router->hasRouters());
65 list($low, $high) = $this->createRouterMocks();
67 $this->router->add($low, 10);
68 $this->router->add($high, 100);
70 $this->assertTrue($this->router->hasRouters());
74 * Routers are supposed to be sorted only once.
75 * This test will check that by trying to get all routers several times.
77 * @covers \Symfony\Cmf\Component\Routing\ChainRouter::sortRouters
78 * @covers \Symfony\Cmf\Component\Routing\ChainRouter::all
80 public function testSortRouters()
82 list($low, $medium, $high) = $this->createRouterMocks();
83 // We're using a mock here and not $this->router because we need to ensure that the sorting operation is done only once.
84 /** @var $router ChainRouter|\PHPUnit_Framework_MockObject_MockObject */
85 $router = $this->buildMock('Symfony\Cmf\Component\Routing\ChainRouter', array('sortRouters'));
87 ->expects($this->once())
88 ->method('sortRouters')
91 array($high, $medium, $low)
96 $router->add($low, 10);
97 $router->add($medium, 50);
98 $router->add($high, 100);
99 $expectedSortedRouters = array($high, $medium, $low);
100 // Let's get all routers 5 times, we should only sort once.
101 for ($i = 0; $i < 5; ++$i) {
102 $this->assertSame($expectedSortedRouters, $router->all());
107 * This test ensures that if a router is being added on the fly, the sorting is reset.
109 * @covers \Symfony\Cmf\Component\Routing\ChainRouter::sortRouters
110 * @covers \Symfony\Cmf\Component\Routing\ChainRouter::all
111 * @covers \Symfony\Cmf\Component\Routing\ChainRouter::add
113 public function testReSortRouters()
115 list($low, $medium, $high) = $this->createRouterMocks();
116 $highest = clone $high;
117 // We're using a mock here and not $this->router because we need to ensure that the sorting operation is done only once.
118 /** @var $router ChainRouter|\PHPUnit_Framework_MockObject_MockObject */
119 $router = $this->buildMock('Symfony\Cmf\Component\Routing\ChainRouter', array('sortRouters'));
121 ->expects($this->at(0))
122 ->method('sortRouters')
125 array($high, $medium, $low)
129 // The second time sortRouters() is called, we're supposed to get the newly added router ($highest)
131 ->expects($this->at(1))
132 ->method('sortRouters')
135 array($highest, $high, $medium, $low)
140 $router->add($low, 10);
141 $router->add($medium, 50);
142 $router->add($high, 100);
143 $this->assertSame(array($high, $medium, $low), $router->all());
145 // Now adding another router on the fly, sorting must have been reset
146 $router->add($highest, 101);
147 $this->assertSame(array($highest, $high, $medium, $low), $router->all());
151 * context must be propagated to chained routers and be stored locally.
153 public function testContext()
155 list($low, $high) = $this->createRouterMocks();
158 ->expects($this->once())
159 ->method('setContext')
160 ->with($this->context)
164 ->expects($this->once())
165 ->method('setContext')
166 ->with($this->context)
169 $this->router->add($low, 10);
170 $this->router->add($high, 100);
172 $this->router->setContext($this->context);
173 $this->assertSame($this->context, $this->router->getContext());
177 * context must be propagated also when routers are added after context is set.
179 public function testContextOrder()
181 list($low, $high) = $this->createRouterMocks();
184 ->expects($this->once())
185 ->method('setContext')
186 ->with($this->context)
190 ->expects($this->once())
191 ->method('setContext')
192 ->with($this->context)
195 $this->router->setContext($this->context);
197 $this->router->add($low, 10);
198 $this->router->add($high, 100);
200 $this->router->all();
202 $this->assertSame($this->context, $this->router->getContext());
206 * The first usable match is used, no further routers are queried once a match is found.
208 public function testMatch()
211 list($lower, $low, $high) = $this->createRouterMocks();
214 ->expects($this->once())
217 ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
220 ->expects($this->once())
223 ->will($this->returnValue(array('test')))
226 ->expects($this->never())
228 $this->router->add($lower, 5);
229 $this->router->add($low, 10);
230 $this->router->add($high, 100);
232 $result = $this->router->match('/test');
233 $this->assertEquals(array('test'), $result);
237 * The first usable match is used, no further routers are queried once a match is found.
239 public function testMatchRequest()
242 list($lower, $low, $high) = $this->createRouterMocks();
244 $highest = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\RequestMatcher');
246 $request = Request::create('/test');
249 ->expects($this->once())
250 ->method('matchRequest')
251 ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
254 ->expects($this->once())
257 ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
260 ->expects($this->once())
263 ->will($this->returnValue(array('test')))
266 ->expects($this->never())
270 $this->router->add($lower, 5);
271 $this->router->add($low, 10);
272 $this->router->add($high, 100);
273 $this->router->add($highest, 200);
275 $result = $this->router->matchRequest($request);
276 $this->assertEquals(array('test'), $result);
280 * Call match on ChainRouter that has RequestMatcher in the chain.
282 public function testMatchWithRequestMatchers()
286 list($low) = $this->createRouterMocks();
288 $high = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\RequestMatcher');
291 ->expects($this->once())
292 ->method('matchRequest')
293 ->with($this->callback(function (Request $r) use ($url) {
294 return $r->getPathInfo() === $url;
296 ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
299 ->expects($this->once())
302 ->will($this->returnValue(array('test')))
305 $this->router->add($low, 10);
306 $this->router->add($high, 20);
308 $result = $this->router->match($url);
309 $this->assertEquals(array('test'), $result);
312 public function provideBaseUrl()
321 * Call match on ChainRouter that has RequestMatcher in the chain.
323 * @dataProvider provideBaseUrl
325 public function testMatchWithRequestMatchersAndContext($baseUrl)
329 list($low) = $this->createRouterMocks();
331 $high = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\RequestMatcher');
334 ->expects($this->once())
335 ->method('matchRequest')
336 ->with($this->callback(function (Request $r) use ($url, $baseUrl) {
337 return true === $r->isSecure()
338 && 'foobar.com' === $r->getHost()
339 && 4433 === $r->getPort()
340 && $baseUrl === $r->getBaseUrl()
341 && $url === $r->getPathInfo()
344 ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
347 ->expects($this->once())
350 ->will($this->returnValue(array('test')))
353 $this->router->add($low, 10);
354 $this->router->add($high, 20);
356 $requestContext = new RequestContext();
357 $requestContext->setScheme('https');
358 $requestContext->setHost('foobar.com');
359 $requestContext->setHttpsPort(4433);
360 $requestContext->setBaseUrl($baseUrl);
361 $this->router->setContext($requestContext);
363 $result = $this->router->match($url);
364 $this->assertEquals(array('test'), $result);
368 * If there is a method not allowed but another router matches, that one is used.
370 public function testMatchAndNotAllowed()
373 list($low, $high) = $this->createRouterMocks();
376 ->expects($this->once())
379 ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array())))
382 ->expects($this->once())
385 ->will($this->returnValue(array('test')))
387 $this->router->add($low, 10);
388 $this->router->add($high, 100);
390 $result = $this->router->match('/test');
391 $this->assertEquals(array('test'), $result);
395 * If there is a method not allowed but another router matches, that one is used.
397 public function testMatchRequestAndNotAllowed()
400 list($low, $high) = $this->createRouterMocks();
403 ->expects($this->once())
406 ->will($this->throwException(new \Symfony\Component\Routing\Exception\MethodNotAllowedException(array())))
409 ->expects($this->once())
412 ->will($this->returnValue(array('test')))
414 $this->router->add($low, 10);
415 $this->router->add($high, 100);
417 $result = $this->router->matchRequest(Request::create('/test'));
418 $this->assertEquals(array('test'), $result);
422 * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
424 public function testMatchNotFound()
427 list($low, $high) = $this->createRouterMocks();
430 ->expects($this->once())
433 ->will($this->throwException(new ResourceNotFoundException()))
436 ->expects($this->once())
439 ->will($this->throwException(new ResourceNotFoundException()))
441 $this->router->add($low, 10);
442 $this->router->add($high, 100);
444 $this->router->match('/test');
448 * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
450 public function testMatchRequestNotFound()
453 list($low, $high) = $this->createRouterMocks();
456 ->expects($this->once())
459 ->will($this->throwException(new ResourceNotFoundException()))
462 ->expects($this->once())
465 ->will($this->throwException(new ResourceNotFoundException()))
467 $this->router->add($low, 10);
468 $this->router->add($high, 100);
470 $this->router->matchRequest(Request::create('/test'));
474 * Call match on ChainRouter that has RequestMatcher in the chain.
476 * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
477 * @expectedExceptionMessage None of the routers in the chain matched url '/test'
479 public function testMatchWithRequestMatchersNotFound()
482 $request = Request::create('/test');
484 $high = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\RequestMatcher');
487 ->expects($this->once())
488 ->method('matchRequest')
490 ->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
493 $this->router->add($high, 20);
495 $this->router->match($url);
499 * If any of the routers throws a not allowed exception and no other matches, we need to see this.
501 * @expectedException \Symfony\Component\Routing\Exception\MethodNotAllowedException
503 public function testMatchMethodNotAllowed()
506 list($low, $high) = $this->createRouterMocks();
509 ->expects($this->once())
512 ->will($this->throwException(new MethodNotAllowedException(array())))
515 ->expects($this->once())
518 ->will($this->throwException(new ResourceNotFoundException()))
520 $this->router->add($low, 10);
521 $this->router->add($high, 100);
523 $this->router->match('/test');
527 * If any of the routers throws a not allowed exception and no other matches, we need to see this.
529 * @expectedException \Symfony\Component\Routing\Exception\MethodNotAllowedException
531 public function testMatchRequestMethodNotAllowed()
534 list($low, $high) = $this->createRouterMocks();
537 ->expects($this->once())
540 ->will($this->throwException(new MethodNotAllowedException(array())))
543 ->expects($this->once())
546 ->will($this->throwException(new ResourceNotFoundException()))
548 $this->router->add($low, 10);
549 $this->router->add($high, 100);
551 $this->router->matchRequest(Request::create('/test'));
554 public function testGenerate()
558 $parameters = array('test' => 'value');
559 list($lower, $low, $high) = $this->createRouterMocks();
562 ->expects($this->once())
564 ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
565 ->will($this->throwException(new RouteNotFoundException()))
568 ->expects($this->once())
570 ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
571 ->will($this->returnValue($url))
574 ->expects($this->never())
578 $this->router->add($low, 10);
579 $this->router->add($high, 100);
581 $result = $this->router->generate($name, $parameters);
582 $this->assertEquals($url, $result);
586 * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
588 public function testGenerateNotFound()
591 $parameters = array('test' => 'value');
592 list($low, $high) = $this->createRouterMocks();
595 ->expects($this->once())
597 ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
598 ->will($this->throwException(new RouteNotFoundException()))
600 $low->expects($this->once())
602 ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
603 ->will($this->throwException(new RouteNotFoundException()))
605 $this->router->add($low, 10);
606 $this->router->add($high, 100);
608 $this->router->generate($name, $parameters);
612 * Route is an object but no versatile generator around to do the debug message.
614 * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
616 public function testGenerateObjectNotFound()
618 $name = new \stdClass();
619 $parameters = array('test' => 'value');
621 $defaultRouter = $this->getMock('Symfony\Component\Routing\RouterInterface');
624 ->expects($this->never())
628 $this->router->add($defaultRouter, 200);
630 $this->router->generate($name, $parameters);
634 * A versatile router will generate the debug message.
636 * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
638 public function testGenerateObjectNotFoundVersatile()
640 $name = new \stdClass();
641 $parameters = array('test' => 'value');
643 $chainedRouter = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\VersatileRouter');
645 ->expects($this->once())
647 ->will($this->returnValue(true))
649 $chainedRouter->expects($this->once())
651 ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
652 ->will($this->throwException(new RouteNotFoundException()))
654 $chainedRouter->expects($this->once())
655 ->method('getRouteDebugMessage')
656 ->with($name, $parameters)
657 ->will($this->returnValue('message'))
660 $this->router->add($chainedRouter, 10);
662 $this->router->generate($name, $parameters);
665 public function testGenerateObjectName()
667 $name = new \stdClass();
668 $parameters = array('test' => 'value');
670 $defaultRouter = $this->getMock('Symfony\Component\Routing\RouterInterface');
671 $chainedRouter = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\VersatileRouter');
674 ->expects($this->never())
678 ->expects($this->once())
680 ->will($this->returnValue(true))
683 ->expects($this->once())
685 ->with($name, $parameters, UrlGeneratorInterface::ABSOLUTE_PATH)
686 ->will($this->returnValue($name))
689 $this->router->add($defaultRouter, 200);
690 $this->router->add($chainedRouter, 100);
692 $result = $this->router->generate($name, $parameters);
693 $this->assertEquals($name, $result);
696 public function testWarmup()
699 list($low) = $this->createRouterMocks();
702 ->expects($this->never())
705 $high = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\WarmableRouterMock');
707 ->expects($this->once())
712 $this->router->add($low, 10);
713 $this->router->add($high, 100);
715 $this->router->warmUp($dir);
718 public function testRouteCollection()
720 list($low, $high) = $this->createRouterMocks();
721 $lowcol = new RouteCollection();
722 $lowcol->add('low', $this->buildMock('Symfony\Component\Routing\Route'));
723 $highcol = new RouteCollection();
724 $highcol->add('high', $this->buildMock('Symfony\Component\Routing\Route'));
727 ->expects($this->once())
728 ->method('getRouteCollection')
729 ->will($this->returnValue($lowcol))
732 ->expects($this->once())
733 ->method('getRouteCollection')
734 ->will($this->returnValue($highcol))
737 $this->router->add($low, 10);
738 $this->router->add($high, 100);
740 $collection = $this->router->getRouteCollection();
741 $this->assertInstanceOf('Symfony\Component\Routing\RouteCollection', $collection);
744 foreach ($collection->all() as $name => $route) {
745 $this->assertInstanceOf('Symfony\Component\Routing\Route', $route);
748 $this->assertEquals(array('high', 'low'), $names);
752 * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
754 public function testSupport()
756 $router = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\VersatileRouter');
758 ->expects($this->once())
760 ->will($this->returnValue(false))
764 ->expects($this->never())
766 ->will($this->returnValue(false))
769 $this->router->add($router);
771 $this->router->generate('foobar');
775 * @return RouterInterface[]|\PHPUnit_Framework_MockObject_MockObject[]
777 protected function createRouterMocks()
780 $this->getMock('Symfony\Component\Routing\RouterInterface'),
781 $this->getMock('Symfony\Component\Routing\RouterInterface'),
782 $this->getMock('Symfony\Component\Routing\RouterInterface'),
787 abstract class WarmableRouterMock implements RouterInterface, WarmableInterface
791 abstract class RequestMatcher implements RouterInterface, RequestMatcherInterface
795 abstract class VersatileRouter implements VersatileGeneratorInterface, RequestMatcherInterface