5 * Contains \Drupal\Tests\Core\Cache\Context\CacheContextsManagerTest.
8 namespace Drupal\Tests\Core\Cache\Context;
10 use Drupal\Core\Cache\CacheableMetadata;
11 use Drupal\Core\Cache\Context\CacheContextsManager;
12 use Drupal\Core\Cache\Context\CacheContextInterface;
13 use Drupal\Core\Cache\Context\CalculatedCacheContextInterface;
14 use Drupal\Core\DependencyInjection\ContainerBuilder;
15 use Drupal\Tests\UnitTestCase;
16 use Symfony\Component\DependencyInjection\Container;
19 * @coversDefaultClass \Drupal\Core\Cache\Context\CacheContextsManager
22 class CacheContextsManagerTest extends UnitTestCase {
25 * @covers ::optimizeTokens
27 * @dataProvider providerTestOptimizeTokens
29 public function testOptimizeTokens(array $context_tokens, array $optimized_context_tokens) {
30 $container = $this->getMockBuilder('Drupal\Core\DependencyInjection\Container')
31 ->disableOriginalConstructor()
33 $container->expects($this->any())
35 ->will($this->returnValueMap([
36 ['cache_context.a', Container::EXCEPTION_ON_INVALID_REFERENCE, new FooCacheContext()],
37 ['cache_context.a.b', Container::EXCEPTION_ON_INVALID_REFERENCE, new FooCacheContext()],
38 ['cache_context.a.b.c', Container::EXCEPTION_ON_INVALID_REFERENCE, new BazCacheContext()],
39 ['cache_context.x', Container::EXCEPTION_ON_INVALID_REFERENCE, new BazCacheContext()],
40 ['cache_context.a.b.no-optimize', Container::EXCEPTION_ON_INVALID_REFERENCE, new NoOptimizeCacheContext()],
42 $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
44 $this->assertSame($optimized_context_tokens, $cache_contexts_manager->optimizeTokens($context_tokens));
48 * Provides a list of context token sets.
50 public function providerTestOptimizeTokens() {
52 [['a', 'x'], ['a', 'x']],
53 [['a.b', 'x'], ['a.b', 'x']],
55 // Direct ancestor, single-level hierarchy.
56 [['a', 'a.b'], ['a']],
57 [['a.b', 'a'], ['a']],
59 // Direct ancestor, multi-level hierarchy.
60 [['a.b', 'a.b.c'], ['a.b']],
61 [['a.b.c', 'a.b'], ['a.b']],
64 [['a', 'a.b.c'], ['a']],
65 [['a.b.c', 'a'], ['a']],
67 // Direct & indirect ancestors.
68 [['a', 'a.b', 'a.b.c'], ['a']],
69 [['a', 'a.b.c', 'a.b'], ['a']],
70 [['a.b', 'a', 'a.b.c'], ['a']],
71 [['a.b', 'a.b.c', 'a'], ['a']],
72 [['a.b.c', 'a.b', 'a'], ['a']],
73 [['a.b.c', 'a', 'a.b'], ['a']],
76 [['a', 'a.b.c:foo'], ['a']],
77 [['a.b.c:foo', 'a'], ['a']],
78 [['a.b.c:foo', 'a.b.c'], ['a.b.c']],
80 // max-age 0 is treated as non-optimizable.
81 [['a.b.no-optimize', 'a.b', 'a'], ['a.b.no-optimize', 'a']],
86 * @covers ::convertTokensToKeys
88 public function testConvertTokensToKeys() {
89 $container = $this->getMockContainer();
90 $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
92 $new_keys = $cache_contexts_manager->convertTokensToKeys([
99 '[baz:parameterA]=cnenzrgreN',
100 '[baz:parameterB]=cnenzrgreO',
103 $this->assertEquals($expected, $new_keys->getKeys());
107 * @covers ::convertTokensToKeys
109 public function testInvalidContext() {
110 $container = $this->getMockContainer();
111 $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
113 $this->setExpectedException(\AssertionError::class);
114 $cache_contexts_manager->convertTokensToKeys(["non-cache-context"]);
118 * @covers ::convertTokensToKeys
120 * @dataProvider providerTestInvalidCalculatedContext
122 public function testInvalidCalculatedContext($context_token) {
123 $container = $this->getMockContainer();
124 $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
126 $this->setExpectedException(\Exception::class);
127 $cache_contexts_manager->convertTokensToKeys([$context_token]);
131 * Provides a list of invalid 'baz' cache contexts: the parameter is missing.
133 public function providerTestInvalidCalculatedContext() {
140 public function testAvailableContextStrings() {
141 $cache_contexts_manager = new CacheContextsManager($this->getMockContainer(), $this->getContextsFixture());
142 $contexts = $cache_contexts_manager->getAll();
143 $this->assertEquals(["foo", "baz"], $contexts);
146 public function testAvailableContextLabels() {
147 $container = $this->getMockContainer();
148 $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
149 $labels = $cache_contexts_manager->getLabels();
150 $expected = ["foo" => "Foo"];
151 $this->assertEquals($expected, $labels);
154 protected function getContextsFixture() {
155 return ['foo', 'baz'];
158 protected function getMockContainer() {
159 $container = $this->getMockBuilder('Drupal\Core\DependencyInjection\Container')
160 ->disableOriginalConstructor()
162 $container->expects($this->any())
164 ->will($this->returnValueMap([
165 ['cache_context.foo', Container::EXCEPTION_ON_INVALID_REFERENCE, new FooCacheContext()],
166 ['cache_context.baz', Container::EXCEPTION_ON_INVALID_REFERENCE, new BazCacheContext()],
172 * Provides a list of cache context token arrays.
176 public function validateTokensProvider() {
180 [['foo', 'foo.bar'], FALSE],
181 [['foo', 'baz:llama'], FALSE],
183 [[FALSE], 'Cache contexts must be strings, boolean given.'],
184 [[TRUE], 'Cache contexts must be strings, boolean given.'],
185 [['foo', FALSE], 'Cache contexts must be strings, boolean given.'],
186 [[NULL], 'Cache contexts must be strings, NULL given.'],
187 [['foo', NULL], 'Cache contexts must be strings, NULL given.'],
188 [[1337], 'Cache contexts must be strings, integer given.'],
189 [['foo', 1337], 'Cache contexts must be strings, integer given.'],
190 [[3.14], 'Cache contexts must be strings, double given.'],
191 [['foo', 3.14], 'Cache contexts must be strings, double given.'],
192 [[[]], 'Cache contexts must be strings, array given.'],
193 [['foo', []], 'Cache contexts must be strings, array given.'],
194 [['foo', ['bar']], 'Cache contexts must be strings, array given.'],
195 [[new \stdClass()], 'Cache contexts must be strings, object given.'],
196 [['foo', new \stdClass()], 'Cache contexts must be strings, object given.'],
198 [['foo.bar', 'qux'], '"qux" is not a valid cache context ID.'],
199 [['qux', 'baz'], '"qux" is not a valid cache context ID.'],
204 * @covers ::validateTokens
206 * @dataProvider validateTokensProvider
208 public function testValidateContexts(array $contexts, $expected_exception_message) {
209 $container = new ContainerBuilder();
210 $cache_contexts_manager = new CacheContextsManager($container, ['foo', 'foo.bar', 'baz']);
211 if ($expected_exception_message !== FALSE) {
212 $this->setExpectedException('LogicException', $expected_exception_message);
214 // If it doesn't throw an exception, validateTokens() returns NULL.
215 $this->assertNull($cache_contexts_manager->validateTokens($contexts));
221 * Fake cache context class.
223 class FooCacheContext implements CacheContextInterface {
228 public static function getLabel() {
235 public function getContext() {
242 public function getCacheableMetadata() {
243 return new CacheableMetadata();
249 * Fake calculated cache context class.
251 class BazCacheContext implements CalculatedCacheContextInterface {
256 public static function getLabel() {
263 public function getContext($parameter = NULL) {
264 if (!is_string($parameter) || strlen($parameter) === 0) {
265 throw new \Exception();
267 return str_rot13($parameter);
273 public function getCacheableMetadata($parameter = NULL) {
274 return new CacheableMetadata();
280 * Non-optimizable context class.
282 class NoOptimizeCacheContext implements CacheContextInterface {
287 public static function getLabel() {
294 public function getContext() {
301 public function getCacheableMetadata() {
302 $cacheable_metadata = new CacheableMetadata();
303 return $cacheable_metadata->setCacheMaxAge(0);