Merge "properly stop output buffering"
[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 $this->queueRand = JobQueue::factory(
36 array( 'order' => 'random', 'claimTTL' => 0 ) + $baseConfig );
37 $this->queueRandTTL = JobQueue::factory(
38 array( 'order' => 'random', 'claimTTL' => 10 ) + $baseConfig );
39 $this->queueFifo = JobQueue::factory(
40 array( 'order' => 'fifo', 'claimTTL' => 0 ) + $baseConfig );
41 $this->queueFifoTTL = JobQueue::factory(
42 array( 'order' => 'fifo', 'claimTTL' => 10 ) + $baseConfig );
43 if ( $baseConfig['class'] !== 'JobQueueDB' ) { // DB namespace with prefix or temp tables
44 foreach ( array( 'queueRand', 'queueRandTTL', 'queueFifo', 'queueFifoTTL' ) as $q ) {
45 $this->$q->setTestingPrefix( 'unittests-' . wfRandomString( 32 ) );
46 }
47 }
48 }
49
50 protected function tearDown() {
51 global $wgMemc;
52 parent::tearDown();
53 foreach ( array( 'queueRand', 'queueRandTTL', 'queueFifo', 'queueFifoTTL' ) as $q ) {
54 do {
55 $job = $this->$q->pop();
56 if ( $job ) {
57 $this->$q->ack( $job );
58 }
59 } while ( $job );
60 }
61 $this->queueRand = null;
62 $this->queueRandTTL = null;
63 $this->queueFifo = null;
64 $this->queueFifoTTL = null;
65 $wgMemc = $this->old['wgMemc'];
66 }
67
68 /**
69 * @dataProvider provider_queueLists
70 */
71 function testProperties( $queue, $order, $recycles, $desc ) {
72 $queue = $this->$queue;
73
74 $this->assertEquals( wfWikiID(), $queue->getWiki(), "Proper wiki ID ($desc)" );
75 $this->assertEquals( 'null', $queue->getType(), "Proper job type ($desc)" );
76 }
77
78 /**
79 * @dataProvider provider_queueLists
80 */
81 function testBasicOperations( $queue, $order, $recycles, $desc ) {
82 $queue = $this->$queue;
83 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
84
85 $queue->flushCaches();
86 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
87 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
88
89 $this->assertTrue( $queue->push( $this->newJob() ), "Push worked ($desc)" );
90 $this->assertTrue( $queue->batchPush( array( $this->newJob() ) ), "Push worked ($desc)" );
91
92 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
93
94 $queue->flushCaches();
95 $this->assertEquals( 2, $queue->getSize(), "Queue size is correct ($desc)" );
96 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
97
98 $job1 = $queue->pop();
99 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
100
101 $queue->flushCaches();
102 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
103
104 $queue->flushCaches();
105 if ( $recycles ) {
106 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
107 } else {
108 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
109 }
110
111 $job2 = $queue->pop();
112 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
113 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
114
115 $queue->flushCaches();
116 if ( $recycles ) {
117 $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
118 } else {
119 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
120 }
121
122 $queue->ack( $job1 );
123
124 $queue->flushCaches();
125 if ( $recycles ) {
126 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
127 } else {
128 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
129 }
130
131 $queue->ack( $job2 );
132
133 $queue->flushCaches();
134 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
135 }
136
137 /**
138 * @dataProvider provider_queueLists
139 */
140 function testBasicDeduplication( $queue, $order, $recycles, $desc ) {
141 $queue = $this->$queue;
142
143 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
144
145 $queue->flushCaches();
146 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
147 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
148
149 $this->assertTrue( $queue->batchPush(
150 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ) ),
151 "Push worked ($desc)" );
152
153 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
154
155 $queue->flushCaches();
156 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
157 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
158
159 $this->assertTrue( $queue->batchPush(
160 array( $this->newDedupedJob(), $this->newDedupedJob(), $this->newDedupedJob() ) ),
161 "Push worked ($desc)" );
162
163 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
164
165 $queue->flushCaches();
166 $this->assertEquals( 1, $queue->getSize(), "Queue size is correct ($desc)" );
167 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
168
169 $job1 = $queue->pop();
170 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
171
172 $queue->flushCaches();
173 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
174 if ( $recycles ) {
175 $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
176 } else {
177 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
178 }
179
180 $queue->ack( $job1 );
181
182 $queue->flushCaches();
183 $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
184 }
185
186 /**
187 * @dataProvider provider_queueLists
188 */
189 function testRootDeduplication( $queue, $order, $recycles, $desc ) {
190 $queue = $this->$queue;
191
192 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
193
194 $queue->flushCaches();
195 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
196 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
197
198 $id = wfRandomString( 32 );
199 $root1 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
200 for ( $i = 0; $i < 5; ++$i ) {
201 $this->assertTrue( $queue->push( $this->newJob( 0, $root1 ) ), "Push worked ($desc)" );
202 }
203 $queue->deduplicateRootJob( $this->newJob( 0, $root1 ) );
204 sleep( 1 ); // roo job timestamp will increase
205 $root2 = Job::newRootJobParams( "nulljobspam:$id" ); // task ID/timestamp
206 $this->assertNotEquals( $root1['rootJobTimestamp'], $root2['rootJobTimestamp'],
207 "Root job signatures have different timestamps." );
208 for ( $i = 0; $i < 5; ++$i ) {
209 $this->assertTrue( $queue->push( $this->newJob( 0, $root2 ) ), "Push worked ($desc)" );
210 }
211 $queue->deduplicateRootJob( $this->newJob( 0, $root2 ) );
212
213 $this->assertFalse( $queue->isEmpty(), "Queue is not empty ($desc)" );
214
215 $queue->flushCaches();
216 $this->assertEquals( 10, $queue->getSize(), "Queue size is correct ($desc)" );
217 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
218
219 $dupcount = 0;
220 $jobs = array();
221 do {
222 $job = $queue->pop();
223 if ( $job ) {
224 $jobs[] = $job;
225 $queue->ack( $job );
226 }
227 if ( $job instanceof DuplicateJob ) {
228 ++$dupcount;
229 }
230 } while ( $job );
231
232 $this->assertEquals( 10, count( $jobs ), "Correct number of jobs popped ($desc)" );
233 $this->assertEquals( 5, $dupcount, "Correct number of duplicate jobs popped ($desc)" );
234 }
235
236 /**
237 * @dataProvider provider_fifoQueueLists
238 */
239 function testJobOrder( $queue, $recycles, $desc ) {
240 $queue = $this->$queue;
241
242 $this->assertTrue( $queue->isEmpty(), "Queue is empty ($desc)" );
243
244 $queue->flushCaches();
245 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
246 $this->assertEquals( 0, $queue->getAcquiredCount(), "Queue is empty ($desc)" );
247
248 for ( $i = 0; $i < 10; ++$i ) {
249 $this->assertTrue( $queue->push( $this->newJob( $i ) ), "Push worked ($desc)" );
250 }
251
252 for ( $i = 0; $i < 10; ++$i ) {
253 $job = $queue->pop();
254 $this->assertTrue( $job instanceof Job, "Jobs popped from queue ($desc)" );
255 $params = $job->getParams();
256 $this->assertEquals( $i, $params['i'], "Job popped from queue is FIFO ($desc)" );
257 $queue->ack( $job );
258 }
259
260 $this->assertFalse( $queue->pop(), "Queue is not empty ($desc)" );
261
262 $queue->flushCaches();
263 $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
264 $this->assertEquals( 0, $queue->getAcquiredCount(), "No jobs active ($desc)" );
265 }
266
267 function provider_queueLists() {
268 return array(
269 array( 'queueRand', 'rand', false, 'Random queue without ack()' ),
270 array( 'queueRandTTL', 'rand', true, 'Random queue with ack()' ),
271 array( 'queueFifo', 'fifo', false, 'Ordered queue without ack()' ),
272 array( 'queueFifoTTL', 'fifo', true, 'Ordered queue with ack()' )
273 );
274 }
275
276 function provider_fifoQueueLists() {
277 return array(
278 array( 'queueFifo', false, 'Ordered queue without ack()' ),
279 array( 'queueFifoTTL', true, 'Ordered queue with ack()' )
280 );
281 }
282
283 function newJob( $i = 0, $rootJob = array() ) {
284 return new NullJob( Title::newMainPage(),
285 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 0, 'i' => $i ) + $rootJob );
286 }
287
288 function newDedupedJob( $i = 0, $rootJob = array() ) {
289 return new NullJob( Title::newMainPage(),
290 array( 'lives' => 0, 'usleep' => 0, 'removeDuplicates' => 1, 'i' => $i ) + $rootJob );
291 }
292 }