2012-06-30 03:08:06 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test for ProcessCacheLRU class.
|
|
|
|
|
*
|
|
|
|
|
* Note that it uses the ProcessCacheLRUTestable class which extends some
|
|
|
|
|
* properties and methods visibility. That class is defined at the end of the
|
|
|
|
|
* file containing this class.
|
|
|
|
|
*
|
|
|
|
|
* @group Cache
|
|
|
|
|
*/
|
2014-12-30 04:53:24 +00:00
|
|
|
class ProcessCacheLRUTest extends PHPUnit_Framework_TestCase {
|
2012-06-30 03:08:06 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper to verify emptiness of a cache object.
|
|
|
|
|
* Compare against an array so we get the cache content difference.
|
|
|
|
|
*/
|
2015-01-29 20:06:25 +00:00
|
|
|
protected function assertCacheEmpty( $cache, $msg = 'Cache should be empty' ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->assertAttributeEquals( [], 'cache', $cache, $msg );
|
2012-06-30 03:08:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper to fill a cache object passed by reference
|
|
|
|
|
*/
|
2015-01-29 20:06:25 +00:00
|
|
|
protected function fillCache( &$cache, $numEntries ) {
|
2012-06-30 03:08:06 +00:00
|
|
|
// Fill cache with three values
|
2013-02-14 13:10:38 +00:00
|
|
|
for ( $i = 1; $i <= $numEntries; $i++ ) {
|
2012-06-30 03:08:06 +00:00
|
|
|
$cache->set( "cache-key-$i", "prop-$i", "value-$i" );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generates an array of what would be expected in cache for a given cache
|
|
|
|
|
* size and a number of entries filled in sequentially
|
|
|
|
|
*/
|
2015-01-29 20:06:25 +00:00
|
|
|
protected function getExpectedCache( $cacheMaxEntries, $entryToFill ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$expected = [];
|
2012-06-30 03:08:06 +00:00
|
|
|
|
2013-02-14 13:10:38 +00:00
|
|
|
if ( $entryToFill === 0 ) {
|
2015-01-29 20:06:25 +00:00
|
|
|
// The cache is empty!
|
2016-02-17 09:09:32 +00:00
|
|
|
return [];
|
2013-02-14 13:10:38 +00:00
|
|
|
} elseif ( $entryToFill <= $cacheMaxEntries ) {
|
2015-01-29 20:06:25 +00:00
|
|
|
// Cache is not fully filled
|
2012-06-30 03:08:06 +00:00
|
|
|
$firstKey = 1;
|
|
|
|
|
} else {
|
2015-01-29 20:06:25 +00:00
|
|
|
// Cache overflowed
|
2012-06-30 03:08:06 +00:00
|
|
|
$firstKey = 1 + $entryToFill - $cacheMaxEntries;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-14 13:10:38 +00:00
|
|
|
$lastKey = $entryToFill;
|
2012-06-30 03:08:06 +00:00
|
|
|
|
2013-02-14 13:10:38 +00:00
|
|
|
for ( $i = $firstKey; $i <= $lastKey; $i++ ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$expected["cache-key-$i"] = [ "prop-$i" => "value-$i" ];
|
2012-06-30 03:08:06 +00:00
|
|
|
}
|
2013-04-26 12:00:22 +00:00
|
|
|
|
2012-06-30 03:08:06 +00:00
|
|
|
return $expected;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Highlight diff between assertEquals and assertNotSame
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testPhpUnitArrayEquality() {
|
2016-02-17 09:09:32 +00:00
|
|
|
$one = [ 'A' => 1, 'B' => 2 ];
|
|
|
|
|
$two = [ 'B' => 2, 'A' => 1 ];
|
2015-01-29 20:06:25 +00:00
|
|
|
// ==
|
|
|
|
|
$this->assertEquals( $one, $two );
|
|
|
|
|
// ===
|
|
|
|
|
$this->assertNotSame( $one, $two );
|
2012-06-30 03:08:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideInvalidConstructorArg
|
2015-04-22 16:46:29 +00:00
|
|
|
* @expectedException Wikimedia\Assert\ParameterAssertionException
|
2015-01-29 20:06:25 +00:00
|
|
|
* @covers ProcessCacheLRU::__construct
|
2012-06-30 03:08:06 +00:00
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testConstructorGivenInvalidValue( $maxSize ) {
|
2013-04-26 07:48:46 +00:00
|
|
|
new ProcessCacheLRUTestable( $maxSize );
|
2012-06-30 03:08:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Value which are forbidden by the constructor
|
|
|
|
|
*/
|
2012-10-08 10:56:20 +00:00
|
|
|
public static function provideInvalidConstructorArg() {
|
2016-02-17 09:09:32 +00:00
|
|
|
return [
|
|
|
|
|
[ null ],
|
|
|
|
|
[ [] ],
|
|
|
|
|
[ new stdClass() ],
|
|
|
|
|
[ 0 ],
|
|
|
|
|
[ '5' ],
|
|
|
|
|
[ -1 ],
|
|
|
|
|
];
|
2012-06-30 03:08:06 +00:00
|
|
|
}
|
|
|
|
|
|
2015-01-29 20:06:25 +00:00
|
|
|
/**
|
|
|
|
|
* @covers ProcessCacheLRU::get
|
|
|
|
|
* @covers ProcessCacheLRU::set
|
2016-02-23 03:18:31 +00:00
|
|
|
* @covers ProcessCacheLRU::has
|
2015-01-29 20:06:25 +00:00
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testAddAndGetAKey() {
|
2012-06-30 03:08:06 +00:00
|
|
|
$oneCache = new ProcessCacheLRUTestable( 1 );
|
|
|
|
|
$this->assertCacheEmpty( $oneCache );
|
|
|
|
|
|
|
|
|
|
// First set just one value
|
|
|
|
|
$oneCache->set( 'cache-key', 'prop1', 'value1' );
|
|
|
|
|
$this->assertEquals( 1, $oneCache->getEntriesCount() );
|
|
|
|
|
$this->assertTrue( $oneCache->has( 'cache-key', 'prop1' ) );
|
|
|
|
|
$this->assertEquals( 'value1', $oneCache->get( 'cache-key', 'prop1' ) );
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 20:06:25 +00:00
|
|
|
/**
|
|
|
|
|
* @covers ProcessCacheLRU::set
|
|
|
|
|
* @covers ProcessCacheLRU::get
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testDeleteOldKey() {
|
2012-06-30 03:08:06 +00:00
|
|
|
$oneCache = new ProcessCacheLRUTestable( 1 );
|
|
|
|
|
$this->assertCacheEmpty( $oneCache );
|
|
|
|
|
|
|
|
|
|
$oneCache->set( 'cache-key', 'prop1', 'value1' );
|
|
|
|
|
$oneCache->set( 'cache-key', 'prop1', 'value2' );
|
|
|
|
|
$this->assertEquals( 'value2', $oneCache->get( 'cache-key', 'prop1' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This test that we properly overflow when filling a cache with
|
|
|
|
|
* a sequence of always different cache-keys. Meant to verify we correclty
|
|
|
|
|
* delete the older key.
|
|
|
|
|
*
|
2015-01-29 20:06:25 +00:00
|
|
|
* @covers ProcessCacheLRU::set
|
2012-06-30 03:08:06 +00:00
|
|
|
* @dataProvider provideCacheFilling
|
2014-04-17 18:43:42 +00:00
|
|
|
* @param int $cacheMaxEntries Maximum entry the created cache will hold
|
|
|
|
|
* @param int $entryToFill Number of entries to insert in the created cache.
|
2012-06-30 03:08:06 +00:00
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testFillingCache( $cacheMaxEntries, $entryToFill, $msg = '' ) {
|
2012-06-30 03:08:06 +00:00
|
|
|
$cache = new ProcessCacheLRUTestable( $cacheMaxEntries );
|
2013-02-14 13:10:38 +00:00
|
|
|
$this->fillCache( $cache, $entryToFill );
|
2012-06-30 03:08:06 +00:00
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
$this->getExpectedCache( $cacheMaxEntries, $entryToFill ),
|
|
|
|
|
$cache->getCache(),
|
|
|
|
|
"Filling a $cacheMaxEntries entries cache with $entryToFill entries"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Provider for testFillingCache
|
|
|
|
|
*/
|
2012-10-08 10:56:20 +00:00
|
|
|
public static function provideCacheFilling() {
|
2012-06-30 03:08:06 +00:00
|
|
|
// ($cacheMaxEntries, $entryToFill, $msg='')
|
2016-02-17 09:09:32 +00:00
|
|
|
return [
|
|
|
|
|
[ 1, 0 ],
|
|
|
|
|
[ 1, 1 ],
|
2015-01-29 20:06:25 +00:00
|
|
|
// overflow
|
2016-02-17 09:09:32 +00:00
|
|
|
[ 1, 2 ],
|
2015-01-29 20:06:25 +00:00
|
|
|
// overflow
|
2016-02-17 09:09:32 +00:00
|
|
|
[ 5, 33 ],
|
|
|
|
|
];
|
2012-06-30 03:08:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a cache with only one remaining entry then update
|
|
|
|
|
* the first inserted entry. Should bump it to the top.
|
2015-01-29 20:06:25 +00:00
|
|
|
*
|
|
|
|
|
* @covers ProcessCacheLRU::set
|
2012-06-30 03:08:06 +00:00
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testReplaceExistingKeyShouldBumpEntryToTop() {
|
2012-06-30 03:08:06 +00:00
|
|
|
$maxEntries = 3;
|
|
|
|
|
|
|
|
|
|
$cache = new ProcessCacheLRUTestable( $maxEntries );
|
|
|
|
|
// Fill cache leaving just one remaining slot
|
|
|
|
|
$this->fillCache( $cache, $maxEntries - 1 );
|
|
|
|
|
|
|
|
|
|
// Set an existing cache key
|
|
|
|
|
$cache->set( "cache-key-1", "prop-1", "new-value-for-1" );
|
|
|
|
|
|
|
|
|
|
$this->assertSame(
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
|
|
|
|
'cache-key-2' => [ 'prop-2' => 'value-2' ],
|
|
|
|
|
'cache-key-1' => [ 'prop-1' => 'new-value-for-1' ],
|
|
|
|
|
],
|
2012-06-30 03:08:06 +00:00
|
|
|
$cache->getCache()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 20:06:25 +00:00
|
|
|
/**
|
|
|
|
|
* @covers ProcessCacheLRU::get
|
|
|
|
|
* @covers ProcessCacheLRU::set
|
2016-02-23 03:18:31 +00:00
|
|
|
* @covers ProcessCacheLRU::has
|
2015-01-29 20:06:25 +00:00
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testRecentlyAccessedKeyStickIn() {
|
2012-06-30 03:08:06 +00:00
|
|
|
$cache = new ProcessCacheLRUTestable( 2 );
|
2013-02-14 13:10:38 +00:00
|
|
|
$cache->set( 'first', 'prop1', 'value1' );
|
2012-06-30 03:08:06 +00:00
|
|
|
$cache->set( 'second', 'prop2', 'value2' );
|
|
|
|
|
|
|
|
|
|
// Get first
|
|
|
|
|
$cache->get( 'first', 'prop1' );
|
|
|
|
|
// Cache a third value, should invalidate the least used one
|
|
|
|
|
$cache->set( 'third', 'prop3', 'value3' );
|
|
|
|
|
|
|
|
|
|
$this->assertFalse( $cache->has( 'second', 'prop2' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This first create a full cache then update the value for the 2nd
|
|
|
|
|
* filled entry.
|
|
|
|
|
* Given a cache having 1,2,3 as key, updating 2 should bump 2 to
|
|
|
|
|
* the top of the queue with the new value: 1,3,2* (* = updated).
|
2015-01-29 20:06:25 +00:00
|
|
|
*
|
|
|
|
|
* @covers ProcessCacheLRU::set
|
|
|
|
|
* @covers ProcessCacheLRU::get
|
2012-06-30 03:08:06 +00:00
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testReplaceExistingKeyInAFullCacheShouldBumpToTop() {
|
2012-06-30 03:08:06 +00:00
|
|
|
$maxEntries = 3;
|
|
|
|
|
|
|
|
|
|
$cache = new ProcessCacheLRUTestable( $maxEntries );
|
|
|
|
|
$this->fillCache( $cache, $maxEntries );
|
|
|
|
|
|
|
|
|
|
// Set an existing cache key
|
|
|
|
|
$cache->set( "cache-key-2", "prop-2", "new-value-for-2" );
|
|
|
|
|
$this->assertSame(
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
|
|
|
|
'cache-key-1' => [ 'prop-1' => 'value-1' ],
|
|
|
|
|
'cache-key-3' => [ 'prop-3' => 'value-3' ],
|
|
|
|
|
'cache-key-2' => [ 'prop-2' => 'new-value-for-2' ],
|
|
|
|
|
],
|
2012-06-30 03:08:06 +00:00
|
|
|
$cache->getCache()
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals( 'new-value-for-2',
|
|
|
|
|
$cache->get( 'cache-key-2', 'prop-2' )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 20:06:25 +00:00
|
|
|
/**
|
|
|
|
|
* @covers ProcessCacheLRU::set
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testBumpExistingKeyToTop() {
|
2012-06-30 03:08:06 +00:00
|
|
|
$cache = new ProcessCacheLRUTestable( 3 );
|
|
|
|
|
$this->fillCache( $cache, 3 );
|
|
|
|
|
|
|
|
|
|
// Set the very first cache key to a new value
|
|
|
|
|
$cache->set( "cache-key-1", "prop-1", "new value for 1" );
|
|
|
|
|
$this->assertEquals(
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
|
|
|
|
'cache-key-2' => [ 'prop-2' => 'value-2' ],
|
|
|
|
|
'cache-key-3' => [ 'prop-3' => 'value-3' ],
|
|
|
|
|
'cache-key-1' => [ 'prop-1' => 'new value for 1' ],
|
|
|
|
|
],
|
2012-06-30 03:08:06 +00:00
|
|
|
$cache->getCache()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Overrides some ProcessCacheLRU methods and properties accessibility.
|
|
|
|
|
*/
|
|
|
|
|
class ProcessCacheLRUTestable extends ProcessCacheLRU {
|
2016-02-17 09:09:32 +00:00
|
|
|
public $cache = [];
|
2012-06-30 03:08:06 +00:00
|
|
|
|
|
|
|
|
public function getCache() {
|
|
|
|
|
return $this->cache;
|
|
|
|
|
}
|
2013-02-14 13:10:38 +00:00
|
|
|
|
2012-06-30 03:08:06 +00:00
|
|
|
public function getEntriesCount() {
|
|
|
|
|
return count( $this->cache );
|
|
|
|
|
}
|
|
|
|
|
}
|