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'));
149 * Tests the getAliasByPath method for a path that is not in the whitelist.
151 * @covers ::getAliasByPath
153 public function testGetAliasByPathWhitelist() {
154 $path_part1 = $this->randomMachineName();
155 $path_part2 = $this->randomMachineName();
156 $path = '/' . $path_part1 . '/' . $path_part2;
158 $this->setUpCurrentLanguage();
160 $this->aliasWhitelist->expects($this->any())
163 ->will($this->returnValue(FALSE));
165 // The whitelist returns FALSE for that path part, so the storage should
167 $this->aliasStorage->expects($this->never())
168 ->method('lookupPathAlias');
170 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
174 * Tests the getAliasByPath method for a path that has no matching alias.
176 * @covers ::getAliasByPath
178 public function testGetAliasByPathNoMatch() {
179 $path_part1 = $this->randomMachineName();
180 $path_part2 = $this->randomMachineName();
181 $path = '/' . $path_part1 . '/' . $path_part2;
183 $language = $this->setUpCurrentLanguage();
185 $this->aliasManager->setCacheKey($this->path);
187 $this->aliasWhitelist->expects($this->any())
190 ->will($this->returnValue(TRUE));
192 $this->aliasStorage->expects($this->once())
193 ->method('lookupPathAlias')
194 ->with($path, $language->getId())
195 ->will($this->returnValue(NULL));
197 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
198 // Call it twice to test the static cache.
199 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
201 // This needs to write out the cache.
202 $this->cache->expects($this->once())
204 ->with($this->cacheKey, [$language->getId() => [$path]], (int) $_SERVER['REQUEST_TIME'] + (60 * 60 * 24));
206 $this->aliasManager->writeCache();
210 * Tests the getAliasByPath method for a path that has a matching alias.
212 * @covers ::getAliasByPath
213 * @covers ::writeCache
215 public function testGetAliasByPathMatch() {
216 $path_part1 = $this->randomMachineName();
217 $path_part2 = $this->randomMachineName();
218 $path = '/' . $path_part1 . '/' . $path_part2;
219 $alias = $this->randomMachineName();
221 $language = $this->setUpCurrentLanguage();
223 $this->aliasManager->setCacheKey($this->path);
225 $this->aliasWhitelist->expects($this->any())
228 ->will($this->returnValue(TRUE));
230 $this->aliasStorage->expects($this->once())
231 ->method('lookupPathAlias')
232 ->with($path, $language->getId())
233 ->will($this->returnValue($alias));
235 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
236 // Call it twice to test the static cache.
237 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
239 // This needs to write out the cache.
240 $this->cache->expects($this->once())
242 ->with($this->cacheKey, [$language->getId() => [$path]], (int) $_SERVER['REQUEST_TIME'] + (60 * 60 * 24));
244 $this->aliasManager->writeCache();
248 * Tests the getAliasByPath method for a path that is preloaded.
250 * @covers ::getAliasByPath
251 * @covers ::writeCache
253 public function testGetAliasByPathCachedMatch() {
254 $path_part1 = $this->randomMachineName();
255 $path_part2 = $this->randomMachineName();
256 $path = '/' . $path_part1 . '/' . $path_part2;
257 $alias = $this->randomMachineName();
259 $language = $this->setUpCurrentLanguage();
261 $cached_paths = [$language->getId() => [$path]];
262 $this->cache->expects($this->once())
264 ->with($this->cacheKey)
265 ->will($this->returnValue((object) ['data' => $cached_paths]));
267 // Simulate a request so that the preloaded paths are fetched.
268 $this->aliasManager->setCacheKey($this->path);
270 $this->aliasWhitelist->expects($this->any())
273 ->will($this->returnValue(TRUE));
275 $this->aliasStorage->expects($this->once())
276 ->method('preloadPathAlias')
277 ->with($cached_paths[$language->getId()], $language->getId())
278 ->will($this->returnValue([$path => $alias]));
280 // LookupPathAlias should not be called.
281 $this->aliasStorage->expects($this->never())
282 ->method('lookupPathAlias');
284 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
285 // Call it twice to test the static cache.
286 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
288 // This must not write to the cache again.
289 $this->cache->expects($this->never())
291 $this->aliasManager->writeCache();
295 * Tests the getAliasByPath cache when a different language is requested.
297 * @covers ::getAliasByPath
298 * @covers ::writeCache
300 public function testGetAliasByPathCachedMissLanguage() {
301 $path_part1 = $this->randomMachineName();
302 $path_part2 = $this->randomMachineName();
303 $path = '/' . $path_part1 . '/' . $path_part2;
304 $alias = $this->randomMachineName();
306 $language = $this->setUpCurrentLanguage();
307 $cached_language = new Language(['id' => 'de']);
309 $cached_paths = [$cached_language->getId() => [$path]];
310 $this->cache->expects($this->once())
312 ->with($this->cacheKey)
313 ->will($this->returnValue((object) ['data' => $cached_paths]));
315 // Simulate a request so that the preloaded paths are fetched.
316 $this->aliasManager->setCacheKey($this->path);
318 $this->aliasWhitelist->expects($this->any())
321 ->will($this->returnValue(TRUE));
323 // The requested language is different than the cached, so this will
325 $this->aliasStorage->expects($this->never())
326 ->method('preloadPathAlias');
327 $this->aliasStorage->expects($this->once())
328 ->method('lookupPathAlias')
329 ->with($path, $language->getId())
330 ->will($this->returnValue($alias));
332 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
333 // Call it twice to test the static cache.
334 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
336 // There is already a cache entry, so this should not write out to the
338 $this->cache->expects($this->never())
340 $this->aliasManager->writeCache();
344 * Tests the getAliasByPath cache with a preloaded path without alias.
346 * @covers ::getAliasByPath
347 * @covers ::writeCache
349 public function testGetAliasByPathCachedMissNoAlias() {
350 $path_part1 = $this->randomMachineName();
351 $path_part2 = $this->randomMachineName();
352 $path = '/' . $path_part1 . '/' . $path_part2;
353 $cached_path = $this->randomMachineName();
354 $cached_alias = $this->randomMachineName();
356 $language = $this->setUpCurrentLanguage();
358 $cached_paths = [$language->getId() => [$cached_path, $path]];
359 $this->cache->expects($this->once())
361 ->with($this->cacheKey)
362 ->will($this->returnValue((object) ['data' => $cached_paths]));
364 // Simulate a request so that the preloaded paths are fetched.
365 $this->aliasManager->setCacheKey($this->path);
367 $this->aliasWhitelist->expects($this->any())
370 ->will($this->returnValue(TRUE));
372 $this->aliasStorage->expects($this->once())
373 ->method('preloadPathAlias')
374 ->with($cached_paths[$language->getId()], $language->getId())
375 ->will($this->returnValue([$cached_path => $cached_alias]));
377 // LookupPathAlias() should not be called.
378 $this->aliasStorage->expects($this->never())
379 ->method('lookupPathAlias');
381 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
382 // Call it twice to test the static cache.
383 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
385 // This must not write to the cache again.
386 $this->cache->expects($this->never())
388 $this->aliasManager->writeCache();
392 * Tests the getAliasByPath cache with an unpreloaded path without alias.
394 * @covers ::getAliasByPath
395 * @covers ::writeCache
397 public function testGetAliasByPathUncachedMissNoAlias() {
398 $path_part1 = $this->randomMachineName();
399 $path_part2 = $this->randomMachineName();
400 $path = '/' . $path_part1 . '/' . $path_part2;
401 $cached_path = $this->randomMachineName();
402 $cached_alias = $this->randomMachineName();
404 $language = $this->setUpCurrentLanguage();
406 $cached_paths = [$language->getId() => [$cached_path]];
407 $this->cache->expects($this->once())
409 ->with($this->cacheKey)
410 ->will($this->returnValue((object) ['data' => $cached_paths]));
412 // Simulate a request so that the preloaded paths are fetched.
413 $this->aliasManager->setCacheKey($this->path);
415 $this->aliasWhitelist->expects($this->any())
418 ->will($this->returnValue(TRUE));
420 $this->aliasStorage->expects($this->once())
421 ->method('preloadPathAlias')
422 ->with($cached_paths[$language->getId()], $language->getId())
423 ->will($this->returnValue([$cached_path => $cached_alias]));
425 $this->aliasStorage->expects($this->once())
426 ->method('lookupPathAlias')
427 ->with($path, $language->getId())
428 ->will($this->returnValue(NULL));
430 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
431 // Call it twice to test the static cache.
432 $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
434 // There is already a cache entry, so this should not write out to the
436 $this->cache->expects($this->never())
438 $this->aliasManager->writeCache();
442 * @covers ::cacheClear
444 public function testCacheClear() {
447 $language = $this->setUpCurrentLanguage();
448 $this->aliasStorage->expects($this->exactly(2))
449 ->method('lookupPathAlias')
450 ->with($path, $language->getId())
451 ->willReturn($alias);
452 $this->aliasWhitelist->expects($this->any())
456 // Populate the lookup map.
457 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path, $language->getId()));
459 // Check that the cache is populated.
460 $original_storage = clone $this->aliasStorage;
461 $this->aliasStorage->expects($this->never())
462 ->method('lookupPathSource');
463 $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias, $language->getId()));
465 // Clear specific source.
466 $this->cache->expects($this->exactly(2))
468 $this->aliasManager->cacheClear($path);
470 // Ensure cache has been cleared (this will be the 2nd call to
471 // `lookupPathAlias` if cache is cleared).
472 $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path, $language->getId()));
474 // Clear non-existent source.
475 $this->aliasManager->cacheClear('non-existent');
479 * Tests the getAliasByPath cache with an unpreloaded path with alias.
481 * @covers ::getAliasByPath
482 * @covers ::writeCache
484 public function testGetAliasByPathUncachedMissWithAlias() {
485 $path_part1 = $this->randomMachineName();
486 $path_part2 = $this->randomMachineName();
487 $path = '/' . $path_part1 . '/' . $path_part2;
488 $cached_path = $this->randomMachineName();
489 $cached_no_alias_path = $this->randomMachineName();
490 $cached_alias = $this->randomMachineName();
491 $new_alias = $this->randomMachineName();
493 $language = $this->setUpCurrentLanguage();
495 $cached_paths = [$language->getId() => [$cached_path, $cached_no_alias_path]];
496 $this->cache->expects($this->once())
498 ->with($this->cacheKey)
499 ->will($this->returnValue((object) ['data' => $cached_paths]));
501 // Simulate a request so that the preloaded paths are fetched.
502 $this->aliasManager->setCacheKey($this->path);
504 $this->aliasWhitelist->expects($this->any())
507 ->will($this->returnValue(TRUE));
509 $this->aliasStorage->expects($this->once())
510 ->method('preloadPathAlias')
511 ->with($cached_paths[$language->getId()], $language->getId())
512 ->will($this->returnValue([$cached_path => $cached_alias]));
514 $this->aliasStorage->expects($this->once())
515 ->method('lookupPathAlias')
516 ->with($path, $language->getId())
517 ->will($this->returnValue($new_alias));
519 $this->assertEquals($new_alias, $this->aliasManager->getAliasByPath($path));
520 // Call it twice to test the static cache.
521 $this->assertEquals($new_alias, $this->aliasManager->getAliasByPath($path));
523 // There is already a cache entry, so this should not write out to the
525 $this->cache->expects($this->never())
527 $this->aliasManager->writeCache();
531 * Sets up the current language.
533 * @return \Drupal\Core\Language\LanguageInterface
534 * The current language object.
536 protected function setUpCurrentLanguage() {
537 $language = new Language(['id' => 'en']);
539 $this->languageManager->expects($this->any())
540 ->method('getCurrentLanguage')
541 ->with(LanguageInterface::TYPE_URL)
542 ->will($this->returnValue($language));