Merge "(bug 17602) fix Monobook action tabs not quite touching the page body"
[lhc/web/wiklou.git] / tests / phpunit / includes / jobqueue / JobQueueTest.php
1 <?php
2
3 /**
4 * @group JobQueue
5 * @group medium
6 * @group Database
7 */
8 class JobQueueTest extends MediaWikiTestCase {
9 protected $key;
10 protected $queueRand, $queueRandTTL, $queueFifo, $queueFifoTTL;
11
12 function __construct( $name = null, array $data = array(), $dataName = '' ) {
13 parent::__construct( $name, $data, $dataName );
14
15 $this->tablesUsed[] = 'job';
16 }
17
18 protected function setUp() {
19 global $wgJobTypeConf;
20 parent::setUp();
21
22 $this->setMwGlobals( 'wgMemc', new HashBagOStuff() );
23
24 if ( $this->getCliArg( 'use-jobqueue=' ) ) {
25 $name = $this->getCliArg( 'use-jobqueue=' );
26 if ( !isset( $wgJobTypeConf[$name] ) ) {
27 throw new MWException( "No \$wgJobTypeConf entry for '$name'." );
28 }
29 $baseConfig = $wgJobTypeConf[$name];
30 } else {
31 $baseConfig = array( 'class' => 'JobQueueDB' );
32 }
33 $baseConfig['type'] = 'null';
34 $baseConfig['wiki'] = wfWikiID();
35 $variants = array(
36 'queueRand' => array( 'order' => 'random', 'claimTTL' => 0 ),
37 'queueRandTTL' => array( 'order' => 'random', 'claimTTL' => 10 ),
38 'queueTimestamp' => array( 'order' => 'timestamp', 'claimTTL' => 0 ),
39 'queueTimestampTTL' => array( 'order' => 'timestamp', 'claimTTL' => 10 ),
40 'queueFifo' => array( 'order' => 'fifo', 'claimTTL' => 0 ),
41 'queueFifoTTL' => array( 'order' => 'fifo', 'claimTTL' => 10 ),
42 );
43 foreach ( $variants as $q => $settings ) {
44 try {
45 $this->$q = JobQueue::factory( $settings + $baseConfig );
46 if ( !( $this->$q instanceof JobQueueDB ) ) {
47 $this->$q->setTestingPrefix( 'unittests-' . wfRandomString( 32 ) );
48 }
49 } catch ( MWException $e ) {
50 // unsupported?
51 // @todo What if it was another error?
52 };
53 }
54 }
55
56 protected function tearDown() {
57 parent::tearDown();
58 foreach (
59 array(
60 'queueRand', 'queueRandTTL', 'queueTimestamp', 'queueTimestampTTL',
61 'queueFifo', 'queueFifoTTL'
62 ) as $q
63 ) {
64 if ( $this->$q ) {
65 $this->$q->delete();
66 }
67 $this->$q = null;
68 }
69 }
70
71 /**
72 * @dataProvider provider_queueLists
73 */
74 function testProperties( $queue, $recycles, $desc ) {
75 $queue = $this->$queue;
76 if ( !$queue ) {
77 $this->markTestSkipped( $desc );
78 }
79
80 $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
81 $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
82 }
83
84 /**
85 * @dataProvider provider_queueLists
86 */
87 function testBasicOperations( $queue, $recycles, $desc ) {
88 $queue = $this->$queue;
89 if ( !$queue ) {
90 $this->markTestSkipped( $desc );
91 }
92
93 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
94
95 $queue->flushCaches();
96 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
97 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
98
99 $this->assertTrue( $queue->push( $this->newJob() ), "Push worked ($desc)" );
100 $this->assertTrue( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
101
102 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
103
104 $queue->flushCaches();
105 $this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
106 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
107 $jobs = iterator_to_array( $queue->getAllQueuedJobs() );
108 $this->assertEquals( 2, count( $jobs ), "Queue iterator size is correct ($desc)" );
109
110 $job1 = $queue->pop();
111 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
112
113 $queue->flushCaches();
114 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
115
116 $queue->flushCaches();
117 if ( $recycles ) {
118 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
119 } else {
120 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
121 }
122
123 $job2 = $queue->pop();
124 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
125 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
126
127 $queue->flushCaches();
128 if ( $recycles ) {
129 $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
130 } else {
131 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
132 }
133
134 $queue->ack( $job1 );
135
136 $queue->flushCaches();
137 if ( $recycles ) {
138 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
139 } else {
140 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
141 }
142
143 $queue->ack( $job2 );
144
145 $queue->flushCaches();
146 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
147
148 $this->assertTrue( $queue->batchPush( array( $this->newJob(), $this->newJob() ) ),
149 "Push worked ($desc)" );
150 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
151
152 $queue->delete();
153 $queue->flushCaches();
154 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
155 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
156 }
157
158 /**
159 * @dataProvider provider_queueLists
160 */
161 function testBasicDeduplication( $queue, $recycles, $desc ) {
162 $queue = $this->$queue;
163 if ( !$queue ) {
164 $this->markTestSkipped( $desc );
165 }
166
167 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
168
169 $queue->flushCaches();
170 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
171 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
172
173 $this->assertTrue(
174 $queue->batchPush(
175 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
176 ),
177 "Push worked ($desc)" );
178
179 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
180
181 $queue->flushCaches();
182 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
183 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
184
185 $this->assertTrue(
186 $queue->batchPush(
187 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
188 ),
189 "Push worked ($desc)"
190 );
191
192 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
193
194 $queue->flushCaches();
195 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
196 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
197
198 $job1 = $queue->pop();
199 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
200
201 $queue->flushCaches();
202 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
203 if ( $recycles ) {
204 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
205 } else {
206 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
207 }
208
209 $queue->ack( $job1 );
210
211 $queue->flushCaches();
212 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
213 }
214
215 /**
216 * @dataProvider provider_queueLists
217 */
218 function testRootDeduplication( $queue, $recycles, $desc ) {
219 $queue = $this->$queue;
220 if ( !$queue ) {
221 $this->markTestSkipped( $desc );
222 }
223
224 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
225
226 $queue->flushCaches();
227 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
228 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
229
230 $id = wfRandomString( 32 );
231 $root1 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
232 for ( $i = 0; $i < 5; ++$i ) {
233 $this->assertTrue( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
234 }
235 $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
236 sleep( 1 ); // roo job timestamp will increase
237 $root2 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
238 $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
239 "Root job signatures have different timestamps." );
240 for ( $i = 0; $i < 5; ++$i ) {
241 $this->assertTrue( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
242 }
243 $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
244
245 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
246
247 $queue->flushCaches();
248 $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
249 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
250
251 $dupcount = 0;
252 $jobs = array();
253 do {
254 $job = $queue->pop();
255 if ( $job ) {
256 $jobs[] = $job;
257 $queue->ack( $job );
258 }
259 if ( $job instanceof DuplicateJob ) {
260 ++$dupcount;
261 }
262 } while ( $job );
263
264 $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
265 $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
266 }
267
268 /**
269 * @dataProvider provider_fifoQueueLists
270 */
271 function testJobOrder( $queue, $recycles, $desc ) {
272 $queue = $this->$queue;
273 if ( !$queue ) {
274 $this->markTestSkipped( $desc );
275 }
276
277 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
278
279 $queue->flushCaches();
280 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
281 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
282
283 for ( $i = 0; $i < 10; ++$i ) {
284 $this->assertTrue( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
285 }
286
287 for ( $i = 0; $i < 10; ++$i ) {
288 $job = $queue->pop();
289 $this->assertTrue( $job instanceof Job, "Jobs popped from queue ($desc)" );
290 $params = $job->getParams();
291 $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
292 $queue->ack( $job );
293 }
294
295 $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
296
297 $queue->flushCaches();
298 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
299 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
300 }
301
302 public static function provider_queueLists() {
303 return array(
304 array( 'queueRand', false, 'Random queue without ack()' ),
305 array( 'queueRandTTL', true, 'Random queue with ack()' ),
306 array( 'queueTimestamp', false, 'Time ordered queue without ack()' ),
307 array( 'queueTimestampTTL', true, 'Time ordered queue with ack()' ),
308 array( 'queueFifo', false, 'FIFO ordered queue without ack()' ),
309 array( 'queueFifoTTL', true, 'FIFO ordered queue with ack()' )
310 );
311 }
312
313 public static function provider_fifoQueueLists() {
314 return array(
315 array( 'queueFifo', false, 'Ordered queue without ack()' ),
316 array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
317 );
318 }
319
320 function newJob( $i = 0, $rootJob = array() ) {
321 return new NullJob( Title::newMainPage(),
322 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) + $rootJob );
323 }
324
325 function newDedupedJob( $i = 0, $rootJob = array() ) {
326 return new NullJob( Title::newMainPage(),
327 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) + $rootJob );
328 }
329 }