Merge "Add DROP INDEX support to DatabaseSqlite::replaceVars method"
[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 * @covers JobQueue::getWiki
74 */
75 public function testGetWiki( $queue, $recycles, $desc ) {
76 $queue = $this->$queue;
77 if ( !$queue ) {
78 $this->markTestSkipped( $desc );
79 }
80 $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
81 }
82
83 /**
84 * @dataProvider provider_queueLists
85 * @covers JobQueue::getType
86 */
87 public function testGetType( $queue, $recycles, $desc ) {
88 $queue = $this->$queue;
89 if ( !$queue ) {
90 $this->markTestSkipped( $desc );
91 }
92 $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
93 }
94
95 /**
96 * @dataProvider provider_queueLists
97 * @covers JobQueue
98 */
99 public function testBasicOperations( $queue, $recycles, $desc ) {
100 $queue = $this->$queue;
101 if ( !$queue ) {
102 $this->markTestSkipped( $desc );
103 }
104
105 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
106
107 $queue->flushCaches();
108 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
109 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
110
111 $this->assertTrue( $queue->push( $this->newJob() ), "Push worked ($desc)" );
112 $this->assertTrue( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
113
114 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
115
116 $queue->flushCaches();
117 $this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
118 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
119 $jobs = iterator_to_array( $queue->getAllQueuedJobs() );
120 $this->assertEquals( 2, count( $jobs ), "Queue iterator size is correct ($desc)" );
121
122 $job1 = $queue->pop();
123 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
124
125 $queue->flushCaches();
126 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
127
128 $queue->flushCaches();
129 if ( $recycles ) {
130 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
131 } else {
132 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
133 }
134
135 $job2 = $queue->pop();
136 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
137 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
138
139 $queue->flushCaches();
140 if ( $recycles ) {
141 $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
142 } else {
143 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
144 }
145
146 $queue->ack( $job1 );
147
148 $queue->flushCaches();
149 if ( $recycles ) {
150 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
151 } else {
152 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
153 }
154
155 $queue->ack( $job2 );
156
157 $queue->flushCaches();
158 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
159
160 $this->assertTrue( $queue->batchPush( array( $this->newJob(), $this->newJob() ) ),
161 "Push worked ($desc)" );
162 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
163
164 $queue->delete();
165 $queue->flushCaches();
166 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
167 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
168 }
169
170 /**
171 * @dataProvider provider_queueLists
172 * @covers JobQueue
173 */
174 public function testBasicDeduplication( $queue, $recycles, $desc ) {
175 $queue = $this->$queue;
176 if ( !$queue ) {
177 $this->markTestSkipped( $desc );
178 }
179
180 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
181
182 $queue->flushCaches();
183 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
184 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
185
186 $this->assertTrue(
187 $queue->batchPush(
188 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
189 ),
190 "Push worked ($desc)" );
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 $this->assertTrue(
199 $queue->batchPush(
200 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
201 ),
202 "Push worked ($desc)"
203 );
204
205 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
206
207 $queue->flushCaches();
208 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
209 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
210
211 $job1 = $queue->pop();
212 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
213
214 $queue->flushCaches();
215 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
216 if ( $recycles ) {
217 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
218 } else {
219 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
220 }
221
222 $queue->ack( $job1 );
223
224 $queue->flushCaches();
225 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
226 }
227
228 /**
229 * @dataProvider provider_queueLists
230 * @covers JobQueue
231 */
232 public function testRootDeduplication( $queue, $recycles, $desc ) {
233 $queue = $this->$queue;
234 if ( !$queue ) {
235 $this->markTestSkipped( $desc );
236 }
237
238 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
239
240 $queue->flushCaches();
241 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
242 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
243
244 $id = wfRandomString( 32 );
245 $root1 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
246 for ( $i = 0; $i < 5; ++$i ) {
247 $this->assertTrue( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
248 }
249 $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
250 sleep( 1 ); // roo job timestamp will increase
251 $root2 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
252 $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
253 "Root job signatures have different timestamps." );
254 for ( $i = 0; $i < 5; ++$i ) {
255 $this->assertTrue( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
256 }
257 $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
258
259 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
260
261 $queue->flushCaches();
262 $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
263 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
264
265 $dupcount = 0;
266 $jobs = array();
267 do {
268 $job = $queue->pop();
269 if ( $job ) {
270 $jobs[] = $job;
271 $queue->ack( $job );
272 }
273 if ( $job instanceof DuplicateJob ) {
274 ++$dupcount;
275 }
276 } while ( $job );
277
278 $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
279 $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
280 }
281
282 /**
283 * @dataProvider provider_fifoQueueLists
284 * @covers JobQueue
285 */
286 public function testJobOrder( $queue, $recycles, $desc ) {
287 $queue = $this->$queue;
288 if ( !$queue ) {
289 $this->markTestSkipped( $desc );
290 }
291
292 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
293
294 $queue->flushCaches();
295 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
296 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
297
298 for ( $i = 0; $i < 10; ++$i ) {
299 $this->assertTrue( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
300 }
301
302 for ( $i = 0; $i < 10; ++$i ) {
303 $job = $queue->pop();
304 $this->assertTrue( $job instanceof Job, "Jobs popped from queue ($desc)" );
305 $params = $job->getParams();
306 $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
307 $queue->ack( $job );
308 }
309
310 $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
311
312 $queue->flushCaches();
313 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
314 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
315 }
316
317 public static function provider_queueLists() {
318 return array(
319 array( 'queueRand', false, 'Random queue without ack()' ),
320 array( 'queueRandTTL', true, 'Random queue with ack()' ),
321 array( 'queueTimestamp', false, 'Time ordered queue without ack()' ),
322 array( 'queueTimestampTTL', true, 'Time ordered queue with ack()' ),
323 array( 'queueFifo', false, 'FIFO ordered queue without ack()' ),
324 array( 'queueFifoTTL', true, 'FIFO ordered queue with ack()' )
325 );
326 }
327
328 public static function provider_fifoQueueLists() {
329 return array(
330 array( 'queueFifo', false, 'Ordered queue without ack()' ),
331 array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
332 );
333 }
334
335 function newJob( $i = 0, $rootJob = array() ) {
336 return new NullJob( Title::newMainPage(),
337 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) + $rootJob );
338 }
339
340 function newDedupedJob( $i = 0, $rootJob = array() ) {
341 return new NullJob( Title::newMainPage(),
342 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) + $rootJob );
343 }
344 }