assertAttributeEquals( array(), 'cache', $cache, $msg ); } /** * Helper to fill a cache object passed by reference */ protected 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 */ protected 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 Wikimedia\Assert\ParameterAssertionException * @covers ProcessCacheLRU::__construct */ 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 ), ); } /** * @covers ProcessCacheLRU::get * @covers ProcessCacheLRU::set * @covers ProcessCacheLRU::het */ 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' ) ); } /** * @covers ProcessCacheLRU::set * @covers ProcessCacheLRU::get */ 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. * * @covers ProcessCacheLRU::set * @dataProvider provideCacheFilling * @param int $cacheMaxEntries Maximum entry the created cache will hold * @param int $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 ), // overflow array( 1, 2 ), // overflow array( 5, 33 ), ); } /** * Create a cache with only one remaining entry then update * the first inserted entry. Should bump it to the top. * * @covers ProcessCacheLRU::set */ 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() ); } /** * @covers ProcessCacheLRU::get * @covers ProcessCacheLRU::set * @covers ProcessCacheLRU::het */ 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). * * @covers ProcessCacheLRU::set * @covers ProcessCacheLRU::get */ 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' ) ); } /** * @covers ProcessCacheLRU::set */ 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 ); } }