3 namespace MediaWiki\Tests\Maintenance
;
7 use MediaWiki\MediaWikiServices
;
12 use Wikimedia\Rdbms\IDatabase
;
13 use Wikimedia\Rdbms\LoadBalancer
;
18 * Tests for page dumps of BackupDumper
22 * @covers BackupDumper
24 class BackupDumperPageTest
extends DumpTestCase
{
26 // We'll add several pages, revision and texts. The following variables hold the
28 private $pageId1, $pageId2, $pageId3, $pageId4;
29 private $pageTitle1, $pageTitle2, $pageTitle3, $pageTitle4;
30 private $revId1_1, $textId1_1;
31 private $revId2_1, $textId2_1, $revId2_2, $textId2_2;
32 private $revId2_3, $textId2_3, $revId2_4, $textId2_4;
33 private $revId3_1, $textId3_1, $revId3_2, $textId3_2;
34 private $revId4_1, $textId4_1;
35 private $namespace, $talk_namespace;
38 * @var LoadBalancer|null
40 private $streamingLoadBalancer = null;
42 function addDBData() {
43 // be sure, titles created here using english namespace names
44 $this->setContentLang( 'en' );
46 $this->tablesUsed
[] = 'page';
47 $this->tablesUsed
[] = 'revision';
48 $this->tablesUsed
[] = 'ip_changes';
49 $this->tablesUsed
[] = 'text';
52 $this->namespace = $this->getDefaultWikitextNS();
53 $this->talk_namespace
= NS_TALK
;
55 if ( $this->namespace === $this->talk_namespace
) {
56 // @todo work around this.
57 throw new MWException( "The default wikitext namespace is the talk namespace. "
58 . " We can't currently deal with that." );
61 $this->pageTitle1
= Title
::newFromText( 'BackupDumperTestP1', $this->namespace );
62 $page = WikiPage
::factory( $this->pageTitle1
);
63 list( $this->revId1_1
, $this->textId1_1
) = $this->addRevision( $page,
64 "BackupDumperTestP1Text1", "BackupDumperTestP1Summary1" );
65 $this->pageId1
= $page->getId();
67 $this->pageTitle2
= Title
::newFromText( 'BackupDumperTestP2', $this->namespace );
68 $page = WikiPage
::factory( $this->pageTitle2
);
69 list( $this->revId2_1
, $this->textId2_1
) = $this->addRevision( $page,
70 "BackupDumperTestP2Text1", "BackupDumperTestP2Summary1" );
71 list( $this->revId2_2
, $this->textId2_2
) = $this->addRevision( $page,
72 "BackupDumperTestP2Text2", "BackupDumperTestP2Summary2" );
73 list( $this->revId2_3
, $this->textId2_3
) = $this->addRevision( $page,
74 "BackupDumperTestP2Text3", "BackupDumperTestP2Summary3" );
75 list( $this->revId2_4
, $this->textId2_4
) = $this->addRevision( $page,
76 "BackupDumperTestP2Text4 some additional Text ",
77 "BackupDumperTestP2Summary4 extra " );
78 $this->pageId2
= $page->getId();
80 $this->pageTitle3
= Title
::newFromText( 'BackupDumperTestP3', $this->namespace );
81 $page = WikiPage
::factory( $this->pageTitle3
);
82 list( $this->revId3_1
, $this->textId3_1
) = $this->addRevision( $page,
83 "BackupDumperTestP3Text1", "BackupDumperTestP2Summary1" );
84 list( $this->revId3_2
, $this->textId3_2
) = $this->addRevision( $page,
85 "BackupDumperTestP3Text2", "BackupDumperTestP2Summary2" );
86 $this->pageId3
= $page->getId();
87 $page->doDeleteArticle( "Testing ;)" );
89 $this->pageTitle4
= Title
::newFromText( 'BackupDumperTestP1', $this->talk_namespace
);
90 $page = WikiPage
::factory( $this->pageTitle4
);
91 list( $this->revId4_1
, $this->textId4_1
) = $this->addRevision( $page,
92 "Talk about BackupDumperTestP1 Text1",
93 "Talk BackupDumperTestP1 Summary1" );
94 $this->pageId4
= $page->getId();
95 } catch ( Exception
$e ) {
96 // We'd love to pass $e directly. However, ... see
97 // documentation of exceptionFromAddDBData in
99 $this->exceptionFromAddDBData
= $e;
103 protected function setUp() {
106 // Since we will restrict dumping by page ranges (to allow
107 // working tests, even if the db gets prepopulated by a base
108 // class), we have to assert, that the page id are consecutively
111 [ $this->pageId2
, $this->pageId3
, $this->pageId4
],
112 [ $this->pageId1 +
1, $this->pageId2 +
1, $this->pageId3 +
1 ],
113 "Page ids increasing without holes" );
116 function tearDown() {
119 if ( isset( $this->streamingLoadBalancer
) ) {
120 $this->streamingLoadBalancer
->closeAll();
125 * Returns a new database connection which is separate from the conenctions returned
126 * by the default LoadBalancer instance.
130 private function newStreamingDBConnection() {
131 // Create a *new* LoadBalancer, so no connections are shared
132 if ( !$this->streamingLoadBalancer
) {
133 $lbFactory = MediaWikiServices
::getInstance()->getDBLoadBalancerFactory();
135 $this->streamingLoadBalancer
= $lbFactory->newMainLB();
138 $db = $this->streamingLoadBalancer
->getConnection( DB_REPLICA
);
140 // Make sure the DB connection has the fake table clones and the fake table prefix
141 MediaWikiTestCase
::setupDatabaseWithTestPrefix( $db );
143 // Make sure the DB connection has all the test data
144 $this->copyTestData( $this->db
, $db );
151 * @param int $startId
156 private function newDumpBackup( $argv, $startId, $endId ) {
157 $dumper = new DumpBackup( $argv );
158 $dumper->startId
= $startId;
159 $dumper->endId
= $endId;
160 $dumper->reporting
= false;
162 // NOTE: The copyTestData() method used by newStreamingDBConnection()
163 // doesn't work with SQLite (T217607).
164 // But DatabaseSqlite doesn't support streaming anyway, so just skip that part.
165 if ( $this->db
->getType() === 'sqlite' ) {
166 $dumper->setDB( $this->db
);
168 $dumper->setDB( $this->newStreamingDBConnection() );
174 public function schemaVersionProvider() {
175 foreach ( XmlDumpWriter
::$supportedSchemas as $schemaVersion ) {
176 yield
[ $schemaVersion ];
181 * @dataProvider schemaVersionProvider
183 function testFullTextPlain( $schemaVersion ) {
184 // Preparing the dump
185 $fname = $this->getNewTempFile();
187 $dumper = $this->newDumpBackup(
188 [ '--full', '--quiet', '--output', 'file:' . $fname, '--schema-version', $schemaVersion ],
193 // Performing the dump
196 // Checking the dumped data
197 $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
198 $asserter = $this->getDumpAsserter( $schemaVersion );
200 $asserter->assertDumpStart( $fname );
203 $asserter->assertPageStart(
206 $this->pageTitle1
->getPrefixedText()
208 $asserter->assertRevision(
210 "BackupDumperTestP1Summary1",
213 "0bolhl6ol7i6x0e7yq91gxgaan39j87",
214 "BackupDumperTestP1Text1"
216 $asserter->assertPageEnd();
219 $asserter->assertPageStart(
222 $this->pageTitle2
->getPrefixedText()
224 $asserter->assertRevision(
226 "BackupDumperTestP2Summary1",
229 "jprywrymfhysqllua29tj3sc7z39dl2",
230 "BackupDumperTestP2Text1"
232 $asserter->assertRevision(
234 "BackupDumperTestP2Summary2",
237 "b7vj5ks32po5m1z1t1br4o7scdwwy95",
238 "BackupDumperTestP2Text2",
241 $asserter->assertRevision(
243 "BackupDumperTestP2Summary3",
246 "jfunqmh1ssfb8rs43r19w98k28gg56r",
247 "BackupDumperTestP2Text3",
250 $asserter->assertRevision(
252 "BackupDumperTestP2Summary4 extra",
255 "6o1ciaxa6pybnqprmungwofc4lv00wv",
256 "BackupDumperTestP2Text4 some additional Text",
259 $asserter->assertPageEnd();
262 // -> Page is marked deleted. Hence not visible
265 $asserter->assertPageStart(
267 $this->talk_namespace
,
268 $this->pageTitle4
->getPrefixedText()
270 $asserter->assertRevision(
272 "Talk BackupDumperTestP1 Summary1",
275 "nktofwzd0tl192k3zfepmlzxoax1lpe",
276 "Talk about BackupDumperTestP1 Text1",
278 CONTENT_MODEL_WIKITEXT
,
279 CONTENT_FORMAT_WIKITEXT
,
282 $asserter->assertPageEnd();
284 $asserter->assertDumpEnd();
286 // FIXME: add multi-slot test case!
290 * @dataProvider schemaVersionProvider
292 function testFullStubPlain( $schemaVersion ) {
293 // Preparing the dump
294 $fname = $this->getNewTempFile();
296 $dumper = $this->newDumpBackup(
303 '--schema-version', $schemaVersion,
309 // Performing the dump
312 // Checking the dumped data
313 $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
314 $asserter = $this->getDumpAsserter( $schemaVersion );
316 $asserter->assertDumpStart( $fname );
319 $asserter->assertPageStart(
322 $this->pageTitle1
->getPrefixedText()
324 $asserter->assertRevision(
326 "BackupDumperTestP1Summary1",
329 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
331 $asserter->assertPageEnd();
334 $asserter->assertPageStart(
337 $this->pageTitle2
->getPrefixedText()
339 $asserter->assertRevision(
341 "BackupDumperTestP2Summary1",
344 "jprywrymfhysqllua29tj3sc7z39dl2"
346 $asserter->assertRevision(
348 "BackupDumperTestP2Summary2",
351 "b7vj5ks32po5m1z1t1br4o7scdwwy95",
355 $asserter->assertRevision(
357 "BackupDumperTestP2Summary3",
360 "jfunqmh1ssfb8rs43r19w98k28gg56r",
364 $asserter->assertRevision(
366 "BackupDumperTestP2Summary4 extra",
369 "6o1ciaxa6pybnqprmungwofc4lv00wv",
373 $asserter->assertPageEnd();
376 // -> Page is marked deleted. Hence not visible
379 $asserter->assertPageStart(
381 $this->talk_namespace
,
382 $this->pageTitle4
->getPrefixedText()
384 $asserter->assertRevision(
386 "Talk BackupDumperTestP1 Summary1",
389 "nktofwzd0tl192k3zfepmlzxoax1lpe"
391 $asserter->assertPageEnd();
393 $asserter->assertDumpEnd();
397 * @dataProvider schemaVersionProvider
399 function testCurrentStubPlain( $schemaVersion ) {
400 // Preparing the dump
401 $fname = $this->getNewTempFile();
403 $dumper = $this->newDumpBackup(
404 [ '--output', 'file:' . $fname, '--schema-version', $schemaVersion ],
409 // Performing the dump
410 $dumper->dump( WikiExporter
::CURRENT
, WikiExporter
::STUB
);
412 // Checking the dumped data
413 $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
415 $asserter = $this->getDumpAsserter( $schemaVersion );
416 $asserter->assertDumpStart( $fname );
419 $asserter->assertPageStart(
422 $this->pageTitle1
->getPrefixedText()
424 $asserter->assertRevision(
426 "BackupDumperTestP1Summary1",
429 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
431 $asserter->assertPageEnd();
434 $asserter->assertPageStart(
437 $this->pageTitle2
->getPrefixedText()
439 $asserter->assertRevision(
441 "BackupDumperTestP2Summary4 extra",
444 "6o1ciaxa6pybnqprmungwofc4lv00wv",
448 $asserter->assertPageEnd();
451 // -> Page is marked deleted. Hence not visible
454 $asserter->assertPageStart(
456 $this->talk_namespace
,
457 $this->pageTitle4
->getPrefixedText()
459 $asserter->assertRevision(
461 "Talk BackupDumperTestP1 Summary1",
464 "nktofwzd0tl192k3zfepmlzxoax1lpe"
466 $asserter->assertPageEnd();
468 $asserter->assertDumpEnd();
471 function testCurrentStubGzip() {
472 $this->checkHasGzip();
474 // Preparing the dump
475 $fname = $this->getNewTempFile();
477 $dumper = $this->newDumpBackup(
478 [ '--output', 'gzip:' . $fname ],
483 // Performing the dump
484 $dumper->dump( WikiExporter
::CURRENT
, WikiExporter
::STUB
);
486 // Checking the dumped data
487 $this->gunzip( $fname );
489 $asserter = $this->getDumpAsserter();
490 $asserter->assertDumpStart( $fname );
493 $asserter->assertPageStart(
496 $this->pageTitle1
->getPrefixedText()
498 $asserter->assertRevision(
500 "BackupDumperTestP1Summary1",
503 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
505 $asserter->assertPageEnd();
508 $asserter->assertPageStart(
511 $this->pageTitle2
->getPrefixedText()
513 $asserter->assertRevision(
515 "BackupDumperTestP2Summary4 extra",
518 "6o1ciaxa6pybnqprmungwofc4lv00wv",
522 $asserter->assertPageEnd();
525 // -> Page is marked deleted. Hence not visible
528 $asserter->assertPageStart(
530 $this->talk_namespace
,
531 $this->pageTitle4
->getPrefixedText()
533 $asserter->assertRevision( $this->revId4_1
, "Talk BackupDumperTestP1 Summary1",
534 $this->textId4_1
, 35, "nktofwzd0tl192k3zfepmlzxoax1lpe" );
535 $asserter->assertPageEnd();
537 $asserter->assertDumpEnd();
541 * xmldumps-backup typically performs a single dump that that writes
543 * - gzipped stubs of everything (meta-history)
544 * - gzipped stubs of latest revisions of all pages (meta-current)
545 * - gzipped stubs of latest revisions of all pages of namespage 0
548 * We reproduce such a setup with our mini fixture, although we omit
549 * chunks, and all the other gimmicks of xmldumps-backup.
551 * @dataProvider schemaVersionProvider
553 function testXmlDumpsBackupUseCase( $schemaVersion ) {
554 $this->checkHasGzip();
556 $fnameMetaHistory = $this->getNewTempFile();
557 $fnameMetaCurrent = $this->getNewTempFile();
558 $fnameArticles = $this->getNewTempFile();
560 $dumper = $this->newDumpBackup(
561 [ "--full", "--stub", "--output=gzip:" . $fnameMetaHistory,
562 "--output=gzip:" . $fnameMetaCurrent, "--filter=latest",
563 "--output=gzip:" . $fnameArticles, "--filter=latest",
564 "--filter=notalk", "--filter=namespace:!NS_USER",
565 "--reporting=1000", '--schema-version', $schemaVersion
570 $dumper->reporting
= true;
572 // xmldumps-backup uses reporting. We will not check the exact reported
573 // message, as they are dependent on the processing power of the used
574 // computer. We only check that reporting does not crash the dumping
575 // and that something is reported
576 $dumper->stderr
= fopen( 'php://output', 'a' );
577 if ( $dumper->stderr
=== false ) {
578 $this->fail( "Could not open stream for stderr" );
581 // Performing the dump
582 $dumper->dump( WikiExporter
::FULL
, WikiExporter
::STUB
);
584 $this->assertTrue( fclose( $dumper->stderr
), "Closing stderr handle" );
586 // Checking meta-history -------------------------------------------------
588 $this->gunzip( $fnameMetaHistory );
589 $this->assertDumpSchema( $fnameMetaHistory, $this->getXmlSchemaPath( $schemaVersion ) );
591 $asserter = $this->getDumpAsserter( $schemaVersion );
592 $asserter->assertDumpStart( $fnameMetaHistory );
595 $asserter->assertPageStart(
598 $this->pageTitle1
->getPrefixedText()
600 $asserter->assertRevision(
602 "BackupDumperTestP1Summary1",
605 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
607 $asserter->assertPageEnd();
610 $asserter->assertPageStart(
613 $this->pageTitle2
->getPrefixedText()
615 $asserter->assertRevision(
617 "BackupDumperTestP2Summary1",
620 "jprywrymfhysqllua29tj3sc7z39dl2"
622 $asserter->assertRevision(
624 "BackupDumperTestP2Summary2",
627 "b7vj5ks32po5m1z1t1br4o7scdwwy95",
631 $asserter->assertRevision(
633 "BackupDumperTestP2Summary3",
636 "jfunqmh1ssfb8rs43r19w98k28gg56r",
640 $asserter->assertRevision(
642 "BackupDumperTestP2Summary4 extra",
645 "6o1ciaxa6pybnqprmungwofc4lv00wv",
649 $asserter->assertPageEnd();
652 // -> Page is marked deleted. Hence not visible
655 $asserter->assertPageStart(
657 $this->talk_namespace
,
658 $this->pageTitle4
->getPrefixedText( $schemaVersion )
660 $asserter->assertRevision(
662 "Talk BackupDumperTestP1 Summary1",
665 "nktofwzd0tl192k3zfepmlzxoax1lpe"
667 $asserter->assertPageEnd();
669 $asserter->assertDumpEnd();
671 // Checking meta-current -------------------------------------------------
673 $this->gunzip( $fnameMetaCurrent );
674 $this->assertDumpSchema( $fnameMetaCurrent, $this->getXmlSchemaPath( $schemaVersion ) );
676 $asserter = $this->getDumpAsserter( $schemaVersion );
677 $asserter->assertDumpStart( $fnameMetaCurrent );
680 $asserter->assertPageStart(
683 $this->pageTitle1
->getPrefixedText()
685 $asserter->assertRevision(
687 "BackupDumperTestP1Summary1",
690 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
692 $asserter->assertPageEnd();
695 $asserter->assertPageStart(
698 $this->pageTitle2
->getPrefixedText()
700 $asserter->assertRevision(
702 "BackupDumperTestP2Summary4 extra",
705 "6o1ciaxa6pybnqprmungwofc4lv00wv",
709 $asserter->assertPageEnd();
712 // -> Page is marked deleted. Hence not visible
715 $asserter->assertPageStart(
717 $this->talk_namespace
,
718 $this->pageTitle4
->getPrefixedText()
720 $asserter->assertRevision(
722 "Talk BackupDumperTestP1 Summary1",
725 "nktofwzd0tl192k3zfepmlzxoax1lpe"
727 $asserter->assertPageEnd();
729 $asserter->assertDumpEnd();
731 // Checking articles -------------------------------------------------
733 $this->gunzip( $fnameArticles );
734 $this->assertDumpSchema( $fnameArticles, $this->getXmlSchemaPath( $schemaVersion ) );
736 $asserter = $this->getDumpAsserter( $schemaVersion );
737 $asserter->assertDumpStart( $fnameArticles );
740 $asserter->assertPageStart(
743 $this->pageTitle1
->getPrefixedText()
745 $asserter->assertRevision(
747 "BackupDumperTestP1Summary1",
750 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
752 $asserter->assertPageEnd();
755 $asserter->assertPageStart(
758 $this->pageTitle2
->getPrefixedText()
760 $asserter->assertRevision(
762 "BackupDumperTestP2Summary4 extra",
765 "6o1ciaxa6pybnqprmungwofc4lv00wv",
769 $asserter->assertPageEnd();
772 // -> Page is marked deleted. Hence not visible
775 // -> Page is not in $this->namespace. Hence not visible
777 $asserter->assertDumpEnd();
779 $this->expectETAOutput();