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'
66 $job = $this->$q->pop();
68 $this->$q->ack( $job );
77 * @dataProvider provider_queueLists
79 function testProperties( $queue, $recycles, $desc ) {
80 $queue = $this->$queue;
82 $this->markTestSkipped( $desc );
85 $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
86 $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
90 * @dataProvider provider_queueLists
92 function testBasicOperations( $queue, $recycles, $desc ) {
93 $queue = $this->$queue;
95 $this->markTestSkipped( $desc );
98 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
100 $queue->flushCaches();
101 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
102 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
104 $this->assertTrue( $queue->push( $this->newJob() ), "Push worked ($desc)" );
105 $this->assertTrue( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
107 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
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)" );
115 $job1 = $queue->pop();
116 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
118 $queue->flushCaches();
119 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
121 $queue->flushCaches();
123 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
125 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
128 $job2 = $queue->pop();
129 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
130 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
132 $queue->flushCaches();
134 $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
136 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
139 $queue->ack( $job1 );
141 $queue->flushCaches();
143 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
145 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
148 $queue->ack( $job2 );
150 $queue->flushCaches();
151 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
155 * @dataProvider provider_queueLists
157 function testBasicDeduplication( $queue, $recycles, $desc ) {
158 $queue = $this->$queue;
160 $this->markTestSkipped( $desc );
163 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
165 $queue->flushCaches();
166 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
167 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
171 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
173 "Push worked ($desc)" );
175 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
177 $queue->flushCaches();
178 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
179 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
183 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
185 "Push worked ($desc)"
188 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
190 $queue->flushCaches();
191 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
192 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
194 $job1 = $queue->pop();
195 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
197 $queue->flushCaches();
198 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
200 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
202 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
205 $queue->ack( $job1 );
207 $queue->flushCaches();
208 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
212 * @dataProvider provider_queueLists
214 function testRootDeduplication( $queue, $recycles, $desc ) {
215 $queue = $this->$queue;
217 $this->markTestSkipped( $desc );
220 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
222 $queue->flushCaches();
223 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
224 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
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)" );
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)" );
239 $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
241 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
243 $queue->flushCaches();
244 $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
245 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
250 $job = $queue->pop();
255 if ( $job instanceof DuplicateJob
) {
260 $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
261 $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
265 * @dataProvider provider_fifoQueueLists
267 function testJobOrder( $queue, $recycles, $desc ) {
268 $queue = $this->$queue;
270 $this->markTestSkipped( $desc );
273 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
275 $queue->flushCaches();
276 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
277 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
279 for ( $i = 0; $i < 10; ++
$i ) {
280 $this->assertTrue( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
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)" );
291 $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
293 $queue->flushCaches();
294 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
295 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
298 public static function provider_queueLists() {
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()' )
309 public static function provider_fifoQueueLists() {
311 array( 'queueFifo', false, 'Ordered queue without ack()' ),
312 array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
316 function newJob( $i = 0, $rootJob = array() ) {
317 return new NullJob( Title
::newMainPage(),
318 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) +
$rootJob );
321 function newDedupedJob( $i = 0, $rootJob = array() ) {
322 return new NullJob( Title
::newMainPage(),
323 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) +
$rootJob );