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