Merge "rdbms: Group disconnect/reconnect errors by DB server name"
[lhc/web/wiklou.git] / tests / phpunit / includes / RevisionIntegrationTest.php
1 <?php
2
3 /**
4 * @group ContentHandler
5 * @group Database
6 *
7 * @group medium
8 */
9 class RevisionIntegrationTest extends MediaWikiTestCase {
10
11 /**
12 * @var WikiPage $testPage
13 */
14 private $testPage;
15
16 public function __construct( $name = null, array $data = [], $dataName = '' ) {
17 parent::__construct( $name, $data, $dataName );
18
19 $this->tablesUsed = array_merge( $this->tablesUsed,
20 [
21 'page',
22 'revision',
23 'ip_changes',
24 'text',
25 'archive',
26
27 'recentchanges',
28 'logging',
29
30 'page_props',
31 'pagelinks',
32 'categorylinks',
33 'langlinks',
34 'externallinks',
35 'imagelinks',
36 'templatelinks',
37 'iwlinks'
38 ]
39 );
40 }
41
42 protected function setUp() {
43 global $wgContLang;
44
45 parent::setUp();
46
47 $this->mergeMwGlobalArrayValue(
48 'wgExtraNamespaces',
49 [
50 12312 => 'Dummy',
51 12313 => 'Dummy_talk',
52 ]
53 );
54
55 $this->mergeMwGlobalArrayValue(
56 'wgNamespaceContentModels',
57 [
58 12312 => DummyContentForTesting::MODEL_ID,
59 ]
60 );
61
62 $this->mergeMwGlobalArrayValue(
63 'wgContentHandlers',
64 [
65 DummyContentForTesting::MODEL_ID => 'DummyContentHandlerForTesting',
66 RevisionTestModifyableContent::MODEL_ID => 'RevisionTestModifyableContentHandler',
67 ]
68 );
69
70 MWNamespace::clearCaches();
71 // Reset namespace cache
72 $wgContLang->resetNamespaces();
73 if ( !$this->testPage ) {
74 $this->testPage = WikiPage::factory( Title::newFromText( 'UTPage' ) );
75 }
76 }
77
78 protected function tearDown() {
79 global $wgContLang;
80
81 parent::tearDown();
82
83 MWNamespace::clearCaches();
84 // Reset namespace cache
85 $wgContLang->resetNamespaces();
86 }
87
88 private function makeRevisionWithProps( $props = null ) {
89 if ( $props === null ) {
90 $props = [];
91 }
92
93 if ( !isset( $props['content'] ) && !isset( $props['text'] ) ) {
94 $props['text'] = 'Lorem Ipsum';
95 }
96
97 if ( !isset( $props['comment'] ) ) {
98 $props['comment'] = 'just a test';
99 }
100
101 if ( !isset( $props['page'] ) ) {
102 $props['page'] = $this->testPage->getId();
103 }
104
105 $rev = new Revision( $props );
106
107 $dbw = wfGetDB( DB_MASTER );
108 $rev->insertOn( $dbw );
109
110 return $rev;
111 }
112
113 /**
114 * @param string $titleString
115 * @param string $text
116 * @param string|null $model
117 *
118 * @return WikiPage
119 */
120 private function createPage( $titleString, $text, $model = null ) {
121 if ( !preg_match( '/:/', $titleString ) &&
122 ( $model === null || $model === CONTENT_MODEL_WIKITEXT )
123 ) {
124 $ns = $this->getDefaultWikitextNS();
125 $titleString = MWNamespace::getCanonicalName( $ns ) . ':' . $titleString;
126 }
127
128 $title = Title::newFromText( $titleString );
129 $wikipage = new WikiPage( $title );
130
131 // Delete the article if it already exists
132 if ( $wikipage->exists() ) {
133 $wikipage->doDeleteArticle( "done" );
134 }
135
136 $content = ContentHandler::makeContent( $text, $title, $model );
137 $wikipage->doEditContent( $content, __METHOD__, EDIT_NEW );
138
139 return $wikipage;
140 }
141
142 private function assertRevEquals( Revision $orig, Revision $rev = null ) {
143 $this->assertNotNull( $rev, 'missing revision' );
144
145 $this->assertEquals( $orig->getId(), $rev->getId() );
146 $this->assertEquals( $orig->getPage(), $rev->getPage() );
147 $this->assertEquals( $orig->getTimestamp(), $rev->getTimestamp() );
148 $this->assertEquals( $orig->getUser(), $rev->getUser() );
149 $this->assertEquals( $orig->getContentModel(), $rev->getContentModel() );
150 $this->assertEquals( $orig->getContentFormat(), $rev->getContentFormat() );
151 $this->assertEquals( $orig->getSha1(), $rev->getSha1() );
152 }
153
154 /**
155 * @covers Revision::insertOn
156 */
157 public function testInsertOn_success() {
158 $parentId = $this->testPage->getLatest();
159
160 // If an ExternalStore is set don't use it.
161 $this->setMwGlobals( 'wgDefaultExternalStore', false );
162
163 $rev = new Revision( [
164 'page' => $this->testPage->getId(),
165 'title' => $this->testPage->getTitle(),
166 'text' => 'Revision Text',
167 'comment' => 'Revision comment',
168 ] );
169
170 $revId = $rev->insertOn( wfGetDB( DB_MASTER ) );
171
172 $this->assertInternalType( 'integer', $revId );
173 $this->assertInternalType( 'integer', $rev->getTextId() );
174 $this->assertSame( $revId, $rev->getId() );
175
176 $this->assertSelect(
177 'text',
178 [ 'old_id', 'old_text' ],
179 "old_id = {$rev->getTextId()}",
180 [ [ strval( $rev->getTextId() ), 'Revision Text' ] ]
181 );
182 $this->assertSelect(
183 'revision',
184 [
185 'rev_id',
186 'rev_page',
187 'rev_text_id',
188 'rev_user',
189 'rev_minor_edit',
190 'rev_deleted',
191 'rev_len',
192 'rev_parent_id',
193 'rev_sha1',
194 ],
195 "rev_id = {$rev->getId()}",
196 [ [
197 strval( $rev->getId() ),
198 strval( $this->testPage->getId() ),
199 strval( $rev->getTextId() ),
200 '0',
201 '0',
202 '0',
203 '13',
204 strval( $parentId ),
205 's0ngbdoxagreuf2vjtuxzwdz64n29xm',
206 ] ]
207 );
208 }
209
210 /**
211 * @covers Revision::insertOn
212 */
213 public function testInsertOn_exceptionOnNoPage() {
214 // If an ExternalStore is set don't use it.
215 $this->setMwGlobals( 'wgDefaultExternalStore', false );
216 $this->setExpectedException(
217 MWException::class,
218 "Cannot insert revision: page ID must be nonzero"
219 );
220
221 $rev = new Revision( [] );
222
223 $rev->insertOn( wfGetDB( DB_MASTER ) );
224 }
225
226 /**
227 * @covers Revision::newFromTitle
228 */
229 public function testNewFromTitle_withoutId() {
230 $latestRevId = $this->testPage->getLatest();
231
232 $rev = Revision::newFromTitle( $this->testPage->getTitle() );
233
234 $this->assertTrue( $this->testPage->getTitle()->equals( $rev->getTitle() ) );
235 $this->assertEquals( $latestRevId, $rev->getId() );
236 }
237
238 /**
239 * @covers Revision::newFromTitle
240 */
241 public function testNewFromTitle_withId() {
242 $latestRevId = $this->testPage->getLatest();
243
244 $rev = Revision::newFromTitle( $this->testPage->getTitle(), $latestRevId );
245
246 $this->assertTrue( $this->testPage->getTitle()->equals( $rev->getTitle() ) );
247 $this->assertEquals( $latestRevId, $rev->getId() );
248 }
249
250 /**
251 * @covers Revision::newFromTitle
252 */
253 public function testNewFromTitle_withBadId() {
254 $latestRevId = $this->testPage->getLatest();
255
256 $rev = Revision::newFromTitle( $this->testPage->getTitle(), $latestRevId + 1 );
257
258 $this->assertNull( $rev );
259 }
260
261 /**
262 * @covers Revision::newFromRow
263 */
264 public function testNewFromRow() {
265 $orig = $this->makeRevisionWithProps();
266
267 $dbr = wfGetDB( DB_REPLICA );
268 $res = $dbr->select( 'revision', Revision::selectFields(), [ 'rev_id' => $orig->getId() ] );
269 $this->assertTrue( is_object( $res ), 'query failed' );
270
271 $row = $res->fetchObject();
272 $res->free();
273
274 $rev = Revision::newFromRow( $row );
275
276 $this->assertRevEquals( $orig, $rev );
277 }
278
279 public function provideNewFromArchiveRow() {
280 yield [
281 true,
282 function ( $f ) {
283 return $f;
284 },
285 ];
286 yield [
287 false,
288 function ( $f ) {
289 return $f;
290 },
291 ];
292 yield [
293 true,
294 function ( $f ) {
295 return $f + [ 'ar_namespace', 'ar_title' ];
296 },
297 ];
298 yield [
299 false,
300 function ( $f ) {
301 return $f + [ 'ar_namespace', 'ar_title' ];
302 },
303 ];
304 yield [
305 true,
306 function ( $f ) {
307 unset( $f['ar_text_id'] );
308 return $f;
309 },
310 ];
311 yield [
312 false,
313 function ( $f ) {
314 unset( $f['ar_text_id'] );
315 return $f;
316 },
317 ];
318 }
319
320 /**
321 * @dataProvider provideNewFromArchiveRow
322 * @covers Revision::newFromArchiveRow
323 */
324 public function testNewFromArchiveRow( $contentHandlerUseDB, $selectModifier ) {
325 $this->setMwGlobals( 'wgContentHandlerUseDB', $contentHandlerUseDB );
326
327 $page = $this->createPage(
328 'RevisionStorageTest_testNewFromArchiveRow',
329 'Lorem Ipsum',
330 CONTENT_MODEL_WIKITEXT
331 );
332 $orig = $page->getRevision();
333 $page->doDeleteArticle( 'test Revision::newFromArchiveRow' );
334
335 $dbr = wfGetDB( DB_REPLICA );
336 $selectFields = $selectModifier( Revision::selectArchiveFields() );
337 $res = $dbr->select(
338 'archive', $selectFields, [ 'ar_rev_id' => $orig->getId() ]
339 );
340 $this->assertTrue( is_object( $res ), 'query failed' );
341
342 $row = $res->fetchObject();
343 $res->free();
344
345 $rev = Revision::newFromArchiveRow( $row );
346
347 $this->assertRevEquals( $orig, $rev );
348 }
349
350 /**
351 * @covers Revision::newFromArchiveRow
352 */
353 public function testNewFromArchiveRowOverrides() {
354 $page = $this->createPage(
355 'RevisionStorageTest_testNewFromArchiveRow',
356 'Lorem Ipsum',
357 CONTENT_MODEL_WIKITEXT
358 );
359 $orig = $page->getRevision();
360 $page->doDeleteArticle( 'test Revision::newFromArchiveRow' );
361
362 $dbr = wfGetDB( DB_REPLICA );
363 $res = $dbr->select(
364 'archive', Revision::selectArchiveFields(), [ 'ar_rev_id' => $orig->getId() ]
365 );
366 $this->assertTrue( is_object( $res ), 'query failed' );
367
368 $row = $res->fetchObject();
369 $res->free();
370
371 $rev = Revision::newFromArchiveRow( $row, [ 'comment' => 'SOMEOVERRIDE' ] );
372
373 $this->assertNotEquals( $orig->getComment(), $rev->getComment() );
374 $this->assertEquals( 'SOMEOVERRIDE', $rev->getComment() );
375 }
376
377 /**
378 * @covers Revision::newFromId
379 */
380 public function testNewFromId() {
381 $orig = $this->testPage->getRevision();
382 $rev = Revision::newFromId( $orig->getId() );
383 $this->assertRevEquals( $orig, $rev );
384 }
385
386 /**
387 * @covers Revision::newFromPageId
388 */
389 public function testNewFromPageId() {
390 $rev = Revision::newFromPageId( $this->testPage->getId() );
391 $this->assertRevEquals(
392 $this->testPage->getRevision(),
393 $rev
394 );
395 }
396
397 /**
398 * @covers Revision::newFromPageId
399 */
400 public function testNewFromPageIdWithLatestId() {
401 $rev = Revision::newFromPageId(
402 $this->testPage->getId(),
403 $this->testPage->getLatest()
404 );
405 $this->assertRevEquals(
406 $this->testPage->getRevision(),
407 $rev
408 );
409 }
410
411 /**
412 * @covers Revision::newFromPageId
413 */
414 public function testNewFromPageIdWithNotLatestId() {
415 $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
416 $rev = Revision::newFromPageId(
417 $this->testPage->getId(),
418 $this->testPage->getRevision()->getPrevious()->getId()
419 );
420 $this->assertRevEquals(
421 $this->testPage->getRevision()->getPrevious(),
422 $rev
423 );
424 }
425
426 /**
427 * @covers Revision::fetchRevision
428 */
429 public function testFetchRevision() {
430 // Hidden process cache assertion below
431 $this->testPage->getRevision()->getId();
432
433 $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
434 $id = $this->testPage->getRevision()->getId();
435
436 $res = Revision::fetchRevision( $this->testPage->getTitle() );
437
438 # note: order is unspecified
439 $rows = [];
440 while ( ( $row = $res->fetchObject() ) ) {
441 $rows[$row->rev_id] = $row;
442 }
443
444 $this->assertEquals( 1, count( $rows ), 'expected exactly one revision' );
445 $this->assertArrayHasKey( $id, $rows, 'missing revision with id ' . $id );
446 }
447
448 /**
449 * @covers Revision::getPage
450 */
451 public function testGetPage() {
452 $page = $this->testPage;
453
454 $orig = $this->makeRevisionWithProps( [ 'page' => $page->getId() ] );
455 $rev = Revision::newFromId( $orig->getId() );
456
457 $this->assertEquals( $page->getId(), $rev->getPage() );
458 }
459
460 /**
461 * @covers Revision::isCurrent
462 */
463 public function testIsCurrent() {
464 $rev1 = $this->testPage->getRevision();
465
466 # @todo find out if this should be true
467 # $this->assertTrue( $rev1->isCurrent() );
468
469 $rev1x = Revision::newFromId( $rev1->getId() );
470 $this->assertTrue( $rev1x->isCurrent() );
471
472 $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
473 $rev2 = $this->testPage->getRevision();
474
475 # @todo find out if this should be true
476 # $this->assertTrue( $rev2->isCurrent() );
477
478 $rev1x = Revision::newFromId( $rev1->getId() );
479 $this->assertFalse( $rev1x->isCurrent() );
480
481 $rev2x = Revision::newFromId( $rev2->getId() );
482 $this->assertTrue( $rev2x->isCurrent() );
483 }
484
485 /**
486 * @covers Revision::getPrevious
487 */
488 public function testGetPrevious() {
489 $oldestRevision = $this->testPage->getOldestRevision();
490 $latestRevision = $this->testPage->getLatest();
491
492 $this->assertNull( $oldestRevision->getPrevious() );
493
494 $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
495 $newRevision = $this->testPage->getRevision();
496
497 $this->assertNotNull( $newRevision->getPrevious() );
498 $this->assertEquals( $latestRevision, $newRevision->getPrevious()->getId() );
499 }
500
501 /**
502 * @covers Revision::getNext
503 */
504 public function testGetNext() {
505 $rev1 = $this->testPage->getRevision();
506
507 $this->assertNull( $rev1->getNext() );
508
509 $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
510 $rev2 = $this->testPage->getRevision();
511
512 $this->assertNotNull( $rev1->getNext() );
513 $this->assertEquals( $rev2->getId(), $rev1->getNext()->getId() );
514 }
515
516 /**
517 * @covers Revision::newNullRevision
518 */
519 public function testNewNullRevision() {
520 $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
521 $orig = $this->testPage->getRevision();
522
523 $dbw = wfGetDB( DB_MASTER );
524 $rev = Revision::newNullRevision( $dbw, $this->testPage->getId(), 'a null revision', false );
525
526 $this->assertNotEquals( $orig->getId(), $rev->getId(),
527 'new null revision should have a different id from the original revision' );
528 $this->assertEquals( $orig->getTextId(), $rev->getTextId(),
529 'new null revision should have the same text id as the original revision' );
530 $this->assertEquals( __METHOD__, $rev->getContent()->getNativeData() );
531 }
532
533 /**
534 * @covers Revision::insertOn
535 */
536 public function testInsertOn() {
537 $ip = '2600:387:ed7:947e:8c16:a1ad:dd34:1dd7';
538
539 $orig = $this->makeRevisionWithProps( [
540 'user_text' => $ip
541 ] );
542
543 // Make sure the revision was copied to ip_changes
544 $dbr = wfGetDB( DB_REPLICA );
545 $res = $dbr->select( 'ip_changes', '*', [ 'ipc_rev_id' => $orig->getId() ] );
546 $row = $res->fetchObject();
547
548 $this->assertEquals( IP::toHex( $ip ), $row->ipc_hex );
549 $this->assertEquals( $orig->getTimestamp(), $row->ipc_rev_timestamp );
550 }
551
552 public static function provideUserWasLastToEdit() {
553 yield 'actually the last edit' => [ 3, true ];
554 yield 'not the current edit, but still by this user' => [ 2, true ];
555 yield 'edit by another user' => [ 1, false ];
556 yield 'first edit, by this user, but another user edited in the mean time' => [ 0, false ];
557 }
558
559 /**
560 * @dataProvider provideUserWasLastToEdit
561 */
562 public function testUserWasLastToEdit( $sinceIdx, $expectedLast ) {
563 $userA = User::newFromName( "RevisionStorageTest_userA" );
564 $userB = User::newFromName( "RevisionStorageTest_userB" );
565
566 if ( $userA->getId() === 0 ) {
567 $userA = User::createNew( $userA->getName() );
568 }
569
570 if ( $userB->getId() === 0 ) {
571 $userB = User::createNew( $userB->getName() );
572 }
573
574 $ns = $this->getDefaultWikitextNS();
575
576 $dbw = wfGetDB( DB_MASTER );
577 $revisions = [];
578
579 // create revisions -----------------------------
580 $page = WikiPage::factory( Title::newFromText(
581 'RevisionStorageTest_testUserWasLastToEdit', $ns ) );
582 $page->insertOn( $dbw );
583
584 $revisions[0] = new Revision( [
585 'page' => $page->getId(),
586 // we need the title to determine the page's default content model
587 'title' => $page->getTitle(),
588 'timestamp' => '20120101000000',
589 'user' => $userA->getId(),
590 'text' => 'zero',
591 'content_model' => CONTENT_MODEL_WIKITEXT,
592 'summary' => 'edit zero'
593 ] );
594 $revisions[0]->insertOn( $dbw );
595
596 $revisions[1] = new Revision( [
597 'page' => $page->getId(),
598 // still need the title, because $page->getId() is 0 (there's no entry in the page table)
599 'title' => $page->getTitle(),
600 'timestamp' => '20120101000100',
601 'user' => $userA->getId(),
602 'text' => 'one',
603 'content_model' => CONTENT_MODEL_WIKITEXT,
604 'summary' => 'edit one'
605 ] );
606 $revisions[1]->insertOn( $dbw );
607
608 $revisions[2] = new Revision( [
609 'page' => $page->getId(),
610 'title' => $page->getTitle(),
611 'timestamp' => '20120101000200',
612 'user' => $userB->getId(),
613 'text' => 'two',
614 'content_model' => CONTENT_MODEL_WIKITEXT,
615 'summary' => 'edit two'
616 ] );
617 $revisions[2]->insertOn( $dbw );
618
619 $revisions[3] = new Revision( [
620 'page' => $page->getId(),
621 'title' => $page->getTitle(),
622 'timestamp' => '20120101000300',
623 'user' => $userA->getId(),
624 'text' => 'three',
625 'content_model' => CONTENT_MODEL_WIKITEXT,
626 'summary' => 'edit three'
627 ] );
628 $revisions[3]->insertOn( $dbw );
629
630 $revisions[4] = new Revision( [
631 'page' => $page->getId(),
632 'title' => $page->getTitle(),
633 'timestamp' => '20120101000200',
634 'user' => $userA->getId(),
635 'text' => 'zero',
636 'content_model' => CONTENT_MODEL_WIKITEXT,
637 'summary' => 'edit four'
638 ] );
639 $revisions[4]->insertOn( $dbw );
640
641 // test it ---------------------------------
642 $since = $revisions[$sinceIdx]->getTimestamp();
643
644 $wasLast = Revision::userWasLastToEdit( $dbw, $page->getId(), $userA->getId(), $since );
645
646 $this->assertEquals( $expectedLast, $wasLast );
647 }
648
649 /**
650 * @param string $text
651 * @param string $title
652 * @param string $model
653 * @param string $format
654 *
655 * @return Revision
656 */
657 private function newTestRevision( $text, $title = "Test",
658 $model = CONTENT_MODEL_WIKITEXT, $format = null
659 ) {
660 if ( is_string( $title ) ) {
661 $title = Title::newFromText( $title );
662 }
663
664 $content = ContentHandler::makeContent( $text, $title, $model, $format );
665
666 $rev = new Revision(
667 [
668 'id' => 42,
669 'page' => 23,
670 'title' => $title,
671
672 'content' => $content,
673 'length' => $content->getSize(),
674 'comment' => "testing",
675 'minor_edit' => false,
676
677 'content_format' => $format,
678 ]
679 );
680
681 return $rev;
682 }
683
684 public function provideGetContentModel() {
685 // NOTE: we expect the help namespace to always contain wikitext
686 return [
687 [ 'hello world', 'Help:Hello', null, null, CONTENT_MODEL_WIKITEXT ],
688 [ 'hello world', 'User:hello/there.css', null, null, CONTENT_MODEL_CSS ],
689 [ serialize( 'hello world' ), 'Dummy:Hello', null, null, DummyContentForTesting::MODEL_ID ],
690 ];
691 }
692
693 /**
694 * @dataProvider provideGetContentModel
695 * @covers Revision::getContentModel
696 */
697 public function testGetContentModel( $text, $title, $model, $format, $expectedModel ) {
698 $rev = $this->newTestRevision( $text, $title, $model, $format );
699
700 $this->assertEquals( $expectedModel, $rev->getContentModel() );
701 }
702
703 public function provideGetContentFormat() {
704 // NOTE: we expect the help namespace to always contain wikitext
705 return [
706 [ 'hello world', 'Help:Hello', null, null, CONTENT_FORMAT_WIKITEXT ],
707 [ 'hello world', 'Help:Hello', CONTENT_MODEL_CSS, null, CONTENT_FORMAT_CSS ],
708 [ 'hello world', 'User:hello/there.css', null, null, CONTENT_FORMAT_CSS ],
709 [ serialize( 'hello world' ), 'Dummy:Hello', null, null, DummyContentForTesting::MODEL_ID ],
710 ];
711 }
712
713 /**
714 * @dataProvider provideGetContentFormat
715 * @covers Revision::getContentFormat
716 */
717 public function testGetContentFormat( $text, $title, $model, $format, $expectedFormat ) {
718 $rev = $this->newTestRevision( $text, $title, $model, $format );
719
720 $this->assertEquals( $expectedFormat, $rev->getContentFormat() );
721 }
722
723 public function provideGetContentHandler() {
724 // NOTE: we expect the help namespace to always contain wikitext
725 return [
726 [ 'hello world', 'Help:Hello', null, null, 'WikitextContentHandler' ],
727 [ 'hello world', 'User:hello/there.css', null, null, 'CssContentHandler' ],
728 [ serialize( 'hello world' ), 'Dummy:Hello', null, null, 'DummyContentHandlerForTesting' ],
729 ];
730 }
731
732 /**
733 * @dataProvider provideGetContentHandler
734 * @covers Revision::getContentHandler
735 */
736 public function testGetContentHandler( $text, $title, $model, $format, $expectedClass ) {
737 $rev = $this->newTestRevision( $text, $title, $model, $format );
738
739 $this->assertEquals( $expectedClass, get_class( $rev->getContentHandler() ) );
740 }
741
742 public function provideGetContent() {
743 // NOTE: we expect the help namespace to always contain wikitext
744 return [
745 [ 'hello world', 'Help:Hello', null, null, Revision::FOR_PUBLIC, 'hello world' ],
746 [
747 serialize( 'hello world' ),
748 'Hello',
749 DummyContentForTesting::MODEL_ID,
750 null,
751 Revision::FOR_PUBLIC,
752 serialize( 'hello world' )
753 ],
754 [
755 serialize( 'hello world' ),
756 'Dummy:Hello',
757 null,
758 null,
759 Revision::FOR_PUBLIC,
760 serialize( 'hello world' )
761 ],
762 ];
763 }
764
765 /**
766 * @dataProvider provideGetContent
767 * @covers Revision::getContent
768 */
769 public function testGetContent( $text, $title, $model, $format,
770 $audience, $expectedSerialization
771 ) {
772 $rev = $this->newTestRevision( $text, $title, $model, $format );
773 $content = $rev->getContent( $audience );
774
775 $this->assertEquals(
776 $expectedSerialization,
777 is_null( $content ) ? null : $content->serialize( $format )
778 );
779 }
780
781 /**
782 * @covers Revision::getContent
783 */
784 public function testGetContent_failure() {
785 $rev = new Revision( [
786 'page' => $this->testPage->getId(),
787 'content_model' => $this->testPage->getContentModel(),
788 'text_id' => 123456789, // not in the test DB
789 ] );
790
791 $this->assertNull( $rev->getContent(),
792 "getContent() should return null if the revision's text blob could not be loaded." );
793
794 // NOTE: check this twice, once for lazy initialization, and once with the cached value.
795 $this->assertNull( $rev->getContent(),
796 "getContent() should return null if the revision's text blob could not be loaded." );
797 }
798
799 public function provideGetSize() {
800 return [
801 [ "hello world.", CONTENT_MODEL_WIKITEXT, 12 ],
802 [ serialize( "hello world." ), DummyContentForTesting::MODEL_ID, 12 ],
803 ];
804 }
805
806 /**
807 * @covers Revision::getSize
808 * @dataProvider provideGetSize
809 */
810 public function testGetSize( $text, $model, $expected_size ) {
811 $rev = $this->newTestRevision( $text, 'RevisionTest_testGetSize', $model );
812 $this->assertEquals( $expected_size, $rev->getSize() );
813 }
814
815 public function provideGetSha1() {
816 return [
817 [ "hello world.", CONTENT_MODEL_WIKITEXT, Revision::base36Sha1( "hello world." ) ],
818 [
819 serialize( "hello world." ),
820 DummyContentForTesting::MODEL_ID,
821 Revision::base36Sha1( serialize( "hello world." ) )
822 ],
823 ];
824 }
825
826 /**
827 * @covers Revision::getSha1
828 * @dataProvider provideGetSha1
829 */
830 public function testGetSha1( $text, $model, $expected_hash ) {
831 $rev = $this->newTestRevision( $text, 'RevisionTest_testGetSha1', $model );
832 $this->assertEquals( $expected_hash, $rev->getSha1() );
833 }
834
835 /**
836 * Tests whether $rev->getContent() returns a clone when needed.
837 *
838 * @covers Revision::getContent
839 */
840 public function testGetContentClone() {
841 $content = new RevisionTestModifyableContent( "foo" );
842
843 $rev = new Revision(
844 [
845 'id' => 42,
846 'page' => 23,
847 'title' => Title::newFromText( "testGetContentClone_dummy" ),
848
849 'content' => $content,
850 'length' => $content->getSize(),
851 'comment' => "testing",
852 'minor_edit' => false,
853 ]
854 );
855
856 /** @var RevisionTestModifyableContent $content */
857 $content = $rev->getContent( Revision::RAW );
858 $content->setText( "bar" );
859
860 /** @var RevisionTestModifyableContent $content2 */
861 $content2 = $rev->getContent( Revision::RAW );
862 // content is mutable, expect clone
863 $this->assertNotSame( $content, $content2, "expected a clone" );
864 // clone should contain the original text
865 $this->assertEquals( "foo", $content2->getText() );
866
867 $content2->setText( "bla bla" );
868 // clones should be independent
869 $this->assertEquals( "bar", $content->getText() );
870 }
871
872 /**
873 * Tests whether $rev->getContent() returns the same object repeatedly if appropriate.
874 * @covers Revision::getContent
875 */
876 public function testGetContentUncloned() {
877 $rev = $this->newTestRevision( "hello", "testGetContentUncloned_dummy", CONTENT_MODEL_WIKITEXT );
878 $content = $rev->getContent( Revision::RAW );
879 $content2 = $rev->getContent( Revision::RAW );
880
881 // for immutable content like wikitext, this should be the same object
882 $this->assertSame( $content, $content2 );
883 }
884
885 /**
886 * @covers Revision::loadFromId
887 */
888 public function testLoadFromId() {
889 $rev = $this->testPage->getRevision();
890 $this->assertRevEquals(
891 $rev,
892 Revision::loadFromId( wfGetDB( DB_MASTER ), $rev->getId() )
893 );
894 }
895
896 /**
897 * @covers Revision::loadFromPageId
898 */
899 public function testLoadFromPageId() {
900 $this->assertRevEquals(
901 $this->testPage->getRevision(),
902 Revision::loadFromPageId( wfGetDB( DB_MASTER ), $this->testPage->getId() )
903 );
904 }
905
906 /**
907 * @covers Revision::loadFromPageId
908 */
909 public function testLoadFromPageIdWithLatestRevId() {
910 $this->assertRevEquals(
911 $this->testPage->getRevision(),
912 Revision::loadFromPageId(
913 wfGetDB( DB_MASTER ),
914 $this->testPage->getId(),
915 $this->testPage->getLatest()
916 )
917 );
918 }
919
920 /**
921 * @covers Revision::loadFromPageId
922 */
923 public function testLoadFromPageIdWithNotLatestRevId() {
924 $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
925 $this->assertRevEquals(
926 $this->testPage->getRevision()->getPrevious(),
927 Revision::loadFromPageId(
928 wfGetDB( DB_MASTER ),
929 $this->testPage->getId(),
930 $this->testPage->getRevision()->getPrevious()->getId()
931 )
932 );
933 }
934
935 /**
936 * @covers Revision::loadFromTitle
937 */
938 public function testLoadFromTitle() {
939 $this->assertRevEquals(
940 $this->testPage->getRevision(),
941 Revision::loadFromTitle( wfGetDB( DB_MASTER ), $this->testPage->getTitle() )
942 );
943 }
944
945 /**
946 * @covers Revision::loadFromTitle
947 */
948 public function testLoadFromTitleWithLatestRevId() {
949 $this->assertRevEquals(
950 $this->testPage->getRevision(),
951 Revision::loadFromTitle(
952 wfGetDB( DB_MASTER ),
953 $this->testPage->getTitle(),
954 $this->testPage->getLatest()
955 )
956 );
957 }
958
959 /**
960 * @covers Revision::loadFromTitle
961 */
962 public function testLoadFromTitleWithNotLatestRevId() {
963 $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
964 $this->assertRevEquals(
965 $this->testPage->getRevision()->getPrevious(),
966 Revision::loadFromTitle(
967 wfGetDB( DB_MASTER ),
968 $this->testPage->getTitle(),
969 $this->testPage->getRevision()->getPrevious()->getId()
970 )
971 );
972 }
973
974 /**
975 * @covers Revision::loadFromTimestamp()
976 */
977 public function testLoadFromTimestamp() {
978 $this->assertRevEquals(
979 $this->testPage->getRevision(),
980 Revision::loadFromTimestamp(
981 wfGetDB( DB_MASTER ),
982 $this->testPage->getTitle(),
983 $this->testPage->getRevision()->getTimestamp()
984 )
985 );
986 }
987
988 }