Merge "ApiMain: Fix call to Linker::makeHeadline()"
[lhc/web/wiklou.git] / tests / phpunit / includes / RevisionTest.php
1 <?php
2
3 use MediaWiki\Storage\RevisionStore;
4 use MediaWiki\Storage\SqlBlobStore;
5 use Wikimedia\Rdbms\IDatabase;
6 use Wikimedia\Rdbms\LoadBalancer;
7
8 /**
9 * Test cases in RevisionTest should not interact with the Database.
10 * For test cases that need Database interaction see RevisionDbTestBase.
11 */
12 class RevisionTest extends MediaWikiTestCase {
13
14 public function provideConstructFromArray() {
15 yield 'with text' => [
16 [
17 'text' => 'hello world.',
18 'content_model' => CONTENT_MODEL_JAVASCRIPT
19 ],
20 ];
21 yield 'with content' => [
22 [
23 'content' => new JavaScriptContent( 'hellow world.' )
24 ],
25 ];
26 // FIXME: test with and without user ID, and with a user object.
27 // We can't prepare that here though, since we don't yet have a dummy DB
28 }
29
30 /**
31 * @param string $model
32 * @return Title
33 */
34 public function getMockTitle( $model = CONTENT_MODEL_WIKITEXT ) {
35 $mock = $this->getMockBuilder( Title::class )
36 ->disableOriginalConstructor()
37 ->getMock();
38 $mock->expects( $this->any() )
39 ->method( 'getNamespace' )
40 ->will( $this->returnValue( $this->getDefaultWikitextNS() ) );
41 $mock->expects( $this->any() )
42 ->method( 'getPrefixedText' )
43 ->will( $this->returnValue( 'RevisionTest' ) );
44 $mock->expects( $this->any() )
45 ->method( 'getDBKey' )
46 ->will( $this->returnValue( 'RevisionTest' ) );
47 $mock->expects( $this->any() )
48 ->method( 'getArticleID' )
49 ->will( $this->returnValue( 23 ) );
50 $mock->expects( $this->any() )
51 ->method( 'getModel' )
52 ->will( $this->returnValue( $model ) );
53
54 return $mock;
55 }
56
57 /**
58 * @dataProvider provideConstructFromArray
59 * @covers Revision::__construct
60 * @covers Revision::constructFromRowArray
61 */
62 public function testConstructFromArray( $rowArray ) {
63 $rev = new Revision( $rowArray, 0, $this->getMockTitle() );
64 $this->assertNotNull( $rev->getContent(), 'no content object available' );
65 $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() );
66 $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() );
67 }
68
69 /**
70 * @covers Revision::__construct
71 * @covers Revision::constructFromRowArray
72 */
73 public function testConstructFromEmptyArray() {
74 $rev = new Revision( [], 0, $this->getMockTitle() );
75 $this->assertNull( $rev->getContent(), 'no content object should be available' );
76 }
77
78 public function provideConstructFromArray_userSetAsExpected() {
79 yield 'no user defaults to wgUser' => [
80 [
81 'content' => new JavaScriptContent( 'hello world.' ),
82 ],
83 null,
84 null,
85 ];
86 yield 'user text and id' => [
87 [
88 'content' => new JavaScriptContent( 'hello world.' ),
89 'user_text' => 'SomeTextUserName',
90 'user' => 99,
91
92 ],
93 99,
94 'SomeTextUserName',
95 ];
96 yield 'user text only' => [
97 [
98 'content' => new JavaScriptContent( 'hello world.' ),
99 'user_text' => '111.111.111.111',
100 ],
101 0,
102 '111.111.111.111',
103 ];
104 }
105
106 /**
107 * @dataProvider provideConstructFromArray_userSetAsExpected
108 * @covers Revision::__construct
109 * @covers Revision::constructFromRowArray
110 *
111 * @param array $rowArray
112 * @param mixed $expectedUserId null to expect the current wgUser ID
113 * @param mixed $expectedUserName null to expect the current wgUser name
114 */
115 public function testConstructFromArray_userSetAsExpected(
116 array $rowArray,
117 $expectedUserId,
118 $expectedUserName
119 ) {
120 $testUser = $this->getTestUser()->getUser();
121 $this->setMwGlobals( 'wgUser', $testUser );
122 if ( $expectedUserId === null ) {
123 $expectedUserId = $testUser->getId();
124 }
125 if ( $expectedUserName === null ) {
126 $expectedUserName = $testUser->getName();
127 }
128
129 $rev = new Revision( $rowArray, 0, $this->getMockTitle() );
130 $this->assertEquals( $expectedUserId, $rev->getUser() );
131 $this->assertEquals( $expectedUserName, $rev->getUserText() );
132 }
133
134 public function provideConstructFromArrayThrowsExceptions() {
135 yield 'content and text_id both not empty' => [
136 [
137 'content' => new WikitextContent( 'GOAT' ),
138 'text_id' => 'someid',
139 ],
140 new MWException( "Text already stored in external store (id someid), " .
141 "can't serialize content object" )
142 ];
143 yield 'unknown user id and no user name' => [
144 [
145 'content' => new JavaScriptContent( 'hello world.' ),
146 'user' => 9989,
147 ],
148 new MWException( 'user_text not given, and unknown user ID 9989' )
149 ];
150 yield 'with bad content object (class)' => [
151 [ 'content' => new stdClass() ],
152 new MWException( 'content field must contain a Content object.' )
153 ];
154 yield 'with bad content object (string)' => [
155 [ 'content' => 'ImAGoat' ],
156 new MWException( 'content field must contain a Content object.' )
157 ];
158 yield 'bad row format' => [
159 'imastring, not a row',
160 new InvalidArgumentException(
161 '$row must be a row object, an associative array, or a RevisionRecord'
162 )
163 ];
164 }
165
166 /**
167 * @dataProvider provideConstructFromArrayThrowsExceptions
168 * @covers Revision::__construct
169 * @covers RevisionStore::newMutableRevisionFromArray
170 */
171 public function testConstructFromArrayThrowsExceptions( $rowArray, Exception $expectedException ) {
172 $this->setExpectedException(
173 get_class( $expectedException ),
174 $expectedException->getMessage(),
175 $expectedException->getCode()
176 );
177 new Revision( $rowArray, 0, $this->getMockTitle() );
178 }
179
180 /**
181 * @covers Revision::__construct
182 * @covers RevisionStore::newMutableRevisionFromArray
183 */
184 public function testConstructFromNothing() {
185 $this->setExpectedException(
186 InvalidArgumentException::class
187 );
188 new Revision( [] );
189 }
190
191 public function provideConstructFromRow() {
192 yield 'Full construction' => [
193 [
194 'rev_id' => '42',
195 'rev_page' => '23',
196 'rev_text_id' => '2',
197 'rev_timestamp' => '20171017114835',
198 'rev_user_text' => '127.0.0.1',
199 'rev_user' => '0',
200 'rev_minor_edit' => '0',
201 'rev_deleted' => '0',
202 'rev_len' => '46',
203 'rev_parent_id' => '1',
204 'rev_sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
205 'rev_comment_text' => 'Goat Comment!',
206 'rev_comment_data' => null,
207 'rev_comment_cid' => null,
208 'rev_content_format' => 'GOATFORMAT',
209 'rev_content_model' => 'GOATMODEL',
210 ],
211 function ( RevisionTest $testCase, Revision $rev ) {
212 $testCase->assertSame( 42, $rev->getId() );
213 $testCase->assertSame( 23, $rev->getPage() );
214 $testCase->assertSame( 2, $rev->getTextId() );
215 $testCase->assertSame( '20171017114835', $rev->getTimestamp() );
216 $testCase->assertSame( '127.0.0.1', $rev->getUserText() );
217 $testCase->assertSame( 0, $rev->getUser() );
218 $testCase->assertSame( false, $rev->isMinor() );
219 $testCase->assertSame( false, $rev->isDeleted( Revision::DELETED_TEXT ) );
220 $testCase->assertSame( 46, $rev->getSize() );
221 $testCase->assertSame( 1, $rev->getParentId() );
222 $testCase->assertSame( 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z', $rev->getSha1() );
223 $testCase->assertSame( 'Goat Comment!', $rev->getComment() );
224 $testCase->assertSame( 'GOATFORMAT', $rev->getContentFormat() );
225 $testCase->assertSame( 'GOATMODEL', $rev->getContentModel() );
226 }
227 ];
228 yield 'default field values' => [
229 [
230 'rev_id' => '42',
231 'rev_page' => '23',
232 'rev_text_id' => '2',
233 'rev_timestamp' => '20171017114835',
234 'rev_user_text' => '127.0.0.1',
235 'rev_user' => '0',
236 'rev_minor_edit' => '0',
237 'rev_deleted' => '0',
238 'rev_comment_text' => 'Goat Comment!',
239 'rev_comment_data' => null,
240 'rev_comment_cid' => null,
241 ],
242 function ( RevisionTest $testCase, Revision $rev ) {
243 // parent ID may be null
244 $testCase->assertSame( null, $rev->getParentId(), 'revision id' );
245
246 // given fields
247 $testCase->assertSame( $rev->getTimestamp(), '20171017114835', 'timestamp' );
248 $testCase->assertSame( $rev->getUserText(), '127.0.0.1', 'user name' );
249 $testCase->assertSame( $rev->getUser(), 0, 'user id' );
250 $testCase->assertSame( $rev->getComment(), 'Goat Comment!' );
251 $testCase->assertSame( false, $rev->isMinor(), 'minor edit' );
252 $testCase->assertSame( 0, $rev->getVisibility(), 'visibility flags' );
253
254 // computed fields
255 $testCase->assertNotNull( $rev->getSize(), 'size' );
256 $testCase->assertNotNull( $rev->getSha1(), 'hash' );
257
258 // NOTE: model and format will be detected based on the namespace of the (mock) title
259 $testCase->assertSame( 'text/x-wiki', $rev->getContentFormat(), 'format' );
260 $testCase->assertSame( 'wikitext', $rev->getContentModel(), 'model' );
261 }
262 ];
263 }
264
265 /**
266 * @dataProvider provideConstructFromRow
267 * @covers Revision::__construct
268 * @covers RevisionStore::newRevisionFromRow
269 */
270 public function testConstructFromRow( array $arrayData, $assertions ) {
271 $data = 'Hello goat.'; // needs to match model and format
272
273 $blobStore = $this->getMockBuilder( SqlBlobStore::class )
274 ->disableOriginalConstructor()
275 ->getMock();
276
277 $blobStore->method( 'getBlob' )
278 ->will( $this->returnValue( $data ) );
279
280 $blobStore->method( 'getTextIdFromAddress' )
281 ->will( $this->returnCallback(
282 function ( $address ) {
283 // Turn "tt:1234" into 12345.
284 // Note that this must be functional so we can test getTextId().
285 // Ideally, we'd un-mock getTextIdFromAddress and use its actual implementation.
286 $parts = explode( ':', $address );
287 return (int)array_pop( $parts );
288 }
289 ) );
290
291 // Note override internal service, so RevisionStore uses it as well.
292 $this->setService( '_SqlBlobStore', $blobStore );
293
294 $row = (object)$arrayData;
295 $rev = new Revision( $row, 0, $this->getMockTitle() );
296 $assertions( $this, $rev );
297 }
298
299 public function provideGetRevisionText() {
300 yield 'Generic test' => [
301 'This is a goat of revision text.',
302 [
303 'old_flags' => '',
304 'old_text' => 'This is a goat of revision text.',
305 ],
306 ];
307 }
308
309 public function provideGetId() {
310 yield [
311 [],
312 null
313 ];
314 yield [
315 [ 'id' => 998 ],
316 998
317 ];
318 }
319
320 /**
321 * @dataProvider provideGetId
322 * @covers Revision::getId
323 */
324 public function testGetId( $rowArray, $expectedId ) {
325 $rev = new Revision( $rowArray, 0, $this->getMockTitle() );
326 $this->assertEquals( $expectedId, $rev->getId() );
327 }
328
329 public function provideSetId() {
330 yield [ '123', 123 ];
331 yield [ 456, 456 ];
332 }
333
334 /**
335 * @dataProvider provideSetId
336 * @covers Revision::setId
337 */
338 public function testSetId( $input, $expected ) {
339 $rev = new Revision( [], 0, $this->getMockTitle() );
340 $rev->setId( $input );
341 $this->assertSame( $expected, $rev->getId() );
342 }
343
344 public function provideSetUserIdAndName() {
345 yield [ '123', 123, 'GOaT' ];
346 yield [ 456, 456, 'GOaT' ];
347 }
348
349 /**
350 * @dataProvider provideSetUserIdAndName
351 * @covers Revision::setUserIdAndName
352 */
353 public function testSetUserIdAndName( $inputId, $expectedId, $name ) {
354 $rev = new Revision( [], 0, $this->getMockTitle() );
355 $rev->setUserIdAndName( $inputId, $name );
356 $this->assertSame( $expectedId, $rev->getUser( Revision::RAW ) );
357 $this->assertEquals( $name, $rev->getUserText( Revision::RAW ) );
358 }
359
360 public function provideGetTextId() {
361 yield [ [], null ];
362 yield [ [ 'text_id' => '123' ], 123 ];
363 yield [ [ 'text_id' => 456 ], 456 ];
364 }
365
366 /**
367 * @dataProvider provideGetTextId
368 * @covers Revision::getTextId()
369 */
370 public function testGetTextId( $rowArray, $expected ) {
371 $rev = new Revision( $rowArray, 0, $this->getMockTitle() );
372 $this->assertSame( $expected, $rev->getTextId() );
373 }
374
375 public function provideGetParentId() {
376 yield [ [], null ];
377 yield [ [ 'parent_id' => '123' ], 123 ];
378 yield [ [ 'parent_id' => 456 ], 456 ];
379 }
380
381 /**
382 * @dataProvider provideGetParentId
383 * @covers Revision::getParentId()
384 */
385 public function testGetParentId( $rowArray, $expected ) {
386 $rev = new Revision( $rowArray, 0, $this->getMockTitle() );
387 $this->assertSame( $expected, $rev->getParentId() );
388 }
389
390 /**
391 * @covers Revision::getRevisionText
392 * @dataProvider provideGetRevisionText
393 */
394 public function testGetRevisionText( $expected, $rowData, $prefix = 'old_', $wiki = false ) {
395 $this->assertEquals(
396 $expected,
397 Revision::getRevisionText( (object)$rowData, $prefix, $wiki ) );
398 }
399
400 public function provideGetRevisionTextWithZlibExtension() {
401 yield 'Generic gzip test' => [
402 'This is a small goat of revision text.',
403 [
404 'old_flags' => 'gzip',
405 'old_text' => gzdeflate( 'This is a small goat of revision text.' ),
406 ],
407 ];
408 }
409
410 /**
411 * @covers Revision::getRevisionText
412 * @dataProvider provideGetRevisionTextWithZlibExtension
413 */
414 public function testGetRevisionWithZlibExtension( $expected, $rowData ) {
415 $this->checkPHPExtension( 'zlib' );
416 $this->testGetRevisionText( $expected, $rowData );
417 }
418
419 private function getWANObjectCache() {
420 return new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
421 }
422
423 /**
424 * @return SqlBlobStore
425 */
426 private function getBlobStore() {
427 /** @var LoadBalancer $lb */
428 $lb = $this->getMockBuilder( LoadBalancer::class )
429 ->disableOriginalConstructor()
430 ->getMock();
431
432 $cache = $this->getWANObjectCache();
433
434 $blobStore = new SqlBlobStore( $lb, $cache );
435 return $blobStore;
436 }
437
438 /**
439 * @return RevisionStore
440 */
441 private function getRevisionStore() {
442 /** @var LoadBalancer $lb */
443 $lb = $this->getMockBuilder( LoadBalancer::class )
444 ->disableOriginalConstructor()
445 ->getMock();
446
447 $cache = $this->getWANObjectCache();
448
449 $blobStore = new RevisionStore( $lb, $this->getBlobStore(), $cache );
450 return $blobStore;
451 }
452
453 public function provideGetRevisionTextWithLegacyEncoding() {
454 yield 'Utf8Native' => [
455 "Wiki est l'\xc3\xa9cole superieur !",
456 'fr',
457 'iso-8859-1',
458 [
459 'old_flags' => 'utf-8',
460 'old_text' => "Wiki est l'\xc3\xa9cole superieur !",
461 ]
462 ];
463 yield 'Utf8Legacy' => [
464 "Wiki est l'\xc3\xa9cole superieur !",
465 'fr',
466 'iso-8859-1',
467 [
468 'old_flags' => '',
469 'old_text' => "Wiki est l'\xe9cole superieur !",
470 ]
471 ];
472 }
473
474 /**
475 * @covers Revision::getRevisionText
476 * @dataProvider provideGetRevisionTextWithLegacyEncoding
477 */
478 public function testGetRevisionWithLegacyEncoding( $expected, $lang, $encoding, $rowData ) {
479 $blobStore = $this->getBlobStore();
480 $blobStore->setLegacyEncoding( $encoding, Language::factory( $lang ) );
481 $this->setService( 'BlobStore', $blobStore );
482
483 $this->testGetRevisionText( $expected, $rowData );
484 }
485
486 public function provideGetRevisionTextWithGzipAndLegacyEncoding() {
487 /**
488 * WARNING!
489 * Do not set the external flag!
490 * Otherwise, getRevisionText will hit the live database (if ExternalStore is enabled)!
491 */
492 yield 'Utf8NativeGzip' => [
493 "Wiki est l'\xc3\xa9cole superieur !",
494 'fr',
495 'iso-8859-1',
496 [
497 'old_flags' => 'gzip,utf-8',
498 'old_text' => gzdeflate( "Wiki est l'\xc3\xa9cole superieur !" ),
499 ]
500 ];
501 yield 'Utf8LegacyGzip' => [
502 "Wiki est l'\xc3\xa9cole superieur !",
503 'fr',
504 'iso-8859-1',
505 [
506 'old_flags' => 'gzip',
507 'old_text' => gzdeflate( "Wiki est l'\xe9cole superieur !" ),
508 ]
509 ];
510 }
511
512 /**
513 * @covers Revision::getRevisionText
514 * @dataProvider provideGetRevisionTextWithGzipAndLegacyEncoding
515 */
516 public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $lang, $encoding, $rowData ) {
517 $this->checkPHPExtension( 'zlib' );
518
519 $blobStore = $this->getBlobStore();
520 $blobStore->setLegacyEncoding( $encoding, Language::factory( $lang ) );
521 $this->setService( 'BlobStore', $blobStore );
522
523 $this->testGetRevisionText( $expected, $rowData );
524 }
525
526 /**
527 * @covers Revision::compressRevisionText
528 */
529 public function testCompressRevisionTextUtf8() {
530 $row = new stdClass;
531 $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
532 $row->old_flags = Revision::compressRevisionText( $row->old_text );
533 $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ),
534 "Flags should contain 'utf-8'" );
535 $this->assertFalse( false !== strpos( $row->old_flags, 'gzip' ),
536 "Flags should not contain 'gzip'" );
537 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
538 $row->old_text, "Direct check" );
539 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
540 Revision::getRevisionText( $row ), "getRevisionText" );
541 }
542
543 /**
544 * @covers Revision::compressRevisionText
545 */
546 public function testCompressRevisionTextUtf8Gzip() {
547 $this->checkPHPExtension( 'zlib' );
548
549 $blobStore = $this->getBlobStore();
550 $blobStore->setCompressBlobs( true );
551 $this->setService( 'BlobStore', $blobStore );
552
553 $row = new stdClass;
554 $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
555 $row->old_flags = Revision::compressRevisionText( $row->old_text );
556 $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ),
557 "Flags should contain 'utf-8'" );
558 $this->assertTrue( false !== strpos( $row->old_flags, 'gzip' ),
559 "Flags should contain 'gzip'" );
560 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
561 gzinflate( $row->old_text ), "Direct check" );
562 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
563 Revision::getRevisionText( $row ), "getRevisionText" );
564 }
565
566 /**
567 * @covers Revision::loadFromTitle
568 */
569 public function testLoadFromTitle() {
570 $title = $this->getMockTitle();
571
572 $conditions = [
573 'rev_id=page_latest',
574 'page_namespace' => $title->getNamespace(),
575 'page_title' => $title->getDBkey()
576 ];
577
578 $row = (object)[
579 'rev_id' => '42',
580 'rev_page' => $title->getArticleID(),
581 'rev_text_id' => '2',
582 'rev_timestamp' => '20171017114835',
583 'rev_user_text' => '127.0.0.1',
584 'rev_user' => '0',
585 'rev_minor_edit' => '0',
586 'rev_deleted' => '0',
587 'rev_len' => '46',
588 'rev_parent_id' => '1',
589 'rev_sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
590 'rev_comment_text' => 'Goat Comment!',
591 'rev_comment_data' => null,
592 'rev_comment_cid' => null,
593 'rev_content_format' => 'GOATFORMAT',
594 'rev_content_model' => 'GOATMODEL',
595 ];
596
597 $db = $this->getMock( IDatabase::class );
598 $db->expects( $this->any() )
599 ->method( 'getDomainId' )
600 ->will( $this->returnValue( wfWikiID() ) );
601 $db->expects( $this->once() )
602 ->method( 'selectRow' )
603 ->with(
604 $this->equalTo( [ 'revision', 'page', 'user' ] ),
605 // We don't really care about the fields are they come from the selectField methods
606 $this->isType( 'array' ),
607 $this->equalTo( $conditions ),
608 // Method name
609 $this->stringContains( 'fetchRevisionRowFromConds' ),
610 // We don't really care about the options here
611 $this->isType( 'array' ),
612 // We don't really care about the join conds are they come from the joinCond methods
613 $this->isType( 'array' )
614 )
615 ->willReturn( $row );
616
617 $revision = Revision::loadFromTitle( $db, $title );
618
619 $this->assertEquals( $title->getArticleID(), $revision->getTitle()->getArticleID() );
620 $this->assertEquals( $row->rev_id, $revision->getId() );
621 $this->assertEquals( $row->rev_len, $revision->getSize() );
622 $this->assertEquals( $row->rev_sha1, $revision->getSha1() );
623 $this->assertEquals( $row->rev_parent_id, $revision->getParentId() );
624 $this->assertEquals( $row->rev_timestamp, $revision->getTimestamp() );
625 $this->assertEquals( $row->rev_comment_text, $revision->getComment() );
626 $this->assertEquals( $row->rev_user_text, $revision->getUserText() );
627 }
628
629 public function provideDecompressRevisionText() {
630 yield '(no legacy encoding), false in false out' => [ false, false, [], false ];
631 yield '(no legacy encoding), empty in empty out' => [ false, '', [], '' ];
632 yield '(no legacy encoding), empty in empty out' => [ false, 'A', [], 'A' ];
633 yield '(no legacy encoding), string in with gzip flag returns string' => [
634 // gzip string below generated with gzdeflate( 'AAAABBAAA' )
635 false, "sttttr\002\022\000", [ 'gzip' ], 'AAAABBAAA',
636 ];
637 yield '(no legacy encoding), string in with object flag returns false' => [
638 // gzip string below generated with serialize( 'JOJO' )
639 false, "s:4:\"JOJO\";", [ 'object' ], false,
640 ];
641 yield '(no legacy encoding), serialized object in with object flag returns string' => [
642 false,
643 // Using a TitleValue object as it has a getText method (which is needed)
644 serialize( new TitleValue( 0, 'HHJJDDFF' ) ),
645 [ 'object' ],
646 'HHJJDDFF',
647 ];
648 yield '(no legacy encoding), serialized object in with object & gzip flag returns string' => [
649 false,
650 // Using a TitleValue object as it has a getText method (which is needed)
651 gzdeflate( serialize( new TitleValue( 0, '8219JJJ840' ) ) ),
652 [ 'object', 'gzip' ],
653 '8219JJJ840',
654 ];
655 yield '(ISO-8859-1 encoding), string in string out' => [
656 'ISO-8859-1',
657 iconv( 'utf-8', 'ISO-8859-1', "1®Àþ1" ),
658 [],
659 '1®Àþ1',
660 ];
661 yield '(ISO-8859-1 encoding), serialized object in with gzip flags returns string' => [
662 'ISO-8859-1',
663 gzdeflate( iconv( 'utf-8', 'ISO-8859-1', "4®Àþ4" ) ),
664 [ 'gzip' ],
665 '4®Àþ4',
666 ];
667 yield '(ISO-8859-1 encoding), serialized object in with object flags returns string' => [
668 'ISO-8859-1',
669 serialize( new TitleValue( 0, iconv( 'utf-8', 'ISO-8859-1', "3®Àþ3" ) ) ),
670 [ 'object' ],
671 '3®Àþ3',
672 ];
673 yield '(ISO-8859-1 encoding), serialized object in with object & gzip flags returns string' => [
674 'ISO-8859-1',
675 gzdeflate( serialize( new TitleValue( 0, iconv( 'utf-8', 'ISO-8859-1', "2®Àþ2" ) ) ) ),
676 [ 'gzip', 'object' ],
677 '2®Àþ2',
678 ];
679 }
680
681 /**
682 * @dataProvider provideDecompressRevisionText
683 * @covers Revision::decompressRevisionText
684 *
685 * @param bool $legacyEncoding
686 * @param mixed $text
687 * @param array $flags
688 * @param mixed $expected
689 */
690 public function testDecompressRevisionText( $legacyEncoding, $text, $flags, $expected ) {
691 $blobStore = $this->getBlobStore();
692 if ( $legacyEncoding ) {
693 $blobStore->setLegacyEncoding( $legacyEncoding, Language::factory( 'en' ) );
694 }
695
696 $this->setService( 'BlobStore', $blobStore );
697 $this->assertSame(
698 $expected,
699 Revision::decompressRevisionText( $text, $flags )
700 );
701 }
702
703 /**
704 * @covers Revision::getRevisionText
705 */
706 public function testGetRevisionText_returnsFalseWhenNoTextField() {
707 $this->assertFalse( Revision::getRevisionText( new stdClass() ) );
708 }
709
710 public function provideTestGetRevisionText_returnsDecompressedTextFieldWhenNotExternal() {
711 yield 'Just text' => [
712 (object)[ 'old_text' => 'SomeText' ],
713 'old_',
714 'SomeText'
715 ];
716 // gzip string below generated with gzdeflate( 'AAAABBAAA' )
717 yield 'gzip text' => [
718 (object)[
719 'old_text' => "sttttr\002\022\000",
720 'old_flags' => 'gzip'
721 ],
722 'old_',
723 'AAAABBAAA'
724 ];
725 yield 'gzip text and different prefix' => [
726 (object)[
727 'jojo_text' => "sttttr\002\022\000",
728 'jojo_flags' => 'gzip'
729 ],
730 'jojo_',
731 'AAAABBAAA'
732 ];
733 }
734
735 /**
736 * @dataProvider provideTestGetRevisionText_returnsDecompressedTextFieldWhenNotExternal
737 * @covers Revision::getRevisionText
738 */
739 public function testGetRevisionText_returnsDecompressedTextFieldWhenNotExternal(
740 $row,
741 $prefix,
742 $expected
743 ) {
744 $this->assertSame( $expected, Revision::getRevisionText( $row, $prefix ) );
745 }
746
747 public function provideTestGetRevisionText_external_returnsFalseWhenNotEnoughUrlParts() {
748 yield 'Just some text' => [ 'someNonUrlText' ];
749 yield 'No second URL part' => [ 'someProtocol://' ];
750 }
751
752 /**
753 * @dataProvider provideTestGetRevisionText_external_returnsFalseWhenNotEnoughUrlParts
754 * @covers Revision::getRevisionText
755 */
756 public function testGetRevisionText_external_returnsFalseWhenNotEnoughUrlParts(
757 $text
758 ) {
759 $this->assertFalse(
760 Revision::getRevisionText(
761 (object)[
762 'old_text' => $text,
763 'old_flags' => 'external',
764 ]
765 )
766 );
767 }
768
769 /**
770 * @covers Revision::getRevisionText
771 */
772 public function testGetRevisionText_external_noOldId() {
773 $this->setService(
774 'ExternalStoreFactory',
775 new ExternalStoreFactory( [ 'ForTesting' ] )
776 );
777 $this->assertSame(
778 'AAAABBAAA',
779 Revision::getRevisionText(
780 (object)[
781 'old_text' => 'ForTesting://cluster1/12345',
782 'old_flags' => 'external,gzip',
783 ]
784 )
785 );
786 }
787
788 /**
789 * @covers Revision::getRevisionText
790 */
791 public function testGetRevisionText_external_oldId() {
792 $cache = $this->getWANObjectCache();
793 $this->setService( 'MainWANObjectCache', $cache );
794
795 $this->setService(
796 'ExternalStoreFactory',
797 new ExternalStoreFactory( [ 'ForTesting' ] )
798 );
799
800 $lb = $this->getMockBuilder( LoadBalancer::class )
801 ->disableOriginalConstructor()
802 ->getMock();
803
804 $blobStore = new SqlBlobStore( $lb, $cache );
805 $this->setService( 'BlobStore', $blobStore );
806
807 $this->assertSame(
808 'AAAABBAAA',
809 Revision::getRevisionText(
810 (object)[
811 'old_text' => 'ForTesting://cluster1/12345',
812 'old_flags' => 'external,gzip',
813 'old_id' => '7777',
814 ]
815 )
816 );
817
818 $cacheKey = $cache->makeKey( 'revisiontext', 'textid', 'tt:7777' );
819 $this->assertSame( 'AAAABBAAA', $cache->get( $cacheKey ) );
820 }
821
822 /**
823 * @covers Revision::userJoinCond
824 */
825 public function testUserJoinCond() {
826 $this->hideDeprecated( 'Revision::userJoinCond' );
827 $this->assertEquals(
828 [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
829 Revision::userJoinCond()
830 );
831 }
832
833 /**
834 * @covers Revision::pageJoinCond
835 */
836 public function testPageJoinCond() {
837 $this->hideDeprecated( 'Revision::pageJoinCond' );
838 $this->assertEquals(
839 [ 'INNER JOIN', [ 'page_id = rev_page' ] ],
840 Revision::pageJoinCond()
841 );
842 }
843
844 public function provideSelectFields() {
845 yield [
846 true,
847 [
848 'rev_id',
849 'rev_page',
850 'rev_text_id',
851 'rev_timestamp',
852 'rev_user_text',
853 'rev_user',
854 'rev_minor_edit',
855 'rev_deleted',
856 'rev_len',
857 'rev_parent_id',
858 'rev_sha1',
859 'rev_comment_text' => 'rev_comment',
860 'rev_comment_data' => 'NULL',
861 'rev_comment_cid' => 'NULL',
862 'rev_content_format',
863 'rev_content_model',
864 ]
865 ];
866 yield [
867 false,
868 [
869 'rev_id',
870 'rev_page',
871 'rev_text_id',
872 'rev_timestamp',
873 'rev_user_text',
874 'rev_user',
875 'rev_minor_edit',
876 'rev_deleted',
877 'rev_len',
878 'rev_parent_id',
879 'rev_sha1',
880 'rev_comment_text' => 'rev_comment',
881 'rev_comment_data' => 'NULL',
882 'rev_comment_cid' => 'NULL',
883 ]
884 ];
885 }
886
887 /**
888 * @dataProvider provideSelectFields
889 * @covers Revision::selectFields
890 * @todo a true unit test would mock CommentStore
891 */
892 public function testSelectFields( $contentHandlerUseDB, $expected ) {
893 $this->hideDeprecated( 'Revision::selectFields' );
894 $this->setMwGlobals( 'wgContentHandlerUseDB', $contentHandlerUseDB );
895 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
896 $this->assertEquals( $expected, Revision::selectFields() );
897 }
898
899 public function provideSelectArchiveFields() {
900 yield [
901 true,
902 [
903 'ar_id',
904 'ar_page_id',
905 'ar_rev_id',
906 'ar_text',
907 'ar_text_id',
908 'ar_timestamp',
909 'ar_user_text',
910 'ar_user',
911 'ar_minor_edit',
912 'ar_deleted',
913 'ar_len',
914 'ar_parent_id',
915 'ar_sha1',
916 'ar_comment_text' => 'ar_comment',
917 'ar_comment_data' => 'NULL',
918 'ar_comment_cid' => 'NULL',
919 'ar_content_format',
920 'ar_content_model',
921 ]
922 ];
923 yield [
924 false,
925 [
926 'ar_id',
927 'ar_page_id',
928 'ar_rev_id',
929 'ar_text',
930 'ar_text_id',
931 'ar_timestamp',
932 'ar_user_text',
933 'ar_user',
934 'ar_minor_edit',
935 'ar_deleted',
936 'ar_len',
937 'ar_parent_id',
938 'ar_sha1',
939 'ar_comment_text' => 'ar_comment',
940 'ar_comment_data' => 'NULL',
941 'ar_comment_cid' => 'NULL',
942 ]
943 ];
944 }
945
946 /**
947 * @dataProvider provideSelectArchiveFields
948 * @covers Revision::selectArchiveFields
949 * @todo a true unit test would mock CommentStore
950 */
951 public function testSelectArchiveFields( $contentHandlerUseDB, $expected ) {
952 $this->hideDeprecated( 'Revision::selectArchiveFields' );
953 $this->setMwGlobals( 'wgContentHandlerUseDB', $contentHandlerUseDB );
954 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
955 $this->assertEquals( $expected, Revision::selectArchiveFields() );
956 }
957
958 /**
959 * @covers Revision::selectTextFields
960 */
961 public function testSelectTextFields() {
962 $this->hideDeprecated( 'Revision::selectTextFields' );
963 $this->assertEquals(
964 [
965 'old_text',
966 'old_flags',
967 ],
968 Revision::selectTextFields()
969 );
970 }
971
972 /**
973 * @covers Revision::selectPageFields
974 */
975 public function testSelectPageFields() {
976 $this->hideDeprecated( 'Revision::selectPageFields' );
977 $this->assertEquals(
978 [
979 'page_namespace',
980 'page_title',
981 'page_id',
982 'page_latest',
983 'page_is_redirect',
984 'page_len',
985 ],
986 Revision::selectPageFields()
987 );
988 }
989
990 /**
991 * @covers Revision::selectUserFields
992 */
993 public function testSelectUserFields() {
994 $this->hideDeprecated( 'Revision::selectUserFields' );
995 $this->assertEquals(
996 [
997 'user_name',
998 ],
999 Revision::selectUserFields()
1000 );
1001 }
1002
1003 public function provideGetArchiveQueryInfo() {
1004 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD' => [
1005 [
1006 'wgContentHandlerUseDB' => false,
1007 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1008 ],
1009 [
1010 'tables' => [ 'archive' ],
1011 'fields' => [
1012 'ar_id',
1013 'ar_page_id',
1014 'ar_namespace',
1015 'ar_title',
1016 'ar_rev_id',
1017 'ar_text',
1018 'ar_text_id',
1019 'ar_timestamp',
1020 'ar_user_text',
1021 'ar_user',
1022 'ar_minor_edit',
1023 'ar_deleted',
1024 'ar_len',
1025 'ar_parent_id',
1026 'ar_sha1',
1027 'ar_comment_text' => 'ar_comment',
1028 'ar_comment_data' => 'NULL',
1029 'ar_comment_cid' => 'NULL',
1030 ],
1031 'joins' => [],
1032 ]
1033 ];
1034 yield 'wgContentHandlerUseDB true, wgCommentTableSchemaMigrationStage OLD' => [
1035 [
1036 'wgContentHandlerUseDB' => true,
1037 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1038 ],
1039 [
1040 'tables' => [ 'archive' ],
1041 'fields' => [
1042 'ar_id',
1043 'ar_page_id',
1044 'ar_namespace',
1045 'ar_title',
1046 'ar_rev_id',
1047 'ar_text',
1048 'ar_text_id',
1049 'ar_timestamp',
1050 'ar_user_text',
1051 'ar_user',
1052 'ar_minor_edit',
1053 'ar_deleted',
1054 'ar_len',
1055 'ar_parent_id',
1056 'ar_sha1',
1057 'ar_comment_text' => 'ar_comment',
1058 'ar_comment_data' => 'NULL',
1059 'ar_comment_cid' => 'NULL',
1060 'ar_content_format',
1061 'ar_content_model',
1062 ],
1063 'joins' => [],
1064 ]
1065 ];
1066 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_BOTH' => [
1067 [
1068 'wgContentHandlerUseDB' => false,
1069 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_BOTH,
1070 ],
1071 [
1072 'tables' => [
1073 'archive',
1074 'comment_ar_comment' => 'comment',
1075 ],
1076 'fields' => [
1077 'ar_id',
1078 'ar_page_id',
1079 'ar_namespace',
1080 'ar_title',
1081 'ar_rev_id',
1082 'ar_text',
1083 'ar_text_id',
1084 'ar_timestamp',
1085 'ar_user_text',
1086 'ar_user',
1087 'ar_minor_edit',
1088 'ar_deleted',
1089 'ar_len',
1090 'ar_parent_id',
1091 'ar_sha1',
1092 'ar_comment_text' => 'COALESCE( comment_ar_comment.comment_text, ar_comment )',
1093 'ar_comment_data' => 'comment_ar_comment.comment_data',
1094 'ar_comment_cid' => 'comment_ar_comment.comment_id',
1095 ],
1096 'joins' => [
1097 'comment_ar_comment' => [
1098 'LEFT JOIN',
1099 'comment_ar_comment.comment_id = ar_comment_id',
1100 ],
1101 ],
1102 ]
1103 ];
1104 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_NEW' => [
1105 [
1106 'wgContentHandlerUseDB' => false,
1107 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_NEW,
1108 ],
1109 [
1110 'tables' => [
1111 'archive',
1112 'comment_ar_comment' => 'comment',
1113 ],
1114 'fields' => [
1115 'ar_id',
1116 'ar_page_id',
1117 'ar_namespace',
1118 'ar_title',
1119 'ar_rev_id',
1120 'ar_text',
1121 'ar_text_id',
1122 'ar_timestamp',
1123 'ar_user_text',
1124 'ar_user',
1125 'ar_minor_edit',
1126 'ar_deleted',
1127 'ar_len',
1128 'ar_parent_id',
1129 'ar_sha1',
1130 'ar_comment_text' => 'COALESCE( comment_ar_comment.comment_text, ar_comment )',
1131 'ar_comment_data' => 'comment_ar_comment.comment_data',
1132 'ar_comment_cid' => 'comment_ar_comment.comment_id',
1133 ],
1134 'joins' => [
1135 'comment_ar_comment' => [
1136 'LEFT JOIN',
1137 'comment_ar_comment.comment_id = ar_comment_id',
1138 ],
1139 ],
1140 ]
1141 ];
1142 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage NEW' => [
1143 [
1144 'wgContentHandlerUseDB' => false,
1145 'wgCommentTableSchemaMigrationStage' => MIGRATION_NEW,
1146 ],
1147 [
1148 'tables' => [
1149 'archive',
1150 'comment_ar_comment' => 'comment',
1151 ],
1152 'fields' => [
1153 'ar_id',
1154 'ar_page_id',
1155 'ar_namespace',
1156 'ar_title',
1157 'ar_rev_id',
1158 'ar_text',
1159 'ar_text_id',
1160 'ar_timestamp',
1161 'ar_user_text',
1162 'ar_user',
1163 'ar_minor_edit',
1164 'ar_deleted',
1165 'ar_len',
1166 'ar_parent_id',
1167 'ar_sha1',
1168 'ar_comment_text' => 'comment_ar_comment.comment_text',
1169 'ar_comment_data' => 'comment_ar_comment.comment_data',
1170 'ar_comment_cid' => 'comment_ar_comment.comment_id',
1171 ],
1172 'joins' => [
1173 'comment_ar_comment' => [
1174 'JOIN',
1175 'comment_ar_comment.comment_id = ar_comment_id',
1176 ],
1177 ],
1178 ]
1179 ];
1180 }
1181
1182 /**
1183 * @covers Revision::getArchiveQueryInfo
1184 * @dataProvider provideGetArchiveQueryInfo
1185 */
1186 public function testGetArchiveQueryInfo( $globals, $expected ) {
1187 $this->setMwGlobals( $globals );
1188
1189 $revisionStore = $this->getRevisionStore();
1190 $revisionStore->setContentHandlerUseDB( $globals['wgContentHandlerUseDB'] );
1191 $this->setService( 'RevisionStore', $revisionStore );
1192
1193 $this->assertEquals(
1194 $expected,
1195 Revision::getArchiveQueryInfo()
1196 );
1197 }
1198
1199 public function provideGetQueryInfo() {
1200 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts none' => [
1201 [
1202 'wgContentHandlerUseDB' => false,
1203 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1204 ],
1205 [],
1206 [
1207 'tables' => [ 'revision' ],
1208 'fields' => [
1209 'rev_id',
1210 'rev_page',
1211 'rev_text_id',
1212 'rev_timestamp',
1213 'rev_user_text',
1214 'rev_user',
1215 'rev_minor_edit',
1216 'rev_deleted',
1217 'rev_len',
1218 'rev_parent_id',
1219 'rev_sha1',
1220 'rev_comment_text' => 'rev_comment',
1221 'rev_comment_data' => 'NULL',
1222 'rev_comment_cid' => 'NULL',
1223 ],
1224 'joins' => [],
1225 ],
1226 ];
1227 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts page' => [
1228 [
1229 'wgContentHandlerUseDB' => false,
1230 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1231 ],
1232 [ 'page' ],
1233 [
1234 'tables' => [ 'revision', 'page' ],
1235 'fields' => [
1236 'rev_id',
1237 'rev_page',
1238 'rev_text_id',
1239 'rev_timestamp',
1240 'rev_user_text',
1241 'rev_user',
1242 'rev_minor_edit',
1243 'rev_deleted',
1244 'rev_len',
1245 'rev_parent_id',
1246 'rev_sha1',
1247 'rev_comment_text' => 'rev_comment',
1248 'rev_comment_data' => 'NULL',
1249 'rev_comment_cid' => 'NULL',
1250 'page_namespace',
1251 'page_title',
1252 'page_id',
1253 'page_latest',
1254 'page_is_redirect',
1255 'page_len',
1256 ],
1257 'joins' => [
1258 'page' => [
1259 'INNER JOIN',
1260 [ 'page_id = rev_page' ],
1261 ],
1262 ],
1263 ],
1264 ];
1265 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts user' => [
1266 [
1267 'wgContentHandlerUseDB' => false,
1268 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1269 ],
1270 [ 'user' ],
1271 [
1272 'tables' => [ 'revision', 'user' ],
1273 'fields' => [
1274 'rev_id',
1275 'rev_page',
1276 'rev_text_id',
1277 'rev_timestamp',
1278 'rev_user_text',
1279 'rev_user',
1280 'rev_minor_edit',
1281 'rev_deleted',
1282 'rev_len',
1283 'rev_parent_id',
1284 'rev_sha1',
1285 'rev_comment_text' => 'rev_comment',
1286 'rev_comment_data' => 'NULL',
1287 'rev_comment_cid' => 'NULL',
1288 'user_name',
1289 ],
1290 'joins' => [
1291 'user' => [
1292 'LEFT JOIN',
1293 [
1294 'rev_user != 0',
1295 'user_id = rev_user',
1296 ],
1297 ],
1298 ],
1299 ],
1300 ];
1301 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts text' => [
1302 [
1303 'wgContentHandlerUseDB' => false,
1304 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1305 ],
1306 [ 'text' ],
1307 [
1308 'tables' => [ 'revision', 'text' ],
1309 'fields' => [
1310 'rev_id',
1311 'rev_page',
1312 'rev_text_id',
1313 'rev_timestamp',
1314 'rev_user_text',
1315 'rev_user',
1316 'rev_minor_edit',
1317 'rev_deleted',
1318 'rev_len',
1319 'rev_parent_id',
1320 'rev_sha1',
1321 'rev_comment_text' => 'rev_comment',
1322 'rev_comment_data' => 'NULL',
1323 'rev_comment_cid' => 'NULL',
1324 'old_text',
1325 'old_flags',
1326 ],
1327 'joins' => [
1328 'text' => [
1329 'INNER JOIN',
1330 [ 'rev_text_id=old_id' ],
1331 ],
1332 ],
1333 ],
1334 ];
1335 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts 3' => [
1336 [
1337 'wgContentHandlerUseDB' => false,
1338 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1339 ],
1340 [ 'text', 'page', 'user' ],
1341 [
1342 'tables' => [ 'revision', 'page', 'user', 'text' ],
1343 'fields' => [
1344 'rev_id',
1345 'rev_page',
1346 'rev_text_id',
1347 'rev_timestamp',
1348 'rev_user_text',
1349 'rev_user',
1350 'rev_minor_edit',
1351 'rev_deleted',
1352 'rev_len',
1353 'rev_parent_id',
1354 'rev_sha1',
1355 'rev_comment_text' => 'rev_comment',
1356 'rev_comment_data' => 'NULL',
1357 'rev_comment_cid' => 'NULL',
1358 'page_namespace',
1359 'page_title',
1360 'page_id',
1361 'page_latest',
1362 'page_is_redirect',
1363 'page_len',
1364 'user_name',
1365 'old_text',
1366 'old_flags',
1367 ],
1368 'joins' => [
1369 'page' => [
1370 'INNER JOIN',
1371 [ 'page_id = rev_page' ],
1372 ],
1373 'user' => [
1374 'LEFT JOIN',
1375 [
1376 'rev_user != 0',
1377 'user_id = rev_user',
1378 ],
1379 ],
1380 'text' => [
1381 'INNER JOIN',
1382 [ 'rev_text_id=old_id' ],
1383 ],
1384 ],
1385 ],
1386 ];
1387 yield 'wgContentHandlerUseDB true, wgCommentTableSchemaMigrationStage OLD, opts none' => [
1388 [
1389 'wgContentHandlerUseDB' => true,
1390 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1391 ],
1392 [],
1393 [
1394 'tables' => [ 'revision' ],
1395 'fields' => [
1396 'rev_id',
1397 'rev_page',
1398 'rev_text_id',
1399 'rev_timestamp',
1400 'rev_user_text',
1401 'rev_user',
1402 'rev_minor_edit',
1403 'rev_deleted',
1404 'rev_len',
1405 'rev_parent_id',
1406 'rev_sha1',
1407 'rev_comment_text' => 'rev_comment',
1408 'rev_comment_data' => 'NULL',
1409 'rev_comment_cid' => 'NULL',
1410 'rev_content_format',
1411 'rev_content_model',
1412 ],
1413 'joins' => [],
1414 ],
1415 ];
1416 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_BOTH, opts none' => [
1417 [
1418 'wgContentHandlerUseDB' => false,
1419 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_BOTH,
1420 ],
1421 [],
1422 [
1423 'tables' => [
1424 'revision',
1425 'temp_rev_comment' => 'revision_comment_temp',
1426 'comment_rev_comment' => 'comment',
1427 ],
1428 'fields' => [
1429 'rev_id',
1430 'rev_page',
1431 'rev_text_id',
1432 'rev_timestamp',
1433 'rev_user_text',
1434 'rev_user',
1435 'rev_minor_edit',
1436 'rev_deleted',
1437 'rev_len',
1438 'rev_parent_id',
1439 'rev_sha1',
1440 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )',
1441 'rev_comment_data' => 'comment_rev_comment.comment_data',
1442 'rev_comment_cid' => 'comment_rev_comment.comment_id',
1443 ],
1444 'joins' => [
1445 'temp_rev_comment' => [
1446 'LEFT JOIN',
1447 'temp_rev_comment.revcomment_rev = rev_id',
1448 ],
1449 'comment_rev_comment' => [
1450 'LEFT JOIN',
1451 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id',
1452 ],
1453 ],
1454 ],
1455 ];
1456 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_NEW, opts none' => [
1457 [
1458 'wgContentHandlerUseDB' => false,
1459 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_NEW,
1460 ],
1461 [],
1462 [
1463 'tables' => [
1464 'revision',
1465 'temp_rev_comment' => 'revision_comment_temp',
1466 'comment_rev_comment' => 'comment',
1467 ],
1468 'fields' => [
1469 'rev_id',
1470 'rev_page',
1471 'rev_text_id',
1472 'rev_timestamp',
1473 'rev_user_text',
1474 'rev_user',
1475 'rev_minor_edit',
1476 'rev_deleted',
1477 'rev_len',
1478 'rev_parent_id',
1479 'rev_sha1',
1480 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )',
1481 'rev_comment_data' => 'comment_rev_comment.comment_data',
1482 'rev_comment_cid' => 'comment_rev_comment.comment_id',
1483 ],
1484 'joins' => [
1485 'temp_rev_comment' => [
1486 'LEFT JOIN',
1487 'temp_rev_comment.revcomment_rev = rev_id',
1488 ],
1489 'comment_rev_comment' => [
1490 'LEFT JOIN',
1491 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id',
1492 ],
1493 ],
1494 ],
1495 ];
1496 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage NEW, opts none' => [
1497 [
1498 'wgContentHandlerUseDB' => false,
1499 'wgCommentTableSchemaMigrationStage' => MIGRATION_NEW,
1500 ],
1501 [],
1502 [
1503 'tables' => [
1504 'revision',
1505 'temp_rev_comment' => 'revision_comment_temp',
1506 'comment_rev_comment' => 'comment',
1507 ],
1508 'fields' => [
1509 'rev_id',
1510 'rev_page',
1511 'rev_text_id',
1512 'rev_timestamp',
1513 'rev_user_text',
1514 'rev_user',
1515 'rev_minor_edit',
1516 'rev_deleted',
1517 'rev_len',
1518 'rev_parent_id',
1519 'rev_sha1',
1520 'rev_comment_text' => 'comment_rev_comment.comment_text',
1521 'rev_comment_data' => 'comment_rev_comment.comment_data',
1522 'rev_comment_cid' => 'comment_rev_comment.comment_id',
1523 ],
1524 'joins' => [
1525 'temp_rev_comment' => [
1526 'JOIN',
1527 'temp_rev_comment.revcomment_rev = rev_id',
1528 ],
1529 'comment_rev_comment' => [
1530 'JOIN',
1531 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id',
1532 ],
1533 ],
1534 ],
1535 ];
1536 }
1537
1538 /**
1539 * @covers Revision::getQueryInfo
1540 * @dataProvider provideGetQueryInfo
1541 */
1542 public function testGetQueryInfo( $globals, $options, $expected ) {
1543 $this->setMwGlobals( $globals );
1544
1545 $revisionStore = $this->getRevisionStore();
1546 $revisionStore->setContentHandlerUseDB( $globals['wgContentHandlerUseDB'] );
1547 $this->setService( 'RevisionStore', $revisionStore );
1548
1549 $this->assertEquals(
1550 $expected,
1551 Revision::getQueryInfo( $options )
1552 );
1553 }
1554
1555 }