8 class JobQueueTest
extends MediaWikiTestCase
{
10 protected $queueRand, $queueRandTTL, $queueFifo, $queueFifoTTL;
11 protected $old = array();
13 function __construct( $name = null, array $data = array(), $dataName = '' ) {
14 parent
::__construct( $name, $data, $dataName );
16 $this->tablesUsed
[] = 'job';
19 protected function setUp() {
20 global $wgMemc, $wgJobTypeConf;
22 $this->old
['wgMemc'] = $wgMemc;
23 $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 ) {}; // unsupported? (@TODO: what if it was another error?)
53 protected function tearDown() {
57 'queueRand', 'queueRandTTL', 'queueTimestamp', 'queueTimestampTTL',
58 'queueFifo', 'queueFifoTTL'
62 $job = $this->$q->pop();
64 $this->$q->ack( $job );
70 $wgMemc = $this->old
['wgMemc'];
74 * @dataProvider provider_queueLists
76 function testProperties( $queue, $recycles, $desc ) {
77 $queue = $this->$queue;
79 $this->markTestSkipped( $desc );
82 $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
83 $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
87 * @dataProvider provider_queueLists
89 function testBasicOperations( $queue, $recycles, $desc ) {
90 $queue = $this->$queue;
92 $this->markTestSkipped( $desc );
95 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
97 $queue->flushCaches();
98 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
99 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
101 $this->assertTrue( $queue->push( $this->newJob() ), "Push worked ($desc)" );
102 $this->assertTrue( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
104 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
106 $queue->flushCaches();
107 $this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
108 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
110 $job1 = $queue->pop();
111 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
113 $queue->flushCaches();
114 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
116 $queue->flushCaches();
118 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
120 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
123 $job2 = $queue->pop();
124 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
125 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
127 $queue->flushCaches();
129 $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
131 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
134 $queue->ack( $job1 );
136 $queue->flushCaches();
138 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
140 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
143 $queue->ack( $job2 );
145 $queue->flushCaches();
146 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
150 * @dataProvider provider_queueLists
152 function testBasicDeduplication( $queue, $recycles, $desc ) {
153 $queue = $this->$queue;
155 $this->markTestSkipped( $desc );
159 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
161 $queue->flushCaches();
162 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
163 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
165 $this->assertTrue( $queue->batchPush(
166 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ) ),
167 "Push worked ($desc)" );
169 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
171 $queue->flushCaches();
172 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
173 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
175 $this->assertTrue( $queue->batchPush(
176 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ) ),
177 "Push worked ($desc)" );
179 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
181 $queue->flushCaches();
182 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
183 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
185 $job1 = $queue->pop();
186 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
188 $queue->flushCaches();
189 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
191 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
193 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
196 $queue->ack( $job1 );
198 $queue->flushCaches();
199 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
203 * @dataProvider provider_queueLists
205 function testRootDeduplication( $queue, $recycles, $desc ) {
206 $queue = $this->$queue;
208 $this->markTestSkipped( $desc );
212 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
214 $queue->flushCaches();
215 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
216 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
218 $id = wfRandomString( 32 );
219 $root1 = Job
::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
220 for ( $i = 0; $i < 5; ++
$i ) {
221 $this->assertTrue( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
223 $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
224 sleep( 1 ); // roo job timestamp will increase
225 $root2 = Job
::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
226 $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
227 "Root job signatures have different timestamps." );
228 for ( $i = 0; $i < 5; ++
$i ) {
229 $this->assertTrue( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
231 $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
233 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
235 $queue->flushCaches();
236 $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
237 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
242 $job = $queue->pop();
247 if ( $job instanceof DuplicateJob
) {
252 $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
253 $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
257 * @dataProvider provider_fifoQueueLists
259 function testJobOrder( $queue, $recycles, $desc ) {
260 $queue = $this->$queue;
262 $this->markTestSkipped( $desc );
266 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
268 $queue->flushCaches();
269 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
270 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
272 for ( $i = 0; $i < 10; ++
$i ) {
273 $this->assertTrue( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
276 for ( $i = 0; $i < 10; ++
$i ) {
277 $job = $queue->pop();
278 $this->assertTrue( $job instanceof Job
, "Jobs popped from queue ($desc)" );
279 $params = $job->getParams();
280 $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
284 $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
286 $queue->flushCaches();
287 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
288 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
291 public static function provider_queueLists() {
293 array( 'queueRand', false, 'Random queue without ack()' ),
294 array( 'queueRandTTL', true, 'Random queue with ack()' ),
295 array( 'queueTimestamp', false, 'Time ordered queue without ack()' ),
296 array( 'queueTimestampTTL', true, 'Time ordered queue with ack()' ),
297 array( 'queueFifo', false, 'FIFO ordered queue without ack()' ),
298 array( 'queueFifoTTL', true, 'FIFO ordered queue with ack()' )
302 public static function provider_fifoQueueLists() {
304 array( 'queueFifo', false, 'Ordered queue without ack()' ),
305 array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
309 function newJob( $i = 0, $rootJob = array() ) {
310 return new NullJob( Title
::newMainPage(),
311 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) +
$rootJob );
314 function newDedupedJob( $i = 0, $rootJob = array() ) {
315 return new NullJob( Title
::newMainPage(),
316 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) +
$rootJob );