Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/CachePoolTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ abstract class CachePoolTest extends TestCase
*/
abstract public function createCachePool(): CacheItemPoolInterface;

/**
* A second pool backed by the same storage as createCachePool(), under a different namespace.
*
* Return null when the implementation cannot provide one, the test is then skipped.
*/
public function createCachePoolWithOtherNamespace(): ?CacheItemPoolInterface
{
return null;
}

#[Before]
public function setupService()
{
Expand Down Expand Up @@ -318,6 +328,34 @@ public function testClearWithDeferredItems()
$this->assertFalse($this->cache->getItem('key')->isHit(), 'Deferred items must be cleared on clear(). ');
}

public function testClearDoesNotAffectOtherNamespaces()
{
if (isset($this->skippedTests[__FUNCTION__])) {
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
}

$other = $this->createCachePoolWithOtherNamespace();

if (null === $other) {
$this->markTestSkipped('This implementation cannot provide a second pool on the same storage with another namespace.');
}

try {
$this->cache->save($this->cache->getItem('key')->set('value'));
$other->save($other->getItem('key')->set('other value'));

$this->assertTrue($this->cache->clear(), 'clear() must return true if cache was cleared. ');
$this->assertFalse($this->cache->hasItem('key'), 'The cleared pool should be empty after it is cleared.');

$otherItem = $other->getItem('key');
$this->assertTrue($otherItem->isHit(), 'Clearing a pool must not clear a pool using another namespace on the same storage.');
$this->assertSame('other value', $otherItem->get(), 'The item stored in the other namespace must keep its own value.');
} finally {
// The tearDownService() hook only clears $this->cache, so the other pool is cleared here.
$other->clear();
}
}

public function testDeleteItem()
{
if (isset($this->skippedTests[__FUNCTION__])) {
Expand Down
52 changes: 51 additions & 1 deletion src/TaggableCachePoolTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ abstract class TaggableCachePoolTest extends TestCase
*/
abstract public function createCachePool(): TaggableCacheItemPoolInterface;

/**
* A second pool backed by the same storage as createCachePool(), under a different namespace.
*
* Return null when the implementation cannot provide one, the test is then skipped.
*/
public function createCachePoolWithOtherNamespace(): ?TaggableCacheItemPoolInterface
{
return null;
}

#[Before]
public function setupService()
{
Expand Down Expand Up @@ -221,7 +231,7 @@ public function testRemoveTagWhenItemIsRemoved()

// Save the item and then delete it
$this->cache->save($item);
$this->cache->deleteItem('key');
$this->assertTrue($this->cache->deleteItem('key'), 'deleteItem() must return true when a tagged item is deleted');

// Create a new item (same key) (no tags)
$item = $this->cache->getItem('key')->set('value');
Expand Down Expand Up @@ -286,16 +296,28 @@ public function testInvalidateTags()
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
}

// Matched through both invalidated tags at once.
$item = $this->cache->getItem('key')->set('value');
$item->setTags(['tag1', 'tag2']);
$this->cache->save($item);
// Matched through tag2 only, so tag2 must be handled on its own.
$item = $this->cache->getItem('key2')->set('value');
$item->setTags(['tag2']);
$this->cache->save($item);
// Matched through tag1 only, so tag1 must be handled on its own.
$item = $this->cache->getItem('key3')->set('value');
$item->setTags(['tag1']);
$this->cache->save($item);
// Matched by none of the invalidated tags.
$item = $this->cache->getItem('key4')->set('value');
$item->setTags(['tag3']);
$this->cache->save($item);

$this->cache->invalidateTags(['tag1', 'tag2']);
$this->assertFalse($this->cache->hasItem('key'), 'Item should be cleared when tag is invalidated');
$this->assertFalse($this->cache->hasItem('key2'), 'Item should be cleared when tag is invalidated');
$this->assertFalse($this->cache->hasItem('key3'), 'Item should be cleared when tag is invalidated');
$this->assertTrue($this->cache->hasItem('key4'), 'An item tagged with none of the invalidated tags must survive');

// Create a new item (no tags)
$item = $this->cache->getItem('key')->set('value');
Expand All @@ -305,6 +327,34 @@ public function testInvalidateTags()
$this->assertTrue($this->cache->hasItem('key'), 'Item k list should be removed when clearing the tags');
}

public function testInvalidateTagsDoesNotAffectOtherNamespaces()
{
if (isset($this->skippedTests[__FUNCTION__])) {
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
}

$other = $this->createCachePoolWithOtherNamespace();

if (null === $other) {
$this->markTestSkipped('This implementation cannot provide a second pool on the same storage with another namespace.');
}

try {
$this->cache->save($this->cache->getItem('key')->set('value')->setTags(['tag1']));
$other->save($other->getItem('key')->set('other value')->setTags(['tag1']));

$this->assertTrue($this->cache->invalidateTags(['tag1']));
$this->assertFalse($this->cache->hasItem('key'), 'The invalidated item should be gone from its own pool.');

$otherItem = $other->getItem('key');
$this->assertTrue($otherItem->isHit(), 'Invalidating a tag must not reach items of a pool using another namespace on the same storage.');
$this->assertSame('other value', $otherItem->get(), 'The item stored in the other namespace must keep its own value.');
} finally {
// The tearDownService() hook only clears $this->cache, so the other pool is cleared here.
$other->clear();
}
}

public function testInvalidateTagsValidatesEveryTagBeforeMutation()
{
if (isset($this->skippedTests[__FUNCTION__])) {
Expand Down
9 changes: 9 additions & 0 deletions tests/CachePoolTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,22 @@ final class CachePoolTest extends CachePoolContract
{
private ?string $namespace = null;

private ?string $otherNamespace = null;

public function createCachePool(): CacheItemPoolInterface
{
$this->namespace ??= bin2hex(random_bytes(16));

return new ValidatingCachePool(new FilesystemAdapter($this->namespace));
}

public function createCachePoolWithOtherNamespace(): CacheItemPoolInterface
{
$this->otherNamespace ??= bin2hex(random_bytes(16));

return new ValidatingCachePool(new FilesystemAdapter($this->otherNamespace));
}

public function testDataProvidersExposeCases()
{
self::assertNotEmpty(self::invalidKeys());
Expand Down
12 changes: 9 additions & 3 deletions tests/Fixtures/TaggableCachePool.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,25 @@
use Cache\TagInterop\TaggableCacheItemInterface;
use Cache\TagInterop\TaggableCacheItemPoolInterface;
use Psr\Cache\CacheItemInterface;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ProxyAdapter;
use Symfony\Component\Cache\Exception\InvalidArgumentException;

final class TaggableCachePool implements TaggableCacheItemPoolInterface
{
private readonly ArrayAdapter $pool;
private readonly AdapterInterface $pool;

/** @var array<string, array<string, string>> */
private array $tagsByKey = [];

public function __construct()
/**
* Passing a storage and a namespace puts several pools on one key space, where the
* namespace is a real key prefix, as a shared table or collection would.
*/
public function __construct(?AdapterInterface $storage = null, string $namespace = '')
{
$this->pool = new ArrayAdapter();
$this->pool = null === $storage ? new ArrayAdapter() : new ProxyAdapter($storage, $namespace);
}

public function getItem(string $key): TaggableCacheItemInterface
Expand Down
15 changes: 14 additions & 1 deletion tests/TaggableCachePoolTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,25 @@
use Cache\IntegrationTests\Tests\Fixtures\TaggableCachePool;
use Cache\TagInterop\TaggableCacheItemPoolInterface;
use PHPUnit\Framework\SkippedTest;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

final class TaggableCachePoolTest extends TaggableCachePoolContract
{
private ?ArrayAdapter $storage = null;

public function createCachePool(): TaggableCacheItemPoolInterface
{
return new TaggableCachePool();
return new TaggableCachePool($this->sharedStorage(), 'first');
}

public function createCachePoolWithOtherNamespace(): TaggableCacheItemPoolInterface
{
return new TaggableCachePool($this->sharedStorage(), 'second');
}

private function sharedStorage(): ArrayAdapter
{
return $this->storage ??= new ArrayAdapter();
}

public function testDataProviderExposesCases()
Expand Down