test: coverage recording now needs to be explicit
[mediawiki.git] / tests / phpunit / includes / jobqueue / JobQueueTest.php
blob699015314f1bc708cc6190bb9e37b39d7e9f570c
1 <?php
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;
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;
20 parent::setUp();
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];
30 } else {
31 $baseConfig = array( 'class' => 'JobQueueDB' );
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 ),
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 ) );
49 } catch ( MWException $e ) {
50 // unsupported?
51 // @todo What if it was another error?
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 $this->$q->delete();
67 $this->$q = null;
71 /**
72 * @dataProvider provider_queueLists
74 function testProperties( $queue, $recycles, $desc ) {
75 $queue = $this->$queue;
76 if ( !$queue ) {
77 $this->markTestSkipped( $desc );
80 $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
81 $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
84 /**
85 * @dataProvider provider_queueLists
87 function testBasicOperations( $queue, $recycles, $desc ) {
88 $queue = $this->$queue;
89 if ( !$queue ) {
90 $this->markTestSkipped( $desc );
93 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
95 $queue->flushCaches();
96 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
97 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
99 $this->assertTrue( $queue->push( $this->newJob() ), "Push worked ($desc)" );
100 $this->assertTrue( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
102 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
104 $queue->flushCaches();
105 $this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
106 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
107 $jobs = iterator_to_array( $queue->getAllQueuedJobs() );
108 $this->assertEquals( 2, count( $jobs ), "Queue iterator size is correct ($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();
117 if ( $recycles ) {
118 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
119 } else {
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();
128 if ( $recycles ) {
129 $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
130 } else {
131 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
134 $queue->ack( $job1 );
136 $queue->flushCaches();
137 if ( $recycles ) {
138 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
139 } else {
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)" );
148 $this->assertTrue( $queue->batchPush( array( $this->newJob(), $this->newJob() ) ),
149 "Push worked ($desc)" );
150 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
152 $queue->delete();
153 $queue->flushCaches();
154 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
155 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
159 * @dataProvider provider_queueLists
161 function testBasicDeduplication( $queue, $recycles, $desc ) {
162 $queue = $this->$queue;
163 if ( !$queue ) {
164 $this->markTestSkipped( $desc );
167 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
169 $queue->flushCaches();
170 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
171 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
173 $this->assertTrue(
174 $queue->batchPush(
175 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 $this->assertTrue(
186 $queue->batchPush(
187 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() )
189 "Push worked ($desc)"
192 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
194 $queue->flushCaches();
195 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
196 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
198 $job1 = $queue->pop();
199 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
201 $queue->flushCaches();
202 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
203 if ( $recycles ) {
204 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
205 } else {
206 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
209 $queue->ack( $job1 );
211 $queue->flushCaches();
212 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
216 * @dataProvider provider_queueLists
218 function testRootDeduplication( $queue, $recycles, $desc ) {
219 $queue = $this->$queue;
220 if ( !$queue ) {
221 $this->markTestSkipped( $desc );
224 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
226 $queue->flushCaches();
227 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
228 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
230 $id = wfRandomString( 32 );
231 $root1 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
232 for ( $i = 0; $i < 5; ++$i ) {
233 $this->assertTrue( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
235 $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
236 sleep( 1 ); // roo job timestamp will increase
237 $root2 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
238 $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
239 "Root job signatures have different timestamps." );
240 for ( $i = 0; $i < 5; ++$i ) {
241 $this->assertTrue( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
243 $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
245 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
247 $queue->flushCaches();
248 $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
249 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
251 $dupcount = 0;
252 $jobs = array();
253 do {
254 $job = $queue->pop();
255 if ( $job ) {
256 $jobs[] = $job;
257 $queue->ack( $job );
259 if ( $job instanceof DuplicateJob ) {
260 ++$dupcount;
262 } while ( $job );
264 $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
265 $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
269 * @dataProvider provider_fifoQueueLists
271 function testJobOrder( $queue, $recycles, $desc ) {
272 $queue = $this->$queue;
273 if ( !$queue ) {
274 $this->markTestSkipped( $desc );
277 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
279 $queue->flushCaches();
280 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
281 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
283 for ( $i = 0; $i < 10; ++$i ) {
284 $this->assertTrue( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
287 for ( $i = 0; $i < 10; ++$i ) {
288 $job = $queue->pop();
289 $this->assertTrue( $job instanceof Job, "Jobs popped from queue ($desc)" );
290 $params = $job->getParams();
291 $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
292 $queue->ack( $job );
295 $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
297 $queue->flushCaches();
298 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
299 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
302 public static function provider_queueLists() {
303 return array(
304 array( 'queueRand', false, 'Random queue without ack()' ),
305 array( 'queueRandTTL', true, 'Random queue with ack()' ),
306 array( 'queueTimestamp', false, 'Time ordered queue without ack()' ),
307 array( 'queueTimestampTTL', true, 'Time ordered queue with ack()' ),
308 array( 'queueFifo', false, 'FIFO ordered queue without ack()' ),
309 array( 'queueFifoTTL', true, 'FIFO ordered queue with ack()' )
313 public static function provider_fifoQueueLists() {
314 return array(
315 array( 'queueFifo', false, 'Ordered queue without ack()' ),
316 array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
320 function newJob( $i = 0, $rootJob = array() ) {
321 return new NullJob( Title::newMainPage(),
322 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) + $rootJob );
325 function newDedupedJob( $i = 0, $rootJob = array() ) {
326 return new NullJob( Title::newMainPage(),
327 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) + $rootJob );