Merge "Add checkDependencies.php"
[lhc/web/wiklou.git] / tests / phpunit / includes / deferred / DeferredUpdatesTest.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4
5 class DeferredUpdatesTest extends MediaWikiTestCase {
6
7 /**
8 * @covers DeferredUpdates::addUpdate
9 * @covers DeferredUpdates::push
10 * @covers DeferredUpdates::doUpdates
11 * @covers DeferredUpdates::handleUpdateQueue
12 * @covers DeferredUpdates::attemptUpdate
13 */
14 public function testAddAndRun() {
15 $update = $this->getMockBuilder( DeferrableUpdate::class )
16 ->setMethods( [ 'doUpdate' ] )->getMock();
17 $update->expects( $this->once() )->method( 'doUpdate' );
18
19 DeferredUpdates::addUpdate( $update );
20 DeferredUpdates::doUpdates();
21 }
22
23 /**
24 * @covers DeferredUpdates::addUpdate
25 * @covers DeferredUpdates::push
26 */
27 public function testAddMergeable() {
28 $this->setMwGlobals( 'wgCommandLineMode', false );
29
30 $update1 = $this->getMockBuilder( MergeableUpdate::class )
31 ->setMethods( [ 'merge', 'doUpdate' ] )->getMock();
32 $update1->expects( $this->once() )->method( 'merge' );
33 $update1->expects( $this->never() )->method( 'doUpdate' );
34
35 $update2 = $this->getMockBuilder( MergeableUpdate::class )
36 ->setMethods( [ 'merge', 'doUpdate' ] )->getMock();
37 $update2->expects( $this->never() )->method( 'merge' );
38 $update2->expects( $this->never() )->method( 'doUpdate' );
39
40 DeferredUpdates::addUpdate( $update1 );
41 DeferredUpdates::addUpdate( $update2 );
42 }
43
44 /**
45 * @covers DeferredUpdates::addCallableUpdate
46 * @covers MWCallableUpdate::getOrigin
47 */
48 public function testAddCallableUpdate() {
49 $this->setMwGlobals( 'wgCommandLineMode', true );
50
51 $ran = 0;
52 DeferredUpdates::addCallableUpdate( function () use ( &$ran ) {
53 $ran++;
54 } );
55 DeferredUpdates::doUpdates();
56
57 $this->assertSame( 1, $ran, 'Update ran' );
58 }
59
60 /**
61 * @covers DeferredUpdates::getPendingUpdates
62 * @covers DeferredUpdates::clearPendingUpdates
63 */
64 public function testGetPendingUpdates() {
65 // Prevent updates from running
66 $this->setMwGlobals( 'wgCommandLineMode', false );
67
68 $pre = DeferredUpdates::PRESEND;
69 $post = DeferredUpdates::POSTSEND;
70 $all = DeferredUpdates::ALL;
71
72 $update = $this->getMock( DeferrableUpdate::class );
73 $update->expects( $this->never() )
74 ->method( 'doUpdate' );
75
76 DeferredUpdates::addUpdate( $update, $pre );
77 $this->assertCount( 1, DeferredUpdates::getPendingUpdates( $pre ) );
78 $this->assertCount( 0, DeferredUpdates::getPendingUpdates( $post ) );
79 $this->assertCount( 1, DeferredUpdates::getPendingUpdates( $all ) );
80 $this->assertCount( 1, DeferredUpdates::getPendingUpdates() );
81 DeferredUpdates::clearPendingUpdates();
82 $this->assertCount( 0, DeferredUpdates::getPendingUpdates() );
83
84 DeferredUpdates::addUpdate( $update, $post );
85 $this->assertCount( 0, DeferredUpdates::getPendingUpdates( $pre ) );
86 $this->assertCount( 1, DeferredUpdates::getPendingUpdates( $post ) );
87 $this->assertCount( 1, DeferredUpdates::getPendingUpdates( $all ) );
88 $this->assertCount( 1, DeferredUpdates::getPendingUpdates() );
89 DeferredUpdates::clearPendingUpdates();
90 $this->assertCount( 0, DeferredUpdates::getPendingUpdates() );
91 }
92
93 /**
94 * @covers DeferredUpdates::doUpdates
95 * @covers DeferredUpdates::handleUpdateQueue
96 * @covers DeferredUpdates::addUpdate
97 */
98 public function testDoUpdatesWeb() {
99 $this->setMwGlobals( 'wgCommandLineMode', false );
100
101 $updates = [
102 '1' => "deferred update 1;\n",
103 '2' => "deferred update 2;\n",
104 '2-1' => "deferred update 1 within deferred update 2;\n",
105 '2-2' => "deferred update 2 within deferred update 2;\n",
106 '3' => "deferred update 3;\n",
107 '3-1' => "deferred update 1 within deferred update 3;\n",
108 '3-2' => "deferred update 2 within deferred update 3;\n",
109 '3-1-1' => "deferred update 1 within deferred update 1 within deferred update 3;\n",
110 '3-2-1' => "deferred update 1 within deferred update 2 with deferred update 3;\n",
111 ];
112 DeferredUpdates::addCallableUpdate(
113 function () use ( $updates ) {
114 echo $updates['1'];
115 }
116 );
117 DeferredUpdates::addCallableUpdate(
118 function () use ( $updates ) {
119 echo $updates['2'];
120 DeferredUpdates::addCallableUpdate(
121 function () use ( $updates ) {
122 echo $updates['2-1'];
123 }
124 );
125 DeferredUpdates::addCallableUpdate(
126 function () use ( $updates ) {
127 echo $updates['2-2'];
128 }
129 );
130 }
131 );
132 DeferredUpdates::addCallableUpdate(
133 function () use ( $updates ) {
134 echo $updates['3'];
135 DeferredUpdates::addCallableUpdate(
136 function () use ( $updates ) {
137 echo $updates['3-1'];
138 DeferredUpdates::addCallableUpdate(
139 function () use ( $updates ) {
140 echo $updates['3-1-1'];
141 }
142 );
143 }
144 );
145 DeferredUpdates::addCallableUpdate(
146 function () use ( $updates ) {
147 echo $updates['3-2'];
148 DeferredUpdates::addCallableUpdate(
149 function () use ( $updates ) {
150 echo $updates['3-2-1'];
151 }
152 );
153 }
154 );
155 }
156 );
157
158 $this->assertEquals( 3, DeferredUpdates::pendingUpdatesCount() );
159
160 $this->expectOutputString( implode( '', $updates ) );
161
162 DeferredUpdates::doUpdates();
163
164 $x = null;
165 $y = null;
166 DeferredUpdates::addCallableUpdate(
167 function () use ( &$x ) {
168 $x = 'Sherity';
169 },
170 DeferredUpdates::PRESEND
171 );
172 DeferredUpdates::addCallableUpdate(
173 function () use ( &$y ) {
174 $y = 'Marychu';
175 },
176 DeferredUpdates::POSTSEND
177 );
178
179 $this->assertNull( $x, "Update not run yet" );
180 $this->assertNull( $y, "Update not run yet" );
181
182 DeferredUpdates::doUpdates( 'run', DeferredUpdates::PRESEND );
183 $this->assertEquals( "Sherity", $x, "PRESEND update ran" );
184 $this->assertNull( $y, "POSTSEND update not run yet" );
185
186 DeferredUpdates::doUpdates( 'run', DeferredUpdates::POSTSEND );
187 $this->assertEquals( "Marychu", $y, "POSTSEND update ran" );
188 }
189
190 /**
191 * @covers DeferredUpdates::doUpdates
192 * @covers DeferredUpdates::handleUpdateQueue
193 * @covers DeferredUpdates::addUpdate
194 */
195 public function testDoUpdatesCLI() {
196 $this->setMwGlobals( 'wgCommandLineMode', true );
197 $updates = [
198 '1' => "deferred update 1;\n",
199 '2' => "deferred update 2;\n",
200 '2-1' => "deferred update 1 within deferred update 2;\n",
201 '2-2' => "deferred update 2 within deferred update 2;\n",
202 '3' => "deferred update 3;\n",
203 '3-1' => "deferred update 1 within deferred update 3;\n",
204 '3-2' => "deferred update 2 within deferred update 3;\n",
205 '3-1-1' => "deferred update 1 within deferred update 1 within deferred update 3;\n",
206 '3-2-1' => "deferred update 1 within deferred update 2 with deferred update 3;\n",
207 ];
208
209 // clear anything
210 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
211 $lbFactory->commitMasterChanges( __METHOD__ );
212
213 DeferredUpdates::addCallableUpdate(
214 function () use ( $updates ) {
215 echo $updates['1'];
216 }
217 );
218 DeferredUpdates::addCallableUpdate(
219 function () use ( $updates ) {
220 echo $updates['2'];
221 DeferredUpdates::addCallableUpdate(
222 function () use ( $updates ) {
223 echo $updates['2-1'];
224 }
225 );
226 DeferredUpdates::addCallableUpdate(
227 function () use ( $updates ) {
228 echo $updates['2-2'];
229 }
230 );
231 }
232 );
233 DeferredUpdates::addCallableUpdate(
234 function () use ( $updates ) {
235 echo $updates['3'];
236 DeferredUpdates::addCallableUpdate(
237 function () use ( $updates ) {
238 echo $updates['3-1'];
239 DeferredUpdates::addCallableUpdate(
240 function () use ( $updates ) {
241 echo $updates['3-1-1'];
242 }
243 );
244 }
245 );
246 DeferredUpdates::addCallableUpdate(
247 function () use ( $updates ) {
248 echo $updates['3-2'];
249 DeferredUpdates::addCallableUpdate(
250 function () use ( $updates ) {
251 echo $updates['3-2-1'];
252 }
253 );
254 }
255 );
256 }
257 );
258
259 $this->expectOutputString( implode( '', $updates ) );
260
261 DeferredUpdates::doUpdates();
262 }
263
264 /**
265 * @covers DeferredUpdates::doUpdates
266 * @covers DeferredUpdates::handleUpdateQueue
267 * @covers DeferredUpdates::addUpdate
268 */
269 public function testPresendAddOnPostsendRun() {
270 $this->setMwGlobals( 'wgCommandLineMode', true );
271
272 $x = false;
273 $y = false;
274 // clear anything
275 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
276 $lbFactory->commitMasterChanges( __METHOD__ );
277
278 DeferredUpdates::addCallableUpdate(
279 function () use ( &$x, &$y ) {
280 $x = true;
281 DeferredUpdates::addCallableUpdate(
282 function () use ( &$y ) {
283 $y = true;
284 },
285 DeferredUpdates::PRESEND
286 );
287 },
288 DeferredUpdates::POSTSEND
289 );
290
291 DeferredUpdates::doUpdates();
292
293 $this->assertTrue( $x, "Outer POSTSEND update ran" );
294 $this->assertTrue( $y, "Nested PRESEND update ran" );
295 }
296
297 /**
298 * @covers DeferredUpdates::attemptUpdate
299 */
300 public function testRunUpdateTransactionScope() {
301 $this->setMwGlobals( 'wgCommandLineMode', false );
302
303 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
304 $this->assertFalse( $lbFactory->hasTransactionRound(), 'Initial state' );
305
306 $ran = 0;
307 DeferredUpdates::addCallableUpdate( function () use ( &$ran, $lbFactory ) {
308 $ran++;
309 $this->assertTrue( $lbFactory->hasTransactionRound(), 'Has transaction' );
310 } );
311 DeferredUpdates::doUpdates();
312
313 $this->assertSame( 1, $ran, 'Update ran' );
314 $this->assertFalse( $lbFactory->hasTransactionRound(), 'Final state' );
315 }
316
317 /**
318 * @covers DeferredUpdates::attemptUpdate
319 * @covers TransactionRoundDefiningUpdate::getOrigin
320 */
321 public function testRunOuterScopeUpdate() {
322 $this->setMwGlobals( 'wgCommandLineMode', false );
323
324 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
325 $this->assertFalse( $lbFactory->hasTransactionRound(), 'Initial state' );
326
327 $ran = 0;
328 DeferredUpdates::addUpdate( new TransactionRoundDefiningUpdate(
329 function () use ( &$ran, $lbFactory ) {
330 $ran++;
331 $this->assertFalse( $lbFactory->hasTransactionRound(), 'No transaction' );
332 } )
333 );
334 DeferredUpdates::doUpdates();
335
336 $this->assertSame( 1, $ran, 'Update ran' );
337 }
338
339 /**
340 * @covers DeferredUpdates::tryOpportunisticExecute
341 */
342 public function testTryOpportunisticExecute() {
343 $calls = [];
344 $callback1 = function () use ( &$calls ) {
345 $calls[] = 1;
346 };
347 $callback2 = function () use ( &$calls ) {
348 $calls[] = 2;
349 };
350
351 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
352 $lbFactory->beginMasterChanges( __METHOD__ );
353
354 DeferredUpdates::addCallableUpdate( $callback1 );
355 $this->assertEquals( [], $calls );
356
357 DeferredUpdates::tryOpportunisticExecute( 'run' );
358 $this->assertEquals( [], $calls );
359
360 $dbw = wfGetDB( DB_MASTER );
361 $dbw->onTransactionCommitOrIdle( function () use ( &$calls, $callback2 ) {
362 DeferredUpdates::addCallableUpdate( $callback2 );
363 $this->assertEquals( [], $calls );
364 $calls[] = 'oti';
365 } );
366 $this->assertEquals( 1, $dbw->trxLevel() );
367 $this->assertEquals( [], $calls );
368
369 $lbFactory->commitMasterChanges( __METHOD__ );
370
371 $this->assertEquals( [ 'oti' ], $calls );
372
373 DeferredUpdates::tryOpportunisticExecute( 'run' );
374 $this->assertEquals( [ 'oti', 1, 2 ], $calls );
375 }
376 }