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
;
16 * @covers \MediaWiki\Maintenance\MaintenanceFatalError
17 * @covers \MediaWiki\Maintenance\Maintenance
20 class MaintenanceTest
extends MaintenanceBaseTestCase
{
25 protected function getMaintenanceClass() {
26 return Maintenance
::class;
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.
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.
50 * @dataProvider provideOutputData
52 public function testOutput( $outputs, $expected, $extraNL ) {
53 foreach ( $outputs as $data ) {
54 if ( is_array( $data ) ) {
55 [ $msg, $channel ] = $data;
60 $this->maintenance
->output( $msg, $channel );
62 $this->assertOutputPrePostShutdown( $expected, $extraNL );
65 public static function provideOutputData() {
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" ],
98 [ "foo", "bazChannel" ],
100 [ "\n", "bazChannel" ],
101 [ "ba", "bazChannel" ],
102 [ "", "bazChannel" ],
103 [ "r\n", "bazChannel" ],
110 [ "", "bazChannel" ],
111 [ "foo", "bazChannel" ],
112 [ "", "bazChannel" ],
113 [ "\nb", "bazChannel" ],
114 [ "a", "bazChannel" ],
115 [ "", "bazChannel" ],
116 [ "r\n", "bazChannel" ],
123 [ "foo", "bazChannel" ],
124 [ "bar", "bazChannel" ],
125 [ "qux", "quuxChannel" ],
126 [ "corge", "bazChannel" ],
128 "foobar\nqux\ncorge",
133 [ "foo", "bazChannel" ],
134 [ "bar\n", "bazChannel" ],
135 [ "qux\n", "quuxChannel" ],
136 [ "corge", "bazChannel" ],
138 "foobar\nqux\ncorge",
144 [ "bar", "bazChannel" ],
146 [ "quux", "bazChannel" ],
153 [ "foo", "bazChannel" ],
155 [ "qux", "bazChannel" ],
169 [ [ "foo", "", "bar" ], "foobar", false ],
170 [ [ "foo", false, "bar" ], "foobar", false ],
173 [ "qux", "quuxChannel" ],
183 [ "foo", "bazChannel" ],
184 [ "", "bazChannel" ],
185 [ "bar", "bazChannel" ],
192 [ "foo", "bazChannel" ],
193 [ false, "bazChannel" ],
194 [ "bar", "bazChannel" ],
203 * @dataProvider provideOutputChanneledData
205 public function testOutputChanneled( $outputs, $expected, $extraNL ) {
206 foreach ( $outputs as $data ) {
207 if ( is_array( $data ) ) {
208 [ $msg, $channel ] = $data;
213 $this->maintenance
->outputChanneled( $msg, $channel );
215 $this->assertOutputPrePostShutdown( $expected, $extraNL );
218 public static function provideOutputChanneledData() {
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" ]
235 [ "foo", "bazChannel" ],
236 [ "bar", "bazChannel" ],
243 [ "", "bazChannel" ],
244 [ "foo", "bazChannel" ],
245 [ "", "bazChannel" ],
246 [ "\nb", "bazChannel" ],
247 [ "a", "bazChannel" ],
248 [ "", "bazChannel" ],
249 [ "r", "bazChannel" ],
256 [ "foo", "bazChannel" ],
257 [ "bar", "bazChannel" ],
258 [ "qux", "quuxChannel" ],
259 [ "corge", "bazChannel" ],
261 "foobar\nqux\ncorge",
266 [ "foo", "bazChannel" ],
267 [ "bar", "bazChannel" ],
268 [ "qux", "quuxChannel" ],
269 [ "corge", "bazChannel" ],
271 "foobar\nqux\ncorge",
276 [ "foo", "bazChannel" ],
279 [ "corge", "bazChannel" ],
281 "foo\nbar\nqux\ncorge",
287 [ "bar", "bazChannel" ],
289 [ "quux", "bazChannel" ],
291 "foo\nbar\nqux\nquux",
296 [ "foo", "bazChannel" ],
298 [ "qux", "bazChannel" ],
301 "foo\nbar\nqux\nquux\n",
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() );
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() {
540 $this->maintenance
->hasOption( 'somearg' ),
541 'Non existent option not found'
545 $this->maintenance
->getOption( 'somearg', 'default' ),
546 'Non existent option falls back to default'
550 $this->maintenance
->hasOption( 'somearg' ),
551 'Non existent option not found after getting'
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() {
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() {
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() {
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() );
676 $shouldHaveBatchSizeOption,
677 $this->maintenance
->supportsOption( 'batch-size' )
681 public static function provideSetBatchSize() {
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() {
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';
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(
760 static function () use ( &$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';
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(
791 static function () use ( &$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 );