3 namespace MediaWiki\Tests\Maintenance
;
7 use MediaWiki\MediaWikiServices
;
8 use MediaWiki\Revision\RevisionRecord
;
15 use Wikimedia\Rdbms\IDatabase
;
16 use Wikimedia\Rdbms\LoadBalancer
;
21 * Tests for page dumps of BackupDumper
25 * @covers BackupDumper
27 class BackupDumperPageTest
extends DumpTestCase
{
29 // We'll add several pages, revision and texts. The following variables hold the
31 private $pageId1, $pageId2, $pageId3, $pageId4;
32 private $pageTitle1, $pageTitle2, $pageTitle3, $pageTitle4;
33 private $revId1_1, $textId1_1;
34 private $revId2_1, $textId2_1, $revId2_2, $textId2_2;
35 private $revId2_3, $textId2_3, $revId2_4, $textId2_4;
36 private $revId3_1, $textId3_1, $revId3_2, $textId3_2;
37 private $revId4_1, $textId4_1;
38 private $namespace, $talk_namespace;
41 * @var LoadBalancer|null
43 private $streamingLoadBalancer = null;
45 function addDBData() {
46 // be sure, titles created here using english namespace names
47 $this->setContentLang( 'en' );
49 $this->tablesUsed
[] = 'page';
50 $this->tablesUsed
[] = 'revision';
51 $this->tablesUsed
[] = 'ip_changes';
52 $this->tablesUsed
[] = 'text';
55 $this->namespace = $this->getDefaultWikitextNS();
56 $this->talk_namespace
= NS_TALK
;
58 if ( $this->namespace === $this->talk_namespace
) {
59 // @todo work around this.
60 throw new MWException( "The default wikitext namespace is the talk namespace. "
61 . " We can't currently deal with that." );
64 $this->pageTitle1
= Title
::newFromText( 'BackupDumperTestP1', $this->namespace );
65 $page = WikiPage
::factory( $this->pageTitle1
);
66 list( $this->revId1_1
, $this->textId1_1
) = $this->addRevision( $page,
67 "BackupDumperTestP1Text1", "BackupDumperTestP1Summary1" );
68 $this->pageId1
= $page->getId();
70 $this->pageTitle2
= Title
::newFromText( 'BackupDumperTestP2', $this->namespace );
71 $page = WikiPage
::factory( $this->pageTitle2
);
72 list( $this->revId2_1
, $this->textId2_1
) = $this->addRevision( $page,
73 "BackupDumperTestP2Text1", "BackupDumperTestP2Summary1" );
74 list( $this->revId2_2
, $this->textId2_2
) = $this->addRevision( $page,
75 "BackupDumperTestP2Text2", "BackupDumperTestP2Summary2" );
76 list( $this->revId2_3
, $this->textId2_3
) = $this->addRevision( $page,
77 "BackupDumperTestP2Text3", "BackupDumperTestP2Summary3" );
78 list( $this->revId2_4
, $this->textId2_4
) = $this->addRevision( $page,
79 "BackupDumperTestP2Text4 some additional Text ",
80 "BackupDumperTestP2Summary4 extra " );
81 $this->pageId2
= $page->getId();
83 $revDel = RevisionDeleter
::createList(
85 RequestContext
::getMain(),
89 $revDel->setVisibility( [
90 'value' => [ RevisionRecord
::DELETED_TEXT
=> 1 ],
91 'comment' => 'testing!'
94 $this->pageTitle3
= Title
::newFromText( 'BackupDumperTestP3', $this->namespace );
95 $page = WikiPage
::factory( $this->pageTitle3
);
96 list( $this->revId3_1
, $this->textId3_1
) = $this->addRevision( $page,
97 "BackupDumperTestP3Text1", "BackupDumperTestP2Summary1" );
98 list( $this->revId3_2
, $this->textId3_2
) = $this->addRevision( $page,
99 "BackupDumperTestP3Text2", "BackupDumperTestP2Summary2" );
100 $this->pageId3
= $page->getId();
101 $page->doDeleteArticle( "Testing ;)" );
103 $this->pageTitle4
= Title
::newFromText( 'BackupDumperTestP1', $this->talk_namespace
);
104 $page = WikiPage
::factory( $this->pageTitle4
);
105 list( $this->revId4_1
, $this->textId4_1
) = $this->addRevision( $page,
106 "Talk about BackupDumperTestP1 Text1",
107 "Talk BackupDumperTestP1 Summary1" );
108 $this->pageId4
= $page->getId();
109 } catch ( Exception
$e ) {
110 // We'd love to pass $e directly. However, ... see
111 // documentation of exceptionFromAddDBData in
113 $this->exceptionFromAddDBData
= $e;
117 protected function setUp() {
120 // Since we will restrict dumping by page ranges (to allow
121 // working tests, even if the db gets prepopulated by a base
122 // class), we have to assert, that the page id are consecutively
125 [ $this->pageId2
, $this->pageId3
, $this->pageId4
],
126 [ $this->pageId1 +
1, $this->pageId2 +
1, $this->pageId3 +
1 ],
127 "Page ids increasing without holes" );
130 function tearDown() {
133 if ( isset( $this->streamingLoadBalancer
) ) {
134 $this->streamingLoadBalancer
->closeAll();
139 * Returns a new database connection which is separate from the conenctions returned
140 * by the default LoadBalancer instance.
144 private function newStreamingDBConnection() {
145 // Create a *new* LoadBalancer, so no connections are shared
146 if ( !$this->streamingLoadBalancer
) {
147 $lbFactory = MediaWikiServices
::getInstance()->getDBLoadBalancerFactory();
149 $this->streamingLoadBalancer
= $lbFactory->newMainLB();
152 $db = $this->streamingLoadBalancer
->getConnection( DB_REPLICA
);
154 // Make sure the DB connection has the fake table clones and the fake table prefix
155 MediaWikiTestCase
::setupDatabaseWithTestPrefix( $db );
157 // Make sure the DB connection has all the test data
158 $this->copyTestData( $this->db
, $db );
165 * @param int $startId
170 private function newDumpBackup( $argv, $startId, $endId ) {
171 $dumper = new DumpBackup( $argv );
172 $dumper->startId
= $startId;
173 $dumper->endId
= $endId;
174 $dumper->reporting
= false;
176 // NOTE: The copyTestData() method used by newStreamingDBConnection()
177 // doesn't work with SQLite (T217607).
178 // But DatabaseSqlite doesn't support streaming anyway, so just skip that part.
179 if ( $this->db
->getType() === 'sqlite' ) {
180 $dumper->setDB( $this->db
);
182 $dumper->setDB( $this->newStreamingDBConnection() );
188 public function schemaVersionProvider() {
189 foreach ( XmlDumpWriter
::$supportedSchemas as $schemaVersion ) {
190 yield
[ $schemaVersion ];
195 * @dataProvider schemaVersionProvider
197 function testFullTextPlain( $schemaVersion ) {
198 // Preparing the dump
199 $fname = $this->getNewTempFile();
201 $dumper = $this->newDumpBackup(
202 [ '--full', '--quiet', '--output', 'file:' . $fname, '--schema-version', $schemaVersion ],
207 // Performing the dump
210 // Checking the dumped data
211 $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
212 $asserter = $this->getDumpAsserter( $schemaVersion );
214 $asserter->assertDumpStart( $fname );
217 $asserter->assertPageStart(
220 $this->pageTitle1
->getPrefixedText()
222 $asserter->assertRevision(
224 "BackupDumperTestP1Summary1",
227 "0bolhl6ol7i6x0e7yq91gxgaan39j87",
228 "BackupDumperTestP1Text1"
230 $asserter->assertPageEnd();
233 $asserter->assertPageStart(
236 $this->pageTitle2
->getPrefixedText()
238 $asserter->assertRevision(
240 "BackupDumperTestP2Summary1",
243 "jprywrymfhysqllua29tj3sc7z39dl2",
244 "BackupDumperTestP2Text1"
246 $asserter->assertRevision(
248 "BackupDumperTestP2Summary2",
255 $asserter->assertRevision(
257 "BackupDumperTestP2Summary3",
260 "jfunqmh1ssfb8rs43r19w98k28gg56r",
261 "BackupDumperTestP2Text3",
264 $asserter->assertRevision(
266 "BackupDumperTestP2Summary4 extra",
269 "6o1ciaxa6pybnqprmungwofc4lv00wv",
270 "BackupDumperTestP2Text4 some additional Text",
273 $asserter->assertPageEnd();
276 // -> Page is marked deleted. Hence not visible
279 $asserter->assertPageStart(
281 $this->talk_namespace
,
282 $this->pageTitle4
->getPrefixedText()
284 $asserter->assertRevision(
286 "Talk BackupDumperTestP1 Summary1",
289 "nktofwzd0tl192k3zfepmlzxoax1lpe",
290 "Talk about BackupDumperTestP1 Text1",
292 CONTENT_MODEL_WIKITEXT
,
293 CONTENT_FORMAT_WIKITEXT
,
296 $asserter->assertPageEnd();
298 $asserter->assertDumpEnd();
300 // FIXME: add multi-slot test case!
304 * @dataProvider schemaVersionProvider
306 function testFullStubPlain( $schemaVersion ) {
307 // Preparing the dump
308 $fname = $this->getNewTempFile();
310 $dumper = $this->newDumpBackup(
317 '--schema-version', $schemaVersion,
323 // Performing the dump
326 // Checking the dumped data
327 $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
328 $asserter = $this->getDumpAsserter( $schemaVersion );
330 $asserter->assertDumpStart( $fname );
333 $asserter->assertPageStart(
336 $this->pageTitle1
->getPrefixedText()
338 $asserter->assertRevision(
340 "BackupDumperTestP1Summary1",
343 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
345 $asserter->assertPageEnd();
348 $asserter->assertPageStart(
351 $this->pageTitle2
->getPrefixedText()
353 $asserter->assertRevision(
355 "BackupDumperTestP2Summary1",
358 "jprywrymfhysqllua29tj3sc7z39dl2"
360 $asserter->assertRevision(
362 "BackupDumperTestP2Summary2",
369 $asserter->assertRevision(
371 "BackupDumperTestP2Summary3",
374 "jfunqmh1ssfb8rs43r19w98k28gg56r",
378 $asserter->assertRevision(
380 "BackupDumperTestP2Summary4 extra",
383 "6o1ciaxa6pybnqprmungwofc4lv00wv",
387 $asserter->assertPageEnd();
390 // -> Page is marked deleted. Hence not visible
393 $asserter->assertPageStart(
395 $this->talk_namespace
,
396 $this->pageTitle4
->getPrefixedText()
398 $asserter->assertRevision(
400 "Talk BackupDumperTestP1 Summary1",
403 "nktofwzd0tl192k3zfepmlzxoax1lpe"
405 $asserter->assertPageEnd();
407 $asserter->assertDumpEnd();
411 * @dataProvider schemaVersionProvider
413 function testCurrentStubPlain( $schemaVersion ) {
414 // Preparing the dump
415 $fname = $this->getNewTempFile();
417 $dumper = $this->newDumpBackup(
418 [ '--output', 'file:' . $fname, '--schema-version', $schemaVersion ],
423 // Performing the dump
424 $dumper->dump( WikiExporter
::CURRENT
, WikiExporter
::STUB
);
426 // Checking the dumped data
427 $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
429 $asserter = $this->getDumpAsserter( $schemaVersion );
430 $asserter->assertDumpStart( $fname );
433 $asserter->assertPageStart(
436 $this->pageTitle1
->getPrefixedText()
438 $asserter->assertRevision(
440 "BackupDumperTestP1Summary1",
443 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
445 $asserter->assertPageEnd();
448 $asserter->assertPageStart(
451 $this->pageTitle2
->getPrefixedText()
453 $asserter->assertRevision(
455 "BackupDumperTestP2Summary4 extra",
458 "6o1ciaxa6pybnqprmungwofc4lv00wv",
462 $asserter->assertPageEnd();
465 // -> Page is marked deleted. Hence not visible
468 $asserter->assertPageStart(
470 $this->talk_namespace
,
471 $this->pageTitle4
->getPrefixedText()
473 $asserter->assertRevision(
475 "Talk BackupDumperTestP1 Summary1",
478 "nktofwzd0tl192k3zfepmlzxoax1lpe"
480 $asserter->assertPageEnd();
482 $asserter->assertDumpEnd();
485 function testCurrentStubGzip() {
486 $this->checkHasGzip();
488 // Preparing the dump
489 $fname = $this->getNewTempFile();
491 $dumper = $this->newDumpBackup(
492 [ '--output', 'gzip:' . $fname ],
497 // Performing the dump
498 $dumper->dump( WikiExporter
::CURRENT
, WikiExporter
::STUB
);
500 // Checking the dumped data
501 $this->gunzip( $fname );
503 $asserter = $this->getDumpAsserter();
504 $asserter->assertDumpStart( $fname );
507 $asserter->assertPageStart(
510 $this->pageTitle1
->getPrefixedText()
512 $asserter->assertRevision(
514 "BackupDumperTestP1Summary1",
517 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
519 $asserter->assertPageEnd();
522 $asserter->assertPageStart(
525 $this->pageTitle2
->getPrefixedText()
527 $asserter->assertRevision(
529 "BackupDumperTestP2Summary4 extra",
532 "6o1ciaxa6pybnqprmungwofc4lv00wv",
536 $asserter->assertPageEnd();
539 // -> Page is marked deleted. Hence not visible
542 $asserter->assertPageStart(
544 $this->talk_namespace
,
545 $this->pageTitle4
->getPrefixedText()
547 $asserter->assertRevision( $this->revId4_1
, "Talk BackupDumperTestP1 Summary1",
548 $this->textId4_1
, 35, "nktofwzd0tl192k3zfepmlzxoax1lpe" );
549 $asserter->assertPageEnd();
551 $asserter->assertDumpEnd();
555 * xmldumps-backup typically performs a single dump that that writes
557 * - gzipped stubs of everything (meta-history)
558 * - gzipped stubs of latest revisions of all pages (meta-current)
559 * - gzipped stubs of latest revisions of all pages of namespage 0
562 * We reproduce such a setup with our mini fixture, although we omit
563 * chunks, and all the other gimmicks of xmldumps-backup.
565 * @dataProvider schemaVersionProvider
567 function testXmlDumpsBackupUseCase( $schemaVersion ) {
568 $this->checkHasGzip();
570 $fnameMetaHistory = $this->getNewTempFile();
571 $fnameMetaCurrent = $this->getNewTempFile();
572 $fnameArticles = $this->getNewTempFile();
574 $dumper = $this->newDumpBackup(
575 [ "--full", "--stub", "--output=gzip:" . $fnameMetaHistory,
576 "--output=gzip:" . $fnameMetaCurrent, "--filter=latest",
577 "--output=gzip:" . $fnameArticles, "--filter=latest",
578 "--filter=notalk", "--filter=namespace:!NS_USER",
579 "--reporting=1000", '--schema-version', $schemaVersion
584 $dumper->reporting
= true;
586 // xmldumps-backup uses reporting. We will not check the exact reported
587 // message, as they are dependent on the processing power of the used
588 // computer. We only check that reporting does not crash the dumping
589 // and that something is reported
590 $dumper->stderr
= fopen( 'php://output', 'a' );
591 if ( $dumper->stderr
=== false ) {
592 $this->fail( "Could not open stream for stderr" );
595 // Performing the dump
596 $dumper->dump( WikiExporter
::FULL
, WikiExporter
::STUB
);
598 $this->assertTrue( fclose( $dumper->stderr
), "Closing stderr handle" );
600 // Checking meta-history -------------------------------------------------
602 $this->gunzip( $fnameMetaHistory );
603 $this->assertDumpSchema( $fnameMetaHistory, $this->getXmlSchemaPath( $schemaVersion ) );
605 $asserter = $this->getDumpAsserter( $schemaVersion );
606 $asserter->assertDumpStart( $fnameMetaHistory );
609 $asserter->assertPageStart(
612 $this->pageTitle1
->getPrefixedText()
614 $asserter->assertRevision(
616 "BackupDumperTestP1Summary1",
619 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
621 $asserter->assertPageEnd();
624 $asserter->assertPageStart(
627 $this->pageTitle2
->getPrefixedText()
629 $asserter->assertRevision(
631 "BackupDumperTestP2Summary1",
634 "jprywrymfhysqllua29tj3sc7z39dl2"
636 $asserter->assertRevision(
638 "BackupDumperTestP2Summary2",
645 $asserter->assertRevision(
647 "BackupDumperTestP2Summary3",
650 "jfunqmh1ssfb8rs43r19w98k28gg56r",
654 $asserter->assertRevision(
656 "BackupDumperTestP2Summary4 extra",
659 "6o1ciaxa6pybnqprmungwofc4lv00wv",
663 $asserter->assertPageEnd();
666 // -> Page is marked deleted. Hence not visible
669 $asserter->assertPageStart(
671 $this->talk_namespace
,
672 $this->pageTitle4
->getPrefixedText( $schemaVersion )
674 $asserter->assertRevision(
676 "Talk BackupDumperTestP1 Summary1",
679 "nktofwzd0tl192k3zfepmlzxoax1lpe"
681 $asserter->assertPageEnd();
683 $asserter->assertDumpEnd();
685 // Checking meta-current -------------------------------------------------
687 $this->gunzip( $fnameMetaCurrent );
688 $this->assertDumpSchema( $fnameMetaCurrent, $this->getXmlSchemaPath( $schemaVersion ) );
690 $asserter = $this->getDumpAsserter( $schemaVersion );
691 $asserter->assertDumpStart( $fnameMetaCurrent );
694 $asserter->assertPageStart(
697 $this->pageTitle1
->getPrefixedText()
699 $asserter->assertRevision(
701 "BackupDumperTestP1Summary1",
704 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
706 $asserter->assertPageEnd();
709 $asserter->assertPageStart(
712 $this->pageTitle2
->getPrefixedText()
714 $asserter->assertRevision(
716 "BackupDumperTestP2Summary4 extra",
719 "6o1ciaxa6pybnqprmungwofc4lv00wv",
723 $asserter->assertPageEnd();
726 // -> Page is marked deleted. Hence not visible
729 $asserter->assertPageStart(
731 $this->talk_namespace
,
732 $this->pageTitle4
->getPrefixedText()
734 $asserter->assertRevision(
736 "Talk BackupDumperTestP1 Summary1",
739 "nktofwzd0tl192k3zfepmlzxoax1lpe"
741 $asserter->assertPageEnd();
743 $asserter->assertDumpEnd();
745 // Checking articles -------------------------------------------------
747 $this->gunzip( $fnameArticles );
748 $this->assertDumpSchema( $fnameArticles, $this->getXmlSchemaPath( $schemaVersion ) );
750 $asserter = $this->getDumpAsserter( $schemaVersion );
751 $asserter->assertDumpStart( $fnameArticles );
754 $asserter->assertPageStart(
757 $this->pageTitle1
->getPrefixedText()
759 $asserter->assertRevision(
761 "BackupDumperTestP1Summary1",
764 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
766 $asserter->assertPageEnd();
769 $asserter->assertPageStart(
772 $this->pageTitle2
->getPrefixedText()
774 $asserter->assertRevision(
776 "BackupDumperTestP2Summary4 extra",
779 "6o1ciaxa6pybnqprmungwofc4lv00wv",
783 $asserter->assertPageEnd();
786 // -> Page is marked deleted. Hence not visible
789 // -> Page is not in $this->namespace. Hence not visible
791 $asserter->assertDumpEnd();
793 $this->expectETAOutput();