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