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