Merge "IcuCollation::$tailoringFirstLetters: 'sv', 'vi' verified"
[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
12 function __construct( $name = null, array $data = array(), $dataName = '' ) {
13 parent::__construct( $name, $data, $dataName );
14
15 $this->tablesUsed[] = 'job';
16 }
17
18 protected function setUp() {
19 global $wgJobTypeConf;
20 parent::setUp();
21
22 $this->setMwGlobals( 'wgMemc', new HashBagOStuff() );
23
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 parent::tearDown();
55 foreach ( array(
56 'queueRand', 'queueRandTTL', 'queueTimestamp', 'queueTimestampTTL',
57 'queueFifo', 'queueFifoTTL'
58 ) as $q ) {
59 if ( $this->$q ) {
60 do {
61 $job = $this->$q->pop();
62 if ( $job ) {
63 $this->$q->ack( $job );
64 }
65 } while ( $job );
66 }
67 $this->$q = null;
68 }
69 }
70
71 /**
72 * @dataProvider provider_queueLists
73 */
74 function testProperties( $queue, $recycles, $desc ) {
75 $queue = $this->$queue;
76 if ( !$queue ) {
77 $this->markTestSkipped( $desc );
78 }
79
80 $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
81 $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
82 }
83
84 /**
85 * @dataProvider provider_queueLists
86 */
87 function testBasicOperations( $queue, $recycles, $desc ) {
88 $queue = $this->$queue;
89 if ( !$queue ) {
90 $this->markTestSkipped( $desc );
91 }
92
93 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
94
95 $queue->flushCaches();
96 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
97 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
98
99 $this->assertTrue( $queue->push( $this->newJob() ), "Push worked ($desc)" );
100 $this->assertTrue( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
101
102 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
103
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
108 $job1 = $queue->pop();
109 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
110
111 $queue->flushCaches();
112 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
113
114 $queue->flushCaches();
115 if ( $recycles ) {
116 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
117 } else {
118 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
119 }
120
121 $job2 = $queue->pop();
122 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
123 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
124
125 $queue->flushCaches();
126 if ( $recycles ) {
127 $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
128 } else {
129 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
130 }
131
132 $queue->ack( $job1 );
133
134 $queue->flushCaches();
135 if ( $recycles ) {
136 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
137 } else {
138 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
139 }
140
141 $queue->ack( $job2 );
142
143 $queue->flushCaches();
144 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
145 }
146
147 /**
148 * @dataProvider provider_queueLists
149 */
150 function testBasicDeduplication( $queue, $recycles, $desc ) {
151 $queue = $this->$queue;
152 if ( !$queue ) {
153 $this->markTestSkipped( $desc );
154 }
155
156
157 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
158
159 $queue->flushCaches();
160 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
161 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
162
163 $this->assertTrue( $queue->batchPush(
164 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ) ),
165 "Push worked ($desc)" );
166
167 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
168
169 $queue->flushCaches();
170 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
171 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
172
173 $this->assertTrue( $queue->batchPush(
174 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ) ),
175 "Push worked ($desc)" );
176
177 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
178
179 $queue->flushCaches();
180 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
181 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
182
183 $job1 = $queue->pop();
184 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
185
186 $queue->flushCaches();
187 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
188 if ( $recycles ) {
189 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
190 } else {
191 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
192 }
193
194 $queue->ack( $job1 );
195
196 $queue->flushCaches();
197 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
198 }
199
200 /**
201 * @dataProvider provider_queueLists
202 */
203 function testRootDeduplication( $queue, $recycles, $desc ) {
204 $queue = $this->$queue;
205 if ( !$queue ) {
206 $this->markTestSkipped( $desc );
207 }
208
209
210 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
211
212 $queue->flushCaches();
213 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
214 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
215
216 $id = wfRandomString( 32 );
217 $root1 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
218 for ( $i = 0; $i < 5; ++$i ) {
219 $this->assertTrue( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
220 }
221 $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
222 sleep( 1 ); // roo job timestamp will increase
223 $root2 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
224 $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
225 "Root job signatures have different timestamps." );
226 for ( $i = 0; $i < 5; ++$i ) {
227 $this->assertTrue( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
228 }
229 $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
230
231 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
232
233 $queue->flushCaches();
234 $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
235 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
236
237 $dupcount = 0;
238 $jobs = array();
239 do {
240 $job = $queue->pop();
241 if ( $job ) {
242 $jobs[] = $job;
243 $queue->ack( $job );
244 }
245 if ( $job instanceof DuplicateJob ) {
246 ++$dupcount;
247 }
248 } while ( $job );
249
250 $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
251 $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
252 }
253
254 /**
255 * @dataProvider provider_fifoQueueLists
256 */
257 function testJobOrder( $queue, $recycles, $desc ) {
258 $queue = $this->$queue;
259 if ( !$queue ) {
260 $this->markTestSkipped( $desc );
261 }
262
263
264 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
265
266 $queue->flushCaches();
267 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
268 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
269
270 for ( $i = 0; $i < 10; ++$i ) {
271 $this->assertTrue( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
272 }
273
274 for ( $i = 0; $i < 10; ++$i ) {
275 $job = $queue->pop();
276 $this->assertTrue( $job instanceof Job, "Jobs popped from queue ($desc)" );
277 $params = $job->getParams();
278 $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
279 $queue->ack( $job );
280 }
281
282 $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
283
284 $queue->flushCaches();
285 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
286 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
287 }
288
289 public static function provider_queueLists() {
290 return array(
291 array( 'queueRand', false, 'Random queue without ack()' ),
292 array( 'queueRandTTL', true, 'Random queue with ack()' ),
293 array( 'queueTimestamp', false, 'Time ordered queue without ack()' ),
294 array( 'queueTimestampTTL', true, 'Time ordered queue with ack()' ),
295 array( 'queueFifo', false, 'FIFO ordered queue without ack()' ),
296 array( 'queueFifoTTL', true, 'FIFO ordered queue with ack()' )
297 );
298 }
299
300 public static function provider_fifoQueueLists() {
301 return array(
302 array( 'queueFifo', false, 'Ordered queue without ack()' ),
303 array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
304 );
305 }
306
307 function newJob( $i = 0, $rootJob = array() ) {
308 return new NullJob( Title::newMainPage(),
309 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) + $rootJob );
310 }
311
312 function newDedupedJob( $i = 0, $rootJob = array() ) {
313 return new NullJob( Title::newMainPage(),
314 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) + $rootJob );
315 }
316 }