Merge "[FileBackend] Tweaked TempFSFile::bind() to handle __get()."
[lhc/web/wiklou.git] / tests / phpunit / includes / cache / ProcessCacheLRUTest.php
1 <?php
2
3 /**
4 * Test for ProcessCacheLRU class.
5 *
6 * Note that it uses the ProcessCacheLRUTestable class which extends some
7 * properties and methods visibility. That class is defined at the end of the
8 * file containing this class.
9 *
10 * @group Cache
11 */
12 class ProcessCacheLRUTest extends MediaWikiTestCase {
13
14 /**
15 * Helper to verify emptiness of a cache object.
16 * Compare against an array so we get the cache content difference.
17 */
18 function assertCacheEmpty( $cache, $msg = 'Cache should be empty' ) {
19 $this->assertAttributeEquals( array(), 'cache', $cache, $msg );
20 }
21
22 /**
23 * Helper to fill a cache object passed by reference
24 */
25 function fillCache( &$cache, $numEntries ) {
26 // Fill cache with three values
27 for( $i=1; $i<=$numEntries; $i++) {
28 $cache->set( "cache-key-$i", "prop-$i", "value-$i" );
29 }
30 }
31
32 /**
33 * Generates an array of what would be expected in cache for a given cache
34 * size and a number of entries filled in sequentially
35 */
36 function getExpectedCache( $cacheMaxEntries, $entryToFill ) {
37 $expected = array();
38
39 if( $entryToFill === 0 ) {
40 # The cache is empty!
41 return array();
42 } elseif( $entryToFill <= $cacheMaxEntries ) {
43 # Cache is not fully filled
44 $firstKey = 1;
45 } else {
46 # Cache overflowed
47 $firstKey = 1 + $entryToFill - $cacheMaxEntries;
48 }
49
50 $lastKey = $entryToFill;
51
52 for( $i=$firstKey; $i<=$lastKey; $i++ ) {
53 $expected["cache-key-$i"] = array( "prop-$i" => "value-$i" );
54 }
55 return $expected;
56 }
57
58 /**
59 * Highlight diff between assertEquals and assertNotSame
60 */
61 function testPhpUnitArrayEquality() {
62 $one = array( 'A' => 1, 'B' => 2 );
63 $two = array( 'B' => 2, 'A' => 1 );
64 $this->assertEquals( $one, $two ); // ==
65 $this->assertNotSame( $one, $two ); // ===
66 }
67
68 /**
69 * @dataProvider provideInvalidConstructorArg
70 * @expectedException MWException
71 */
72 function testConstructorGivenInvalidValue( $maxSize ) {
73 $c = new ProcessCacheLRUTestable( $maxSize );
74 }
75
76 /**
77 * Value which are forbidden by the constructor
78 */
79 public static function provideInvalidConstructorArg() {
80 return array(
81 array( null ),
82 array( array() ),
83 array( new stdClass() ),
84 array( 0 ),
85 array( '5' ),
86 array( -1 ),
87 );
88 }
89
90 function testAddAndGetAKey() {
91 $oneCache = new ProcessCacheLRUTestable( 1 );
92 $this->assertCacheEmpty( $oneCache );
93
94 // First set just one value
95 $oneCache->set( 'cache-key', 'prop1', 'value1' );
96 $this->assertEquals( 1, $oneCache->getEntriesCount() );
97 $this->assertTrue( $oneCache->has( 'cache-key', 'prop1' ) );
98 $this->assertEquals( 'value1', $oneCache->get( 'cache-key', 'prop1' ) );
99 }
100
101 function testDeleteOldKey() {
102 $oneCache = new ProcessCacheLRUTestable( 1 );
103 $this->assertCacheEmpty( $oneCache );
104
105 $oneCache->set( 'cache-key', 'prop1', 'value1' );
106 $oneCache->set( 'cache-key', 'prop1', 'value2' );
107 $this->assertEquals( 'value2', $oneCache->get( 'cache-key', 'prop1' ) );
108 }
109
110 /**
111 * This test that we properly overflow when filling a cache with
112 * a sequence of always different cache-keys. Meant to verify we correclty
113 * delete the older key.
114 *
115 * @dataProvider provideCacheFilling
116 * @param $cacheMaxEntries Maximum entry the created cache will hold
117 * @param $entryToFill Number of entries to insert in the created cache.
118 */
119 function testFillingCache( $cacheMaxEntries, $entryToFill, $msg = '' ) {
120 $cache = new ProcessCacheLRUTestable( $cacheMaxEntries );
121 $this->fillCache( $cache, $entryToFill);
122
123 $this->assertSame(
124 $this->getExpectedCache( $cacheMaxEntries, $entryToFill ),
125 $cache->getCache(),
126 "Filling a $cacheMaxEntries entries cache with $entryToFill entries"
127 );
128
129 }
130
131 /**
132 * Provider for testFillingCache
133 */
134 public static function provideCacheFilling() {
135 // ($cacheMaxEntries, $entryToFill, $msg='')
136 return array(
137 array( 1, 0 ),
138 array( 1, 1 ),
139 array( 1, 2 ), # overflow
140 array( 5, 33 ), # overflow
141 );
142
143 }
144
145 /**
146 * Create a cache with only one remaining entry then update
147 * the first inserted entry. Should bump it to the top.
148 */
149 function testReplaceExistingKeyShouldBumpEntryToTop() {
150 $maxEntries = 3;
151
152 $cache = new ProcessCacheLRUTestable( $maxEntries );
153 // Fill cache leaving just one remaining slot
154 $this->fillCache( $cache, $maxEntries - 1 );
155
156 // Set an existing cache key
157 $cache->set( "cache-key-1", "prop-1", "new-value-for-1" );
158
159 $this->assertSame(
160 array(
161 'cache-key-2' => array( 'prop-2' => 'value-2' ),
162 'cache-key-1' => array( 'prop-1' => 'new-value-for-1' ),
163 ),
164 $cache->getCache()
165 );
166 }
167
168 function testRecentlyAccessedKeyStickIn() {
169 $cache = new ProcessCacheLRUTestable( 2 );
170 $cache->set( 'first' , 'prop1', 'value1' );
171 $cache->set( 'second', 'prop2', 'value2' );
172
173 // Get first
174 $cache->get( 'first', 'prop1' );
175 // Cache a third value, should invalidate the least used one
176 $cache->set( 'third', 'prop3', 'value3' );
177
178 $this->assertFalse( $cache->has( 'second', 'prop2' ) );
179 }
180
181 /**
182 * This first create a full cache then update the value for the 2nd
183 * filled entry.
184 * Given a cache having 1,2,3 as key, updating 2 should bump 2 to
185 * the top of the queue with the new value: 1,3,2* (* = updated).
186 */
187 function testReplaceExistingKeyInAFullCacheShouldBumpToTop() {
188 $maxEntries = 3;
189
190 $cache = new ProcessCacheLRUTestable( $maxEntries );
191 $this->fillCache( $cache, $maxEntries );
192
193 // Set an existing cache key
194 $cache->set( "cache-key-2", "prop-2", "new-value-for-2" );
195 $this->assertSame(
196 array(
197 'cache-key-1' => array( 'prop-1' => 'value-1' ),
198 'cache-key-3' => array( 'prop-3' => 'value-3' ),
199 'cache-key-2' => array( 'prop-2' => 'new-value-for-2' ),
200 ),
201 $cache->getCache()
202 );
203 $this->assertEquals( 'new-value-for-2',
204 $cache->get( 'cache-key-2', 'prop-2' )
205 );
206 }
207
208 function testBumpExistingKeyToTop() {
209 $cache = new ProcessCacheLRUTestable( 3 );
210 $this->fillCache( $cache, 3 );
211
212 // Set the very first cache key to a new value
213 $cache->set( "cache-key-1", "prop-1", "new value for 1" );
214 $this->assertEquals(
215 array(
216 'cache-key-2' => array( 'prop-2' => 'value-2' ),
217 'cache-key-3' => array( 'prop-3' => 'value-3' ),
218 'cache-key-1' => array( 'prop-1' => 'new value for 1' ),
219 ),
220 $cache->getCache()
221 );
222
223 }
224
225 }
226
227 /**
228 * Overrides some ProcessCacheLRU methods and properties accessibility.
229 */
230 class ProcessCacheLRUTestable extends ProcessCacheLRU {
231 public $cache = array();
232
233 public function getCache() {
234 return $this->cache;
235 }
236 public function getEntriesCount() {
237 return count( $this->cache );
238 }
239 }