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