Merge "Hygiene: Use strtr() instead of str_replace() for character swapping"
[lhc/web/wiklou.git] / tests / phpunit / maintenance / backupTextPassTest.php
1 <?php
2
3 require_once __DIR__ . "/../../../maintenance/backupTextPass.inc";
4
5 /**
6 * Tests for TextPassDumper that rely on the database
7 *
8 * @group Database
9 * @group Dump
10 * @covers TextPassDumper
11 */
12 class TextPassDumperDatabaseTest extends DumpTestCase {
13
14 // We'll add several pages, revision and texts. The following variables hold the
15 // corresponding ids.
16 private $pageId1, $pageId2, $pageId3, $pageId4;
17 private static $numOfPages = 4;
18 private $revId1_1, $textId1_1;
19 private $revId2_1, $textId2_1, $revId2_2, $textId2_2;
20 private $revId2_3, $textId2_3, $revId2_4, $textId2_4;
21 private $revId3_1, $textId3_1, $revId3_2, $textId3_2;
22 private $revId4_1, $textId4_1;
23 private static $numOfRevs = 8;
24
25 function addDBData() {
26 $this->tablesUsed[] = 'page';
27 $this->tablesUsed[] = 'revision';
28 $this->tablesUsed[] = 'text';
29
30 $this->mergeMwGlobalArrayValue( 'wgContentHandlers', array(
31 "BackupTextPassTestModel" => "BackupTextPassTestModelHandler"
32 ) );
33
34 $ns = $this->getDefaultWikitextNS();
35
36 try {
37 // Simple page
38 $title = Title::newFromText( 'BackupDumperTestP1', $ns );
39 $page = WikiPage::factory( $title );
40 list( $this->revId1_1, $this->textId1_1 ) = $this->addRevision( $page,
41 "BackupDumperTestP1Text1", "BackupDumperTestP1Summary1" );
42 $this->pageId1 = $page->getId();
43
44 // Page with more than one revision
45 $title = Title::newFromText( 'BackupDumperTestP2', $ns );
46 $page = WikiPage::factory( $title );
47 list( $this->revId2_1, $this->textId2_1 ) = $this->addRevision( $page,
48 "BackupDumperTestP2Text1", "BackupDumperTestP2Summary1" );
49 list( $this->revId2_2, $this->textId2_2 ) = $this->addRevision( $page,
50 "BackupDumperTestP2Text2", "BackupDumperTestP2Summary2" );
51 list( $this->revId2_3, $this->textId2_3 ) = $this->addRevision( $page,
52 "BackupDumperTestP2Text3", "BackupDumperTestP2Summary3" );
53 list( $this->revId2_4, $this->textId2_4 ) = $this->addRevision( $page,
54 "BackupDumperTestP2Text4 some additional Text ",
55 "BackupDumperTestP2Summary4 extra " );
56 $this->pageId2 = $page->getId();
57
58 // Deleted page.
59 $title = Title::newFromText( 'BackupDumperTestP3', $ns );
60 $page = WikiPage::factory( $title );
61 list( $this->revId3_1, $this->textId3_1 ) = $this->addRevision( $page,
62 "BackupDumperTestP3Text1", "BackupDumperTestP2Summary1" );
63 list( $this->revId3_2, $this->textId3_2 ) = $this->addRevision( $page,
64 "BackupDumperTestP3Text2", "BackupDumperTestP2Summary2" );
65 $this->pageId3 = $page->getId();
66 $page->doDeleteArticle( "Testing ;)" );
67
68 // Page from non-default namespace and model.
69 // ExportTransform applies.
70
71 if ( $ns === NS_TALK ) {
72 // @todo work around this.
73 throw new MWException( "The default wikitext namespace is the talk namespace. "
74 . " We can't currently deal with that." );
75 }
76
77 $title = Title::newFromText( 'BackupDumperTestP1', NS_TALK );
78 $page = WikiPage::factory( $title );
79 list( $this->revId4_1, $this->textId4_1 ) = $this->addRevision( $page,
80 "Talk about BackupDumperTestP1 Text1",
81 "Talk BackupDumperTestP1 Summary1",
82 "BackupTextPassTestModel" );
83 $this->pageId4 = $page->getId();
84 } catch ( Exception $e ) {
85 // We'd love to pass $e directly. However, ... see
86 // documentation of exceptionFromAddDBData in
87 // DumpTestCase
88 $this->exceptionFromAddDBData = $e;
89 }
90 }
91
92 protected function setUp() {
93 parent::setUp();
94
95 // Since we will restrict dumping by page ranges (to allow
96 // working tests, even if the db gets prepopulated by a base
97 // class), we have to assert, that the page id are consecutively
98 // increasing
99 $this->assertEquals(
100 array( $this->pageId2, $this->pageId3, $this->pageId4 ),
101 array( $this->pageId1 + 1, $this->pageId2 + 1, $this->pageId3 + 1 ),
102 "Page ids increasing without holes" );
103 }
104
105 function testPlain() {
106 // Setting up the dump
107 $nameStub = $this->setUpStub();
108 $nameFull = $this->getNewTempFile();
109 $dumper = new TextPassDumper( array( "--stub=file:" . $nameStub,
110 "--output=file:" . $nameFull ) );
111 $dumper->reporting = false;
112 $dumper->setDb( $this->db );
113
114 // Performing the dump
115 $dumper->dump( WikiExporter::FULL, WikiExporter::TEXT );
116
117 // Checking for correctness of the dumped data
118 $this->assertDumpStart( $nameFull );
119
120 // Page 1
121 $this->assertPageStart( $this->pageId1, NS_MAIN, "BackupDumperTestP1" );
122 $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
123 $this->textId1_1, false, "0bolhl6ol7i6x0e7yq91gxgaan39j87",
124 "BackupDumperTestP1Text1" );
125 $this->assertPageEnd();
126
127 // Page 2
128 $this->assertPageStart( $this->pageId2, NS_MAIN, "BackupDumperTestP2" );
129 $this->assertRevision( $this->revId2_1, "BackupDumperTestP2Summary1",
130 $this->textId2_1, false, "jprywrymfhysqllua29tj3sc7z39dl2",
131 "BackupDumperTestP2Text1" );
132 $this->assertRevision( $this->revId2_2, "BackupDumperTestP2Summary2",
133 $this->textId2_2, false, "b7vj5ks32po5m1z1t1br4o7scdwwy95",
134 "BackupDumperTestP2Text2", $this->revId2_1 );
135 $this->assertRevision( $this->revId2_3, "BackupDumperTestP2Summary3",
136 $this->textId2_3, false, "jfunqmh1ssfb8rs43r19w98k28gg56r",
137 "BackupDumperTestP2Text3", $this->revId2_2 );
138 $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
139 $this->textId2_4, false, "6o1ciaxa6pybnqprmungwofc4lv00wv",
140 "BackupDumperTestP2Text4 some additional Text", $this->revId2_3 );
141 $this->assertPageEnd();
142
143 // Page 3
144 // -> Page is marked deleted. Hence not visible
145
146 // Page 4
147 $this->assertPageStart( $this->pageId4, NS_TALK, "Talk:BackupDumperTestP1" );
148 $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
149 $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
150 "TALK ABOUT BACKUPDUMPERTESTP1 TEXT1",
151 false,
152 "BackupTextPassTestModel",
153 "text/plain" );
154 $this->assertPageEnd();
155
156 $this->assertDumpEnd();
157 }
158
159 function testPrefetchPlain() {
160 // The mapping between ids and text, for the hits of the prefetch mock
161 $prefetchMap = array(
162 array( $this->pageId1, $this->revId1_1, "Prefetch_________1Text1" ),
163 array( $this->pageId2, $this->revId2_3, "Prefetch_________2Text3" )
164 );
165
166 // The mock itself
167 $prefetchMock = $this->getMock( 'BaseDump', array( 'prefetch' ), array(), '', false );
168 $prefetchMock->expects( $this->exactly( 6 ) )
169 ->method( 'prefetch' )
170 ->will( $this->returnValueMap( $prefetchMap ) );
171
172 // Setting up of the dump
173 $nameStub = $this->setUpStub();
174 $nameFull = $this->getNewTempFile();
175 $dumper = new TextPassDumper( array( "--stub=file:"
176 . $nameStub, "--output=file:" . $nameFull ) );
177 $dumper->prefetch = $prefetchMock;
178 $dumper->reporting = false;
179 $dumper->setDb( $this->db );
180
181 // Performing the dump
182 $dumper->dump( WikiExporter::FULL, WikiExporter::TEXT );
183
184 // Checking for correctness of the dumped data
185 $this->assertDumpStart( $nameFull );
186
187 // Page 1
188 $this->assertPageStart( $this->pageId1, NS_MAIN, "BackupDumperTestP1" );
189 // Prefetch kicks in. This is still the SHA-1 of the original text,
190 // But the actual text (with different SHA-1) comes from prefetch.
191 $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
192 $this->textId1_1, false, "0bolhl6ol7i6x0e7yq91gxgaan39j87",
193 "Prefetch_________1Text1" );
194 $this->assertPageEnd();
195
196 // Page 2
197 $this->assertPageStart( $this->pageId2, NS_MAIN, "BackupDumperTestP2" );
198 $this->assertRevision( $this->revId2_1, "BackupDumperTestP2Summary1",
199 $this->textId2_1, false, "jprywrymfhysqllua29tj3sc7z39dl2",
200 "BackupDumperTestP2Text1" );
201 $this->assertRevision( $this->revId2_2, "BackupDumperTestP2Summary2",
202 $this->textId2_2, false, "b7vj5ks32po5m1z1t1br4o7scdwwy95",
203 "BackupDumperTestP2Text2", $this->revId2_1 );
204 // Prefetch kicks in. This is still the SHA-1 of the original text,
205 // But the actual text (with different SHA-1) comes from prefetch.
206 $this->assertRevision( $this->revId2_3, "BackupDumperTestP2Summary3",
207 $this->textId2_3, false, "jfunqmh1ssfb8rs43r19w98k28gg56r",
208 "Prefetch_________2Text3", $this->revId2_2 );
209 $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
210 $this->textId2_4, false, "6o1ciaxa6pybnqprmungwofc4lv00wv",
211 "BackupDumperTestP2Text4 some additional Text", $this->revId2_3 );
212 $this->assertPageEnd();
213
214 // Page 3
215 // -> Page is marked deleted. Hence not visible
216
217 // Page 4
218 $this->assertPageStart( $this->pageId4, NS_TALK, "Talk:BackupDumperTestP1" );
219 $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
220 $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
221 "TALK ABOUT BACKUPDUMPERTESTP1 TEXT1",
222 false,
223 "BackupTextPassTestModel",
224 "text/plain" );
225 $this->assertPageEnd();
226
227 $this->assertDumpEnd();
228 }
229
230 /**
231 * Ensures that checkpoint dumps are used and written, by successively increasing the
232 * stub size and dumping until the duration crosses a threshold.
233 *
234 * @param string $checkpointFormat Either "file" for plain text or "gzip" for gzipped
235 * checkpoint files.
236 */
237 private function checkpointHelper( $checkpointFormat = "file" ) {
238 // Getting temporary names
239 $nameStub = $this->getNewTempFile();
240 $nameOutputDir = $this->getNewTempDirectory();
241
242 $stderr = fopen( 'php://output', 'a' );
243 if ( $stderr === false ) {
244 $this->fail( "Could not open stream for stderr" );
245 }
246
247 $iterations = 32; // We'll start with that many iterations of revisions
248 // in stub. Make sure that the generated volume is above the buffer size
249 // set below. Otherwise, the checkpointing does not trigger.
250 $lastDuration = 0;
251 $minDuration = 2; // We want the dump to take at least this many seconds
252 $checkpointAfter = 0.5; // Generate checkpoint after this many seconds
253
254 // Until a dump takes at least $minDuration seconds, perform a dump and check
255 // duration. If the dump did not take long enough increase the iteration
256 // count, to generate a bigger stub file next time.
257 while ( $lastDuration < $minDuration ) {
258
259 // Setting up the dump
260 wfRecursiveRemoveDir( $nameOutputDir );
261 $this->assertTrue( wfMkdirParents( $nameOutputDir ),
262 "Creating temporary output directory " );
263 $this->setUpStub( $nameStub, $iterations );
264 $dumper = new TextPassDumper( array( "--stub=file:" . $nameStub,
265 "--output=" . $checkpointFormat . ":" . $nameOutputDir . "/full",
266 "--maxtime=1" /*This is in minutes. Fixup is below*/,
267 "--buffersize=32768", // The default of 32 iterations fill up 32KB about twice
268 "--checkpointfile=checkpoint-%s-%s.xml.gz" ) );
269 $dumper->setDb( $this->db );
270 $dumper->maxTimeAllowed = $checkpointAfter; // Patching maxTime from 1 minute
271 $dumper->stderr = $stderr;
272
273 // The actual dump and taking time
274 $ts_before = microtime( true );
275 $dumper->dump( WikiExporter::FULL, WikiExporter::TEXT );
276 $ts_after = microtime( true );
277 $lastDuration = $ts_after - $ts_before;
278
279 // Handling increasing the iteration count for the stubs
280 if ( $lastDuration < $minDuration ) {
281 $old_iterations = $iterations;
282 if ( $lastDuration > 0.2 ) {
283 // lastDuration is big enough, to allow an educated guess
284 $factor = ( $minDuration + 0.5 ) / $lastDuration;
285 if ( ( $factor > 1.1 ) && ( $factor < 100 ) ) {
286 // educated guess is reasonable
287 $iterations = (int)( $iterations * $factor );
288 }
289 }
290
291 if ( $old_iterations == $iterations ) {
292 // Heuristics were not applied, so we just *2.
293 $iterations *= 2;
294 }
295
296 $this->assertLessThan( 50000, $iterations,
297 "Emergency stop against infinitely increasing iteration "
298 . "count ( last duration: $lastDuration )" );
299 }
300 }
301
302 // The dump (hopefully) did take long enough to produce more than one
303 // checkpoint file.
304 //
305 // We now check all the checkpoint files for validity.
306
307 $files = scandir( $nameOutputDir );
308 $this->assertTrue( asort( $files ), "Sorting files in temporary directory" );
309 $fileOpened = false;
310 $lookingForPage = 1;
311 $checkpointFiles = 0;
312
313 // Each run of the following loop body tries to handle exactly 1 /page/ (not
314 // iteration of stub content). $i is only increased after having treated page 4.
315 for ( $i = 0; $i < $iterations; ) {
316
317 // 1. Assuring a file is opened and ready. Skipping across header if
318 // necessary.
319 if ( !$fileOpened ) {
320 $this->assertNotEmpty( $files, "No more existing dump files, "
321 . "but not yet all pages found" );
322 $fname = array_shift( $files );
323 while ( $fname == "." || $fname == ".." ) {
324 $this->assertNotEmpty( $files, "No more existing dump"
325 . " files, but not yet all pages found" );
326 $fname = array_shift( $files );
327 }
328 if ( $checkpointFormat == "gzip" ) {
329 $this->gunzip( $nameOutputDir . "/" . $fname );
330 }
331 $this->assertDumpStart( $nameOutputDir . "/" . $fname );
332 $fileOpened = true;
333 $checkpointFiles++;
334 }
335
336 // 2. Performing a single page check
337 switch ( $lookingForPage ) {
338 case 1:
339 // Page 1
340 $this->assertPageStart( $this->pageId1 + $i * self::$numOfPages, NS_MAIN,
341 "BackupDumperTestP1" );
342 $this->assertRevision( $this->revId1_1 + $i * self::$numOfRevs, "BackupDumperTestP1Summary1",
343 $this->textId1_1, false, "0bolhl6ol7i6x0e7yq91gxgaan39j87",
344 "BackupDumperTestP1Text1" );
345 $this->assertPageEnd();
346
347 $lookingForPage = 2;
348 break;
349
350 case 2:
351 // Page 2
352 $this->assertPageStart( $this->pageId2 + $i * self::$numOfPages, NS_MAIN,
353 "BackupDumperTestP2" );
354 $this->assertRevision( $this->revId2_1 + $i * self::$numOfRevs, "BackupDumperTestP2Summary1",
355 $this->textId2_1, false, "jprywrymfhysqllua29tj3sc7z39dl2",
356 "BackupDumperTestP2Text1" );
357 $this->assertRevision( $this->revId2_2 + $i * self::$numOfRevs, "BackupDumperTestP2Summary2",
358 $this->textId2_2, false, "b7vj5ks32po5m1z1t1br4o7scdwwy95",
359 "BackupDumperTestP2Text2", $this->revId2_1 + $i * self::$numOfRevs );
360 $this->assertRevision( $this->revId2_3 + $i * self::$numOfRevs, "BackupDumperTestP2Summary3",
361 $this->textId2_3, false, "jfunqmh1ssfb8rs43r19w98k28gg56r",
362 "BackupDumperTestP2Text3", $this->revId2_2 + $i * self::$numOfRevs );
363 $this->assertRevision( $this->revId2_4 + $i * self::$numOfRevs,
364 "BackupDumperTestP2Summary4 extra",
365 $this->textId2_4, false, "6o1ciaxa6pybnqprmungwofc4lv00wv",
366 "BackupDumperTestP2Text4 some additional Text",
367 $this->revId2_3 + $i * self::$numOfRevs );
368 $this->assertPageEnd();
369
370 $lookingForPage = 4;
371 break;
372
373 case 4:
374 // Page 4
375 $this->assertPageStart( $this->pageId4 + $i * self::$numOfPages, NS_TALK,
376 "Talk:BackupDumperTestP1" );
377 $this->assertRevision( $this->revId4_1 + $i * self::$numOfRevs,
378 "Talk BackupDumperTestP1 Summary1",
379 $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
380 "TALK ABOUT BACKUPDUMPERTESTP1 TEXT1",
381 false,
382 "BackupTextPassTestModel",
383 "text/plain" );
384 $this->assertPageEnd();
385
386 $lookingForPage = 1;
387
388 // We dealt with the whole iteration.
389 $i++;
390 break;
391
392 default:
393 $this->fail( "Bad setting for lookingForPage ($lookingForPage)" );
394 }
395
396 // 3. Checking for the end of the current checkpoint file
397 if ( $this->xml->nodeType == XMLReader::END_ELEMENT
398 && $this->xml->name == "mediawiki"
399 ) {
400 $this->assertDumpEnd();
401 $fileOpened = false;
402 }
403 }
404
405 // Assuring we completely read all files ...
406 $this->assertFalse( $fileOpened, "Currently read file still open?" );
407 $this->assertEmpty( $files, "Remaining unchecked files" );
408
409 // ... and have dealt with more than one checkpoint file
410 $this->assertGreaterThan(
411 1,
412 $checkpointFiles,
413 "expected more than 1 checkpoint to have been created. "
414 . "Checkpoint interval is $checkpointAfter seconds, maybe your computer is too fast?"
415 );
416
417 $this->expectETAOutput();
418 }
419
420 /**
421 * Broken per T70653.
422 *
423 * @group large
424 * @group Broken
425 */
426 function testCheckpointPlain() {
427 $this->checkpointHelper();
428 }
429
430 /**
431 * tests for working checkpoint generation in gzip format work.
432 *
433 * We keep this test in addition to the simpler self::testCheckpointPlain, as there
434 * were once problems when the used sinks were DumpPipeOutputs.
435 *
436 * xmldumps-backup typically uses bzip2 instead of gzip. However, as bzip2 requires
437 * PHP extensions, we go for gzip instead, which triggers the same relevant code
438 * paths while still being testable on more systems.
439 *
440 * Broken per T70653.
441 *
442 * @group large
443 * @group Broken
444 */
445 function testCheckpointGzip() {
446 $this->checkHasGzip();
447 $this->checkpointHelper( "gzip" );
448 }
449
450 /**
451 * Creates a stub file that is used for testing the text pass of dumps
452 *
453 * @param string $fname (Optional) Absolute name of the file to write
454 * the stub into. If this parameter is null, a new temporary
455 * file is generated that is automatically removed upon tearDown.
456 * @param int $iterations (Optional) specifies how often the block
457 * of 3 pages should go into the stub file. The page and
458 * revision id increase further and further, while the text
459 * id of the first iteration is reused. The pages and revision
460 * of iteration > 1 have no corresponding representation in the database.
461 * @return string Absolute filename of the stub
462 */
463 private function setUpStub( $fname = null, $iterations = 1 ) {
464 if ( $fname === null ) {
465 $fname = $this->getNewTempFile();
466 }
467 $header = '<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" '
468 . 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
469 . 'xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ '
470 . 'http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
471 <siteinfo>
472 <sitename>wikisvn</sitename>
473 <base>http://localhost/wiki-svn/index.php/Main_Page</base>
474 <generator>MediaWiki 1.21alpha</generator>
475 <case>first-letter</case>
476 <namespaces>
477 <namespace key="-2" case="first-letter">Media</namespace>
478 <namespace key="-1" case="first-letter">Special</namespace>
479 <namespace key="0" case="first-letter" />
480 <namespace key="1" case="first-letter">Talk</namespace>
481 <namespace key="2" case="first-letter">User</namespace>
482 <namespace key="3" case="first-letter">User talk</namespace>
483 <namespace key="4" case="first-letter">Wikisvn</namespace>
484 <namespace key="5" case="first-letter">Wikisvn talk</namespace>
485 <namespace key="6" case="first-letter">File</namespace>
486 <namespace key="7" case="first-letter">File talk</namespace>
487 <namespace key="8" case="first-letter">MediaWiki</namespace>
488 <namespace key="9" case="first-letter">MediaWiki talk</namespace>
489 <namespace key="10" case="first-letter">Template</namespace>
490 <namespace key="11" case="first-letter">Template talk</namespace>
491 <namespace key="12" case="first-letter">Help</namespace>
492 <namespace key="13" case="first-letter">Help talk</namespace>
493 <namespace key="14" case="first-letter">Category</namespace>
494 <namespace key="15" case="first-letter">Category talk</namespace>
495 </namespaces>
496 </siteinfo>
497 ';
498 $tail = '</mediawiki>
499 ';
500
501 $content = $header;
502 $iterations = intval( $iterations );
503 for ( $i = 0; $i < $iterations; $i++ ) {
504
505 $page1 = ' <page>
506 <title>BackupDumperTestP1</title>
507 <ns>0</ns>
508 <id>' . ( $this->pageId1 + $i * self::$numOfPages ) . '</id>
509 <revision>
510 <id>' . ( $this->revId1_1 + $i * self::$numOfRevs ) . '</id>
511 <timestamp>2012-04-01T16:46:05Z</timestamp>
512 <contributor>
513 <ip>127.0.0.1</ip>
514 </contributor>
515 <comment>BackupDumperTestP1Summary1</comment>
516 <model>wikitext</model>
517 <format>text/x-wiki</format>
518 <text id="' . $this->textId1_1 . '" bytes="23" />
519 <sha1>0bolhl6ol7i6x0e7yq91gxgaan39j87</sha1>
520 </revision>
521 </page>
522 ';
523 $page2 = ' <page>
524 <title>BackupDumperTestP2</title>
525 <ns>0</ns>
526 <id>' . ( $this->pageId2 + $i * self::$numOfPages ) . '</id>
527 <revision>
528 <id>' . ( $this->revId2_1 + $i * self::$numOfRevs ) . '</id>
529 <timestamp>2012-04-01T16:46:05Z</timestamp>
530 <contributor>
531 <ip>127.0.0.1</ip>
532 </contributor>
533 <comment>BackupDumperTestP2Summary1</comment>
534 <model>wikitext</model>
535 <format>text/x-wiki</format>
536 <text id="' . $this->textId2_1 . '" bytes="23" />
537 <sha1>jprywrymfhysqllua29tj3sc7z39dl2</sha1>
538 </revision>
539 <revision>
540 <id>' . ( $this->revId2_2 + $i * self::$numOfRevs ) . '</id>
541 <parentid>' . ( $this->revId2_1 + $i * self::$numOfRevs ) . '</parentid>
542 <timestamp>2012-04-01T16:46:05Z</timestamp>
543 <contributor>
544 <ip>127.0.0.1</ip>
545 </contributor>
546 <comment>BackupDumperTestP2Summary2</comment>
547 <model>wikitext</model>
548 <format>text/x-wiki</format>
549 <text id="' . $this->textId2_2 . '" bytes="23" />
550 <sha1>b7vj5ks32po5m1z1t1br4o7scdwwy95</sha1>
551 </revision>
552 <revision>
553 <id>' . ( $this->revId2_3 + $i * self::$numOfRevs ) . '</id>
554 <parentid>' . ( $this->revId2_2 + $i * self::$numOfRevs ) . '</parentid>
555 <timestamp>2012-04-01T16:46:05Z</timestamp>
556 <contributor>
557 <ip>127.0.0.1</ip>
558 </contributor>
559 <comment>BackupDumperTestP2Summary3</comment>
560 <model>wikitext</model>
561 <format>text/x-wiki</format>
562 <text id="' . $this->textId2_3 . '" bytes="23" />
563 <sha1>jfunqmh1ssfb8rs43r19w98k28gg56r</sha1>
564 </revision>
565 <revision>
566 <id>' . ( $this->revId2_4 + $i * self::$numOfRevs ) . '</id>
567 <parentid>' . ( $this->revId2_3 + $i * self::$numOfRevs ) . '</parentid>
568 <timestamp>2012-04-01T16:46:05Z</timestamp>
569 <contributor>
570 <ip>127.0.0.1</ip>
571 </contributor>
572 <comment>BackupDumperTestP2Summary4 extra</comment>
573 <model>wikitext</model>
574 <format>text/x-wiki</format>
575 <text id="' . $this->textId2_4 . '" bytes="44" />
576 <sha1>6o1ciaxa6pybnqprmungwofc4lv00wv</sha1>
577 </revision>
578 </page>
579 ';
580 // page 3 not in stub
581
582 $page4 = ' <page>
583 <title>Talk:BackupDumperTestP1</title>
584 <ns>1</ns>
585 <id>' . ( $this->pageId4 + $i * self::$numOfPages ) . '</id>
586 <revision>
587 <id>' . ( $this->revId4_1 + $i * self::$numOfRevs ) . '</id>
588 <timestamp>2012-04-01T16:46:05Z</timestamp>
589 <contributor>
590 <ip>127.0.0.1</ip>
591 </contributor>
592 <comment>Talk BackupDumperTestP1 Summary1</comment>
593 <model>BackupTextPassTestModel</model>
594 <format>text/plain</format>
595 <text id="' . $this->textId4_1 . '" bytes="35" />
596 <sha1>nktofwzd0tl192k3zfepmlzxoax1lpe</sha1>
597 </revision>
598 </page>
599 ';
600 $content .= $page1 . $page2 . $page4;
601 }
602 $content .= $tail;
603 $this->assertEquals( strlen( $content ), file_put_contents(
604 $fname, $content ), "Length of prepared stub" );
605
606 return $fname;
607 }
608 }
609
610 class BackupTextPassTestModelHandler extends TextContentHandler {
611
612 public function __construct() {
613 parent::__construct( 'BackupTextPassTestModel' );
614 }
615
616 public function exportTransform( $text, $format = null ) {
617 return strtoupper( $text );
618 }
619
620 }
621
622 /**
623 * Tests for TextPassDumper that do not rely on the database
624 *
625 * (As the Database group is only detected at class level (not method level), we
626 * cannot bring this test case's tests into the above main test case.)
627 *
628 * @group Dump
629 * @covers TextPassDumper
630 */
631 class TextPassDumperDatabaselessTest extends MediaWikiLangTestCase {
632 /**
633 * Ensures that setting the buffer size is effective.
634 *
635 * @dataProvider bufferSizeProvider
636 */
637 function testBufferSizeSetting( $expected, $size, $msg ) {
638 $dumper = new TextPassDumperAccessor( array( "--buffersize=" . $size ) );
639 $this->assertEquals( $expected, $dumper->getBufferSize(), $msg );
640 }
641
642 /**
643 * Ensures that setting the buffer size is effective.
644 *
645 * @dataProvider bufferSizeProvider
646 */
647 function bufferSizeProvider() {
648 // expected, bufferSize to initialize with, message
649 return array(
650 array( 512 * 1024, 512 * 1024, "Setting 512KB is not effective" ),
651 array( 8192, 8192, "Setting 8KB is not effective" ),
652 array( 4096, 2048, "Could set buffer size below lower bound" )
653 );
654 }
655 }
656
657 /**
658 * Accessor for internal state of TextPassDumper
659 *
660 * Do not warrentless add getters here.
661 */
662 class TextPassDumperAccessor extends TextPassDumper {
663 /**
664 * Gets the bufferSize.
665 *
666 * If bufferSize setting does not work correctly, testCheckpoint... tests
667 * fail and point in the wrong direction. To aid in troubleshooting when
668 * testCheckpoint... tests break at some point in the future, we test the
669 * bufferSize setting, hence need this accessor.
670 *
671 * (Yes, bufferSize is internal state of the TextPassDumper, but aiding
672 * debugging of testCheckpoint... in the future seems to be worth testing
673 * against it nonetheless.)
674 */
675 public function getBufferSize() {
676 return $this->bufferSize;
677 }
678 }