3 namespace Drupal\Tests\Core\Path;
5 use Drupal\Core\Language\Language;
6 use Drupal\Core\Language\LanguageInterface;
7 use Drupal\Core\Path\AliasManager;
8 use Drupal\Tests\UnitTestCase;
11 * @coversDefaultClass \Drupal\Core\Path\AliasManager
14 class AliasManagerTest extends UnitTestCase {
19 * @var \Drupal\Core\Path\AliasManager
21 protected $aliasManager;
26 * @var \Drupal\Core\Path\AliasStorageInterface|\PHPUnit_Framework_MockObject_MockObject
28 protected $aliasStorage;
33 * @var \Drupal\Core\Path\AliasWhitelistInterface|\PHPUnit_Framework_MockObject_MockObject
35 protected $aliasWhitelist;
40 * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
42 protected $languageManager;
47 * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
52 * The internal cache key used by the alias manager.
56 protected $cacheKey = 'preload-paths:key';
59 * The cache key passed to the alias manager.
63 protected $path = 'key';
68 protected function setUp() {
71 $this->aliasStorage = $this->getMock('Drupal\Core\Path\AliasStorageInterface');
72 $this->aliasWhitelist = $this->getMock('Drupal\Core\Path\AliasWhitelistInterface');
73 $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
74 $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
76 $this->aliasManager = new AliasManager($this->aliasStorage, $this->aliasWhitelist, $this->languageManager, $this->cache);
81 * Tests the getPathByAlias method for an alias that have no matching path.
83 * @covers ::getPathByAlias
85 public function testGetPathByAliasNoMatch() {
86 $alias = '/' . $this->randomMachineName();
88 $language = new Language(['id' => 'en']);
90 $this->languageManager->expects($this->any())
91 ->method('getCurrentLanguage')
92 ->with(LanguageInterface::TYPE_URL)
93 ->will($this->returnValue($language));
95 $this->aliasStorage->expects($this->once())
96 ->method('lookupPathSource')
97 ->with($alias, $language->getId())
98 ->will($this->returnValue(NULL));
100 $this->assertEquals($alias, $this->aliasManager->getPathByAlias($alias));
101 // Call it twice to test the static cache.
102 $this->assertEquals($alias, $this->aliasManager->getPathByAlias($alias));
106 * Tests the getPathByAlias method for an alias that have a matching path.
108 * @covers ::getPathByAlias
110 public function testGetPathByAliasNatch() {
111 $alias = $this->randomMachineName();
112 $path = $this->randomMachineName();
114 $language = $this->setUpCurrentLanguage();
116 $this->aliasStorage->expects($this->once())
117 ->method('lookupPathSource')
118 ->with($alias, $language->getId())
119 ->will($this->returnValue($path));
121 $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias));
122 // Call it twice to test the static cache.
123 $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias));
127 * Tests the getPathByAlias method when a langcode is passed explicitly.
129 * @covers ::getPathByAlias
131 public function testGetPathByAliasLangcode() {
132 $alias = $this->randomMachineName();
133 $path = $this->randomMachineName();
135 $this->languageManager->expects($this->never())
136 ->method('getCurrentLanguage');
138 $this->aliasStorage->expects($this->once())
139 ->method('lookupPathSource')
141 ->will($this->returnValue($path));
143 $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias, 'de'));
144 // Call it twice to test the static cache.
145 $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias, 'de'));
150 * Tests the getAliasByPath method for a path that is not in the whitelist.
152 * @covers ::getAliasByPath
154 public function testGetAliasByPathWhitelist() {
155 $path_part1 = $this->randomMachineName();
156 $path_part2 = $this->randomMachineName();
157 $path = '/' . $path_part1 . '/' . $path_part2;
159 $this->setUpCurrentLanguage();
161 $this->aliasWhitelist->expects($this->any())
164 ->will($this->returnValue(FALSE));
166 // The whitelist returns FALSE for that path part, so the storage should
168 $this->aliasStorage->expects($this->never())
169 ->method('lookupPathAlias');
171 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
175 * Tests the getAliasByPath method for a path that has no matching alias.
177 * @covers ::getAliasByPath
179 public function testGetAliasByPathNoMatch() {
180 $path_part1 = $this->randomMachineName();
181 $path_part2 = $this->randomMachineName();
182 $path = '/' . $path_part1 . '/' . $path_part2;
184 $language = $this->setUpCurrentLanguage();
186 $this->aliasManager->setCacheKey($this->path);
188 $this->aliasWhitelist->expects($this->any())
191 ->will($this->returnValue(TRUE));
193 $this->aliasStorage->expects($this->once())
194 ->method('lookupPathAlias')
195 ->with($path, $language->getId())
196 ->will($this->returnValue(NULL));
198 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
199 // Call it twice to test the static cache.
200 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
202 // This needs to write out the cache.
203 $this->cache->expects($this->once())
205 ->with($this->cacheKey, [$language->getId() => [$path]], (int) $_SERVER['REQUEST_TIME'] + (60 * 60 * 24));
207 $this->aliasManager->writeCache();
211 * Tests the getAliasByPath method for a path that has a matching alias.
213 * @covers ::getAliasByPath
214 * @covers ::writeCache
216 public function testGetAliasByPathMatch() {
217 $path_part1 = $this->randomMachineName();
218 $path_part2 = $this->randomMachineName();
219 $path = '/' . $path_part1 . '/' . $path_part2;
220 $alias = $this->randomMachineName();
222 $language = $this->setUpCurrentLanguage();
224 $this->aliasManager->setCacheKey($this->path);
226 $this->aliasWhitelist->expects($this->any())
229 ->will($this->returnValue(TRUE));
231 $this->aliasStorage->expects($this->once())
232 ->method('lookupPathAlias')
233 ->with($path, $language->getId())
234 ->will($this->returnValue($alias));
236 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
237 // Call it twice to test the static cache.
238 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
240 // This needs to write out the cache.
241 $this->cache->expects($this->once())
243 ->with($this->cacheKey, [$language->getId() => [$path]], (int) $_SERVER['REQUEST_TIME'] + (60 * 60 * 24));
245 $this->aliasManager->writeCache();
249 * Tests the getAliasByPath method for a path that is preloaded.
251 * @covers ::getAliasByPath
252 * @covers ::writeCache
254 public function testGetAliasByPathCachedMatch() {
255 $path_part1 = $this->randomMachineName();
256 $path_part2 = $this->randomMachineName();
257 $path = '/' . $path_part1 . '/' . $path_part2;
258 $alias = $this->randomMachineName();
260 $language = $this->setUpCurrentLanguage();
262 $cached_paths = [$language->getId() => [$path]];
263 $this->cache->expects($this->once())
265 ->with($this->cacheKey)
266 ->will($this->returnValue((object) ['data' => $cached_paths]));
268 // Simulate a request so that the preloaded paths are fetched.
269 $this->aliasManager->setCacheKey($this->path);
271 $this->aliasWhitelist->expects($this->any())
274 ->will($this->returnValue(TRUE));
276 $this->aliasStorage->expects($this->once())
277 ->method('preloadPathAlias')
278 ->with($cached_paths[$language->getId()], $language->getId())
279 ->will($this->returnValue([$path => $alias]));
281 // LookupPathAlias should not be called.
282 $this->aliasStorage->expects($this->never())
283 ->method('lookupPathAlias');
285 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
286 // Call it twice to test the static cache.
287 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
289 // This must not write to the cache again.
290 $this->cache->expects($this->never())
292 $this->aliasManager->writeCache();
296 * Tests the getAliasByPath cache when a different language is requested.
298 * @covers ::getAliasByPath
299 * @covers ::writeCache
301 public function testGetAliasByPathCachedMissLanguage() {
302 $path_part1 = $this->randomMachineName();
303 $path_part2 = $this->randomMachineName();
304 $path = '/' . $path_part1 . '/' . $path_part2;
305 $alias = $this->randomMachineName();
307 $language = $this->setUpCurrentLanguage();
308 $cached_language = new Language(['id' => 'de']);
310 $cached_paths = [$cached_language->getId() => [$path]];
311 $this->cache->expects($this->once())
313 ->with($this->cacheKey)
314 ->will($this->returnValue((object) ['data' => $cached_paths]));
316 // Simulate a request so that the preloaded paths are fetched.
317 $this->aliasManager->setCacheKey($this->path);
319 $this->aliasWhitelist->expects($this->any())
322 ->will($this->returnValue(TRUE));
324 // The requested language is different than the cached, so this will
326 $this->aliasStorage->expects($this->never())
327 ->method('preloadPathAlias');
328 $this->aliasStorage->expects($this->once())
329 ->method('lookupPathAlias')
330 ->with($path, $language->getId())
331 ->will($this->returnValue($alias));
333 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
334 // Call it twice to test the static cache.
335 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
337 // There is already a cache entry, so this should not write out to the
339 $this->cache->expects($this->never())
341 $this->aliasManager->writeCache();
345 * Tests the getAliasByPath cache with a preloaded path without alias.
347 * @covers ::getAliasByPath
348 * @covers ::writeCache
350 public function testGetAliasByPathCachedMissNoAlias() {
351 $path_part1 = $this->randomMachineName();
352 $path_part2 = $this->randomMachineName();
353 $path = '/' . $path_part1 . '/' . $path_part2;
354 $cached_path = $this->randomMachineName();
355 $cached_alias = $this->randomMachineName();
357 $language = $this->setUpCurrentLanguage();
359 $cached_paths = [$language->getId() => [$cached_path, $path]];
360 $this->cache->expects($this->once())
362 ->with($this->cacheKey)
363 ->will($this->returnValue((object) ['data' => $cached_paths]));
365 // Simulate a request so that the preloaded paths are fetched.
366 $this->aliasManager->setCacheKey($this->path);
368 $this->aliasWhitelist->expects($this->any())
371 ->will($this->returnValue(TRUE));
373 $this->aliasStorage->expects($this->once())
374 ->method('preloadPathAlias')
375 ->with($cached_paths[$language->getId()], $language->getId())
376 ->will($this->returnValue([$cached_path => $cached_alias]));
378 // LookupPathAlias() should not be called.
379 $this->aliasStorage->expects($this->never())
380 ->method('lookupPathAlias');
382 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
383 // Call it twice to test the static cache.
384 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
386 // This must not write to the cache again.
387 $this->cache->expects($this->never())
389 $this->aliasManager->writeCache();
393 * Tests the getAliasByPath cache with an unpreloaded path without alias.
395 * @covers ::getAliasByPath
396 * @covers ::writeCache
398 public function testGetAliasByPathUncachedMissNoAlias() {
399 $path_part1 = $this->randomMachineName();
400 $path_part2 = $this->randomMachineName();
401 $path = '/' . $path_part1 . '/' . $path_part2;
402 $cached_path = $this->randomMachineName();
403 $cached_alias = $this->randomMachineName();
405 $language = $this->setUpCurrentLanguage();
407 $cached_paths = [$language->getId() => [$cached_path]];
408 $this->cache->expects($this->once())
410 ->with($this->cacheKey)
411 ->will($this->returnValue((object) ['data' => $cached_paths]));
413 // Simulate a request so that the preloaded paths are fetched.
414 $this->aliasManager->setCacheKey($this->path);
416 $this->aliasWhitelist->expects($this->any())
419 ->will($this->returnValue(TRUE));
421 $this->aliasStorage->expects($this->once())
422 ->method('preloadPathAlias')
423 ->with($cached_paths[$language->getId()], $language->getId())
424 ->will($this->returnValue([$cached_path => $cached_alias]));
426 $this->aliasStorage->expects($this->once())
427 ->method('lookupPathAlias')
428 ->with($path, $language->getId())
429 ->will($this->returnValue(NULL));
431 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
432 // Call it twice to test the static cache.
433 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
435 // There is already a cache entry, so this should not write out to the
437 $this->cache->expects($this->never())
439 $this->aliasManager->writeCache();
443 * @covers ::cacheClear
445 public function testCacheClear() {
448 $language = $this->setUpCurrentLanguage();
449 $this->aliasStorage->expects($this->exactly(2))
450 ->method('lookupPathAlias')
451 ->with($path, $language->getId())
452 ->willReturn($alias);
453 $this->aliasWhitelist->expects($this->any())
457 // Populate the lookup map.
458 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path, $language->getId()));
460 // Check that the cache is populated.
461 $original_storage = clone $this->aliasStorage;
462 $this->aliasStorage->expects($this->never())
463 ->method('lookupPathSource');
464 $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias, $language->getId()));
466 // Clear specific source.
467 $this->cache->expects($this->exactly(2))
469 $this->aliasManager->cacheClear($path);
471 // Ensure cache has been cleared (this will be the 2nd call to
472 // `lookupPathAlias` if cache is cleared).
473 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path, $language->getId()));
475 // Clear non-existent source.
476 $this->aliasManager->cacheClear('non-existent');
480 * Tests the getAliasByPath cache with an unpreloaded path with alias.
482 * @covers ::getAliasByPath
483 * @covers ::writeCache
485 public function testGetAliasByPathUncachedMissWithAlias() {
486 $path_part1 = $this->randomMachineName();
487 $path_part2 = $this->randomMachineName();
488 $path = '/' . $path_part1 . '/' . $path_part2;
489 $cached_path = $this->randomMachineName();
490 $cached_no_alias_path = $this->randomMachineName();
491 $cached_alias = $this->randomMachineName();
492 $new_alias = $this->randomMachineName();
494 $language = $this->setUpCurrentLanguage();
496 $cached_paths = [$language->getId() => [$cached_path, $cached_no_alias_path]];
497 $this->cache->expects($this->once())
499 ->with($this->cacheKey)
500 ->will($this->returnValue((object) ['data' => $cached_paths]));
502 // Simulate a request so that the preloaded paths are fetched.
503 $this->aliasManager->setCacheKey($this->path);
505 $this->aliasWhitelist->expects($this->any())
508 ->will($this->returnValue(TRUE));
510 $this->aliasStorage->expects($this->once())
511 ->method('preloadPathAlias')
512 ->with($cached_paths[$language->getId()], $language->getId())
513 ->will($this->returnValue([$cached_path => $cached_alias]));
515 $this->aliasStorage->expects($this->once())
516 ->method('lookupPathAlias')
517 ->with($path, $language->getId())
518 ->will($this->returnValue($new_alias));
520 $this->assertEquals($new_alias, $this->aliasManager->getAliasByPath($path));
521 // Call it twice to test the static cache.
522 $this->assertEquals($new_alias, $this->aliasManager->getAliasByPath($path));
524 // There is already a cache entry, so this should not write out to the
526 $this->cache->expects($this->never())
528 $this->aliasManager->writeCache();
532 * Sets up the current language.
534 * @return \Drupal\Core\Language\LanguageInterface
535 * The current language object.
537 protected function setUpCurrentLanguage() {
538 $language = new Language(['id' => 'en']);
540 $this->languageManager->expects($this->any())
541 ->method('getCurrentLanguage')
542 ->with(LanguageInterface::TYPE_URL)
543 ->will($this->returnValue($language));