8 class JobQueueTest
extends MediaWikiTestCase
{
10 protected $queueRand, $queueRandTTL, $queueFifo, $queueFifoTTL;
12 function __construct( $name = null, array $data = array(), $dataName = '' ) {
13 parent
::__construct( $name, $data, $dataName );
15 $this->tablesUsed
[] = 'job';
18 protected function setUp() {
19 global $wgJobTypeConf;
22 $this->setMwGlobals( 'wgMemc', new HashBagOStuff() );
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'." );
29 $baseConfig = $wgJobTypeConf[$name];
31 $baseConfig = array( 'class' => 'JobQueueDB' );
33 $baseConfig['type'] = 'null';
34 $baseConfig['wiki'] = wfWikiID();
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 ),
43 foreach ( $variants as $q => $settings ) {
45 $this->$q = JobQueue
::factory( $settings +
$baseConfig );
46 if ( !( $this->$q instanceof JobQueueDB
) ) {
47 $this->$q->setTestingPrefix( 'unittests-' . wfRandomString( 32 ) );
49 } catch ( MWException
$e ) {
51 // @todo What if it was another error?
56 protected function tearDown() {
60 'queueRand', 'queueRandTTL', 'queueTimestamp', 'queueTimestampTTL',
61 'queueFifo', 'queueFifoTTL'
72 * @dataProvider provider_queueLists
73 * @covers JobQueue::getWiki
75 public function testGetWiki( $queue, $recycles, $desc ) {
76 $queue = $this->$queue;
78 $this->markTestSkipped( $desc );
80 $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
84 * @dataProvider provider_queueLists
85 * @covers JobQueue::getType
87 public function testGetType( $queue, $recycles, $desc ) {
88 $queue = $this->$queue;
90 $this->markTestSkipped( $desc );
92 $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
96 * @dataProvider provider_queueLists
99 public function testBasicOperations( $queue, $recycles, $desc ) {
100 $queue = $this->$queue;
102 $this->markTestSkipped( $desc );
105 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
107 $queue->flushCaches();
108 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
109 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
111 $this->assertNull( $queue->push( $this->newJob() ), "Push worked ($desc)" );
112 $this->assertNull( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
114 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
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)" );
122 $job1 = $queue->pop();
123 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
125 $queue->flushCaches();
126 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
128 $queue->flushCaches();
130 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
133 $job2 = $queue->pop();
134 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
135 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
137 $queue->flushCaches();
139 $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
142 $queue->ack( $job1 );
144 $queue->flushCaches();
146 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
149 $queue->ack( $job2 );
151 $queue->flushCaches();
152 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
154 $this->assertNull( $queue->batchPush( array( $this->newJob(), $this->newJob() ) ),
155 "Push worked ($desc)" );
156 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
159 $queue->flushCaches();
160 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
161 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
165 * @dataProvider provider_queueLists
168 public function testBasicDeduplication( $queue, $recycles, $desc ) {
169 $queue = $this->$queue;
171 $this->markTestSkipped( $desc );
174 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
176 $queue->flushCaches();
177 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
178 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
182 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
184 "Push worked ($desc)" );
186 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
188 $queue->flushCaches();
189 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
190 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
194 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
196 "Push worked ($desc)"
199 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
201 $queue->flushCaches();
202 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
203 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
205 $job1 = $queue->pop();
206 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
208 $queue->flushCaches();
209 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
211 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
214 $queue->ack( $job1 );
216 $queue->flushCaches();
217 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
221 * @dataProvider provider_queueLists
224 public function testDeduplicationWhileClaimed( $queue, $recycles, $desc ) {
225 $queue = $this->$queue;
227 $this->markTestSkipped( $desc );
230 $job = $this->newDedupedJob();
231 $queue->push( $job );
233 // De-duplication does not apply to already-claimed jobs
235 $queue->push( $job );
239 // Make sure ack() of the twin did not delete the sibling data
240 $this->assertType( 'NullJob', $j );
244 * @dataProvider provider_queueLists
247 public function testRootDeduplication( $queue, $recycles, $desc ) {
248 $queue = $this->$queue;
250 $this->markTestSkipped( $desc );
253 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
255 $queue->flushCaches();
256 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
257 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
259 $id = wfRandomString( 32 );
260 $root1 = Job
::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
261 for ( $i = 0; $i < 5; ++
$i ) {
262 $this->assertNull( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
264 $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
267 # Add a second to UNIX epoch and format back to TS_MW
268 $root2_ts = strtotime( $root2['rootJobTimestamp'] );
270 $root2['rootJobTimestamp'] = wfTimestamp( TS_MW
, $root2_ts );
272 $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
273 "Root job signatures have different timestamps." );
274 for ( $i = 0; $i < 5; ++
$i ) {
275 $this->assertNull( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
277 $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
279 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
281 $queue->flushCaches();
282 $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
283 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
288 $job = $queue->pop();
293 if ( $job instanceof DuplicateJob
) {
298 $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
299 $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
303 * @dataProvider provider_fifoQueueLists
306 public function testJobOrder( $queue, $recycles, $desc ) {
307 $queue = $this->$queue;
309 $this->markTestSkipped( $desc );
312 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
314 $queue->flushCaches();
315 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
316 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
318 for ( $i = 0; $i < 10; ++
$i ) {
319 $this->assertNull( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
322 for ( $i = 0; $i < 10; ++
$i ) {
323 $job = $queue->pop();
324 $this->assertTrue( $job instanceof Job
, "Jobs popped from queue ($desc)" );
325 $params = $job->getParams();
326 $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
330 $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
332 $queue->flushCaches();
333 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
334 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
337 public static function provider_queueLists() {
339 array( 'queueRand', false, 'Random queue without ack()' ),
340 array( 'queueRandTTL', true, 'Random queue with ack()' ),
341 array( 'queueTimestamp', false, 'Time ordered queue without ack()' ),
342 array( 'queueTimestampTTL', true, 'Time ordered queue with ack()' ),
343 array( 'queueFifo', false, 'FIFO ordered queue without ack()' ),
344 array( 'queueFifoTTL', true, 'FIFO ordered queue with ack()' )
348 public static function provider_fifoQueueLists() {
350 array( 'queueFifo', false, 'Ordered queue without ack()' ),
351 array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
355 function newJob( $i = 0, $rootJob = array() ) {
356 return new NullJob( Title
::newMainPage(),
357 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) +
$rootJob );
360 function newDedupedJob( $i = 0, $rootJob = array() ) {
361 return new NullJob( Title
::newMainPage(),
362 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) +
$rootJob );