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