MediaWiki  REL1_22
backupTextPassTest.php
Go to the documentation of this file.
00001 <?php
00002 
00003 require_once __DIR__ . "/../../../maintenance/backupTextPass.inc";
00004 
00011 class TextPassDumperTest extends DumpTestCase {
00012 
00013     // We'll add several pages, revision and texts. The following variables hold the
00014     // corresponding ids.
00015     private $pageId1, $pageId2, $pageId3, $pageId4;
00016     private static $numOfPages = 4;
00017     private $revId1_1, $textId1_1;
00018     private $revId2_1, $textId2_1, $revId2_2, $textId2_2;
00019     private $revId2_3, $textId2_3, $revId2_4, $textId2_4;
00020     private $revId3_1, $textId3_1, $revId3_2, $textId3_2;
00021     private $revId4_1, $textId4_1;
00022     private static $numOfRevs = 8;
00023 
00024     function addDBData() {
00025         $this->tablesUsed[] = 'page';
00026         $this->tablesUsed[] = 'revision';
00027         $this->tablesUsed[] = 'text';
00028 
00029         $ns = $this->getDefaultWikitextNS();
00030 
00031         try {
00032             // Simple page
00033             $title = Title::newFromText( 'BackupDumperTestP1', $ns );
00034             $page = WikiPage::factory( $title );
00035             list( $this->revId1_1, $this->textId1_1 ) = $this->addRevision( $page,
00036                 "BackupDumperTestP1Text1", "BackupDumperTestP1Summary1" );
00037             $this->pageId1 = $page->getId();
00038 
00039             // Page with more than one revision
00040             $title = Title::newFromText( 'BackupDumperTestP2', $ns );
00041             $page = WikiPage::factory( $title );
00042             list( $this->revId2_1, $this->textId2_1 ) = $this->addRevision( $page,
00043                 "BackupDumperTestP2Text1", "BackupDumperTestP2Summary1" );
00044             list( $this->revId2_2, $this->textId2_2 ) = $this->addRevision( $page,
00045                 "BackupDumperTestP2Text2", "BackupDumperTestP2Summary2" );
00046             list( $this->revId2_3, $this->textId2_3 ) = $this->addRevision( $page,
00047                 "BackupDumperTestP2Text3", "BackupDumperTestP2Summary3" );
00048             list( $this->revId2_4, $this->textId2_4 ) = $this->addRevision( $page,
00049                 "BackupDumperTestP2Text4 some additional Text  ",
00050                 "BackupDumperTestP2Summary4 extra " );
00051             $this->pageId2 = $page->getId();
00052 
00053             // Deleted page.
00054             $title = Title::newFromText( 'BackupDumperTestP3', $ns );
00055             $page = WikiPage::factory( $title );
00056             list( $this->revId3_1, $this->textId3_1 ) = $this->addRevision( $page,
00057                 "BackupDumperTestP3Text1", "BackupDumperTestP2Summary1" );
00058             list( $this->revId3_2, $this->textId3_2 ) = $this->addRevision( $page,
00059                 "BackupDumperTestP3Text2", "BackupDumperTestP2Summary2" );
00060             $this->pageId3 = $page->getId();
00061             $page->doDeleteArticle( "Testing ;)" );
00062 
00063             // Page from non-default namespace
00064 
00065             if ( $ns === NS_TALK ) {
00066                 // @todo work around this.
00067                 throw new MWException( "The default wikitext namespace is the talk namespace. "
00068                     . " We can't currently deal with that." );
00069             }
00070 
00071             $title = Title::newFromText( 'BackupDumperTestP1', NS_TALK );
00072             $page = WikiPage::factory( $title );
00073             list( $this->revId4_1, $this->textId4_1 ) = $this->addRevision( $page,
00074                 "Talk about BackupDumperTestP1 Text1",
00075                 "Talk BackupDumperTestP1 Summary1" );
00076             $this->pageId4 = $page->getId();
00077         } catch ( Exception $e ) {
00078             // We'd love to pass $e directly. However, ... see
00079             // documentation of exceptionFromAddDBData in
00080             // DumpTestCase
00081             $this->exceptionFromAddDBData = $e;
00082         }
00083     }
00084 
00085     protected function setUp() {
00086         parent::setUp();
00087 
00088         // Since we will restrict dumping by page ranges (to allow
00089         // working tests, even if the db gets prepopulated by a base
00090         // class), we have to assert, that the page id are consecutively
00091         // increasing
00092         $this->assertEquals(
00093             array( $this->pageId2, $this->pageId3, $this->pageId4 ),
00094             array( $this->pageId1 + 1, $this->pageId2 + 1, $this->pageId3 + 1 ),
00095             "Page ids increasing without holes" );
00096     }
00097 
00098     function testPlain() {
00099         // Setting up the dump
00100         $nameStub = $this->setUpStub();
00101         $nameFull = $this->getNewTempFile();
00102         $dumper = new TextPassDumper( array( "--stub=file:" . $nameStub,
00103             "--output=file:" . $nameFull ) );
00104         $dumper->reporting = false;
00105         $dumper->setDb( $this->db );
00106 
00107         // Performing the dump
00108         $dumper->dump( WikiExporter::FULL, WikiExporter::TEXT );
00109 
00110         // Checking for correctness of the dumped data
00111         $this->assertDumpStart( $nameFull );
00112 
00113         // Page 1
00114         $this->assertPageStart( $this->pageId1, NS_MAIN, "BackupDumperTestP1" );
00115         $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
00116             $this->textId1_1, false, "0bolhl6ol7i6x0e7yq91gxgaan39j87",
00117             "BackupDumperTestP1Text1" );
00118         $this->assertPageEnd();
00119 
00120         // Page 2
00121         $this->assertPageStart( $this->pageId2, NS_MAIN, "BackupDumperTestP2" );
00122         $this->assertRevision( $this->revId2_1, "BackupDumperTestP2Summary1",
00123             $this->textId2_1, false, "jprywrymfhysqllua29tj3sc7z39dl2",
00124             "BackupDumperTestP2Text1" );
00125         $this->assertRevision( $this->revId2_2, "BackupDumperTestP2Summary2",
00126             $this->textId2_2, false, "b7vj5ks32po5m1z1t1br4o7scdwwy95",
00127             "BackupDumperTestP2Text2", $this->revId2_1 );
00128         $this->assertRevision( $this->revId2_3, "BackupDumperTestP2Summary3",
00129             $this->textId2_3, false, "jfunqmh1ssfb8rs43r19w98k28gg56r",
00130             "BackupDumperTestP2Text3", $this->revId2_2 );
00131         $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
00132             $this->textId2_4, false, "6o1ciaxa6pybnqprmungwofc4lv00wv",
00133             "BackupDumperTestP2Text4 some additional Text", $this->revId2_3 );
00134         $this->assertPageEnd();
00135 
00136         // Page 3
00137         // -> Page is marked deleted. Hence not visible
00138 
00139         // Page 4
00140         $this->assertPageStart( $this->pageId4, NS_TALK, "Talk:BackupDumperTestP1" );
00141         $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
00142             $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
00143             "Talk about BackupDumperTestP1 Text1" );
00144         $this->assertPageEnd();
00145 
00146         $this->assertDumpEnd();
00147     }
00148 
00149     function testPrefetchPlain() {
00150         // The mapping between ids and text, for the hits of the prefetch mock
00151         $prefetchMap = array(
00152             array( $this->pageId1, $this->revId1_1, "Prefetch_________1Text1" ),
00153             array( $this->pageId2, $this->revId2_3, "Prefetch_________2Text3" )
00154         );
00155 
00156         // The mock itself
00157         $prefetchMock = $this->getMock( 'BaseDump', array( 'prefetch' ), array(), '', false );
00158         $prefetchMock->expects( $this->exactly( 6 ) )
00159             ->method( 'prefetch' )
00160             ->will( $this->returnValueMap( $prefetchMap ) );
00161 
00162         // Setting up of the dump
00163         $nameStub = $this->setUpStub();
00164         $nameFull = $this->getNewTempFile();
00165         $dumper = new TextPassDumper( array( "--stub=file:"
00166             . $nameStub, "--output=file:" . $nameFull ) );
00167         $dumper->prefetch = $prefetchMock;
00168         $dumper->reporting = false;
00169         $dumper->setDb( $this->db );
00170 
00171         // Performing the dump
00172         $dumper->dump( WikiExporter::FULL, WikiExporter::TEXT );
00173 
00174         // Checking for correctness of the dumped data
00175         $this->assertDumpStart( $nameFull );
00176 
00177         // Page 1
00178         $this->assertPageStart( $this->pageId1, NS_MAIN, "BackupDumperTestP1" );
00179         // Prefetch kicks in. This is still the SHA-1 of the original text,
00180         // But the actual text (with different SHA-1) comes from prefetch.
00181         $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
00182             $this->textId1_1, false, "0bolhl6ol7i6x0e7yq91gxgaan39j87",
00183             "Prefetch_________1Text1" );
00184         $this->assertPageEnd();
00185 
00186         // Page 2
00187         $this->assertPageStart( $this->pageId2, NS_MAIN, "BackupDumperTestP2" );
00188         $this->assertRevision( $this->revId2_1, "BackupDumperTestP2Summary1",
00189             $this->textId2_1, false, "jprywrymfhysqllua29tj3sc7z39dl2",
00190             "BackupDumperTestP2Text1" );
00191         $this->assertRevision( $this->revId2_2, "BackupDumperTestP2Summary2",
00192             $this->textId2_2, false, "b7vj5ks32po5m1z1t1br4o7scdwwy95",
00193             "BackupDumperTestP2Text2", $this->revId2_1 );
00194         // Prefetch kicks in. This is still the SHA-1 of the original text,
00195         // But the actual text (with different SHA-1) comes from prefetch.
00196         $this->assertRevision( $this->revId2_3, "BackupDumperTestP2Summary3",
00197             $this->textId2_3, false, "jfunqmh1ssfb8rs43r19w98k28gg56r",
00198             "Prefetch_________2Text3", $this->revId2_2 );
00199         $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
00200             $this->textId2_4, false, "6o1ciaxa6pybnqprmungwofc4lv00wv",
00201             "BackupDumperTestP2Text4 some additional Text", $this->revId2_3 );
00202         $this->assertPageEnd();
00203 
00204         // Page 3
00205         // -> Page is marked deleted. Hence not visible
00206 
00207         // Page 4
00208         $this->assertPageStart( $this->pageId4, NS_TALK, "Talk:BackupDumperTestP1" );
00209         $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
00210             $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
00211             "Talk about BackupDumperTestP1 Text1" );
00212         $this->assertPageEnd();
00213 
00214         $this->assertDumpEnd();
00215     }
00216 
00224     private function checkpointHelper( $checkpointFormat = "file" ) {
00225         // Getting temporary names
00226         $nameStub = $this->getNewTempFile();
00227         $nameOutputDir = $this->getNewTempDirectory();
00228 
00229         $stderr = fopen( 'php://output', 'a' );
00230         if ( $stderr === false ) {
00231             $this->fail( "Could not open stream for stderr" );
00232         }
00233 
00234         $iterations = 32; // We'll start with that many iterations of revisions in stub
00235         $lastDuration = 0;
00236         $minDuration = 2; // We want the dump to take at least this many seconds
00237         $checkpointAfter = 0.5; // Generate checkpoint after this many seconds
00238 
00239         // Until a dump takes at least $minDuration seconds, perform a dump and check
00240         // duration. If the dump did not take long enough increase the iteration
00241         // count, to generate a bigger stub file next time.
00242         while ( $lastDuration < $minDuration ) {
00243 
00244             // Setting up the dump
00245             wfRecursiveRemoveDir( $nameOutputDir );
00246             $this->assertTrue( wfMkdirParents( $nameOutputDir ),
00247                 "Creating temporary output directory " );
00248             $this->setUpStub( $nameStub, $iterations );
00249             $dumper = new TextPassDumper( array( "--stub=file:" . $nameStub,
00250                 "--output=" . $checkpointFormat . ":" . $nameOutputDir . "/full",
00251                 "--maxtime=1" /*This is in minutes. Fixup is below*/,
00252                 "--checkpointfile=checkpoint-%s-%s.xml.gz" ) );
00253             $dumper->setDb( $this->db );
00254             $dumper->maxTimeAllowed = $checkpointAfter; // Patching maxTime from 1 minute
00255             $dumper->stderr = $stderr;
00256 
00257             // The actual dump and taking time
00258             $ts_before = microtime( true );
00259             $dumper->dump( WikiExporter::FULL, WikiExporter::TEXT );
00260             $ts_after = microtime( true );
00261             $lastDuration = $ts_after - $ts_before;
00262 
00263             // Handling increasing the iteration count for the stubs
00264             if ( $lastDuration < $minDuration ) {
00265                 $old_iterations = $iterations;
00266                 if ( $lastDuration > 0.2 ) {
00267                     // lastDuration is big enough, to allow an educated guess
00268                     $factor = ( $minDuration + 0.5 ) / $lastDuration;
00269                     if ( ( $factor > 1.1 ) && ( $factor < 100 ) ) {
00270                         // educated guess is reasonable
00271                         $iterations = (int)( $iterations * $factor );
00272                     }
00273                 }
00274 
00275                 if ( $old_iterations == $iterations ) {
00276                     // Heuristics were not applied, so we just *2.
00277                     $iterations *= 2;
00278                 }
00279 
00280                 $this->assertLessThan( 50000, $iterations,
00281                     "Emergency stop against infinitely increasing iteration "
00282                         . "count ( last duration: $lastDuration )" );
00283             }
00284         }
00285 
00286         // The dump (hopefully) did take long enough to produce more than one
00287         // checkpoint file.
00288         //
00289         // We now check all the checkpoint files for validity.
00290 
00291         $files = scandir( $nameOutputDir );
00292         $this->assertTrue( asort( $files ), "Sorting files in temporary directory" );
00293         $fileOpened = false;
00294         $lookingForPage = 1;
00295         $checkpointFiles = 0;
00296 
00297         // Each run of the following loop body tries to handle exactly 1 /page/ (not
00298         // iteration of stub content). $i is only increased after having treated page 4.
00299         for ( $i = 0; $i < $iterations; ) {
00300 
00301             // 1. Assuring a file is opened and ready. Skipping across header if
00302             //    necessary.
00303             if ( !$fileOpened ) {
00304                 $this->assertNotEmpty( $files, "No more existing dump files, "
00305                     . "but not yet all pages found" );
00306                 $fname = array_shift( $files );
00307                 while ( $fname == "." || $fname == ".." ) {
00308                     $this->assertNotEmpty( $files, "No more existing dump"
00309                         . " files, but not yet all pages found" );
00310                     $fname = array_shift( $files );
00311                 }
00312                 if ( $checkpointFormat == "gzip" ) {
00313                     $this->gunzip( $nameOutputDir . "/" . $fname );
00314                 }
00315                 $this->assertDumpStart( $nameOutputDir . "/" . $fname );
00316                 $fileOpened = true;
00317                 $checkpointFiles++;
00318             }
00319 
00320             // 2. Performing a single page check
00321             switch ( $lookingForPage ) {
00322                 case 1:
00323                     // Page 1
00324                     $this->assertPageStart( $this->pageId1 + $i * self::$numOfPages, NS_MAIN,
00325                         "BackupDumperTestP1" );
00326                     $this->assertRevision( $this->revId1_1 + $i * self::$numOfRevs, "BackupDumperTestP1Summary1",
00327                         $this->textId1_1, false, "0bolhl6ol7i6x0e7yq91gxgaan39j87",
00328                         "BackupDumperTestP1Text1" );
00329                     $this->assertPageEnd();
00330 
00331                     $lookingForPage = 2;
00332                     break;
00333 
00334                 case 2:
00335                     // Page 2
00336                     $this->assertPageStart( $this->pageId2 + $i * self::$numOfPages, NS_MAIN,
00337                         "BackupDumperTestP2" );
00338                     $this->assertRevision( $this->revId2_1 + $i * self::$numOfRevs, "BackupDumperTestP2Summary1",
00339                         $this->textId2_1, false, "jprywrymfhysqllua29tj3sc7z39dl2",
00340                         "BackupDumperTestP2Text1" );
00341                     $this->assertRevision( $this->revId2_2 + $i * self::$numOfRevs, "BackupDumperTestP2Summary2",
00342                         $this->textId2_2, false, "b7vj5ks32po5m1z1t1br4o7scdwwy95",
00343                         "BackupDumperTestP2Text2", $this->revId2_1 + $i * self::$numOfRevs );
00344                     $this->assertRevision( $this->revId2_3 + $i * self::$numOfRevs, "BackupDumperTestP2Summary3",
00345                         $this->textId2_3, false, "jfunqmh1ssfb8rs43r19w98k28gg56r",
00346                         "BackupDumperTestP2Text3", $this->revId2_2 + $i * self::$numOfRevs );
00347                     $this->assertRevision( $this->revId2_4 + $i * self::$numOfRevs,
00348                         "BackupDumperTestP2Summary4 extra",
00349                         $this->textId2_4, false, "6o1ciaxa6pybnqprmungwofc4lv00wv",
00350                         "BackupDumperTestP2Text4 some additional Text",
00351                         $this->revId2_3 + $i * self::$numOfRevs );
00352                     $this->assertPageEnd();
00353 
00354                     $lookingForPage = 4;
00355                     break;
00356 
00357                 case 4:
00358                     // Page 4
00359                     $this->assertPageStart( $this->pageId4 + $i * self::$numOfPages, NS_TALK,
00360                         "Talk:BackupDumperTestP1" );
00361                     $this->assertRevision( $this->revId4_1 + $i * self::$numOfRevs,
00362                         "Talk BackupDumperTestP1 Summary1",
00363                         $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
00364                         "Talk about BackupDumperTestP1 Text1" );
00365                     $this->assertPageEnd();
00366 
00367                     $lookingForPage = 1;
00368 
00369                     // We dealt with the whole iteration.
00370                     $i++;
00371                     break;
00372 
00373                 default:
00374                     $this->fail( "Bad setting for lookingForPage ($lookingForPage)" );
00375             }
00376 
00377             // 3. Checking for the end of the current checkpoint file
00378             if ( $this->xml->nodeType == XMLReader::END_ELEMENT
00379                 && $this->xml->name == "mediawiki"
00380             ) {
00381                 $this->assertDumpEnd();
00382                 $fileOpened = false;
00383             }
00384         }
00385 
00386         // Assuring we completely read all files ...
00387         $this->assertFalse( $fileOpened, "Currently read file still open?" );
00388         $this->assertEmpty( $files, "Remaining unchecked files" );
00389 
00390         // ... and have dealt with more than one checkpoint file
00391         $this->assertGreaterThan( 1, $checkpointFiles, "expected more than 1 checkpoint to have been created. Checkpoint interval is $checkpointAfter seconds, maybe your computer is too fast?" );
00392 
00393         $this->expectETAOutput();
00394     }
00395 
00399     function testCheckpointPlain() {
00400         $this->checkpointHelper();
00401     }
00402 
00415     function testCheckpointGzip() {
00416         $this->checkHasGzip();
00417         $this->checkpointHelper( "gzip" );
00418     }
00419 
00420 
00436     private function setUpStub( $fname = null, $iterations = 1 ) {
00437         if ( $fname === null ) {
00438             $fname = $this->getNewTempFile();
00439         }
00440         $header = '<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.7/" '
00441             . 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
00442             . 'xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.7/ '
00443             . 'http://www.mediawiki.org/xml/export-0.7.xsd" version="0.7" xml:lang="en">
00444   <siteinfo>
00445     <sitename>wikisvn</sitename>
00446     <base>http://localhost/wiki-svn/index.php/Main_Page</base>
00447     <generator>MediaWiki 1.21alpha</generator>
00448     <case>first-letter</case>
00449     <namespaces>
00450       <namespace key="-2" case="first-letter">Media</namespace>
00451       <namespace key="-1" case="first-letter">Special</namespace>
00452       <namespace key="0" case="first-letter" />
00453       <namespace key="1" case="first-letter">Talk</namespace>
00454       <namespace key="2" case="first-letter">User</namespace>
00455       <namespace key="3" case="first-letter">User talk</namespace>
00456       <namespace key="4" case="first-letter">Wikisvn</namespace>
00457       <namespace key="5" case="first-letter">Wikisvn talk</namespace>
00458       <namespace key="6" case="first-letter">File</namespace>
00459       <namespace key="7" case="first-letter">File talk</namespace>
00460       <namespace key="8" case="first-letter">MediaWiki</namespace>
00461       <namespace key="9" case="first-letter">MediaWiki talk</namespace>
00462       <namespace key="10" case="first-letter">Template</namespace>
00463       <namespace key="11" case="first-letter">Template talk</namespace>
00464       <namespace key="12" case="first-letter">Help</namespace>
00465       <namespace key="13" case="first-letter">Help talk</namespace>
00466       <namespace key="14" case="first-letter">Category</namespace>
00467       <namespace key="15" case="first-letter">Category talk</namespace>
00468     </namespaces>
00469   </siteinfo>
00470 ';
00471         $tail = '</mediawiki>
00472 ';
00473 
00474         $content = $header;
00475         $iterations = intval( $iterations );
00476         for ( $i = 0; $i < $iterations; $i++ ) {
00477 
00478             $page1 = '  <page>
00479     <title>BackupDumperTestP1</title>
00480     <ns>0</ns>
00481     <id>' . ( $this->pageId1 + $i * self::$numOfPages ) . '</id>
00482     <revision>
00483       <id>' . ( $this->revId1_1 + $i * self::$numOfRevs ) . '</id>
00484       <timestamp>2012-04-01T16:46:05Z</timestamp>
00485       <contributor>
00486         <ip>127.0.0.1</ip>
00487       </contributor>
00488       <comment>BackupDumperTestP1Summary1</comment>
00489       <sha1>0bolhl6ol7i6x0e7yq91gxgaan39j87</sha1>
00490       <model>wikitext</model>
00491       <format>text/x-wiki</format>
00492       <text id="' . $this->textId1_1 . '" bytes="23" />
00493     </revision>
00494   </page>
00495 ';
00496             $page2 = '  <page>
00497     <title>BackupDumperTestP2</title>
00498     <ns>0</ns>
00499     <id>' . ( $this->pageId2 + $i * self::$numOfPages ) . '</id>
00500     <revision>
00501       <id>' . ( $this->revId2_1 + $i * self::$numOfRevs ) . '</id>
00502       <timestamp>2012-04-01T16:46:05Z</timestamp>
00503       <contributor>
00504         <ip>127.0.0.1</ip>
00505       </contributor>
00506       <comment>BackupDumperTestP2Summary1</comment>
00507       <sha1>jprywrymfhysqllua29tj3sc7z39dl2</sha1>
00508       <model>wikitext</model>
00509       <format>text/x-wiki</format>
00510       <text id="' . $this->textId2_1 . '" bytes="23" />
00511     </revision>
00512     <revision>
00513       <id>' . ( $this->revId2_2 + $i * self::$numOfRevs ) . '</id>
00514       <parentid>' . ( $this->revId2_1 + $i * self::$numOfRevs ) . '</parentid>
00515       <timestamp>2012-04-01T16:46:05Z</timestamp>
00516       <contributor>
00517         <ip>127.0.0.1</ip>
00518       </contributor>
00519       <comment>BackupDumperTestP2Summary2</comment>
00520       <sha1>b7vj5ks32po5m1z1t1br4o7scdwwy95</sha1>
00521       <model>wikitext</model>
00522       <format>text/x-wiki</format>
00523       <text id="' . $this->textId2_2 . '" bytes="23" />
00524     </revision>
00525     <revision>
00526       <id>' . ( $this->revId2_3 + $i * self::$numOfRevs ) . '</id>
00527       <parentid>' . ( $this->revId2_2 + $i * self::$numOfRevs ) . '</parentid>
00528       <timestamp>2012-04-01T16:46:05Z</timestamp>
00529       <contributor>
00530         <ip>127.0.0.1</ip>
00531       </contributor>
00532       <comment>BackupDumperTestP2Summary3</comment>
00533       <sha1>jfunqmh1ssfb8rs43r19w98k28gg56r</sha1>
00534       <model>wikitext</model>
00535       <format>text/x-wiki</format>
00536       <text id="' . $this->textId2_3 . '" bytes="23" />
00537     </revision>
00538     <revision>
00539       <id>' . ( $this->revId2_4 + $i * self::$numOfRevs ) . '</id>
00540       <parentid>' . ( $this->revId2_3 + $i * self::$numOfRevs ) . '</parentid>
00541       <timestamp>2012-04-01T16:46:05Z</timestamp>
00542       <contributor>
00543         <ip>127.0.0.1</ip>
00544       </contributor>
00545       <comment>BackupDumperTestP2Summary4 extra</comment>
00546       <sha1>6o1ciaxa6pybnqprmungwofc4lv00wv</sha1>
00547       <model>wikitext</model>
00548       <format>text/x-wiki</format>
00549       <text id="' . $this->textId2_4 . '" bytes="44" />
00550     </revision>
00551   </page>
00552 ';
00553             // page 3 not in stub
00554 
00555             $page4 = '  <page>
00556     <title>Talk:BackupDumperTestP1</title>
00557     <ns>1</ns>
00558     <id>' . ( $this->pageId4 + $i * self::$numOfPages ) . '</id>
00559     <revision>
00560       <id>' . ( $this->revId4_1 + $i * self::$numOfRevs ) . '</id>
00561       <timestamp>2012-04-01T16:46:05Z</timestamp>
00562       <contributor>
00563         <ip>127.0.0.1</ip>
00564       </contributor>
00565       <comment>Talk BackupDumperTestP1 Summary1</comment>
00566       <sha1>nktofwzd0tl192k3zfepmlzxoax1lpe</sha1>
00567       <model>wikitext</model>
00568       <format>text/x-wiki</format>
00569       <text id="' . $this->textId4_1 . '" bytes="35" />
00570     </revision>
00571   </page>
00572 ';
00573             $content .= $page1 . $page2 . $page4;
00574         }
00575         $content .= $tail;
00576         $this->assertEquals( strlen( $content ), file_put_contents(
00577             $fname, $content ), "Length of prepared stub" );
00578 
00579         return $fname;
00580     }
00581 }