PageRenderTime 26ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/tests/kohana/RouteTest.php

https://github.com/popovag/kohana_core
PHP | 447 lines | 217 code | 62 blank | 168 comment | 2 complexity | c8adde84705b14a58ac961d731b8288f MD5 | raw file
  1. <?php defined('SYSPATH') OR die('Kohana bootstrap needs to be included before tests run');
  2. /**
  3. * Description of RouteTest
  4. *
  5. * @group kohana
  6. *
  7. * @package Unittest
  8. * @author Kohana Team
  9. * @author BRMatt <matthew@sigswitch.com>
  10. * @copyright (c) 2008-2009 Kohana Team
  11. * @license http://kohanaphp.com/license
  12. */
  13. class Kohana_RouteTest extends Kohana_Unittest_TestCase
  14. {
  15. /**
  16. * Remove all caches
  17. */
  18. public function setUp()
  19. {
  20. parent::setUp();
  21. $this->clean_cache_dir();
  22. }
  23. /**
  24. * Removes cache files created during tests
  25. */
  26. public function tearDown()
  27. {
  28. parent::tearDown();
  29. $this->clean_cache_dir();
  30. }
  31. /**
  32. * Removes all kohana related cache files in the cache directory
  33. */
  34. public function clean_cache_dir()
  35. {
  36. $cache_dir = opendir(Kohana::$cache_dir);
  37. while($dir = readdir($cache_dir))
  38. {
  39. // Cache files are split into directories based on first two characters of hash
  40. if($dir[0] !== '.' AND strlen($dir) === 2)
  41. {
  42. $cache = opendir(Kohana::$cache_dir.'/'.$dir);
  43. while($file = readdir($cache))
  44. {
  45. if($file[0] !== '.')
  46. {
  47. unlink(Kohana::$cache_dir.'/'.$dir.'/'.$file);
  48. }
  49. }
  50. closedir($cache);
  51. rmdir(Kohana::$cache_dir.'/'.$dir);
  52. }
  53. }
  54. closedir($cache_dir);
  55. }
  56. /**
  57. * If Route::get() is asked for a route that does not exist then
  58. * it should throw a Kohana_Exception
  59. *
  60. * Note use of @expectedException
  61. *
  62. * @test
  63. * @covers Route::get
  64. * @expectedException Kohana_Exception
  65. */
  66. public function test_get_throws_exception_if_route_dnx()
  67. {
  68. Route::get('HAHAHAHAHAHAHAHAHA');
  69. }
  70. /**
  71. * Route::all() should return all routes defined via Route::set()
  72. * and not through new Route()
  73. *
  74. * @test
  75. * @covers Route::all
  76. */
  77. public function test_all_returns_all_defined_routes()
  78. {
  79. $defined_routes = self::readAttribute('Route', '_routes');
  80. $this->assertSame($defined_routes, Route::all());
  81. }
  82. /**
  83. * Route::name() should fetch the name of a passed route
  84. * If route is not found then it should return FALSE
  85. *
  86. * @TODO: This test needs to segregate the Route::$_routes singleton
  87. * @test
  88. * @covers Route::name
  89. */
  90. public function test_name_returns_routes_name_or_false_if_dnx()
  91. {
  92. $route = Route::set('flamingo_people', 'flamingo/dance');
  93. $this->assertSame('flamingo_people', Route::name($route));
  94. $route = new Route('dance/dance');
  95. $this->assertFalse(Route::name($route));
  96. }
  97. /**
  98. * If Route::cache() was able to restore routes from the cache then
  99. * it should return TRUE and load the cached routes
  100. *
  101. * @test
  102. * @covers Route::cache
  103. */
  104. public function test_cache_stores_route_objects()
  105. {
  106. $routes = Route::all();
  107. // First we create the cache
  108. Route::cache(TRUE);
  109. // Now lets modify the "current" routes
  110. Route::set('nonsensical_route', 'flabbadaga/ding_dong');
  111. // Then try and load said cache
  112. $this->assertTrue(Route::cache());
  113. // And if all went ok the nonsensical route should be gone...
  114. $this->assertEquals($routes, Route::all());
  115. }
  116. /**
  117. * Route::cache() should return FALSE if cached routes could not be found
  118. *
  119. * The cache is cleared before and after each test in setUp tearDown
  120. * by clean_cache_dir()
  121. *
  122. * @test
  123. * @covers Route::cache
  124. */
  125. public function test_cache_returns_false_if_cache_dnx()
  126. {
  127. $this->assertSame(FALSE, Route::cache(), 'Route cache was not empty');
  128. }
  129. /**
  130. * If the constructor is passed a NULL uri then it should assume it's
  131. * being loaded from the cache & therefore shouldn't override the cached attributes
  132. *
  133. * @test
  134. * @covers Route::__construct
  135. */
  136. public function test_constructor_returns_if_uri_is_null()
  137. {
  138. // We use a mock object to make sure that the route wasn't recompiled
  139. $route = $this->getMock('Route', array('_compile'), array(), '', FALSE);
  140. $route
  141. ->expects($this->never())
  142. ->method('_compile');
  143. $route->__construct(NULL,NULL);
  144. $this->assertAttributeSame('', '_uri', $route);
  145. $this->assertAttributeSame(array(), '_regex', $route);
  146. $this->assertAttributeSame(array('action' => 'index'), '_defaults', $route);
  147. $this->assertAttributeSame(NULL, '_route_regex', $route);
  148. }
  149. /**
  150. * The constructor should only use custom regex if passed a non-empty array
  151. *
  152. * Technically we can't "test" this as the default regex is an empty array, this
  153. * is purely for improving test coverage
  154. *
  155. * @test
  156. * @covers Route::__construct
  157. */
  158. public function test_constructor_only_changes_custom_regex_if_passed()
  159. {
  160. $route = new Route('<controller>/<action>', array());
  161. $this->assertAttributeSame(array(), '_regex', $route);
  162. $route = new Route('<controller>/<action>', NULL);
  163. $this->assertAttributeSame(array(), '_regex', $route);
  164. }
  165. /**
  166. * When we pass custom regex to the route's constructor it should it
  167. * in leu of the default
  168. *
  169. * @test
  170. * @covers Route::__construct
  171. * @covers Route::_compile
  172. */
  173. public function test_route_uses_custom_regex_passed_to_constructor()
  174. {
  175. $regex = array('id' => '[0-9]{1,2}');
  176. $route = new Route('<controller>(/<action>(/<id>))', $regex);
  177. $this->assertAttributeSame($regex, '_regex', $route);
  178. $this->assertAttributeContains(
  179. $regex['id'],
  180. '_route_regex',
  181. $route
  182. );
  183. }
  184. /**
  185. * Route::matches() should return false if the route doesn't match against a uri
  186. *
  187. * @test
  188. * @covers Route::matches
  189. */
  190. public function test_matches_returns_false_on_failure()
  191. {
  192. $route = new Route('projects/(<project_id>/(<controller>(/<action>(/<id>))))');
  193. $this->assertSame(FALSE, $route->matches('apple/pie'));
  194. }
  195. /**
  196. * Route::matches() should return an array of parameters when a match is made
  197. * An parameters that are not matched should not be present in the array of matches
  198. *
  199. * @test
  200. * @covers Route::matches
  201. */
  202. public function test_matches_returns_array_of_parameters_on_successful_match()
  203. {
  204. $route = new Route('(<controller>(/<action>(/<id>)))');
  205. $matches = $route->matches('welcome/index');
  206. $this->assertType('array', $matches);
  207. $this->assertArrayHasKey('controller', $matches);
  208. $this->assertArrayHasKey('action', $matches);
  209. $this->assertArrayNotHasKey('id', $matches);
  210. $this->assertSame(2, count($matches));
  211. $this->assertSame('welcome', $matches['controller']);
  212. $this->assertSame('index', $matches['action']);
  213. }
  214. /**
  215. * Defaults specified with defaults() should be used if their values aren't
  216. * present in the uri
  217. *
  218. * @test
  219. * @covers Route::matches
  220. */
  221. public function test_defaults_are_used_if_params_arent_specified()
  222. {
  223. $route = new Route('(<controller>(/<action>(/<id>)))');
  224. $route->defaults(array('controller' => 'welcome', 'action' => 'index'));
  225. $matches = $route->matches('');
  226. $this->assertType('array', $matches);
  227. $this->assertArrayHasKey('controller', $matches);
  228. $this->assertArrayHasKey('action', $matches);
  229. $this->assertArrayNotHasKey('id', $matches);
  230. $this->assertSame(2, count($matches));
  231. $this->assertSame('welcome', $matches['controller']);
  232. $this->assertSame('index', $matches['action']);
  233. $this->assertSame('unit/test/1', $route->uri(array(
  234. 'controller' => 'unit',
  235. 'action' => 'test',
  236. 'id' => '1'
  237. )));
  238. $this->assertSame('welcome/index', $route->uri());
  239. }
  240. /**
  241. * This tests that routes with required parameters will not match uris without them present
  242. *
  243. * @test
  244. * @covers Route::matches
  245. */
  246. public function test_required_parameters_are_needed()
  247. {
  248. $route = new Route('admin(/<controller>(/<action>(/<id>)))');
  249. $this->assertFalse($route->matches(''));
  250. $matches = $route->matches('admin');
  251. $this->assertType('array', $matches);
  252. $matches = $route->matches('admin/users/add');
  253. $this->assertType('array', $matches);
  254. $this->assertSame(2, count($matches));
  255. $this->assertArrayHasKey('controller', $matches);
  256. $this->assertArrayHasKey('action', $matches);
  257. }
  258. /**
  259. * This tests the reverse routing returns the uri specified in the route
  260. * if it's a static route
  261. *
  262. * A static route is a route without any parameters
  263. *
  264. * @test
  265. * @covers Route::uri
  266. */
  267. public function test_reverse_routing_returns_routes_uri_if_route_is_static()
  268. {
  269. $route = new Route('info/about_us');
  270. $this->assertSame('info/about_us', $route->uri(array('some' => 'random', 'params' => 'to confuse')));
  271. }
  272. /**
  273. * When Route::uri is working on a uri that requires certain parameters to be present
  274. * (i.e. <controller> in '<controller(/<action)') then it should throw an exception
  275. * if the param was not provided
  276. *
  277. * @test
  278. * @covers Route::uri
  279. */
  280. public function test_uri_throws_exception_if_required_params_are_missing()
  281. {
  282. $route = new Route('<controller>(/<action)');
  283. try
  284. {
  285. $route->uri(array('action' => 'awesome-action'));
  286. $this->fail('Route::uri should throw exception if required param is not provided');
  287. }
  288. catch(Exception $e)
  289. {
  290. $this->assertType('Kohana_Exception', $e);
  291. // Check that the error in question is about the controller param
  292. $this->assertContains('controller', $e->getMessage());
  293. }
  294. }
  295. /**
  296. * The logic for replacing required segments is separate (but similar) to that for
  297. * replacing optional segments.
  298. *
  299. * This test asserts that Route::uri will replace required segments with provided
  300. * params
  301. *
  302. * @test
  303. * @covers Route::uri
  304. */
  305. public function test_uri_fills_required_uri_segments_from_params()
  306. {
  307. $route = new Route('<controller>/<action>(/<id>)');
  308. $this->assertSame(
  309. 'users/edit',
  310. $route->uri(array(
  311. 'controller' => 'users',
  312. 'action' => 'edit',
  313. ))
  314. );
  315. $this->assertSame(
  316. 'users/edit/god',
  317. $route->uri(array(
  318. 'controller' => 'users',
  319. 'action' => 'edit',
  320. 'id' => 'god',
  321. ))
  322. );
  323. }
  324. /**
  325. * Provides test data for test_composing_url_from_route()
  326. * @return array
  327. */
  328. public function provider_composing_url_from_route()
  329. {
  330. return array(
  331. array('/welcome'),
  332. array('/news/view/42', array('controller' => 'news', 'action' => 'view', 'id' => 42)),
  333. array('http://kohanaframework.org/news', array('controller' => 'news'), true)
  334. );
  335. }
  336. /**
  337. * Tests Route::url()
  338. *
  339. * Checks the url composing from specific route via Route::url() shortcut
  340. *
  341. * @test
  342. * @dataProvider provider_composing_url_from_route
  343. * @param string $expected
  344. * @param array $params
  345. * @param boolean $protocol
  346. */
  347. public function test_composing_url_from_route($expected, $params = NULL, $protocol = NULL)
  348. {
  349. Route::set('foobar', '(<controller>(/<action>(/<id>)))')
  350. ->defaults(array(
  351. 'controller' => 'welcome',
  352. )
  353. );
  354. $this->setEnvironment(array(
  355. '_SERVER' => array('HTTP_HOST' => 'kohanaframework.org'),
  356. 'Kohana::$base_url' => '/',
  357. 'Request::$protocol' => 'http',
  358. 'Kohana::$index_file' => '',
  359. ));
  360. $this->assertSame($expected, Route::url('foobar', $params, $protocol));
  361. }
  362. /**
  363. * Tests Route::_compile()
  364. *
  365. * Makes sure that compile will use custom regex if specified
  366. *
  367. * @test
  368. * @covers Route::_compile
  369. */
  370. public function test_compile_uses_custom_regex_if_specificed()
  371. {
  372. $route = new Route(
  373. '<controller>(/<action>(/<id>))',
  374. array(
  375. 'controller' => '[a-z]+',
  376. 'id' => '\d+',
  377. )
  378. );
  379. $this->assertAttributeSame(
  380. '#^(?P<controller>[a-z]+)(?:/(?P<action>[^/.,;?\n]++)(?:/(?P<id>\d+))?)?$#uD',
  381. '_route_regex',
  382. $route
  383. );
  384. }
  385. }