3 namespace MediaWiki\Tests\Maintenance
;
7 use MediaWiki\MediaWikiServices
;
12 use Wikimedia\Rdbms\IDatabase
;
13 use Wikimedia\Rdbms\LoadBalancer
;
17 * Tests for page dumps of BackupDumper
21 * @covers BackupDumper
23 class BackupDumperPageTest
extends DumpTestCase
{
25 // We'll add several pages, revision and texts. The following variables hold the
27 private $pageId1, $pageId2, $pageId3, $pageId4;
28 private $pageTitle1, $pageTitle2, $pageTitle3, $pageTitle4;
29 private $revId1_1, $textId1_1;
30 private $revId2_1, $textId2_1, $revId2_2, $textId2_2;
31 private $revId2_3, $textId2_3, $revId2_4, $textId2_4;
32 private $revId3_1, $textId3_1, $revId3_2, $textId3_2;
33 private $revId4_1, $textId4_1;
34 private $namespace, $talk_namespace;
37 * @var LoadBalancer|null
39 private $streamingLoadBalancer = null;
41 function addDBData() {
42 // be sure, titles created here using english namespace names
43 $this->setContentLang( 'en' );
45 $this->tablesUsed
[] = 'page';
46 $this->tablesUsed
[] = 'revision';
47 $this->tablesUsed
[] = 'ip_changes';
48 $this->tablesUsed
[] = 'text';
51 $this->namespace = $this->getDefaultWikitextNS();
52 $this->talk_namespace
= NS_TALK
;
54 if ( $this->namespace === $this->talk_namespace
) {
55 // @todo work around this.
56 throw new MWException( "The default wikitext namespace is the talk namespace. "
57 . " We can't currently deal with that." );
60 $this->pageTitle1
= Title
::newFromText( 'BackupDumperTestP1', $this->namespace );
61 $page = WikiPage
::factory( $this->pageTitle1
);
62 list( $this->revId1_1
, $this->textId1_1
) = $this->addRevision( $page,
63 "BackupDumperTestP1Text1", "BackupDumperTestP1Summary1" );
64 $this->pageId1
= $page->getId();
66 $this->pageTitle2
= Title
::newFromText( 'BackupDumperTestP2', $this->namespace );
67 $page = WikiPage
::factory( $this->pageTitle2
);
68 list( $this->revId2_1
, $this->textId2_1
) = $this->addRevision( $page,
69 "BackupDumperTestP2Text1", "BackupDumperTestP2Summary1" );
70 list( $this->revId2_2
, $this->textId2_2
) = $this->addRevision( $page,
71 "BackupDumperTestP2Text2", "BackupDumperTestP2Summary2" );
72 list( $this->revId2_3
, $this->textId2_3
) = $this->addRevision( $page,
73 "BackupDumperTestP2Text3", "BackupDumperTestP2Summary3" );
74 list( $this->revId2_4
, $this->textId2_4
) = $this->addRevision( $page,
75 "BackupDumperTestP2Text4 some additional Text ",
76 "BackupDumperTestP2Summary4 extra " );
77 $this->pageId2
= $page->getId();
79 $this->pageTitle3
= Title
::newFromText( 'BackupDumperTestP3', $this->namespace );
80 $page = WikiPage
::factory( $this->pageTitle3
);
81 list( $this->revId3_1
, $this->textId3_1
) = $this->addRevision( $page,
82 "BackupDumperTestP3Text1", "BackupDumperTestP2Summary1" );
83 list( $this->revId3_2
, $this->textId3_2
) = $this->addRevision( $page,
84 "BackupDumperTestP3Text2", "BackupDumperTestP2Summary2" );
85 $this->pageId3
= $page->getId();
86 $page->doDeleteArticle( "Testing ;)" );
88 $this->pageTitle4
= Title
::newFromText( 'BackupDumperTestP1', $this->talk_namespace
);
89 $page = WikiPage
::factory( $this->pageTitle4
);
90 list( $this->revId4_1
, $this->textId4_1
) = $this->addRevision( $page,
91 "Talk about BackupDumperTestP1 Text1",
92 "Talk BackupDumperTestP1 Summary1" );
93 $this->pageId4
= $page->getId();
94 } catch ( Exception
$e ) {
95 // We'd love to pass $e directly. However, ... see
96 // documentation of exceptionFromAddDBData in
98 $this->exceptionFromAddDBData
= $e;
102 protected function setUp() {
105 // Since we will restrict dumping by page ranges (to allow
106 // working tests, even if the db gets prepopulated by a base
107 // class), we have to assert, that the page id are consecutively
110 [ $this->pageId2
, $this->pageId3
, $this->pageId4
],
111 [ $this->pageId1 +
1, $this->pageId2 +
1, $this->pageId3 +
1 ],
112 "Page ids increasing without holes" );
115 function tearDown() {
118 if ( isset( $this->streamingLoadBalancer
) ) {
119 $this->streamingLoadBalancer
->closeAll();
124 * Returns a new database connection which is separate from the conenctions returned
125 * by the default LoadBalancer instance.
129 private function newStreamingDBConnection() {
130 // Create a *new* LoadBalancer, so no connections are shared
131 if ( !$this->streamingLoadBalancer
) {
132 $lbFactory = MediaWikiServices
::getInstance()->getDBLoadBalancerFactory();
134 $this->streamingLoadBalancer
= $lbFactory->newMainLB();
137 $db = $this->streamingLoadBalancer
->getConnection( DB_REPLICA
);
139 // Make sure the DB connection has the fake table clones and the fake table prefix
140 MediaWikiTestCase
::setupDatabaseWithTestPrefix( $db );
142 // Make sure the DB connection has all the test data
143 $this->copyTestData( $this->db
, $db );
150 * @param int $startId
155 private function newDumpBackup( $argv, $startId, $endId ) {
156 $dumper = new DumpBackup( $argv );
157 $dumper->startId
= $startId;
158 $dumper->endId
= $endId;
159 $dumper->reporting
= false;
161 // NOTE: The copyTestData() method used by newStreamingDBConnection()
162 // doesn't work with SQLite (T217607).
163 // But DatabaseSqlite doesn't support streaming anyway, so just skip that part.
164 if ( $this->db
->getType() === 'sqlite' ) {
165 $dumper->setDB( $this->db
);
167 $dumper->setDB( $this->newStreamingDBConnection() );
173 public function schemaVersionProvider() {
178 * @dataProvider schemaVersionProvider
180 function testFullTextPlain( $schemaVersion ) {
181 // Preparing the dump
182 $fname = $this->getNewTempFile();
184 $dumper = $this->newDumpBackup(
185 [ '--full', '--quiet', '--output', 'file:' . $fname, '--schema-version', $schemaVersion ],
190 // Performing the dump
193 // Checking the dumped data
194 $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
195 $asserter = $this->getDumpAsserter( $schemaVersion );
197 $asserter->assertDumpStart( $fname );
200 $asserter->assertPageStart(
203 $this->pageTitle1
->getPrefixedText()
205 $asserter->assertRevision(
207 "BackupDumperTestP1Summary1",
210 "0bolhl6ol7i6x0e7yq91gxgaan39j87",
211 "BackupDumperTestP1Text1"
213 $asserter->assertPageEnd();
216 $asserter->assertPageStart(
219 $this->pageTitle2
->getPrefixedText()
221 $asserter->assertRevision(
223 "BackupDumperTestP2Summary1",
226 "jprywrymfhysqllua29tj3sc7z39dl2",
227 "BackupDumperTestP2Text1"
229 $asserter->assertRevision(
231 "BackupDumperTestP2Summary2",
234 "b7vj5ks32po5m1z1t1br4o7scdwwy95",
235 "BackupDumperTestP2Text2",
238 $asserter->assertRevision(
240 "BackupDumperTestP2Summary3",
243 "jfunqmh1ssfb8rs43r19w98k28gg56r",
244 "BackupDumperTestP2Text3",
247 $asserter->assertRevision(
249 "BackupDumperTestP2Summary4 extra",
252 "6o1ciaxa6pybnqprmungwofc4lv00wv",
253 "BackupDumperTestP2Text4 some additional Text",
256 $asserter->assertPageEnd();
259 // -> Page is marked deleted. Hence not visible
262 $asserter->assertPageStart(
264 $this->talk_namespace
,
265 $this->pageTitle4
->getPrefixedText()
267 $asserter->assertRevision(
269 "Talk BackupDumperTestP1 Summary1",
272 "nktofwzd0tl192k3zfepmlzxoax1lpe",
273 "Talk about BackupDumperTestP1 Text1",
275 CONTENT_MODEL_WIKITEXT
,
276 CONTENT_FORMAT_WIKITEXT
,
279 $asserter->assertPageEnd();
281 $asserter->assertDumpEnd();
283 // FIXME: add multi-slot test case!
287 * @dataProvider schemaVersionProvider
289 function testFullStubPlain( $schemaVersion ) {
290 // Preparing the dump
291 $fname = $this->getNewTempFile();
293 $dumper = $this->newDumpBackup(
300 '--schema-version', $schemaVersion,
306 // Performing the dump
309 // Checking the dumped data
310 $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
311 $asserter = $this->getDumpAsserter( $schemaVersion );
313 $asserter->assertDumpStart( $fname );
316 $asserter->assertPageStart(
319 $this->pageTitle1
->getPrefixedText()
321 $asserter->assertRevision(
323 "BackupDumperTestP1Summary1",
326 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
328 $asserter->assertPageEnd();
331 $asserter->assertPageStart(
334 $this->pageTitle2
->getPrefixedText()
336 $asserter->assertRevision(
338 "BackupDumperTestP2Summary1",
341 "jprywrymfhysqllua29tj3sc7z39dl2"
343 $asserter->assertRevision(
345 "BackupDumperTestP2Summary2",
348 "b7vj5ks32po5m1z1t1br4o7scdwwy95",
352 $asserter->assertRevision(
354 "BackupDumperTestP2Summary3",
357 "jfunqmh1ssfb8rs43r19w98k28gg56r",
361 $asserter->assertRevision(
363 "BackupDumperTestP2Summary4 extra",
366 "6o1ciaxa6pybnqprmungwofc4lv00wv",
370 $asserter->assertPageEnd();
373 // -> Page is marked deleted. Hence not visible
376 $asserter->assertPageStart(
378 $this->talk_namespace
,
379 $this->pageTitle4
->getPrefixedText()
381 $asserter->assertRevision(
383 "Talk BackupDumperTestP1 Summary1",
386 "nktofwzd0tl192k3zfepmlzxoax1lpe"
388 $asserter->assertPageEnd();
390 $asserter->assertDumpEnd();
394 * @dataProvider schemaVersionProvider
396 function testCurrentStubPlain( $schemaVersion ) {
397 // Preparing the dump
398 $fname = $this->getNewTempFile();
400 $dumper = $this->newDumpBackup(
401 [ '--output', 'file:' . $fname, '--schema-version', $schemaVersion ],
406 // Performing the dump
407 $dumper->dump( WikiExporter
::CURRENT
, WikiExporter
::STUB
);
409 // Checking the dumped data
410 $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
412 $asserter = $this->getDumpAsserter( $schemaVersion );
413 $asserter->assertDumpStart( $fname );
416 $asserter->assertPageStart(
419 $this->pageTitle1
->getPrefixedText()
421 $asserter->assertRevision(
423 "BackupDumperTestP1Summary1",
426 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
428 $asserter->assertPageEnd();
431 $asserter->assertPageStart(
434 $this->pageTitle2
->getPrefixedText()
436 $asserter->assertRevision(
438 "BackupDumperTestP2Summary4 extra",
441 "6o1ciaxa6pybnqprmungwofc4lv00wv",
445 $asserter->assertPageEnd();
448 // -> Page is marked deleted. Hence not visible
451 $asserter->assertPageStart(
453 $this->talk_namespace
,
454 $this->pageTitle4
->getPrefixedText()
456 $asserter->assertRevision(
458 "Talk BackupDumperTestP1 Summary1",
461 "nktofwzd0tl192k3zfepmlzxoax1lpe"
463 $asserter->assertPageEnd();
465 $asserter->assertDumpEnd();
468 function testCurrentStubGzip() {
469 $this->checkHasGzip();
471 // Preparing the dump
472 $fname = $this->getNewTempFile();
474 $dumper = $this->newDumpBackup(
475 [ '--output', 'gzip:' . $fname ],
480 // Performing the dump
481 $dumper->dump( WikiExporter
::CURRENT
, WikiExporter
::STUB
);
483 // Checking the dumped data
484 $this->gunzip( $fname );
486 $asserter = $this->getDumpAsserter();
487 $asserter->assertDumpStart( $fname );
490 $asserter->assertPageStart(
493 $this->pageTitle1
->getPrefixedText()
495 $asserter->assertRevision(
497 "BackupDumperTestP1Summary1",
500 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
502 $asserter->assertPageEnd();
505 $asserter->assertPageStart(
508 $this->pageTitle2
->getPrefixedText()
510 $asserter->assertRevision(
512 "BackupDumperTestP2Summary4 extra",
515 "6o1ciaxa6pybnqprmungwofc4lv00wv",
519 $asserter->assertPageEnd();
522 // -> Page is marked deleted. Hence not visible
525 $asserter->assertPageStart(
527 $this->talk_namespace
,
528 $this->pageTitle4
->getPrefixedText()
530 $asserter->assertRevision( $this->revId4_1
, "Talk BackupDumperTestP1 Summary1",
531 $this->textId4_1
, 35, "nktofwzd0tl192k3zfepmlzxoax1lpe" );
532 $asserter->assertPageEnd();
534 $asserter->assertDumpEnd();
538 * xmldumps-backup typically performs a single dump that that writes
540 * - gzipped stubs of everything (meta-history)
541 * - gzipped stubs of latest revisions of all pages (meta-current)
542 * - gzipped stubs of latest revisions of all pages of namespage 0
545 * We reproduce such a setup with our mini fixture, although we omit
546 * chunks, and all the other gimmicks of xmldumps-backup.
548 * @dataProvider schemaVersionProvider
550 function testXmlDumpsBackupUseCase( $schemaVersion ) {
551 $this->checkHasGzip();
553 $fnameMetaHistory = $this->getNewTempFile();
554 $fnameMetaCurrent = $this->getNewTempFile();
555 $fnameArticles = $this->getNewTempFile();
557 $dumper = $this->newDumpBackup(
558 [ "--full", "--stub", "--output=gzip:" . $fnameMetaHistory,
559 "--output=gzip:" . $fnameMetaCurrent, "--filter=latest",
560 "--output=gzip:" . $fnameArticles, "--filter=latest",
561 "--filter=notalk", "--filter=namespace:!NS_USER",
562 "--reporting=1000", '--schema-version', $schemaVersion
567 $dumper->reporting
= true;
569 // xmldumps-backup uses reporting. We will not check the exact reported
570 // message, as they are dependent on the processing power of the used
571 // computer. We only check that reporting does not crash the dumping
572 // and that something is reported
573 $dumper->stderr
= fopen( 'php://output', 'a' );
574 if ( $dumper->stderr
=== false ) {
575 $this->fail( "Could not open stream for stderr" );
578 // Performing the dump
579 $dumper->dump( WikiExporter
::FULL
, WikiExporter
::STUB
);
581 $this->assertTrue( fclose( $dumper->stderr
), "Closing stderr handle" );
583 // Checking meta-history -------------------------------------------------
585 $this->gunzip( $fnameMetaHistory );
586 $this->assertDumpSchema( $fnameMetaHistory, $this->getXmlSchemaPath( $schemaVersion ) );
588 $asserter = $this->getDumpAsserter( $schemaVersion );
589 $asserter->assertDumpStart( $fnameMetaHistory );
592 $asserter->assertPageStart(
595 $this->pageTitle1
->getPrefixedText()
597 $asserter->assertRevision(
599 "BackupDumperTestP1Summary1",
602 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
604 $asserter->assertPageEnd();
607 $asserter->assertPageStart(
610 $this->pageTitle2
->getPrefixedText()
612 $asserter->assertRevision(
614 "BackupDumperTestP2Summary1",
617 "jprywrymfhysqllua29tj3sc7z39dl2"
619 $asserter->assertRevision(
621 "BackupDumperTestP2Summary2",
624 "b7vj5ks32po5m1z1t1br4o7scdwwy95",
628 $asserter->assertRevision(
630 "BackupDumperTestP2Summary3",
633 "jfunqmh1ssfb8rs43r19w98k28gg56r",
637 $asserter->assertRevision(
639 "BackupDumperTestP2Summary4 extra",
642 "6o1ciaxa6pybnqprmungwofc4lv00wv",
646 $asserter->assertPageEnd();
649 // -> Page is marked deleted. Hence not visible
652 $asserter->assertPageStart(
654 $this->talk_namespace
,
655 $this->pageTitle4
->getPrefixedText( $schemaVersion )
657 $asserter->assertRevision(
659 "Talk BackupDumperTestP1 Summary1",
662 "nktofwzd0tl192k3zfepmlzxoax1lpe"
664 $asserter->assertPageEnd();
666 $asserter->assertDumpEnd();
668 // Checking meta-current -------------------------------------------------
670 $this->gunzip( $fnameMetaCurrent );
671 $this->assertDumpSchema( $fnameMetaCurrent, $this->getXmlSchemaPath( $schemaVersion ) );
673 $asserter = $this->getDumpAsserter( $schemaVersion );
674 $asserter->assertDumpStart( $fnameMetaCurrent );
677 $asserter->assertPageStart(
680 $this->pageTitle1
->getPrefixedText()
682 $asserter->assertRevision(
684 "BackupDumperTestP1Summary1",
687 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
689 $asserter->assertPageEnd();
692 $asserter->assertPageStart(
695 $this->pageTitle2
->getPrefixedText()
697 $asserter->assertRevision(
699 "BackupDumperTestP2Summary4 extra",
702 "6o1ciaxa6pybnqprmungwofc4lv00wv",
706 $asserter->assertPageEnd();
709 // -> Page is marked deleted. Hence not visible
712 $asserter->assertPageStart(
714 $this->talk_namespace
,
715 $this->pageTitle4
->getPrefixedText()
717 $asserter->assertRevision(
719 "Talk BackupDumperTestP1 Summary1",
722 "nktofwzd0tl192k3zfepmlzxoax1lpe"
724 $asserter->assertPageEnd();
726 $asserter->assertDumpEnd();
728 // Checking articles -------------------------------------------------
730 $this->gunzip( $fnameArticles );
731 $this->assertDumpSchema( $fnameArticles, $this->getXmlSchemaPath( $schemaVersion ) );
733 $asserter = $this->getDumpAsserter( $schemaVersion );
734 $asserter->assertDumpStart( $fnameArticles );
737 $asserter->assertPageStart(
740 $this->pageTitle1
->getPrefixedText()
742 $asserter->assertRevision(
744 "BackupDumperTestP1Summary1",
747 "0bolhl6ol7i6x0e7yq91gxgaan39j87"
749 $asserter->assertPageEnd();
752 $asserter->assertPageStart(
755 $this->pageTitle2
->getPrefixedText()
757 $asserter->assertRevision(
759 "BackupDumperTestP2Summary4 extra",
762 "6o1ciaxa6pybnqprmungwofc4lv00wv",
766 $asserter->assertPageEnd();
769 // -> Page is marked deleted. Hence not visible
772 // -> Page is not in $this->namespace. Hence not visible
774 $asserter->assertDumpEnd();
776 $this->expectETAOutput();