CacheContextsManagerTest.php

Same filename in other branches
  1. 9 core/tests/Drupal/Tests/Core/Cache/Context/CacheContextsManagerTest.php
  2. 8.9.x core/tests/Drupal/Tests/Core/Cache/Context/CacheContextsManagerTest.php
  3. 11.x core/tests/Drupal/Tests/Core/Cache/Context/CacheContextsManagerTest.php

Namespace

Drupal\Tests\Core\Cache\Context

File

core/tests/Drupal/Tests/Core/Cache/Context/CacheContextsManagerTest.php

View source
<?php

declare (strict_types=1);
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;
// cspell:ignore cnenzrgre

/**
 * @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) : void {
        $container = $this->getMockBuilder('Drupal\\Core\\DependencyInjection\\Container')
            ->disableOriginalConstructor()
            ->getMock();
        $container->expects($this->any())
            ->method('get')
            ->willReturnMap([
            [
                '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 static 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() : void {
        $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() : void {
        $container = $this->getMockContainer();
        $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
        $this->expectException(\AssertionError::class);
        $cache_contexts_manager->convertTokensToKeys([
            "non-cache-context",
        ]);
    }
    
    /**
     * @covers ::convertTokensToKeys
     *
     * @dataProvider providerTestInvalidCalculatedContext
     */
    public function testInvalidCalculatedContext($context_token) : void {
        $container = $this->getMockContainer();
        $cache_contexts_manager = new CacheContextsManager($container, $this->getContextsFixture());
        $this->expectException(\Exception::class);
        $cache_contexts_manager->convertTokensToKeys([
            $context_token,
        ]);
    }
    
    /**
     * Provides a list of invalid 'baz' cache contexts: the parameter is missing.
     */
    public static function providerTestInvalidCalculatedContext() {
        return [
            [
                'baz',
            ],
            [
                'baz:',
            ],
        ];
    }
    public function testAvailableContextStrings() : void {
        $cache_contexts_manager = new CacheContextsManager($this->getMockContainer(), $this->getContextsFixture());
        $contexts = $cache_contexts_manager->getAll();
        $this->assertEquals([
            "foo",
            "baz",
        ], $contexts);
    }
    public function testAvailableContextLabels() : void {
        $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')
            ->willReturnMap([
            [
                '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 static 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) : void {
        $container = new ContainerBuilder();
        $cache_contexts_manager = new CacheContextsManager($container, [
            'foo',
            'foo.bar',
            'baz',
        ]);
        if ($expected_exception_message !== FALSE) {
            $this->expectException('LogicException');
            $this->expectExceptionMessage($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);
    }

}

Classes

Title Deprecated Summary
BazCacheContext Fake calculated cache context class.
CacheContextsManagerTest @coversDefaultClass \Drupal\Core\Cache\Context\CacheContextsManager @group Cache
FooCacheContext Fake cache context class.
NoOptimizeCacheContext Non-optimizable context class.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.