Moved ProcessCacheLRU to /libs
authorAaron Schulz <aschulz@wikimedia.org>
Sun, 16 Mar 2014 19:28:18 +0000 (12:28 -0700)
committerAaron Schulz <aschulz@wikimedia.org>
Sun, 16 Mar 2014 23:32:34 +0000 (16:32 -0700)
Change-Id: I7052d04d9847f0310c1e62bd66365c813fddeab5

includes/AutoLoader.php
includes/cache/ProcessCacheLRU.php [deleted file]
includes/libs/ProcessCacheLRU.php [new file with mode: 0644]
tests/phpunit/includes/cache/ProcessCacheLRUTest.php [deleted file]
tests/phpunit/includes/libs/ProcessCacheLRUTest.php [new file with mode: 0644]

index b290e8d..c627da1 100644 (file)
@@ -386,7 +386,6 @@ $wgAutoloadLocalClasses = array(
        'MapCacheLRU' => 'includes/cache/MapCacheLRU.php',
        'MessageCache' => 'includes/cache/MessageCache.php',
        'ObjectFileCache' => 'includes/cache/ObjectFileCache.php',
-       'ProcessCacheLRU' => 'includes/cache/ProcessCacheLRU.php',
        'ResourceFileCache' => 'includes/cache/ResourceFileCache.php',
 
        # includes/changes
@@ -709,6 +708,7 @@ $wgAutoloadLocalClasses = array(
        'JSTokenizer' => 'includes/libs/jsminplus.php',
        'MultiHttpClient' => 'includes/libs/MultiHttpClient.php',
        'MWMessagePack' => 'includes/libs/MWMessagePack.php',
+       'ProcessCacheLRU' => 'includes/libs/ProcessCacheLRU.php',
        'RunningStat' => 'includes/libs/RunningStat.php',
        'ScopedCallback' => 'includes/libs/ScopedCallback.php',
        'ScopedPHPTimeout' => 'includes/libs/ScopedPHPTimeout.php',
diff --git a/includes/cache/ProcessCacheLRU.php b/includes/cache/ProcessCacheLRU.php
deleted file mode 100644 (file)
index 786d74a..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-/**
- * Per-process memory cache for storing items.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Cache
- */
-
-/**
- * Handles per process caching of items
- * @ingroup Cache
- */
-class ProcessCacheLRU {
-       /** @var Array */
-       protected $cache = array(); // (key => prop => value)
-       /** @var Array */
-       protected $cacheTimes = array(); // (key => prop => UNIX timestamp)
-
-       protected $maxCacheKeys; // integer; max entries
-
-       /**
-        * @param $maxKeys integer Maximum number of entries allowed (min 1).
-        * @throws MWException When $maxCacheKeys is not an int or =< 0.
-        */
-       public function __construct( $maxKeys ) {
-               if ( !is_int( $maxKeys ) || $maxKeys < 1 ) {
-                       throw new MWException( __METHOD__ . " must be given an integer and >= 1" );
-               }
-               $this->maxCacheKeys = $maxKeys;
-       }
-
-       /**
-        * Set a property field for a cache entry.
-        * This will prune the cache if it gets too large based on LRU.
-        * If the item is already set, it will be pushed to the top of the cache.
-        *
-        * @param $key string
-        * @param $prop string
-        * @param $value mixed
-        * @return void
-        */
-       public function set( $key, $prop, $value ) {
-               if ( isset( $this->cache[$key] ) ) {
-                       $this->ping( $key ); // push to top
-               } elseif ( count( $this->cache ) >= $this->maxCacheKeys ) {
-                       reset( $this->cache );
-                       $evictKey = key( $this->cache );
-                       unset( $this->cache[$evictKey] );
-                       unset( $this->cacheTimes[$evictKey] );
-               }
-               $this->cache[$key][$prop] = $value;
-               $this->cacheTimes[$key][$prop] = time();
-       }
-
-       /**
-        * Check if a property field exists for a cache entry.
-        *
-        * @param $key string
-        * @param $prop string
-        * @param $maxAge integer Ignore items older than this many seconds (since 1.21)
-        * @return bool
-        */
-       public function has( $key, $prop, $maxAge = 0 ) {
-               if ( isset( $this->cache[$key][$prop] ) ) {
-                       return ( $maxAge <= 0 || ( time() - $this->cacheTimes[$key][$prop] ) <= $maxAge );
-               }
-
-               return false;
-       }
-
-       /**
-        * Get a property field for a cache entry.
-        * This returns null if the property is not set.
-        * If the item is already set, it will be pushed to the top of the cache.
-        *
-        * @param $key string
-        * @param $prop string
-        * @return mixed
-        */
-       public function get( $key, $prop ) {
-               if ( isset( $this->cache[$key][$prop] ) ) {
-                       $this->ping( $key ); // push to top
-                       return $this->cache[$key][$prop];
-               } else {
-                       return null;
-               }
-       }
-
-       /**
-        * Clear one or several cache entries, or all cache entries
-        *
-        * @param $keys string|Array
-        * @return void
-        */
-       public function clear( $keys = null ) {
-               if ( $keys === null ) {
-                       $this->cache = array();
-                       $this->cacheTimes = array();
-               } else {
-                       foreach ( (array)$keys as $key ) {
-                               unset( $this->cache[$key] );
-                               unset( $this->cacheTimes[$key] );
-                       }
-               }
-       }
-
-       /**
-        * Push an entry to the top of the cache
-        *
-        * @param $key string
-        */
-       protected function ping( $key ) {
-               $item = $this->cache[$key];
-               unset( $this->cache[$key] );
-               $this->cache[$key] = $item;
-       }
-}
diff --git a/includes/libs/ProcessCacheLRU.php b/includes/libs/ProcessCacheLRU.php
new file mode 100644 (file)
index 0000000..f2d9f42
--- /dev/null
@@ -0,0 +1,132 @@
+<?php
+/**
+ * Per-process memory cache for storing items.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Cache
+ */
+
+/**
+ * Handles per process caching of items
+ * @ingroup Cache
+ */
+class ProcessCacheLRU {
+       /** @var Array */
+       protected $cache = array(); // (key => prop => value)
+       /** @var Array */
+       protected $cacheTimes = array(); // (key => prop => UNIX timestamp)
+
+       protected $maxCacheKeys; // integer; max entries
+
+       /**
+        * @param $maxKeys integer Maximum number of entries allowed (min 1).
+        * @throws UnexpectedValueException When $maxCacheKeys is not an int or =< 0.
+        */
+       public function __construct( $maxKeys ) {
+               if ( !is_int( $maxKeys ) || $maxKeys < 1 ) {
+                       throw new UnexpectedValueException( __METHOD__ . " must be given an integer >= 1" );
+               }
+               $this->maxCacheKeys = $maxKeys;
+       }
+
+       /**
+        * Set a property field for a cache entry.
+        * This will prune the cache if it gets too large based on LRU.
+        * If the item is already set, it will be pushed to the top of the cache.
+        *
+        * @param $key string
+        * @param $prop string
+        * @param $value mixed
+        * @return void
+        */
+       public function set( $key, $prop, $value ) {
+               if ( isset( $this->cache[$key] ) ) {
+                       $this->ping( $key ); // push to top
+               } elseif ( count( $this->cache ) >= $this->maxCacheKeys ) {
+                       reset( $this->cache );
+                       $evictKey = key( $this->cache );
+                       unset( $this->cache[$evictKey] );
+                       unset( $this->cacheTimes[$evictKey] );
+               }
+               $this->cache[$key][$prop] = $value;
+               $this->cacheTimes[$key][$prop] = time();
+       }
+
+       /**
+        * Check if a property field exists for a cache entry.
+        *
+        * @param $key string
+        * @param $prop string
+        * @param $maxAge integer Ignore items older than this many seconds (since 1.21)
+        * @return bool
+        */
+       public function has( $key, $prop, $maxAge = 0 ) {
+               if ( isset( $this->cache[$key][$prop] ) ) {
+                       return ( $maxAge <= 0 || ( time() - $this->cacheTimes[$key][$prop] ) <= $maxAge );
+               }
+
+               return false;
+       }
+
+       /**
+        * Get a property field for a cache entry.
+        * This returns null if the property is not set.
+        * If the item is already set, it will be pushed to the top of the cache.
+        *
+        * @param $key string
+        * @param $prop string
+        * @return mixed
+        */
+       public function get( $key, $prop ) {
+               if ( isset( $this->cache[$key][$prop] ) ) {
+                       $this->ping( $key ); // push to top
+                       return $this->cache[$key][$prop];
+               } else {
+                       return null;
+               }
+       }
+
+       /**
+        * Clear one or several cache entries, or all cache entries
+        *
+        * @param $keys string|Array
+        * @return void
+        */
+       public function clear( $keys = null ) {
+               if ( $keys === null ) {
+                       $this->cache = array();
+                       $this->cacheTimes = array();
+               } else {
+                       foreach ( (array)$keys as $key ) {
+                               unset( $this->cache[$key] );
+                               unset( $this->cacheTimes[$key] );
+                       }
+               }
+       }
+
+       /**
+        * Push an entry to the top of the cache
+        *
+        * @param $key string
+        */
+       protected function ping( $key ) {
+               $item = $this->cache[$key];
+               unset( $this->cache[$key] );
+               $this->cache[$key] = $item;
+       }
+}
diff --git a/tests/phpunit/includes/cache/ProcessCacheLRUTest.php b/tests/phpunit/includes/cache/ProcessCacheLRUTest.php
deleted file mode 100644 (file)
index d3793d8..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-<?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
- */
-class ProcessCacheLRUTest extends MediaWikiTestCase {
-
-       /**
-        * Helper to verify emptiness of a cache object.
-        * Compare against an array so we get the cache content difference.
-        */
-       function assertCacheEmpty( $cache, $msg = 'Cache should be empty' ) {
-               $this->assertAttributeEquals( array(), 'cache', $cache, $msg );
-       }
-
-       /**
-        * Helper to fill a cache object passed by reference
-        */
-       function fillCache( &$cache, $numEntries ) {
-               // Fill cache with three values
-               for ( $i = 1; $i <= $numEntries; $i++ ) {
-                       $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
-        */
-       function getExpectedCache( $cacheMaxEntries, $entryToFill ) {
-               $expected = array();
-
-               if ( $entryToFill === 0 ) {
-                       # The cache is empty!
-                       return array();
-               } elseif ( $entryToFill <= $cacheMaxEntries ) {
-                       # Cache is not fully filled
-                       $firstKey = 1;
-               } else {
-                       # Cache overflowed
-                       $firstKey = 1 + $entryToFill - $cacheMaxEntries;
-               }
-
-               $lastKey = $entryToFill;
-
-               for ( $i = $firstKey; $i <= $lastKey; $i++ ) {
-                       $expected["cache-key-$i"] = array( "prop-$i" => "value-$i" );
-               }
-
-               return $expected;
-       }
-
-       /**
-        * Highlight diff between assertEquals and assertNotSame
-        */
-       public function testPhpUnitArrayEquality() {
-               $one = array( 'A' => 1, 'B' => 2 );
-               $two = array( 'B' => 2, 'A' => 1 );
-               $this->assertEquals( $one, $two ); // ==
-               $this->assertNotSame( $one, $two ); // ===
-       }
-
-       /**
-        * @dataProvider provideInvalidConstructorArg
-        * @expectedException MWException
-        */
-       public function testConstructorGivenInvalidValue( $maxSize ) {
-               new ProcessCacheLRUTestable( $maxSize );
-       }
-
-       /**
-        * Value which are forbidden by the constructor
-        */
-       public static function provideInvalidConstructorArg() {
-               return array(
-                       array( null ),
-                       array( array() ),
-                       array( new stdClass() ),
-                       array( 0 ),
-                       array( '5' ),
-                       array( -1 ),
-               );
-       }
-
-       public function testAddAndGetAKey() {
-               $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' ) );
-       }
-
-       public function testDeleteOldKey() {
-               $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.
-        *
-        * @dataProvider provideCacheFilling
-        * @param $cacheMaxEntries Maximum entry the created cache will hold
-        * @param $entryToFill Number of entries to insert in the created cache.
-        */
-       public function testFillingCache( $cacheMaxEntries, $entryToFill, $msg = '' ) {
-               $cache = new ProcessCacheLRUTestable( $cacheMaxEntries );
-               $this->fillCache( $cache, $entryToFill );
-
-               $this->assertSame(
-                       $this->getExpectedCache( $cacheMaxEntries, $entryToFill ),
-                       $cache->getCache(),
-                       "Filling a $cacheMaxEntries entries cache with $entryToFill entries"
-               );
-       }
-
-       /**
-        * Provider for testFillingCache
-        */
-       public static function provideCacheFilling() {
-               // ($cacheMaxEntries, $entryToFill, $msg='')
-               return array(
-                       array( 1, 0 ),
-                       array( 1, 1 ),
-                       array( 1, 2 ), # overflow
-                       array( 5, 33 ), # overflow
-               );
-       }
-
-       /**
-        * Create a cache with only one remaining entry then update
-        * the first inserted entry. Should bump it to the top.
-        */
-       public function testReplaceExistingKeyShouldBumpEntryToTop() {
-               $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(
-                       array(
-                               'cache-key-2' => array( 'prop-2' => 'value-2' ),
-                               'cache-key-1' => array( 'prop-1' => 'new-value-for-1' ),
-                       ),
-                       $cache->getCache()
-               );
-       }
-
-       public function testRecentlyAccessedKeyStickIn() {
-               $cache = new ProcessCacheLRUTestable( 2 );
-               $cache->set( 'first', 'prop1', 'value1' );
-               $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).
-        */
-       public function testReplaceExistingKeyInAFullCacheShouldBumpToTop() {
-               $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(
-                       array(
-                               'cache-key-1' => array( 'prop-1' => 'value-1' ),
-                               'cache-key-3' => array( 'prop-3' => 'value-3' ),
-                               'cache-key-2' => array( 'prop-2' => 'new-value-for-2' ),
-                       ),
-                       $cache->getCache()
-               );
-               $this->assertEquals( 'new-value-for-2',
-                       $cache->get( 'cache-key-2', 'prop-2' )
-               );
-       }
-
-       public function testBumpExistingKeyToTop() {
-               $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(
-                       array(
-                               'cache-key-2' => array( 'prop-2' => 'value-2' ),
-                               'cache-key-3' => array( 'prop-3' => 'value-3' ),
-                               'cache-key-1' => array( 'prop-1' => 'new value for 1' ),
-                       ),
-                       $cache->getCache()
-               );
-       }
-}
-
-/**
- * Overrides some ProcessCacheLRU methods and properties accessibility.
- */
-class ProcessCacheLRUTestable extends ProcessCacheLRU {
-       public $cache = array();
-
-       public function getCache() {
-               return $this->cache;
-       }
-
-       public function getEntriesCount() {
-               return count( $this->cache );
-       }
-}
diff --git a/tests/phpunit/includes/libs/ProcessCacheLRUTest.php b/tests/phpunit/includes/libs/ProcessCacheLRUTest.php
new file mode 100644 (file)
index 0000000..3eae810
--- /dev/null
@@ -0,0 +1,237 @@
+<?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
+ */
+class ProcessCacheLRUTest extends MediaWikiTestCase {
+
+       /**
+        * Helper to verify emptiness of a cache object.
+        * Compare against an array so we get the cache content difference.
+        */
+       function assertCacheEmpty( $cache, $msg = 'Cache should be empty' ) {
+               $this->assertAttributeEquals( array(), 'cache', $cache, $msg );
+       }
+
+       /**
+        * Helper to fill a cache object passed by reference
+        */
+       function fillCache( &$cache, $numEntries ) {
+               // Fill cache with three values
+               for ( $i = 1; $i <= $numEntries; $i++ ) {
+                       $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
+        */
+       function getExpectedCache( $cacheMaxEntries, $entryToFill ) {
+               $expected = array();
+
+               if ( $entryToFill === 0 ) {
+                       # The cache is empty!
+                       return array();
+               } elseif ( $entryToFill <= $cacheMaxEntries ) {
+                       # Cache is not fully filled
+                       $firstKey = 1;
+               } else {
+                       # Cache overflowed
+                       $firstKey = 1 + $entryToFill - $cacheMaxEntries;
+               }
+
+               $lastKey = $entryToFill;
+
+               for ( $i = $firstKey; $i <= $lastKey; $i++ ) {
+                       $expected["cache-key-$i"] = array( "prop-$i" => "value-$i" );
+               }
+
+               return $expected;
+       }
+
+       /**
+        * Highlight diff between assertEquals and assertNotSame
+        */
+       public function testPhpUnitArrayEquality() {
+               $one = array( 'A' => 1, 'B' => 2 );
+               $two = array( 'B' => 2, 'A' => 1 );
+               $this->assertEquals( $one, $two ); // ==
+               $this->assertNotSame( $one, $two ); // ===
+       }
+
+       /**
+        * @dataProvider provideInvalidConstructorArg
+        * @expectedException UnexpectedValueException
+        */
+       public function testConstructorGivenInvalidValue( $maxSize ) {
+               new ProcessCacheLRUTestable( $maxSize );
+       }
+
+       /**
+        * Value which are forbidden by the constructor
+        */
+       public static function provideInvalidConstructorArg() {
+               return array(
+                       array( null ),
+                       array( array() ),
+                       array( new stdClass() ),
+                       array( 0 ),
+                       array( '5' ),
+                       array( -1 ),
+               );
+       }
+
+       public function testAddAndGetAKey() {
+               $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' ) );
+       }
+
+       public function testDeleteOldKey() {
+               $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.
+        *
+        * @dataProvider provideCacheFilling
+        * @param $cacheMaxEntries Maximum entry the created cache will hold
+        * @param $entryToFill Number of entries to insert in the created cache.
+        */
+       public function testFillingCache( $cacheMaxEntries, $entryToFill, $msg = '' ) {
+               $cache = new ProcessCacheLRUTestable( $cacheMaxEntries );
+               $this->fillCache( $cache, $entryToFill );
+
+               $this->assertSame(
+                       $this->getExpectedCache( $cacheMaxEntries, $entryToFill ),
+                       $cache->getCache(),
+                       "Filling a $cacheMaxEntries entries cache with $entryToFill entries"
+               );
+       }
+
+       /**
+        * Provider for testFillingCache
+        */
+       public static function provideCacheFilling() {
+               // ($cacheMaxEntries, $entryToFill, $msg='')
+               return array(
+                       array( 1, 0 ),
+                       array( 1, 1 ),
+                       array( 1, 2 ), # overflow
+                       array( 5, 33 ), # overflow
+               );
+       }
+
+       /**
+        * Create a cache with only one remaining entry then update
+        * the first inserted entry. Should bump it to the top.
+        */
+       public function testReplaceExistingKeyShouldBumpEntryToTop() {
+               $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(
+                       array(
+                               'cache-key-2' => array( 'prop-2' => 'value-2' ),
+                               'cache-key-1' => array( 'prop-1' => 'new-value-for-1' ),
+                       ),
+                       $cache->getCache()
+               );
+       }
+
+       public function testRecentlyAccessedKeyStickIn() {
+               $cache = new ProcessCacheLRUTestable( 2 );
+               $cache->set( 'first', 'prop1', 'value1' );
+               $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).
+        */
+       public function testReplaceExistingKeyInAFullCacheShouldBumpToTop() {
+               $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(
+                       array(
+                               'cache-key-1' => array( 'prop-1' => 'value-1' ),
+                               'cache-key-3' => array( 'prop-3' => 'value-3' ),
+                               'cache-key-2' => array( 'prop-2' => 'new-value-for-2' ),
+                       ),
+                       $cache->getCache()
+               );
+               $this->assertEquals( 'new-value-for-2',
+                       $cache->get( 'cache-key-2', 'prop-2' )
+               );
+       }
+
+       public function testBumpExistingKeyToTop() {
+               $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(
+                       array(
+                               'cache-key-2' => array( 'prop-2' => 'value-2' ),
+                               'cache-key-3' => array( 'prop-3' => 'value-3' ),
+                               'cache-key-1' => array( 'prop-1' => 'new value for 1' ),
+                       ),
+                       $cache->getCache()
+               );
+       }
+}
+
+/**
+ * Overrides some ProcessCacheLRU methods and properties accessibility.
+ */
+class ProcessCacheLRUTestable extends ProcessCacheLRU {
+       public $cache = array();
+
+       public function getCache() {
+               return $this->cache;
+       }
+
+       public function getEntriesCount() {
+               return count( $this->cache );
+       }
+}