View source
<?php
namespace Drupal\Tests\variationcache\Unit;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextsManager;
use Drupal\Core\Cache\Context\ContextCacheKeys;
use Drupal\Core\Cache\MemoryBackend;
use Drupal\Tests\UnitTestCase;
use Drupal\variationcache\Cache\CacheRedirect;
use Drupal\variationcache\Cache\VariationCache;
use Prophecy\Argument;
use Symfony\Component\HttpFoundation\RequestStack;
class VariationCacheTest extends UnitTestCase {
protected $requestStack;
protected $memoryBackend;
protected $cacheContextsManager;
protected $variationCache;
protected $cacheKeys = [
'your',
'housing',
'situation',
];
protected $cacheIdBase = 'your:housing:situation';
protected $housingType;
protected $housingTypeCacheability;
protected $gardenType;
protected $gardenTypeCacheability;
protected $houseOrientation;
protected $houseOrientationCacheability;
protected $solarType;
public function setUp() {
parent::setUp();
$this->requestStack = $this
->prophesize(RequestStack::class);
$this->memoryBackend = new MemoryBackend();
$this->cacheContextsManager = $this
->prophesize(CacheContextsManager::class);
$housing_type =& $this->housingType;
$garden_type =& $this->gardenType;
$house_orientation =& $this->houseOrientation;
$solar_type =& $this->solarType;
$this->cacheContextsManager
->convertTokensToKeys(Argument::any())
->will(function ($args) use (&$housing_type, &$garden_type, &$house_orientation, &$solar_type) {
$keys = [];
foreach ($args[0] as $context_id) {
switch ($context_id) {
case 'house.type':
$keys[] = "ht.{$housing_type}";
break;
case 'garden.type':
$keys[] = "gt.{$garden_type}";
break;
case 'house.orientation':
$keys[] = "ho.{$house_orientation}";
break;
case 'solar.type':
$keys[] = "st.{$solar_type}";
break;
default:
$keys[] = $context_id;
}
}
return new ContextCacheKeys($keys);
});
$this->variationCache = new VariationCache($this->requestStack
->reveal(), $this->memoryBackend, $this->cacheContextsManager
->reveal());
$this->housingTypeCacheability = (new CacheableMetadata())
->setCacheTags([
'foo',
])
->setCacheContexts([
'house.type',
]);
$this->gardenTypeCacheability = (new CacheableMetadata())
->setCacheTags([
'bar',
])
->setCacheContexts([
'house.type',
'garden.type',
]);
$this->houseOrientationCacheability = (new CacheableMetadata())
->setCacheTags([
'baz',
])
->setCacheContexts([
'house.type',
'garden.type',
'house.orientation',
]);
}
public function testNoVariations() {
$data = 'You have a nice house!';
$cacheability = (new CacheableMetadata())
->setCacheTags([
'bar',
'foo',
]);
$initial_cacheability = (new CacheableMetadata())
->setCacheTags([
'foo',
]);
$this
->setVariationCacheItem($data, $cacheability, $initial_cacheability);
$this
->assertVariationCacheItem($data, $cacheability, $initial_cacheability);
}
public function testSingleVariation() {
$cacheability = $this->housingTypeCacheability;
$house_data = [
'apartment' => 'You have a nice apartment',
'house' => 'You have a nice house',
];
foreach ($house_data as $housing_type => $data) {
$this->housingType = $housing_type;
$this
->assertVariationCacheMiss($cacheability);
$this
->setVariationCacheItem($data, $cacheability, $cacheability);
$this
->assertVariationCacheItem($data, $cacheability, $cacheability);
$this
->assertCacheBackendItem("{$this->cacheIdBase}:ht.{$housing_type}", $data, $cacheability);
}
}
public function testNestedVariations() {
$possible_outcomes = [
'apartment' => 'You have a nice apartment!',
'house|no-garden' => 'You have a nice house!',
'house|garden|east' => 'You have a nice house with an east-facing garden!',
'house|garden|south' => 'You have a nice house with a south-facing garden!',
'house|garden|west' => 'You have a nice house with a west-facing garden!',
'house|garden|north' => 'You have a nice house with a north-facing garden!',
];
foreach ($possible_outcomes as $cache_context_values => $data) {
list($this->housingType, $this->gardenType, $this->houseOrientation) = explode('|', $cache_context_values . '||');
$cacheability = $this->housingTypeCacheability;
if (!empty($this->houseOrientation)) {
$cacheability = $this->houseOrientationCacheability;
}
elseif (!empty($this->gardenType)) {
$cacheability = $this->gardenTypeCacheability;
}
$this
->assertVariationCacheMiss($this->housingTypeCacheability);
$this
->setVariationCacheItem($data, $cacheability, $this->housingTypeCacheability);
$this
->assertVariationCacheItem($data, $cacheability, $this->housingTypeCacheability);
$cache_id = "{$this->cacheIdBase}:ht.{$this->housingType}";
if (!empty($this->gardenType)) {
$this
->assertCacheBackendItem($cache_id, new CacheRedirect($this->gardenTypeCacheability));
$cache_id .= ":gt.{$this->gardenType}";
}
if (!empty($this->houseOrientation)) {
$this
->assertCacheBackendItem($cache_id, new CacheRedirect($this->houseOrientationCacheability));
$cache_id .= ":ho.{$this->houseOrientation}";
}
$this
->assertCacheBackendItem($cache_id, $data, $cacheability);
}
}
public function testNestedVariationsSelfHealing() {
$cache_id = "{$this->cacheIdBase}:ht.house";
$possible_outcomes = [
'house|garden|east' => 'You have a nice house with an east-facing garden!',
'house|garden|south' => 'You have a nice house with a south-facing garden!',
'house|garden|west' => 'You have a nice house with a west-facing garden!',
'house|garden|north' => 'You have a nice house with a north-facing garden!',
];
foreach ($possible_outcomes as $cache_context_values => $data) {
list($this->housingType, $this->gardenType, $this->houseOrientation) = explode('|', $cache_context_values . '||');
$this
->setVariationCacheItem($data, $this->houseOrientationCacheability, $this->housingTypeCacheability);
}
$this
->assertCacheBackendItem($cache_id, new CacheRedirect($this->houseOrientationCacheability));
list($this->housingType, $this->gardenType, $this->houseOrientation) = [
'house',
'no-garden',
NULL,
];
$this
->setVariationCacheItem('You have a nice house', $this->gardenTypeCacheability, $this->housingTypeCacheability);
$this
->assertCacheBackendItem($cache_id, new CacheRedirect($this->gardenTypeCacheability));
foreach ($possible_outcomes as $cache_context_values => $data) {
list($this->housingType, $this->gardenType, $this->houseOrientation) = explode('|', $cache_context_values . '||');
$this
->assertVariationCacheMiss($this->housingTypeCacheability);
}
$this
->setVariationCacheItem($data, $this->houseOrientationCacheability, $this->housingTypeCacheability);
foreach ($possible_outcomes as $cache_context_values => $data) {
list($this->housingType, $this->gardenType, $this->houseOrientation) = explode('|', $cache_context_values . '||');
$this
->assertVariationCacheItem($data, $this->houseOrientationCacheability, $this->housingTypeCacheability);
}
$this
->assertCacheBackendItem("{$cache_id}:gt.garden", new CacheRedirect($this->houseOrientationCacheability));
}
public function testSplitVariationsSelfHealing() {
$cache_id = "{$this->cacheIdBase}:ht.house";
$this->housingType = 'house';
$this->gardenType = 'garden';
$this->solarType = 'solar';
$initial_cacheability = (new CacheableMetadata())
->setCacheTags([
'foo',
])
->setCacheContexts([
'house.type',
]);
$south_cacheability = (new CacheableMetadata())
->setCacheTags([
'foo',
])
->setCacheContexts([
'house.type',
'house.orientation',
'garden.type',
]);
$north_cacheability = (new CacheableMetadata())
->setCacheTags([
'foo',
])
->setCacheContexts([
'house.type',
'house.orientation',
'solar.type',
]);
$common_cacheability = (new CacheableMetadata())
->setCacheContexts([
'house.type',
'house.orientation',
]);
$this->houseOrientation = 'south';
$this
->setVariationCacheItem('You have a south-facing house with a garden!', $south_cacheability, $initial_cacheability);
$this
->assertCacheBackendItem($cache_id, new CacheRedirect($south_cacheability));
$this->houseOrientation = 'north';
$this
->setVariationCacheItem('You have a north-facing house with solar panels!', $north_cacheability, $initial_cacheability);
$this
->assertCacheBackendItem($cache_id, new CacheRedirect($common_cacheability));
$this
->assertCacheBackendItem("{$cache_id}:ho.north", new CacheRedirect($north_cacheability));
$this->houseOrientation = 'south';
$this
->assertVariationCacheMiss($initial_cacheability);
$this
->setVariationCacheItem('You have a south-facing house with a garden!', $south_cacheability, $initial_cacheability);
$this
->assertCacheBackendItem($cache_id, new CacheRedirect($common_cacheability));
$this
->assertCacheBackendItem("{$cache_id}:ho.south", new CacheRedirect($south_cacheability));
$this->houseOrientation = 'north';
$this
->assertCacheBackendItem($cache_id, new CacheRedirect($common_cacheability));
$this
->assertCacheBackendItem("{$cache_id}:ho.north", new CacheRedirect($north_cacheability));
}
public function testIncompatibleVariationsException() {
$this
->setExpectedException(\LogicException::class, "The complete set of cache contexts for a variation cache item must contain all of the initial cache contexts.");
$this->housingType = 'house';
$house_cacheability = (new CacheableMetadata())
->setCacheContexts([
'house.type',
]);
$this->gardenType = 'garden';
$garden_cacheability = (new CacheableMetadata())
->setCacheContexts([
'garden.type',
]);
$this
->setVariationCacheItem('You have a nice garden!', $garden_cacheability, $garden_cacheability);
$this
->setVariationCacheItem('You have a nice house!', $house_cacheability, $garden_cacheability);
}
protected function setVariationCacheItem($data, CacheableMetadata $cacheability, CacheableMetadata $initial_cacheability) {
$this->variationCache
->set($this->cacheKeys, $data, $cacheability, $initial_cacheability);
}
protected function assertVariationCacheItem($data, CacheableMetadata $cacheability, CacheableMetadata $initial_cacheability) {
$cache_item = $this->variationCache
->get($this->cacheKeys, $initial_cacheability);
$this
->assertNotFalse($cache_item, 'Variable data was stored and retrieved successfully.');
$this
->assertEquals($data, $cache_item->data, 'Variable cache item contains the right data.');
$this
->assertSame($cacheability
->getCacheTags(), $cache_item->tags, 'Variable cache item uses the right cache tags.');
}
protected function assertVariationCacheMiss(CacheableMetadata $initial_cacheability) {
$this
->assertFalse($this->variationCache
->get($this->cacheKeys, $initial_cacheability), 'Nothing could be retrieved for the active cache contexts.');
}
protected function assertCacheBackendItem($cid, $data, CacheableMetadata $cacheability = NULL) {
$cache_backend_item = $this->memoryBackend
->get($cid);
$this
->assertNotFalse($cache_backend_item, 'The data was stored and retrieved successfully.');
$this
->assertEquals($data, $cache_backend_item->data, 'Cache item contains the right data.');
if ($data instanceof CacheRedirect) {
$this
->assertSame([], $cache_backend_item->tags, 'A cache redirect does not use cache tags.');
$this
->assertSame(-1, $cache_backend_item->expire, 'A cache redirect is stored indefinitely.');
}
else {
$this
->assertSame($cacheability
->getCacheTags(), $cache_backend_item->tags, 'Cache item uses the right cache tags.');
}
}
}