Localisation updates from https://translatewiki.net.
[mediawiki.git] / tests / phpunit / maintenance / includes / MaintenanceTest.php
blob1cbea649172e25113571b636534a35ea6773ce23
1 <?php
3 namespace MediaWiki\Tests\Maintenance\Includes;
5 use AtomicSectionUpdate;
6 use MediaWiki\Config\Config;
7 use MediaWiki\Config\HashConfig;
8 use MediaWiki\Deferred\DeferredUpdates;
9 use MediaWiki\Maintenance\Maintenance;
10 use MediaWiki\MediaWikiServices;
11 use MediaWiki\Tests\Maintenance\MaintenanceBaseTestCase;
12 use PHPUnit\Framework\Assert;
13 use Wikimedia\TestingAccessWrapper;
15 /**
16 * @covers \MediaWiki\Maintenance\MaintenanceFatalError
17 * @covers \MediaWiki\Maintenance\Maintenance
18 * @group Database
20 class MaintenanceTest extends MaintenanceBaseTestCase {
22 /**
23 * @inheritDoc
25 protected function getMaintenanceClass() {
26 return Maintenance::class;
29 /**
30 * @see MaintenanceBaseTestCase::createMaintenance
32 * Note to extension authors looking for a model to follow: This function
33 * is normally not needed in a maintenance test, it's only overridden here
34 * because Maintenance is abstract.
35 * @inheritDoc
37 protected function createMaintenance() {
38 $className = $this->getMaintenanceClass();
39 $obj = $this->getMockForAbstractClass( $className );
41 return TestingAccessWrapper::newFromObject( $obj );
44 // Although the following tests do not seem to be too consistent (compare for
45 // example the newlines within the test.*StringString tests, or the
46 // test.*Intermittent.* tests), the objective of these tests is not to describe
47 // consistent behavior, but rather currently existing behavior.
49 /**
50 * @dataProvider provideOutputData
52 public function testOutput( $outputs, $expected, $extraNL ) {
53 foreach ( $outputs as $data ) {
54 if ( is_array( $data ) ) {
55 [ $msg, $channel ] = $data;
56 } else {
57 $msg = $data;
58 $channel = null;
60 $this->maintenance->output( $msg, $channel );
62 $this->assertOutputPrePostShutdown( $expected, $extraNL );
65 public static function provideOutputData() {
66 return [
67 [ [ "" ], "", false ],
68 [ [ "foo" ], "foo", false ],
69 [ [ "foo", "bar" ], "foobar", false ],
70 [ [ "foo\n" ], "foo\n", false ],
71 [ [ "foo\n\n" ], "foo\n\n", false ],
72 [ [ "foo\nbar" ], "foo\nbar", false ],
73 [ [ "foo\nbar\n" ], "foo\nbar\n", false ],
74 [ [ "foo\n", "bar\n" ], "foo\nbar\n", false ],
75 [ [ "", "foo", "", "\n", "ba", "", "r\n" ], "foo\nbar\n", false ],
76 [ [ "", "foo", "", "\nb", "a", "", "r\n" ], "foo\nbar\n", false ],
77 [ [ [ "foo", "bazChannel" ] ], "foo", true ],
78 [ [ [ "foo\n", "bazChannel" ] ], "foo", true ],
80 // If this test fails, note that output takes strings with double line
81 // endings (although output's implementation in this situation calls
82 // outputChanneled with a string ending in a nl ... which is not allowed
83 // according to the documentation of outputChanneled)
84 [ [ [ "foo\n\n", "bazChannel" ] ], "foo\n", true ],
85 [ [ [ "foo\nbar", "bazChannel" ] ], "foo\nbar", true ],
86 [ [ [ "foo\nbar\n", "bazChannel" ] ], "foo\nbar", true ],
89 [ "foo\n", "bazChannel" ],
90 [ "bar\n", "bazChannel" ],
92 "foobar",
93 true
97 [ "", "bazChannel" ],
98 [ "foo", "bazChannel" ],
99 [ "", "bazChannel" ],
100 [ "\n", "bazChannel" ],
101 [ "ba", "bazChannel" ],
102 [ "", "bazChannel" ],
103 [ "r\n", "bazChannel" ],
105 "foobar",
106 true
110 [ "", "bazChannel" ],
111 [ "foo", "bazChannel" ],
112 [ "", "bazChannel" ],
113 [ "\nb", "bazChannel" ],
114 [ "a", "bazChannel" ],
115 [ "", "bazChannel" ],
116 [ "r\n", "bazChannel" ],
118 "foo\nbar",
119 true
123 [ "foo", "bazChannel" ],
124 [ "bar", "bazChannel" ],
125 [ "qux", "quuxChannel" ],
126 [ "corge", "bazChannel" ],
128 "foobar\nqux\ncorge",
129 true
133 [ "foo", "bazChannel" ],
134 [ "bar\n", "bazChannel" ],
135 [ "qux\n", "quuxChannel" ],
136 [ "corge", "bazChannel" ],
138 "foobar\nqux\ncorge",
139 true
143 [ "foo", null ],
144 [ "bar", "bazChannel" ],
145 [ "qux", null ],
146 [ "quux", "bazChannel" ],
148 "foobar\nquxquux",
149 true
153 [ "foo", "bazChannel" ],
154 [ "bar", null ],
155 [ "qux", "bazChannel" ],
156 [ "quux", null ],
158 "foo\nbarqux\nquux",
159 false
163 [ "foo", 1 ],
164 [ "bar", 1.0 ],
166 "foo\nbar",
167 true
169 [ [ "foo", "", "bar" ], "foobar", false ],
170 [ [ "foo", false, "bar" ], "foobar", false ],
173 [ "qux", "quuxChannel" ],
174 "foo",
175 false,
176 "bar"
178 "qux\nfoobar",
179 false
183 [ "foo", "bazChannel" ],
184 [ "", "bazChannel" ],
185 [ "bar", "bazChannel" ],
187 "foobar",
188 true
192 [ "foo", "bazChannel" ],
193 [ false, "bazChannel" ],
194 [ "bar", "bazChannel" ],
196 "foobar",
197 true
203 * @dataProvider provideOutputChanneledData
205 public function testOutputChanneled( $outputs, $expected, $extraNL ) {
206 foreach ( $outputs as $data ) {
207 if ( is_array( $data ) ) {
208 [ $msg, $channel ] = $data;
209 } else {
210 $msg = $data;
211 $channel = null;
213 $this->maintenance->outputChanneled( $msg, $channel );
215 $this->assertOutputPrePostShutdown( $expected, $extraNL );
218 public static function provideOutputChanneledData() {
219 return [
220 [ [ "" ], "\n", false ],
221 [ [ "foo" ], "foo\n", false ],
222 [ [ "foo", "bar" ], "foo\nbar\n", false ],
223 [ [ "foo\nbar" ], "foo\nbar\n", false ],
224 [ [ "", "foo", "", "\nb", "a", "", "r" ], "\nfoo\n\n\nb\na\n\nr\n", false ],
225 [ [ [ "foo", "bazChannel" ] ], "foo", true ],
228 [ "foo\nbar", "bazChannel" ]
230 "foo\nbar",
231 true
235 [ "foo", "bazChannel" ],
236 [ "bar", "bazChannel" ],
238 "foobar",
239 true
243 [ "", "bazChannel" ],
244 [ "foo", "bazChannel" ],
245 [ "", "bazChannel" ],
246 [ "\nb", "bazChannel" ],
247 [ "a", "bazChannel" ],
248 [ "", "bazChannel" ],
249 [ "r", "bazChannel" ],
251 "foo\nbar",
252 true
256 [ "foo", "bazChannel" ],
257 [ "bar", "bazChannel" ],
258 [ "qux", "quuxChannel" ],
259 [ "corge", "bazChannel" ],
261 "foobar\nqux\ncorge",
262 true
266 [ "foo", "bazChannel" ],
267 [ "bar", "bazChannel" ],
268 [ "qux", "quuxChannel" ],
269 [ "corge", "bazChannel" ],
271 "foobar\nqux\ncorge",
272 true
276 [ "foo", "bazChannel" ],
277 [ "bar", null ],
278 [ "qux", null ],
279 [ "corge", "bazChannel" ],
281 "foo\nbar\nqux\ncorge",
282 true
286 [ "foo", null ],
287 [ "bar", "bazChannel" ],
288 [ "qux", null ],
289 [ "quux", "bazChannel" ],
291 "foo\nbar\nqux\nquux",
292 true
296 [ "foo", "bazChannel" ],
297 [ "bar", null ],
298 [ "qux", "bazChannel" ],
299 [ "quux", null ],
301 "foo\nbar\nqux\nquux\n",
302 false
306 [ "foo", 1 ],
307 [ "bar", 1.0 ],
309 "foo\nbar",
310 true
312 [ [ "foo", "", "bar" ], "foo\n\nbar\n", false ],
313 [ [ "foo", false, "bar" ], "foo\nbar\n", false ],
317 public function testCleanupChanneledClean() {
318 $this->maintenance->cleanupChanneled();
319 $this->assertOutputPrePostShutdown( "", false );
322 public function testCleanupChanneledAfterOutput() {
323 $this->maintenance->output( "foo" );
324 $this->maintenance->cleanupChanneled();
325 $this->assertOutputPrePostShutdown( "foo", false );
328 public function testCleanupChanneledAfterOutputWNullChannel() {
329 $this->maintenance->output( "foo", null );
330 $this->maintenance->cleanupChanneled();
331 $this->assertOutputPrePostShutdown( "foo", false );
334 public function testCleanupChanneledAfterOutputWChannel() {
335 $this->maintenance->output( "foo", "bazChannel" );
336 $this->maintenance->cleanupChanneled();
337 $this->assertOutputPrePostShutdown( "foo\n", false );
340 public function testCleanupChanneledAfterNLOutput() {
341 $this->maintenance->output( "foo\n" );
342 $this->maintenance->cleanupChanneled();
343 $this->assertOutputPrePostShutdown( "foo\n", false );
346 public function testCleanupChanneledAfterNLOutputWNullChannel() {
347 $this->maintenance->output( "foo\n", null );
348 $this->maintenance->cleanupChanneled();
349 $this->assertOutputPrePostShutdown( "foo\n", false );
352 public function testCleanupChanneledAfterNLOutputWChannel() {
353 $this->maintenance->output( "foo\n", "bazChannel" );
354 $this->maintenance->cleanupChanneled();
355 $this->assertOutputPrePostShutdown( "foo\n", false );
358 public function testCleanupChanneledAfterOutputChanneledWOChannel() {
359 $this->maintenance->outputChanneled( "foo" );
360 $this->maintenance->cleanupChanneled();
361 $this->assertOutputPrePostShutdown( "foo\n", false );
364 public function testCleanupChanneledAfterOutputChanneledWNullChannel() {
365 $this->maintenance->outputChanneled( "foo", null );
366 $this->maintenance->cleanupChanneled();
367 $this->assertOutputPrePostShutdown( "foo\n", false );
370 public function testCleanupChanneledAfterOutputChanneledWChannel() {
371 $this->maintenance->outputChanneled( "foo", "bazChannel" );
372 $this->maintenance->cleanupChanneled();
373 $this->assertOutputPrePostShutdown( "foo\n", false );
376 public function testMultipleMaintenanceObjectsInteractionOutput() {
377 $m2 = $this->createMaintenance();
379 $this->maintenance->output( "foo" );
380 $m2->output( "bar" );
382 $this->assertEquals( "foobar", $this->getActualOutput(),
383 "Output before shutdown simulation (m2)" );
384 $m2->cleanupChanneled();
385 $this->assertOutputPrePostShutdown( "foobar", false );
388 public function testMultipleMaintenanceObjectsInteractionOutputWNullChannel() {
389 $m2 = $this->createMaintenance();
391 $this->maintenance->output( "foo", null );
392 $m2->output( "bar", null );
394 $this->assertEquals( "foobar", $this->getActualOutput(),
395 "Output before shutdown simulation (m2)" );
396 $m2->cleanupChanneled();
397 $this->assertOutputPrePostShutdown( "foobar", false );
400 public function testMultipleMaintenanceObjectsInteractionOutputWChannel() {
401 $m2 = $this->createMaintenance();
403 $this->maintenance->output( "foo", "bazChannel" );
404 $m2->output( "bar", "bazChannel" );
406 $this->assertEquals( "foobar", $this->getActualOutput(),
407 "Output before shutdown simulation (m2)" );
408 $m2->cleanupChanneled();
409 $this->assertOutputPrePostShutdown( "foobar\n", true );
412 public function testMultipleMaintenanceObjectsInteractionOutputWNullChannelNL() {
413 $m2 = $this->createMaintenance();
415 $this->maintenance->output( "foo\n", null );
416 $m2->output( "bar\n", null );
418 $this->assertEquals( "foo\nbar\n", $this->getActualOutput(),
419 "Output before shutdown simulation (m2)" );
420 $m2->cleanupChanneled();
421 $this->assertOutputPrePostShutdown( "foo\nbar\n", false );
424 public function testMultipleMaintenanceObjectsInteractionOutputWChannelNL() {
425 $m2 = $this->createMaintenance();
427 $this->maintenance->output( "foo\n", "bazChannel" );
428 $m2->output( "bar\n", "bazChannel" );
430 $this->assertEquals( "foobar", $this->getActualOutput(),
431 "Output before shutdown simulation (m2)" );
432 $m2->cleanupChanneled();
433 $this->assertOutputPrePostShutdown( "foobar\n", true );
436 public function testMultipleMaintenanceObjectsInteractionOutputChanneled() {
437 $m2 = $this->createMaintenance();
439 $this->maintenance->outputChanneled( "foo" );
440 $m2->outputChanneled( "bar" );
442 $this->assertEquals( "foo\nbar\n", $this->getActualOutput(),
443 "Output before shutdown simulation (m2)" );
444 $m2->cleanupChanneled();
445 $this->assertOutputPrePostShutdown( "foo\nbar\n", false );
448 public function testMultipleMaintenanceObjectsInteractionOutputChanneledWNullChannel() {
449 $m2 = $this->createMaintenance();
451 $this->maintenance->outputChanneled( "foo", null );
452 $m2->outputChanneled( "bar", null );
454 $this->assertEquals( "foo\nbar\n", $this->getActualOutput(),
455 "Output before shutdown simulation (m2)" );
456 $m2->cleanupChanneled();
457 $this->assertOutputPrePostShutdown( "foo\nbar\n", false );
460 public function testMultipleMaintenanceObjectsInteractionOutputChanneledWChannel() {
461 $m2 = $this->createMaintenance();
463 $this->maintenance->outputChanneled( "foo", "bazChannel" );
464 $m2->outputChanneled( "bar", "bazChannel" );
466 $this->assertEquals( "foobar", $this->getActualOutput(),
467 "Output before shutdown simulation (m2)" );
468 $m2->cleanupChanneled();
469 $this->assertOutputPrePostShutdown( "foobar\n", true );
472 public function testMultipleMaintenanceObjectsInteractionCleanupChanneledWChannel() {
473 $m2 = $this->createMaintenance();
475 $this->maintenance->outputChanneled( "foo", "bazChannel" );
476 $m2->outputChanneled( "bar", "bazChannel" );
478 $this->assertEquals( "foobar", $this->getActualOutput(),
479 "Output before first cleanup" );
480 $this->maintenance->cleanupChanneled();
481 $this->assertEquals( "foobar\n", $this->getActualOutput(),
482 "Output after first cleanup" );
483 $m2->cleanupChanneled();
484 $this->assertEquals( "foobar\n\n", $this->getActualOutput(),
485 "Output after second cleanup" );
487 $m2->cleanupChanneled();
488 $this->assertOutputPrePostShutdown( "foobar\n\n", false );
492 * @covers \MediaWiki\Maintenance\Maintenance::getConfig
494 public function testGetConfig() {
495 $this->assertInstanceOf( Config::class, $this->maintenance->getConfig() );
496 $this->assertSame(
497 MediaWikiServices::getInstance()->getMainConfig(),
498 $this->maintenance->getConfig()
503 * @covers \MediaWiki\Maintenance\Maintenance::setConfig
505 public function testSetConfig() {
506 $conf = new HashConfig();
507 $this->maintenance->setConfig( $conf );
508 $this->assertSame( $conf, $this->maintenance->getConfig() );
511 public function testParseWithMultiArgs() {
512 // Create an option with an argument allowed to be specified multiple times
513 $this->maintenance->addOption( 'multi', 'This option does stuff', false, true, false, true );
514 $this->maintenance->loadWithArgv( [ '--multi', 'this1', '--multi', 'this2' ] );
516 $this->assertEquals( [ 'this1', 'this2' ], $this->maintenance->getOption( 'multi' ) );
517 $this->assertEquals( [ [ 'multi', 'this1' ], [ 'multi', 'this2' ] ],
518 $this->maintenance->orderedOptions );
521 public function testParseMultiOption() {
522 $this->maintenance->addOption( 'multi', 'This option does stuff', false, false, false, true );
523 $this->maintenance->loadWithArgv( [ '--multi', '--multi' ] );
525 $this->assertEquals( [ 1, 1 ], $this->maintenance->getOption( 'multi' ) );
526 $this->assertEquals( [ [ 'multi', 1 ], [ 'multi', 1 ] ], $this->maintenance->orderedOptions );
529 public function testParseArgs() {
530 $this->maintenance->addOption( 'multi', 'This option doesn\'t actually support multiple occurrences' );
531 $this->maintenance->loadWithArgv( [ '--multi=yo' ] );
533 $this->assertEquals( 'yo', $this->maintenance->getOption( 'multi' ) );
534 $this->assertEquals( [ [ 'multi', 'yo' ] ], $this->maintenance->orderedOptions );
537 public function testOptionGetters() {
538 $this->assertSame(
539 false,
540 $this->maintenance->hasOption( 'somearg' ),
541 'Non existent option not found'
543 $this->assertSame(
544 'default',
545 $this->maintenance->getOption( 'somearg', 'default' ),
546 'Non existent option falls back to default'
548 $this->assertSame(
549 false,
550 $this->maintenance->hasOption( 'somearg' ),
551 'Non existent option not found after getting'
553 $this->assertSame(
554 'newdefault',
555 $this->maintenance->getOption( 'somearg', 'newdefault' ),
556 'Non existent option falls back to a new default'
560 public function testLegacyOptionsAccess() {
561 $maintenance = new class () extends Maintenance {
563 * Tests need to be inside the class in order to have access to protected members.
564 * Setting fields in protected arrays doesn't work via TestingAccessWrapper, triggering
565 * an PHP warning ("Indirect modification of overloaded property").
567 public function execute() {
568 $this->setOption( 'test', 'foo' );
569 Assert::assertSame( 'foo', $this->getOption( 'test' ) );
570 Assert::assertSame( 'foo', $this->mOptions['test'] );
572 $this->mOptions['test'] = 'bar';
573 Assert::assertSame( 'bar', $this->getOption( 'test' ) );
575 $this->setArg( 1, 'foo' );
576 Assert::assertSame( 'foo', $this->getArg( 1 ) );
577 Assert::assertSame( 'foo', $this->mArgs[1] );
579 $this->mArgs[1] = 'bar';
580 Assert::assertSame( 'bar', $this->getArg( 1 ) );
584 $maintenance->execute();
587 /** @dataProvider provideFatalError */
588 public function testFatalError( $msg, $errorCode ) {
589 $this->expectCallToFatalError( $errorCode );
590 $this->expectOutputString( $msg . "\n" );
591 $this->maintenance->fatalError( $msg, $errorCode );
594 public static function provideFatalError() {
595 return [
596 'No error message, code as 1' => [ '', 1 ],
597 'Defined error message, code as 3' => [ 'Testing error message', 3 ],
601 /** @dataProvider provideRequiredButMissingExtensions */
602 public function testCheckRequiredExtensionForMissingExtension( $requiredExtensions, $expectedOutputRegex ) {
603 $this->expectCallToFatalError();
604 $this->expectOutputRegex( $expectedOutputRegex );
605 foreach ( $requiredExtensions as $extension ) {
606 $this->maintenance->requireExtension( $extension );
608 $this->maintenance->checkRequiredExtensions();
611 public static function provideRequiredButMissingExtensions() {
612 return [
613 'One missing extension' => [
614 [ 'FakeExtensionForMaintenanceTest' ],
615 '/The "FakeExtensionForMaintenanceTest" extension must be installed.*' .
616 'Please enable it and then try again/'
618 'Two missing extensions' => [
619 [ 'FakeExtensionForMaintenanceTest', 'MissingExtensionTest2' ],
620 '/The following extensions must be installed.*FakeExtensionForMaintenanceTest.*MissingExtensionTest2' .
621 '.*Please enable them and then try again/'
626 public function testValidateUserOptionForMissingArguments() {
627 $this->expectCallToFatalError();
628 $this->expectOutputRegex( '/Test error message/' );
629 $this->maintenance->validateUserOption( 'Test error message' );
632 /** @dataProvider provideInvalidUserOptions */
633 public function testValidateUserOptionForInvalidUserOption( $options, $expectedOutputRegex ) {
634 $this->expectCallToFatalError();
635 $this->expectOutputRegex( $expectedOutputRegex );
636 foreach ( $options as $name => $value ) {
637 $this->maintenance->setOption( $name, $value );
639 $this->maintenance->validateUserOption( 'unused' );
642 public static function provideInvalidUserOptions() {
643 return [
644 'Invalid --user option' => [
645 [ 'user' => 'Non-existent-test-user' ], '/No such user.*Non-existent-test-user/',
647 'Invalid --userid option' => [ [ 'userid' => 0 ], '/No such user id.*0/' ],
651 public function testValidateUserOptionForValidUser() {
652 $testUser = $this->getTestUser()->getUserIdentity();
653 $this->maintenance->setOption( 'userid', $testUser->getId() );
654 $this->assertTrue( $testUser->equals( $this->maintenance->validateUserOption( "unused" ) ) );
657 public function testRunChildForNonExistentClass() {
658 $this->expectCallToFatalError();
659 $this->expectOutputRegex( '/Cannot spawn child.*NonExistingTestClassForMaintenanceTest/' );
660 $this->maintenance->runChild( 'NonExistingTestClassForMaintenanceTest' );
663 public function testSetAllowUnregisteredOptions() {
664 $this->maintenance->setOption( 'abcdef', 'abc' );
665 $this->maintenance->setAllowUnregisteredOptions( true );
666 $this->assertTrue( $this->maintenance->getParameters()->validate() );
667 $this->maintenance->setAllowUnregisteredOptions( false );
668 $this->assertFalse( $this->maintenance->getParameters()->validate() );
671 /** @dataProvider provideSetBatchSize */
672 public function testSetBatchSize( $batchSize, $shouldHaveBatchSizeOption ) {
673 $this->maintenance->setBatchSize( $batchSize );
674 $this->assertSame( $batchSize, $this->maintenance->getBatchSize() );
675 $this->assertSame(
676 $shouldHaveBatchSizeOption,
677 $this->maintenance->supportsOption( 'batch-size' )
681 public static function provideSetBatchSize() {
682 return [
683 'Batch size as 0' => [ 0, false ],
684 'Batch size as 150' => [ 150, true ],
688 public function testPurgeRedundantTextWhenNoPagesExist() {
689 // Regression test for the method breaking if no rows exist in the content_address table.
690 $this->maintenance->purgeRedundantText();
691 $this->expectOutputRegex( '/0 inactive items found[\s\S]*(?!Deleting)/' );
694 public function testDeleteOptionLoop() {
695 $this->maintenance->addOption( 'test-for-deletion', 'testing' );
696 $this->assertTrue( $this->maintenance->getParameters()->supportsOption( 'test-for-deletion' ) );
697 $this->maintenance->deleteOption( 'test-for-deletion' );
698 $this->assertFalse( $this->maintenance->getParameters()->supportsOption( 'test-for-deletion' ) );
701 public function testAddDescription() {
702 $this->maintenance->addDescription( 'testing-description abcdef' );
703 $this->expectCallToFatalError();
704 $this->expectOutputRegex( '/testing-description abcdef/' );
705 $this->maintenance->getParameters()->setName( 'test.php' );
706 $this->maintenance->maybeHelp( true );
709 public function testGetArgName() {
710 $this->maintenance->addArg( 'testing', 'test' );
711 $this->assertSame( 'testing', $this->maintenance->getArgName( 0 ) );
714 public function testHasArg() {
715 $this->maintenance->addArg( 'testing', 'test' );
716 $this->maintenance->setArg( 'testing', 'abc' );
717 $this->assertTrue( $this->maintenance->hasArg( 0 ) );
718 $this->assertTrue( $this->maintenance->hasArg( 'testing' ) );
719 $this->assertSame( [ 'abc' ], $this->maintenance->getArgs() );
722 public function testSetName() {
723 $this->maintenance->setName( 'test.php' );
724 $this->assertSame( 'test.php', $this->maintenance->getName() );
725 $this->assertSame( 'test.php', $this->maintenance->getParameters()->getName() );
728 public function testGetDir() {
729 $this->assertSame( realpath( MW_INSTALL_PATH . '/maintenance' ), realpath( $this->maintenance->getDir() ) );
732 /** @dataProvider provideParseIntList */
733 public function testParseIntList( $text, $expected ) {
734 $this->assertArrayEquals( $expected, $this->maintenance->parseIntList( $text ) );
737 public static function provideParseIntList() {
738 return [
739 'Integers separated by ","' => [ '1,2,3,3', [ 1, 2, 3, 3 ] ],
740 'Integers separated by "|"' => [ '1|2|3|4|4', [ 1, 2, 3, 4, 4 ] ],
744 public function testTransactionRoundCommit() {
745 $m1 = $this->createMaintenance();
746 $fname = get_class( $m1 ) . '::execute';
748 $runs = 0;
749 $dbw = $m1->getPrimaryDB();
751 $this->assertSame( 0, $dbw->trxLevel() );
752 $m1->beginTransactionRound( $fname );
753 $this->assertSame( 0, $dbw->trxLevel() );
755 $dbw->startAtomic( __METHOD__ );
756 $this->assertSame( 1, $dbw->trxLevel() );
757 $update = new AtomicSectionUpdate(
758 $dbw,
759 $fname,
760 static function () use ( &$runs ) {
761 ++$runs;
764 DeferredUpdates::addUpdate( $update );
765 $this->assertSame( 1, DeferredUpdates::pendingUpdatesCount() );
766 $dbw->endAtomic( __METHOD__ );
768 $m1->commitTransactionRound( $fname );
769 $this->assertSame( 0, $dbw->trxLevel() );
770 DeferredUpdates::tryOpportunisticExecute();
771 $this->assertSame( 0, DeferredUpdates::pendingUpdatesCount() );
772 $this->assertSame( 1, $runs );
775 public function testTransactionRoundRollback() {
776 $m1 = $this->createMaintenance();
777 $fname = get_class( $m1 ) . '::execute';
779 $runs = 0;
780 $dbw = $m1->getPrimaryDB();
782 $this->assertSame( 0, $dbw->trxLevel() );
783 $m1->beginTransactionRound( $fname );
784 $this->assertSame( 0, $dbw->trxLevel() );
786 $dbw->startAtomic( __METHOD__ );
787 $this->assertSame( 1, $dbw->trxLevel() );
788 $update = new AtomicSectionUpdate(
789 $dbw,
790 $fname,
791 static function () use ( &$runs ) {
792 ++$runs;
795 DeferredUpdates::addUpdate( $update );
796 $this->assertSame( 1, DeferredUpdates::pendingUpdatesCount() );
798 $m1->rollbackTransactionRound( $fname );
799 $this->assertSame( 0, $dbw->trxLevel() );
800 DeferredUpdates::tryOpportunisticExecute();
801 $this->assertSame( 0, DeferredUpdates::pendingUpdatesCount() );
802 $this->assertSame( 0, $runs );