Version 1
[yaffs-website] / web / core / tests / Drupal / Tests / Core / Cache / Context / CacheContextsManagerTest.php
diff --git a/web/core/tests/Drupal/Tests/Core/Cache/Context/CacheContextsManagerTest.php b/web/core/tests/Drupal/Tests/Core/Cache/Context/CacheContextsManagerTest.php
new file mode 100644 (file)
index 0000000..f89e639
--- /dev/null
@@ -0,0 +1,306 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Cache\Context\CacheContextsManagerTest.
+ */
+
+namespace Drupal\Tests\Core\Cache\Context;
+
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Cache\Context\CacheContextsManager;
+use Drupal\Core\Cache\Context\CacheContextInterface;
+use Drupal\Core\Cache\Context\CalculatedCacheContextInterface;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\DependencyInjection\Container;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Cache\Context\CacheContextsManager
+ * @group Cache
+ */
+class CacheContextsManagerTest extends UnitTestCase {
+
+  /**
+   * @covers ::optimizeTokens
+   *
+   * @dataProvider providerTestOptimizeTokens
+   */
+  public function testOptimizeTokens(array $context_tokens, array $optimized_context_tokens) {
+    $container = $this->getMockBuilder('Drupal\Core\DependencyInjection\Container')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $container->expects($this->any())
+      ->method('get')
+      ->will($this->returnValueMap([
+        ['cache_context.a', Container::EXCEPTION_ON_INVALID_REFERENCE, new FooCacheContext()],
+        ['cache_context.a.b', Container::EXCEPTION_ON_INVALID_REFERENCE, new FooCacheContext()],
+        ['cache_context.a.b.c', Container::EXCEPTION_ON_INVALID_REFERENCE, new BazCacheContext()],
+        ['cache_context.x', Container::EXCEPTION_ON_INVALID_REFERENCE, new BazCacheContext()],
+        ['cache_context.a.b.no-optimize', Container::EXCEPTION_ON_INVALID_REFERENCE, new NoOptimizeCacheContext()],
+      ]));
+    $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
+
+    $this->assertSame($optimized_context_tokens, $cache_contexts_manager->optimizeTokens($context_tokens));
+  }
+
+  /**
+   * Provides a list of context token sets.
+   */
+  public function providerTestOptimizeTokens() {
+    return [
+      [['a', 'x'], ['a', 'x']],
+      [['a.b', 'x'], ['a.b', 'x']],
+
+      // Direct ancestor, single-level hierarchy.
+      [['a', 'a.b'], ['a']],
+      [['a.b', 'a'], ['a']],
+
+      // Direct ancestor, multi-level hierarchy.
+      [['a.b', 'a.b.c'], ['a.b']],
+      [['a.b.c', 'a.b'], ['a.b']],
+
+      // Indirect ancestor.
+      [['a', 'a.b.c'], ['a']],
+      [['a.b.c', 'a'], ['a']],
+
+      // Direct & indirect ancestors.
+      [['a', 'a.b', 'a.b.c'], ['a']],
+      [['a', 'a.b.c', 'a.b'], ['a']],
+      [['a.b', 'a', 'a.b.c'], ['a']],
+      [['a.b', 'a.b.c', 'a'], ['a']],
+      [['a.b.c', 'a.b', 'a'], ['a']],
+      [['a.b.c', 'a', 'a.b'], ['a']],
+
+      // Using parameters.
+      [['a', 'a.b.c:foo'], ['a']],
+      [['a.b.c:foo', 'a'], ['a']],
+      [['a.b.c:foo', 'a.b.c'], ['a.b.c']],
+
+      // max-age 0 is treated as non-optimizable.
+      [['a.b.no-optimize', 'a.b', 'a'], ['a.b.no-optimize', 'a']],
+    ];
+  }
+
+  /**
+   * @covers ::convertTokensToKeys
+   */
+  public function testConvertTokensToKeys() {
+    $container = $this->getMockContainer();
+    $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
+
+    $new_keys = $cache_contexts_manager->convertTokensToKeys([
+      'foo',
+      'baz:parameterA',
+      'baz:parameterB',
+    ]);
+
+    $expected = [
+      '[baz:parameterA]=cnenzrgreN',
+      '[baz:parameterB]=cnenzrgreO',
+      '[foo]=bar',
+    ];
+    $this->assertEquals($expected, $new_keys->getKeys());
+  }
+
+  /**
+   * @covers ::convertTokensToKeys
+   */
+  public function testInvalidContext() {
+    $container = $this->getMockContainer();
+    $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
+
+    $this->setExpectedException(\AssertionError::class);
+    $cache_contexts_manager->convertTokensToKeys(["non-cache-context"]);
+  }
+
+  /**
+   * @covers ::convertTokensToKeys
+   *
+   * @dataProvider providerTestInvalidCalculatedContext
+   */
+  public function testInvalidCalculatedContext($context_token) {
+    $container = $this->getMockContainer();
+    $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
+
+    $this->setExpectedException(\Exception::class);
+    $cache_contexts_manager->convertTokensToKeys([$context_token]);
+  }
+
+  /**
+   * Provides a list of invalid 'baz' cache contexts: the parameter is missing.
+   */
+  public function providerTestInvalidCalculatedContext() {
+    return [
+      ['baz'],
+      ['baz:'],
+    ];
+  }
+
+  public function testAvailableContextStrings() {
+    $cache_contexts_manager = new CacheContextsManager($this->getMockContainer(), $this->getContextsFixture());
+    $contexts = $cache_contexts_manager->getAll();
+    $this->assertEquals(["foo", "baz"], $contexts);
+  }
+
+  public function testAvailableContextLabels() {
+    $container = $this->getMockContainer();
+    $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
+    $labels = $cache_contexts_manager->getLabels();
+    $expected = ["foo" => "Foo"];
+    $this->assertEquals($expected, $labels);
+  }
+
+  protected function getContextsFixture() {
+    return ['foo', 'baz'];
+  }
+
+  protected function getMockContainer() {
+    $container = $this->getMockBuilder('Drupal\Core\DependencyInjection\Container')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $container->expects($this->any())
+      ->method('get')
+      ->will($this->returnValueMap([
+        ['cache_context.foo', Container::EXCEPTION_ON_INVALID_REFERENCE, new FooCacheContext()],
+        ['cache_context.baz', Container::EXCEPTION_ON_INVALID_REFERENCE, new BazCacheContext()],
+      ]));
+    return $container;
+  }
+
+  /**
+   * Provides a list of cache context token arrays.
+   *
+   * @return array
+   */
+  public function validateTokensProvider() {
+    return [
+      [[], FALSE],
+      [['foo'], FALSE],
+      [['foo', 'foo.bar'], FALSE],
+      [['foo', 'baz:llama'], FALSE],
+      // Invalid.
+      [[FALSE], 'Cache contexts must be strings, boolean given.'],
+      [[TRUE], 'Cache contexts must be strings, boolean given.'],
+      [['foo', FALSE], 'Cache contexts must be strings, boolean given.'],
+      [[NULL], 'Cache contexts must be strings, NULL given.'],
+      [['foo', NULL], 'Cache contexts must be strings, NULL given.'],
+      [[1337], 'Cache contexts must be strings, integer given.'],
+      [['foo', 1337], 'Cache contexts must be strings, integer given.'],
+      [[3.14], 'Cache contexts must be strings, double given.'],
+      [['foo', 3.14], 'Cache contexts must be strings, double given.'],
+      [[[]], 'Cache contexts must be strings, array given.'],
+      [['foo', []], 'Cache contexts must be strings, array given.'],
+      [['foo', ['bar']], 'Cache contexts must be strings, array given.'],
+      [[new \stdClass()], 'Cache contexts must be strings, object given.'],
+      [['foo', new \stdClass()], 'Cache contexts must be strings, object given.'],
+      // Non-existing.
+      [['foo.bar', 'qux'], '"qux" is not a valid cache context ID.'],
+      [['qux', 'baz'], '"qux" is not a valid cache context ID.'],
+    ];
+  }
+
+  /**
+   * @covers ::validateTokens
+   *
+   * @dataProvider validateTokensProvider
+   */
+  public function testValidateContexts(array $contexts, $expected_exception_message) {
+    $container = new ContainerBuilder();
+    $cache_contexts_manager = new CacheContextsManager($container, ['foo', 'foo.bar', 'baz']);
+    if ($expected_exception_message !== FALSE) {
+      $this->setExpectedException('LogicException', $expected_exception_message);
+    }
+    // If it doesn't throw an exception, validateTokens() returns NULL.
+    $this->assertNull($cache_contexts_manager->validateTokens($contexts));
+  }
+
+}
+
+/**
+ * Fake cache context class.
+ */
+class FooCacheContext implements CacheContextInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getLabel() {
+    return 'Foo';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getContext() {
+    return 'bar';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheableMetadata() {
+    return new CacheableMetadata();
+  }
+
+}
+
+/**
+ * Fake calculated cache context class.
+ */
+class BazCacheContext implements CalculatedCacheContextInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getLabel() {
+    return 'Baz';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getContext($parameter = NULL) {
+    if (!is_string($parameter) || strlen($parameter) === 0) {
+      throw new \Exception();
+    }
+    return str_rot13($parameter);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheableMetadata($parameter = NULL) {
+    return new CacheableMetadata();
+  }
+
+}
+
+/**
+ * Non-optimizable context class.
+ */
+class NoOptimizeCacheContext implements CacheContextInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getLabel() {
+    return 'Foo';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getContext() {
+    return 'bar';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheableMetadata() {
+    $cacheable_metadata = new CacheableMetadata();
+    return $cacheable_metadata->setCacheMaxAge(0);
+  }
+
+}