Merge "Add RL targets support to OutputPage"
[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 ) {}; // unsupported? (@TODO: what if it was another error?)
50 }
51 }
52
53 protected function tearDown() {
54 parent::tearDown();
55 foreach (
56 array(
57 'queueRand', 'queueRandTTL', 'queueTimestamp', 'queueTimestampTTL',
58 'queueFifo', 'queueFifoTTL'
59 ) as $q
60 ) {
61 if ( $this->$q ) {
62 do {
63 $job = $this->$q->pop();
64 if ( $job ) {
65 $this->$q->ack( $job );
66 }
67 } while ( $job );
68 }
69 $this->$q = null;
70 }
71 }
72
73 /**
74 * @dataProvider provider_queueLists
75 */
76 function testProperties( $queue, $recycles, $desc ) {
77 $queue = $this->$queue;
78 if ( !$queue ) {
79 $this->markTestSkipped( $desc );
80 }
81
82 $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
83 $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
84 }
85
86 /**
87 * @dataProvider provider_queueLists
88 */
89 function testBasicOperations( $queue, $recycles, $desc ) {
90 $queue = $this->$queue;
91 if ( !$queue ) {
92 $this->markTestSkipped( $desc );
93 }
94
95 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
96
97 $queue->flushCaches();
98 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
99 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
100
101 $this->assertTrue( $queue->push( $this->newJob() ), "Push worked ($desc)" );
102 $this->assertTrue( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
103
104 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
105
106 $queue->flushCaches();
107 $this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
108 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($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
149 /**
150 * @dataProvider provider_queueLists
151 */
152 function testBasicDeduplication( $queue, $recycles, $desc ) {
153 $queue = $this->$queue;
154 if ( !$queue ) {
155 $this->markTestSkipped( $desc );
156 }
157
158 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
159
160 $queue->flushCaches();
161 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
162 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
163
164 $this->assertTrue(
165 $queue->batchPush(
166 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
167 ),
168 "Push worked ($desc)" );
169
170 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
171
172 $queue->flushCaches();
173 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
174 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
175
176 $this->assertTrue(
177 $queue->batchPush(
178 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
179 ),
180 "Push worked ($desc)"
181 );
182
183 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
184
185 $queue->flushCaches();
186 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
187 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
188
189 $job1 = $queue->pop();
190 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
191
192 $queue->flushCaches();
193 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
194 if ( $recycles ) {
195 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
196 } else {
197 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
198 }
199
200 $queue->ack( $job1 );
201
202 $queue->flushCaches();
203 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
204 }
205
206 /**
207 * @dataProvider provider_queueLists
208 */
209 function testRootDeduplication( $queue, $recycles, $desc ) {
210 $queue = $this->$queue;
211 if ( !$queue ) {
212 $this->markTestSkipped( $desc );
213 }
214
215 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
216
217 $queue->flushCaches();
218 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
219 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
220
221 $id = wfRandomString( 32 );
222 $root1 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
223 for ( $i = 0; $i < 5; ++$i ) {
224 $this->assertTrue( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
225 }
226 $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
227 sleep( 1 ); // roo job timestamp will increase
228 $root2 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
229 $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
230 "Root job signatures have different timestamps." );
231 for ( $i = 0; $i < 5; ++$i ) {
232 $this->assertTrue( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
233 }
234 $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
235
236 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
237
238 $queue->flushCaches();
239 $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
240 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
241
242 $dupcount = 0;
243 $jobs = array();
244 do {
245 $job = $queue->pop();
246 if ( $job ) {
247 $jobs[] = $job;
248 $queue->ack( $job );
249 }
250 if ( $job instanceof DuplicateJob ) {
251 ++$dupcount;
252 }
253 } while ( $job );
254
255 $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
256 $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
257 }
258
259 /**
260 * @dataProvider provider_fifoQueueLists
261 */
262 function testJobOrder( $queue, $recycles, $desc ) {
263 $queue = $this->$queue;
264 if ( !$queue ) {
265 $this->markTestSkipped( $desc );
266 }
267
268 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
269
270 $queue->flushCaches();
271 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
272 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
273
274 for ( $i = 0; $i < 10; ++$i ) {
275 $this->assertTrue( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
276 }
277
278 for ( $i = 0; $i < 10; ++$i ) {
279 $job = $queue->pop();
280 $this->assertTrue( $job instanceof Job, "Jobs popped from queue ($desc)" );
281 $params = $job->getParams();
282 $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
283 $queue->ack( $job );
284 }
285
286 $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
287
288 $queue->flushCaches();
289 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
290 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
291 }
292
293 public static function provider_queueLists() {
294 return array(
295 array( 'queueRand', false, 'Random queue without ack()' ),
296 array( 'queueRandTTL', true, 'Random queue with ack()' ),
297 array( 'queueTimestamp', false, 'Time ordered queue without ack()' ),
298 array( 'queueTimestampTTL', true, 'Time ordered queue with ack()' ),
299 array( 'queueFifo', false, 'FIFO ordered queue without ack()' ),
300 array( 'queueFifoTTL', true, 'FIFO ordered queue with ack()' )
301 );
302 }
303
304 public static function provider_fifoQueueLists() {
305 return array(
306 array( 'queueFifo', false, 'Ordered queue without ack()' ),
307 array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
308 );
309 }
310
311 function newJob( $i = 0, $rootJob = array() ) {
312 return new NullJob( Title::newMainPage(),
313 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) + $rootJob );
314 }
315
316 function newDedupedJob( $i = 0, $rootJob = array() ) {
317 return new NullJob( Title::newMainPage(),
318 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) + $rootJob );
319 }
320 }