From d588153abdf75261c798b097e1c42253bb196b1a Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org>
Date: Thu, 29 May 2014 14:21:58 +0100
Subject: [PATCH] Issue #2233623 by Berdir: Follow-up to fix Merge
 AliasManagerCacheDecorator into AliasManager.

---
 core/lib/Drupal/Core/Path/AliasManager.php    |   4 +-
 .../Tests/Core/Path/AliasManagerTest.php      | 543 ++++++++++++++++++
 2 files changed, 545 insertions(+), 2 deletions(-)
 create mode 100644 core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php

diff --git a/core/lib/Drupal/Core/Path/AliasManager.php b/core/lib/Drupal/Core/Path/AliasManager.php
index e3b686b07cee..3aae881a61b5 100644
--- a/core/lib/Drupal/Core/Path/AliasManager.php
+++ b/core/lib/Drupal/Core/Path/AliasManager.php
@@ -135,7 +135,7 @@ public function writeCache() {
     if ($this->cacheNeedsWriting && !empty($this->cacheKey)) {
       // Start with the preloaded path lookups, so that cached entries for other
       // languages will not be lost.
-      $path_lookups_lookups = $this->preloadedPathLookups ?: array();
+      $path_lookups = $this->preloadedPathLookups ?: array();
       foreach ($this->lookupMap as $langcode => $lookups) {
         $path_lookups[$langcode] = array_keys($lookups);
         if (!empty($this->noAlias[$langcode])) {
@@ -144,7 +144,7 @@ public function writeCache() {
       }
 
       if (!empty($path_lookups)) {
-        $twenty_four_hours_four_hours = 60 * 60 * 24;
+        $twenty_four_hours = 60 * 60 * 24;
         $this->cache->set($this->cacheKey, $path_lookups, REQUEST_TIME + $twenty_four_hours);
       }
     }
diff --git a/core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php b/core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php
new file mode 100644
index 000000000000..f8d5c0ef9dfd
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php
@@ -0,0 +1,543 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\ExternalUrlTest.
+ */
+
+namespace Drupal\Tests\Core;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Path\AliasManager;
+use Drupal\Core\Url;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+
+/**
+ * @group Drupal
+ * @group Path
+ *
+ * @coversDefaultClass \Drupal\Core\Path\AliasManager
+ */
+class AliasManagerTest extends UnitTestCase {
+
+  /**
+   * The alias manager.
+   *
+   * @var \Drupal\Core\Path\AliasManager
+   */
+  protected $aliasManager;
+
+  /**
+   * Alias storage.
+   *
+   * @var \Drupal\Core\Path\AliasStorageInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $aliasStorage;
+
+  /**
+   * Alias whitelist.
+   *
+   * @var \Drupal\Core\Path\AliasWhitelistInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $aliasWhitelist;
+
+  /**
+   * Language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $languageManager;
+
+  /**
+   * Cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cache;
+
+  /**
+   * The internal cache key used by the alias manager.
+   *
+   * @var string
+   */
+  protected $cacheKey = 'preload-paths:key';
+
+  /**
+   * The cache key passed to the alias manager.
+   *
+   * @var string
+   */
+  protected $path = 'key';
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Url object (external)',
+      'description' => 'Tests the \Drupal\Core\Url class with external paths.',
+      'group' => 'Routing',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->aliasStorage = $this->getMock('Drupal\Core\Path\AliasStorageInterface');
+    $this->aliasWhitelist = $this->getMock('Drupal\Core\Path\AliasWhitelistInterface');
+    $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
+    $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+
+    $this->aliasManager = new AliasManager($this->aliasStorage, $this->aliasWhitelist, $this->languageManager, $this->cache);
+
+  }
+
+  /**
+   * Tests the getPathByAlias method for an alias that have no matching path.
+   *
+   * @covers ::getPathByAlias()
+   */
+  public function testGetPathByAliasNoMatch() {
+    $alias = $this->randomName();
+
+    $language = new Language(array('id' => 'en'));
+
+    $this->languageManager->expects($this->any())
+      ->method('getCurrentLanguage')
+      ->with(LanguageInterface::TYPE_URL)
+      ->will($this->returnValue($language));
+
+    $this->aliasStorage->expects($this->once())
+      ->method('lookupPathSource')
+      ->with($alias, $language->getId())
+      ->will($this->returnValue(NULL));
+
+    $this->assertEquals($alias, $this->aliasManager->getPathByAlias($alias));
+    // Call it twice to test the static cache.
+    $this->assertEquals($alias, $this->aliasManager->getPathByAlias($alias));
+  }
+
+  /**
+   * Tests the getPathByAlias method for an alias that have a matching path.
+   *
+   * @covers ::getPathByAlias()
+   */
+  public function testGetPathByAliasNatch() {
+    $alias = $this->randomName();
+    $path = $this->randomName();
+
+    $language = $this->setUpCurrentLanguage();
+
+    $this->aliasStorage->expects($this->once())
+      ->method('lookupPathSource')
+      ->with($alias, $language->getId())
+      ->will($this->returnValue($path));
+
+    $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias));
+    // Call it twice to test the static cache.
+    $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias));
+  }
+
+  /**
+   * Tests the getPathByAlias method when a langcode is passed explicitly.
+   *
+   * @covers ::getPathByAlias()
+   */
+  public function testGetPathByAliasLangcode() {
+    $alias = $this->randomName();
+    $path = $this->randomName();
+
+    $this->languageManager->expects($this->never())
+      ->method('getCurrentLanguage');
+
+    $this->aliasStorage->expects($this->once())
+      ->method('lookupPathSource')
+      ->with($alias, 'de')
+      ->will($this->returnValue($path));
+
+    $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias, 'de'));
+    // Call it twice to test the static cache.
+    $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias, 'de'));
+  }
+
+
+  /**
+   * Tests the getAliasByPath method for a path that is not in the whitelist.
+   *
+   * @covers ::getAliasByPath()
+   */
+  public function testGetAliasByPathWhitelist() {
+    $path_part1 = $this->randomName();
+    $path_part2 = $this->randomName();
+    $path = $path_part1 . '/' . $path_part2;
+
+    $this->setUpCurrentLanguage();
+
+    $this->aliasWhitelist->expects($this->any())
+      ->method('get')
+      ->with($path_part1)
+      ->will($this->returnValue(FALSE));
+
+    // The whitelist returns FALSE for that path part, so the storage should
+    // never be called.
+    $this->aliasStorage->expects($this->never())
+      ->method('lookupPathAlias');
+
+    $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
+  }
+
+  /**
+   * Tests the getAliasByPath method for a path that has no matching alias.
+   *
+   * @covers ::getAliasByPath()
+   */
+  public function testGetAliasByPathNoMatch() {
+    $path_part1 = $this->randomName();
+    $path_part2 = $this->randomName();
+    $path = $path_part1 . '/' . $path_part2;
+
+    $language = $this->setUpCurrentLanguage();
+
+    $this->aliasManager->setCacheKey($this->path);
+
+    $this->aliasWhitelist->expects($this->any())
+      ->method('get')
+      ->with($path_part1)
+      ->will($this->returnValue(TRUE));
+
+    $this->aliasStorage->expects($this->once())
+      ->method('lookupPathAlias')
+      ->with($path, $language->getId())
+      ->will($this->returnValue(NULL));
+
+    $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
+    // Call it twice to test the static cache.
+    $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
+
+    // This needs to write out the cache.
+    $this->cache->expects($this->once())
+      ->method('set')
+      ->with($this->cacheKey, array($language->getId() => array($path)), REQUEST_TIME + (60 * 60 * 24));
+
+    $this->aliasManager->writeCache();
+  }
+
+  /**
+   * Tests the getAliasByPath method for a path that has a matching alias.
+   *
+   * @covers ::getAliasByPath()
+   * @covers ::writeCache()
+   */
+  public function testGetAliasByPathMatch() {
+    $path_part1 = $this->randomName();
+    $path_part2 = $this->randomName();
+    $path = $path_part1 . '/' . $path_part2;
+    $alias = $this->randomName();
+
+    $language = $this->setUpCurrentLanguage();
+
+    $this->aliasManager->setCacheKey($this->path);
+
+    $this->aliasWhitelist->expects($this->any())
+      ->method('get')
+      ->with($path_part1)
+      ->will($this->returnValue(TRUE));
+
+    $this->aliasStorage->expects($this->once())
+      ->method('lookupPathAlias')
+      ->with($path, $language->getId())
+      ->will($this->returnValue($alias));
+
+    $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
+    // Call it twice to test the static cache.
+    $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
+
+    // This needs to write out the cache.
+    $this->cache->expects($this->once())
+      ->method('set')
+      ->with($this->cacheKey, array($language->getId() => array($path)), REQUEST_TIME + (60 * 60 * 24));
+
+    $this->aliasManager->writeCache();
+  }
+
+  /**
+   * Tests the getAliasByPath method for a path that is preloaded.
+   *
+   * @covers ::getAliasByPath()
+   * @covers ::writeCache()
+   */
+  public function testGetAliasByPathCachedMatch() {
+    $path_part1 = $this->randomName();
+    $path_part2 = $this->randomName();
+    $path = $path_part1 . '/' . $path_part2;
+    $alias = $this->randomName();
+
+    $language = $this->setUpCurrentLanguage();
+
+    $cached_paths = array($language->getId() => array($path));
+    $this->cache->expects($this->once())
+      ->method('get')
+      ->with($this->cacheKey)
+      ->will($this->returnValue((object) array('data' => $cached_paths)));
+
+    // Simulate a request so that the preloaded paths are fetched.
+    $this->aliasManager->setCacheKey($this->path);
+
+    $this->aliasWhitelist->expects($this->any())
+      ->method('get')
+      ->with($path_part1)
+      ->will($this->returnValue(TRUE));
+
+    $this->aliasStorage->expects($this->once())
+      ->method('preloadPathAlias')
+      ->with($cached_paths[$language->getId()], $language->getId())
+      ->will($this->returnValue(array($path => $alias)));
+
+    // LookupPathAlias should not be called.
+    $this->aliasStorage->expects($this->never())
+      ->method('lookupPathAlias');
+
+    $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
+    // Call it twice to test the static cache.
+    $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
+
+    // This must not write to the cache again.
+    $this->cache->expects($this->never())
+      ->method('set');
+    $this->aliasManager->writeCache();
+  }
+
+  /**
+   * Tests the getAliasByPath cache when a different language is requested.
+   *
+   * @covers ::getAliasByPath()
+   * @covers ::writeCache()
+   */
+  public function testGetAliasByPathCachedMissLanguage() {
+    $path_part1 = $this->randomName();
+    $path_part2 = $this->randomName();
+    $path = $path_part1 . '/' . $path_part2;
+    $alias = $this->randomName();
+
+    $language = $this->setUpCurrentLanguage();
+    $cached_language = new Language(array('id' => 'de'));
+
+    $cached_paths = array($cached_language->getId() => array($path));
+    $this->cache->expects($this->once())
+      ->method('get')
+      ->with($this->cacheKey)
+      ->will($this->returnValue((object) array('data' => $cached_paths)));
+
+    // Simulate a request so that the preloaded paths are fetched.
+    $this->aliasManager->setCacheKey($this->path);
+
+    $this->aliasWhitelist->expects($this->any())
+      ->method('get')
+      ->with($path_part1)
+      ->will($this->returnValue(TRUE));
+
+    // The requested language is different than the cached, so this will
+    // need to load.
+    $this->aliasStorage->expects($this->never())
+      ->method('preloadPathAlias');
+    $this->aliasStorage->expects($this->once())
+      ->method('lookupPathAlias')
+      ->with($path, $language->getId())
+      ->will($this->returnValue($alias));
+
+    $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
+    // Call it twice to test the static cache.
+    $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
+
+    // This needs to write out the cache.
+    $expected_new_cache = array(
+      $cached_language->getId() => array($path),
+      $language->getId() => array($path),
+    );
+    $this->cache->expects($this->once())
+      ->method('set')
+      ->with($this->cacheKey, $expected_new_cache, REQUEST_TIME + (60 * 60 * 24));
+    $this->aliasManager->writeCache();
+  }
+
+  /**
+   * Tests the getAliasByPath cache with a preloaded path without alias.
+   *
+   * @covers ::getAliasByPath()
+   * @covers ::writeCache()
+   */
+  public function testGetAliasByPathCachedMissNoAlias() {
+    $path_part1 = $this->randomName();
+    $path_part2 = $this->randomName();
+    $path = $path_part1 . '/' . $path_part2;
+    $cached_path = $this->randomName();
+    $cached_alias = $this->randomName();
+
+    $language = $this->setUpCurrentLanguage();
+
+    $cached_paths = array($language->getId() => array($cached_path, $path));
+    $this->cache->expects($this->once())
+      ->method('get')
+      ->with($this->cacheKey)
+      ->will($this->returnValue((object) array('data' => $cached_paths)));
+
+    // Simulate a request so that the preloaded paths are fetched.
+    $this->aliasManager->setCacheKey($this->path);
+
+    $this->aliasWhitelist->expects($this->any())
+      ->method('get')
+      ->with($path_part1)
+      ->will($this->returnValue(TRUE));
+
+    $this->aliasStorage->expects($this->once())
+      ->method('preloadPathAlias')
+      ->with($cached_paths[$language->getId()], $language->getId())
+      ->will($this->returnValue(array($cached_path => $cached_alias)));
+
+    // LookupPathAlias() should not be called.
+    $this->aliasStorage->expects($this->never())
+      ->method('lookupPathAlias');
+
+    $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
+    // Call it twice to test the static cache.
+    $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
+
+    // This must not write to the cache again.
+    $this->cache->expects($this->never())
+      ->method('set');
+    $this->aliasManager->writeCache();
+  }
+
+  /**
+   * Tests the getAliasByPath cache with an unpreloaded path without alias.
+   *
+   * @covers ::getAliasByPath()
+   * @covers ::writeCache()
+   */
+  public function testGetAliasByPathUncachedMissNoAlias() {
+    $path_part1 = $this->randomName();
+    $path_part2 = $this->randomName();
+    $path = $path_part1 . '/' . $path_part2;
+    $cached_path = $this->randomName();
+    $cached_alias = $this->randomName();
+
+    $language = $this->setUpCurrentLanguage();
+
+    $cached_paths = array($language->getId() => array($cached_path));
+    $this->cache->expects($this->once())
+      ->method('get')
+      ->with($this->cacheKey)
+      ->will($this->returnValue((object) array('data' => $cached_paths)));
+
+    // Simulate a request so that the preloaded paths are fetched.
+    $this->aliasManager->setCacheKey($this->path);
+
+    $this->aliasWhitelist->expects($this->any())
+      ->method('get')
+      ->with($path_part1)
+      ->will($this->returnValue(TRUE));
+
+    $this->aliasStorage->expects($this->once())
+      ->method('preloadPathAlias')
+      ->with($cached_paths[$language->getId()], $language->getId())
+      ->will($this->returnValue(array($cached_path => $cached_alias)));
+
+    $this->aliasStorage->expects($this->once())
+      ->method('lookupPathAlias')
+      ->with($path, $language->getId())
+      ->will($this->returnValue(NULL));
+
+    $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
+    // Call it twice to test the static cache.
+    $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
+
+    // This needs to write out the cache.
+    $expected_new_cache = array(
+      $language->getId() => array($cached_path, $path),
+    );
+    $this->cache->expects($this->once())
+      ->method('set')
+      ->with($this->cacheKey, $expected_new_cache, REQUEST_TIME + (60 * 60 * 24));
+    $this->aliasManager->writeCache();
+  }
+
+  /**
+   * Tests the getAliasByPath cache with an unpreloaded path with alias.
+   *
+   * @covers ::getAliasByPath()
+   * @covers ::writeCache()
+   */
+  public function testGetAliasByPathUncachedMissWithAlias() {
+    $path_part1 = $this->randomName();
+    $path_part2 = $this->randomName();
+    $path = $path_part1 . '/' . $path_part2;
+    $cached_path = $this->randomName();
+    $cached_no_alias_path = $this->randomName();
+    $cached_alias = $this->randomName();
+    $new_alias = $this->randomName();
+
+    $language = $this->setUpCurrentLanguage();
+
+    $cached_paths = array($language->getId() => array($cached_path, $cached_no_alias_path));
+    $this->cache->expects($this->once())
+      ->method('get')
+      ->with($this->cacheKey)
+      ->will($this->returnValue((object) array('data' => $cached_paths)));
+
+    // Simulate a request so that the preloaded paths are fetched.
+    $this->aliasManager->setCacheKey($this->path);
+
+    $this->aliasWhitelist->expects($this->any())
+      ->method('get')
+      ->with($path_part1)
+      ->will($this->returnValue(TRUE));
+
+    $this->aliasStorage->expects($this->once())
+      ->method('preloadPathAlias')
+      ->with($cached_paths[$language->getId()], $language->getId())
+      ->will($this->returnValue(array($cached_path => $cached_alias)));
+
+    $this->aliasStorage->expects($this->once())
+      ->method('lookupPathAlias')
+      ->with($path, $language->getId())
+      ->will($this->returnValue($new_alias));
+
+    $this->assertEquals($new_alias, $this->aliasManager->getAliasByPath($path));
+    // Call it twice to test the static cache.
+    $this->assertEquals($new_alias, $this->aliasManager->getAliasByPath($path));
+
+    // This needs to write out the cache.
+    $expected_new_cache = array(
+      $language->getId() => array($cached_path, $path, $cached_no_alias_path),
+    );
+    $this->cache->expects($this->once())
+      ->method('set')
+      ->with($this->cacheKey, $expected_new_cache, REQUEST_TIME + (60 * 60 * 24));
+    $this->aliasManager->writeCache();
+  }
+
+  /**
+   * Sets up the current language.
+   *
+   * @return \Drupal\Core\Language\LanguageInterface
+   *   The current language object.
+   */
+  protected function setUpCurrentLanguage() {
+    $language = new Language(array('id' => 'en'));
+
+    $this->languageManager->expects($this->any())
+      ->method('getCurrentLanguage')
+      ->with(LanguageInterface::TYPE_URL)
+      ->will($this->returnValue($language));
+
+    return $language;
+  }
+
+}
-- 
GitLab