Tests: Make phpunit providers "public static".
[lhc/web/wiklou.git] / tests / phpunit / includes / jobqueue / JobQueueTest.php
1 <?php
2
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;
11 protected $old = array();
12
13 function __construct( $name = null, array $data = array(), $dataName = '' ) {
14 parent::__construct( $name, $data, $dataName );
15
16 $this->tablesUsed[] = 'job';
17 }
18
19 protected function setUp() {
20 global $wgMemc, $wgJobTypeConf;
21 parent::setUp();
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'." );
28 }
29 $baseConfig = $wgJobTypeConf[$name];
30 } else {
31 $baseConfig = array( 'class' => 'JobQueueDB' );
32 }
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 ),
42 );
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 ) );
48 }
49 } catch ( MWException $e ) {}; // unsupported? (@TODO: what if it was another error?)
50 }
51 }
52
53 protected function tearDown() {
54 global $wgMemc;
55 parent::tearDown();
56 foreach ( array(
57 'queueRand', 'queueRandTTL', 'queueTimestamp', 'queueTimestampTTL',
58 'queueFifo', 'queueFifoTTL'
59 ) as $q ) {
60 if ( $this->$q ) {
61 do {
62 $job = $this->$q->pop();
63 if ( $job ) {
64 $this->$q->ack( $job );
65 }
66 } while ( $job );
67 }
68 $this->$q = null;
69 }
70 $wgMemc = $this->old['wgMemc'];
71 }
72
73 /**
74 * @dataProvider provider_queueLists
75 */
76 function testProperties( $queue, $recycles, $desc ) {
77 $queue = $this->$queue;
78 if ( !$queue ) {
79 $this->markTestSkipped( $desc );
80 }
81
82 $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
83 $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
84 }
85
86 /**
87 * @dataProvider provider_queueLists
88 */
89 function testBasicOperations( $queue, $recycles, $desc ) {
90 $queue = $this->$queue;
91 if ( !$queue ) {
92 $this->markTestSkipped( $desc );
93 }
94
95 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
96
97 $queue->flushCaches();
98 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
99 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
100
101 $this->assertTrue( $queue->push( $this->newJob() ), "Push worked ($desc)" );
102 $this->assertTrue( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
103
104 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
105
106 $queue->flushCaches();
107 $this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
108 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
109
110 $job1 = $queue->pop();
111 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
112
113 $queue->flushCaches();
114 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
115
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)" );
121 }
122
123 $job2 = $queue->pop();
124 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
125 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
126
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)" );
132 }
133
134 $queue->ack( $job1 );
135
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)" );
141 }
142
143 $queue->ack( $job2 );
144
145 $queue->flushCaches();
146 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
147 }
148
149 /**
150 * @dataProvider provider_queueLists
151 */
152 function testBasicDeduplication( $queue, $recycles, $desc ) {
153 $queue = $this->$queue;
154 if ( !$queue ) {
155 $this->markTestSkipped( $desc );
156 }
157
158
159 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
160
161 $queue->flushCaches();
162 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
163 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
164
165 $this->assertTrue( $queue->batchPush(
166 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ) ),
167 "Push worked ($desc)" );
168
169 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
170
171 $queue->flushCaches();
172 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
173 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
174
175 $this->assertTrue( $queue->batchPush(
176 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ) ),
177 "Push worked ($desc)" );
178
179 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
180
181 $queue->flushCaches();
182 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
183 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
184
185 $job1 = $queue->pop();
186 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
187
188 $queue->flushCaches();
189 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
190 if ( $recycles ) {
191 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
192 } else {
193 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
194 }
195
196 $queue->ack( $job1 );
197
198 $queue->flushCaches();
199 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
200 }
201
202 /**
203 * @dataProvider provider_queueLists
204 */
205 function testRootDeduplication( $queue, $recycles, $desc ) {
206 $queue = $this->$queue;
207 if ( !$queue ) {
208 $this->markTestSkipped( $desc );
209 }
210
211
212 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
213
214 $queue->flushCaches();
215 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
216 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
217
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)" );
222 }
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)" );
230 }
231 $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
232
233 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
234
235 $queue->flushCaches();
236 $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
237 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
238
239 $dupcount = 0;
240 $jobs = array();
241 do {
242 $job = $queue->pop();
243 if ( $job ) {
244 $jobs[] = $job;
245 $queue->ack( $job );
246 }
247 if ( $job instanceof DuplicateJob ) {
248 ++$dupcount;
249 }
250 } while ( $job );
251
252 $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
253 $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
254 }
255
256 /**
257 * @dataProvider provider_fifoQueueLists
258 */
259 function testJobOrder( $queue, $recycles, $desc ) {
260 $queue = $this->$queue;
261 if ( !$queue ) {
262 $this->markTestSkipped( $desc );
263 }
264
265
266 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
267
268 $queue->flushCaches();
269 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
270 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
271
272 for ( $i = 0; $i < 10; ++$i ) {
273 $this->assertTrue( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
274 }
275
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)" );
281 $queue->ack( $job );
282 }
283
284 $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
285
286 $queue->flushCaches();
287 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
288 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
289 }
290
291 public static function provider_queueLists() {
292 return array(
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()' )
299 );
300 }
301
302 public static function provider_fifoQueueLists() {
303 return array(
304 array( 'queueFifo', false, 'Ordered queue without ack()' ),
305 array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
306 );
307 }
308
309 function newJob( $i = 0, $rootJob = array() ) {
310 return new NullJob( Title::newMainPage(),
311 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) + $rootJob );
312 }
313
314 function newDedupedJob( $i = 0, $rootJob = array() ) {
315 return new NullJob( Title::newMainPage(),
316 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) + $rootJob );
317 }
318 }