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 if ( $this->getCliArg( 'use-jobqueue' ) ) {
23 $name = $this->getCliArg( 'use-jobqueue' );
24 if ( !isset( $wgJobTypeConf[$name] ) ) {
25 throw new MWException( "No \$wgJobTypeConf entry for '$name'." );
27 $baseConfig = $wgJobTypeConf[$name];
29 $baseConfig = array( 'class' => 'JobQueueDB' );
31 $baseConfig['type'] = 'null';
32 $baseConfig['wiki'] = wfWikiID();
34 'queueRand' => array( 'order' => 'random', 'claimTTL' => 0 ),
35 'queueRandTTL' => array( 'order' => 'random', 'claimTTL' => 10 ),
36 'queueTimestamp' => array( 'order' => 'timestamp', 'claimTTL' => 0 ),
37 'queueTimestampTTL' => array( 'order' => 'timestamp', 'claimTTL' => 10 ),
38 'queueFifo' => array( 'order' => 'fifo', 'claimTTL' => 0 ),
39 'queueFifoTTL' => array( 'order' => 'fifo', 'claimTTL' => 10 ),
41 foreach ( $variants as $q => $settings ) {
43 $this->$q = JobQueue
::factory( $settings +
$baseConfig );
44 if ( !( $this->$q instanceof JobQueueDB
) ) {
45 $this->$q->setTestingPrefix( 'unittests-' . wfRandomString( 32 ) );
47 } catch ( MWException
$e ) {
49 // @todo What if it was another error?
54 protected function tearDown() {
58 'queueRand', 'queueRandTTL', 'queueTimestamp', 'queueTimestampTTL',
59 'queueFifo', 'queueFifoTTL'
70 * @dataProvider provider_queueLists
71 * @covers JobQueue::getWiki
73 public function testGetWiki( $queue, $recycles, $desc ) {
74 $queue = $this->$queue;
76 $this->markTestSkipped( $desc );
78 $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
82 * @dataProvider provider_queueLists
83 * @covers JobQueue::getType
85 public function testGetType( $queue, $recycles, $desc ) {
86 $queue = $this->$queue;
88 $this->markTestSkipped( $desc );
90 $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
94 * @dataProvider provider_queueLists
97 public function testBasicOperations( $queue, $recycles, $desc ) {
98 $queue = $this->$queue;
100 $this->markTestSkipped( $desc );
103 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
105 $queue->flushCaches();
106 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
107 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
109 $this->assertNull( $queue->push( $this->newJob() ), "Push worked ($desc)" );
110 $this->assertNull( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
112 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
114 $queue->flushCaches();
115 $this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
116 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
117 $jobs = iterator_to_array( $queue->getAllQueuedJobs() );
118 $this->assertEquals( 2, count( $jobs ), "Queue iterator size is correct ($desc)" );
120 $job1 = $queue->pop();
121 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
123 $queue->flushCaches();
124 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
126 $queue->flushCaches();
128 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
131 $job2 = $queue->pop();
132 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
133 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
135 $queue->flushCaches();
137 $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
140 $queue->ack( $job1 );
142 $queue->flushCaches();
144 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
147 $queue->ack( $job2 );
149 $queue->flushCaches();
150 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
152 $this->assertNull( $queue->batchPush( array( $this->newJob(), $this->newJob() ) ),
153 "Push worked ($desc)" );
154 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
157 $queue->flushCaches();
158 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
159 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
163 * @dataProvider provider_queueLists
166 public function testBasicDeduplication( $queue, $recycles, $desc ) {
167 $queue = $this->$queue;
169 $this->markTestSkipped( $desc );
172 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
174 $queue->flushCaches();
175 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
176 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
180 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
182 "Push worked ($desc)" );
184 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
186 $queue->flushCaches();
187 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
188 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
192 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
194 "Push worked ($desc)"
197 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
199 $queue->flushCaches();
200 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
201 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
203 $job1 = $queue->pop();
204 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
206 $queue->flushCaches();
207 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
209 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
212 $queue->ack( $job1 );
214 $queue->flushCaches();
215 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
219 * @dataProvider provider_queueLists
222 public function testDeduplicationWhileClaimed( $queue, $recycles, $desc ) {
223 $queue = $this->$queue;
225 $this->markTestSkipped( $desc );
228 $job = $this->newDedupedJob();
229 $queue->push( $job );
231 // De-duplication does not apply to already-claimed jobs
233 $queue->push( $job );
237 // Make sure ack() of the twin did not delete the sibling data
238 $this->assertType( 'NullJob', $j );
242 * @dataProvider provider_queueLists
245 public function testRootDeduplication( $queue, $recycles, $desc ) {
246 $queue = $this->$queue;
248 $this->markTestSkipped( $desc );
251 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
253 $queue->flushCaches();
254 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
255 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
257 $id = wfRandomString( 32 );
258 $root1 = Job
::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
259 for ( $i = 0; $i < 5; ++
$i ) {
260 $this->assertNull( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
262 $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
265 # Add a second to UNIX epoch and format back to TS_MW
266 $root2_ts = strtotime( $root2['rootJobTimestamp'] );
268 $root2['rootJobTimestamp'] = wfTimestamp( TS_MW
, $root2_ts );
270 $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
271 "Root job signatures have different timestamps." );
272 for ( $i = 0; $i < 5; ++
$i ) {
273 $this->assertNull( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
275 $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
277 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
279 $queue->flushCaches();
280 $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
281 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
286 $job = $queue->pop();
291 if ( $job instanceof DuplicateJob
) {
296 $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
297 $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
301 * @dataProvider provider_fifoQueueLists
304 public function testJobOrder( $queue, $recycles, $desc ) {
305 $queue = $this->$queue;
307 $this->markTestSkipped( $desc );
310 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
312 $queue->flushCaches();
313 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
314 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
316 for ( $i = 0; $i < 10; ++
$i ) {
317 $this->assertNull( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
320 for ( $i = 0; $i < 10; ++
$i ) {
321 $job = $queue->pop();
322 $this->assertTrue( $job instanceof Job
, "Jobs popped from queue ($desc)" );
323 $params = $job->getParams();
324 $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
328 $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
330 $queue->flushCaches();
331 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
332 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
335 public static function provider_queueLists() {
337 array( 'queueRand', false, 'Random queue without ack()' ),
338 array( 'queueRandTTL', true, 'Random queue with ack()' ),
339 array( 'queueTimestamp', false, 'Time ordered queue without ack()' ),
340 array( 'queueTimestampTTL', true, 'Time ordered queue with ack()' ),
341 array( 'queueFifo', false, 'FIFO ordered queue without ack()' ),
342 array( 'queueFifoTTL', true, 'FIFO ordered queue with ack()' )
346 public static function provider_fifoQueueLists() {
348 array( 'queueFifo', false, 'Ordered queue without ack()' ),
349 array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
353 function newJob( $i = 0, $rootJob = array() ) {
354 return new NullJob( Title
::newMainPage(),
355 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) +
$rootJob );
358 function newDedupedJob( $i = 0, $rootJob = array() ) {
359 return new NullJob( Title
::newMainPage(),
360 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) +
$rootJob );