]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - tests/phpunit/includes/page/WikiPageTest.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / tests / phpunit / includes / page / WikiPageTest.php
1 <?php
2
3 /**
4  * @group ContentHandler
5  * @group Database
6  * ^--- important, causes temporary tables to be used instead of the real database
7  * @group medium
8  */
9 class WikiPageTest extends MediaWikiLangTestCase {
10
11         protected $pages_to_delete;
12
13         function __construct( $name = null, array $data = [], $dataName = '' ) {
14                 parent::__construct( $name, $data, $dataName );
15
16                 $this->tablesUsed = array_merge(
17                         $this->tablesUsed,
18                         [ 'page',
19                                 'revision',
20                                 'archive',
21                                 'ip_changes',
22                                 'text',
23
24                                 'recentchanges',
25                                 'logging',
26
27                                 'page_props',
28                                 'pagelinks',
29                                 'categorylinks',
30                                 'langlinks',
31                                 'externallinks',
32                                 'imagelinks',
33                                 'templatelinks',
34                                 'iwlinks' ] );
35         }
36
37         protected function setUp() {
38                 parent::setUp();
39                 $this->pages_to_delete = [];
40
41                 LinkCache::singleton()->clear(); # avoid cached redirect status, etc
42         }
43
44         protected function tearDown() {
45                 foreach ( $this->pages_to_delete as $p ) {
46                         /* @var $p WikiPage */
47
48                         try {
49                                 if ( $p->exists() ) {
50                                         $p->doDeleteArticle( "testing done." );
51                                 }
52                         } catch ( MWException $ex ) {
53                                 // fail silently
54                         }
55                 }
56                 parent::tearDown();
57         }
58
59         /**
60          * @param Title|string $title
61          * @param string|null $model
62          * @return WikiPage
63          */
64         protected function newPage( $title, $model = null ) {
65                 if ( is_string( $title ) ) {
66                         $ns = $this->getDefaultWikitextNS();
67                         $title = Title::newFromText( $title, $ns );
68                 }
69
70                 $p = new WikiPage( $title );
71
72                 $this->pages_to_delete[] = $p;
73
74                 return $p;
75         }
76
77         /**
78          * @param string|Title|WikiPage $page
79          * @param string $text
80          * @param int $model
81          *
82          * @return WikiPage
83          */
84         protected function createPage( $page, $text, $model = null ) {
85                 if ( is_string( $page ) || $page instanceof Title ) {
86                         $page = $this->newPage( $page, $model );
87                 }
88
89                 $content = ContentHandler::makeContent( $text, $page->getTitle(), $model );
90                 $page->doEditContent( $content, "testing", EDIT_NEW );
91
92                 return $page;
93         }
94
95         /**
96          * @covers WikiPage::doEditContent
97          * @covers WikiPage::doModify
98          * @covers WikiPage::doCreate
99          * @covers WikiPage::doEditUpdates
100          */
101         public function testDoEditContent() {
102                 $page = $this->newPage( "WikiPageTest_testDoEditContent" );
103                 $title = $page->getTitle();
104
105                 $content = ContentHandler::makeContent(
106                         "[[Lorem ipsum]] dolor sit amet, consetetur sadipscing elitr, sed diam "
107                                 . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.",
108                         $title,
109                         CONTENT_MODEL_WIKITEXT
110                 );
111
112                 $page->doEditContent( $content, "[[testing]] 1" );
113
114                 $this->assertTrue( $title->getArticleID() > 0, "Title object should have new page id" );
115                 $this->assertTrue( $page->getId() > 0, "WikiPage should have new page id" );
116                 $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" );
117                 $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" );
118
119                 $id = $page->getId();
120
121                 # ------------------------
122                 $dbr = wfGetDB( DB_REPLICA );
123                 $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
124                 $n = $res->numRows();
125                 $res->free();
126
127                 $this->assertEquals( 1, $n, 'pagelinks should contain one link from the page' );
128
129                 # ------------------------
130                 $page = new WikiPage( $title );
131
132                 $retrieved = $page->getContent();
133                 $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
134
135                 # ------------------------
136                 $content = ContentHandler::makeContent(
137                         "At vero eos et accusam et justo duo [[dolores]] et ea rebum. "
138                                 . "Stet clita kasd [[gubergren]], no sea takimata sanctus est.",
139                         $title,
140                         CONTENT_MODEL_WIKITEXT
141                 );
142
143                 $page->doEditContent( $content, "testing 2" );
144
145                 # ------------------------
146                 $page = new WikiPage( $title );
147
148                 $retrieved = $page->getContent();
149                 $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
150
151                 # ------------------------
152                 $dbr = wfGetDB( DB_REPLICA );
153                 $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
154                 $n = $res->numRows();
155                 $res->free();
156
157                 $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
158         }
159
160         /**
161          * @covers WikiPage::doDeleteArticle
162          */
163         public function testDoDeleteArticle() {
164                 $page = $this->createPage(
165                         "WikiPageTest_testDoDeleteArticle",
166                         "[[original text]] foo",
167                         CONTENT_MODEL_WIKITEXT
168                 );
169                 $id = $page->getId();
170
171                 $page->doDeleteArticle( "testing deletion" );
172
173                 $this->assertFalse(
174                         $page->getTitle()->getArticleID() > 0,
175                         "Title object should now have page id 0"
176                 );
177                 $this->assertFalse( $page->getId() > 0, "WikiPage should now have page id 0" );
178                 $this->assertFalse(
179                         $page->exists(),
180                         "WikiPage::exists should return false after page was deleted"
181                 );
182                 $this->assertNull(
183                         $page->getContent(),
184                         "WikiPage::getContent should return null after page was deleted"
185                 );
186
187                 $t = Title::newFromText( $page->getTitle()->getPrefixedText() );
188                 $this->assertFalse(
189                         $t->exists(),
190                         "Title::exists should return false after page was deleted"
191                 );
192
193                 // Run the job queue
194                 JobQueueGroup::destroySingletons();
195                 $jobs = new RunJobs;
196                 $jobs->loadParamsAndArgs( null, [ 'quiet' => true ], null );
197                 $jobs->execute();
198
199                 # ------------------------
200                 $dbr = wfGetDB( DB_REPLICA );
201                 $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
202                 $n = $res->numRows();
203                 $res->free();
204
205                 $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
206         }
207
208         /**
209          * @covers WikiPage::doDeleteUpdates
210          */
211         public function testDoDeleteUpdates() {
212                 $page = $this->createPage(
213                         "WikiPageTest_testDoDeleteArticle",
214                         "[[original text]] foo",
215                         CONTENT_MODEL_WIKITEXT
216                 );
217                 $id = $page->getId();
218
219                 // Similar to MovePage logic
220                 wfGetDB( DB_MASTER )->delete( 'page', [ 'page_id' => $id ], __METHOD__ );
221                 $page->doDeleteUpdates( $id );
222
223                 // Run the job queue
224                 JobQueueGroup::destroySingletons();
225                 $jobs = new RunJobs;
226                 $jobs->loadParamsAndArgs( null, [ 'quiet' => true ], null );
227                 $jobs->execute();
228
229                 # ------------------------
230                 $dbr = wfGetDB( DB_REPLICA );
231                 $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
232                 $n = $res->numRows();
233                 $res->free();
234
235                 $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
236         }
237
238         /**
239          * @covers WikiPage::getRevision
240          */
241         public function testGetRevision() {
242                 $page = $this->newPage( "WikiPageTest_testGetRevision" );
243
244                 $rev = $page->getRevision();
245                 $this->assertNull( $rev );
246
247                 # -----------------
248                 $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
249
250                 $rev = $page->getRevision();
251
252                 $this->assertEquals( $page->getLatest(), $rev->getId() );
253                 $this->assertEquals( "some text", $rev->getContent()->getNativeData() );
254         }
255
256         /**
257          * @covers WikiPage::getContent
258          */
259         public function testGetContent() {
260                 $page = $this->newPage( "WikiPageTest_testGetContent" );
261
262                 $content = $page->getContent();
263                 $this->assertNull( $content );
264
265                 # -----------------
266                 $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
267
268                 $content = $page->getContent();
269                 $this->assertEquals( "some text", $content->getNativeData() );
270         }
271
272         /**
273          * @covers WikiPage::getContentModel
274          */
275         public function testGetContentModel() {
276                 global $wgContentHandlerUseDB;
277
278                 if ( !$wgContentHandlerUseDB ) {
279                         $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' );
280                 }
281
282                 $page = $this->createPage(
283                         "WikiPageTest_testGetContentModel",
284                         "some text",
285                         CONTENT_MODEL_JAVASCRIPT
286                 );
287
288                 $page = new WikiPage( $page->getTitle() );
289                 $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $page->getContentModel() );
290         }
291
292         /**
293          * @covers WikiPage::getContentHandler
294          */
295         public function testGetContentHandler() {
296                 global $wgContentHandlerUseDB;
297
298                 if ( !$wgContentHandlerUseDB ) {
299                         $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' );
300                 }
301
302                 $page = $this->createPage(
303                         "WikiPageTest_testGetContentHandler",
304                         "some text",
305                         CONTENT_MODEL_JAVASCRIPT
306                 );
307
308                 $page = new WikiPage( $page->getTitle() );
309                 $this->assertEquals( 'JavaScriptContentHandler', get_class( $page->getContentHandler() ) );
310         }
311
312         /**
313          * @covers WikiPage::exists
314          */
315         public function testExists() {
316                 $page = $this->newPage( "WikiPageTest_testExists" );
317                 $this->assertFalse( $page->exists() );
318
319                 # -----------------
320                 $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
321                 $this->assertTrue( $page->exists() );
322
323                 $page = new WikiPage( $page->getTitle() );
324                 $this->assertTrue( $page->exists() );
325
326                 # -----------------
327                 $page->doDeleteArticle( "done testing" );
328                 $this->assertFalse( $page->exists() );
329
330                 $page = new WikiPage( $page->getTitle() );
331                 $this->assertFalse( $page->exists() );
332         }
333
334         public static function provideHasViewableContent() {
335                 return [
336                         [ 'WikiPageTest_testHasViewableContent', false, true ],
337                         [ 'Special:WikiPageTest_testHasViewableContent', false ],
338                         [ 'MediaWiki:WikiPageTest_testHasViewableContent', false ],
339                         [ 'Special:Userlogin', true ],
340                         [ 'MediaWiki:help', true ],
341                 ];
342         }
343
344         /**
345          * @dataProvider provideHasViewableContent
346          * @covers WikiPage::hasViewableContent
347          */
348         public function testHasViewableContent( $title, $viewable, $create = false ) {
349                 $page = $this->newPage( $title );
350                 $this->assertEquals( $viewable, $page->hasViewableContent() );
351
352                 if ( $create ) {
353                         $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
354                         $this->assertTrue( $page->hasViewableContent() );
355
356                         $page = new WikiPage( $page->getTitle() );
357                         $this->assertTrue( $page->hasViewableContent() );
358                 }
359         }
360
361         public static function provideGetRedirectTarget() {
362                 return [
363                         [ 'WikiPageTest_testGetRedirectTarget_1', CONTENT_MODEL_WIKITEXT, "hello world", null ],
364                         [
365                                 'WikiPageTest_testGetRedirectTarget_2',
366                                 CONTENT_MODEL_WIKITEXT,
367                                 "#REDIRECT [[hello world]]",
368                                 "Hello world"
369                         ],
370                 ];
371         }
372
373         /**
374          * @dataProvider provideGetRedirectTarget
375          * @covers WikiPage::getRedirectTarget
376          */
377         public function testGetRedirectTarget( $title, $model, $text, $target ) {
378                 $this->setMwGlobals( [
379                         'wgCapitalLinks' => true,
380                 ] );
381
382                 $page = $this->createPage( $title, $text, $model );
383
384                 # sanity check, because this test seems to fail for no reason for some people.
385                 $c = $page->getContent();
386                 $this->assertEquals( 'WikitextContent', get_class( $c ) );
387
388                 # now, test the actual redirect
389                 $t = $page->getRedirectTarget();
390                 $this->assertEquals( $target, is_null( $t ) ? null : $t->getPrefixedText() );
391         }
392
393         /**
394          * @dataProvider provideGetRedirectTarget
395          * @covers WikiPage::isRedirect
396          */
397         public function testIsRedirect( $title, $model, $text, $target ) {
398                 $page = $this->createPage( $title, $text, $model );
399                 $this->assertEquals( !is_null( $target ), $page->isRedirect() );
400         }
401
402         public static function provideIsCountable() {
403                 return [
404
405                         // any
406                         [ 'WikiPageTest_testIsCountable',
407                                 CONTENT_MODEL_WIKITEXT,
408                                 '',
409                                 'any',
410                                 true
411                         ],
412                         [ 'WikiPageTest_testIsCountable',
413                                 CONTENT_MODEL_WIKITEXT,
414                                 'Foo',
415                                 'any',
416                                 true
417                         ],
418
419                         // comma
420                         [ 'WikiPageTest_testIsCountable',
421                                 CONTENT_MODEL_WIKITEXT,
422                                 'Foo',
423                                 'comma',
424                                 false
425                         ],
426                         [ 'WikiPageTest_testIsCountable',
427                                 CONTENT_MODEL_WIKITEXT,
428                                 'Foo, bar',
429                                 'comma',
430                                 true
431                         ],
432
433                         // link
434                         [ 'WikiPageTest_testIsCountable',
435                                 CONTENT_MODEL_WIKITEXT,
436                                 'Foo',
437                                 'link',
438                                 false
439                         ],
440                         [ 'WikiPageTest_testIsCountable',
441                                 CONTENT_MODEL_WIKITEXT,
442                                 'Foo [[bar]]',
443                                 'link',
444                                 true
445                         ],
446
447                         // redirects
448                         [ 'WikiPageTest_testIsCountable',
449                                 CONTENT_MODEL_WIKITEXT,
450                                 '#REDIRECT [[bar]]',
451                                 'any',
452                                 false
453                         ],
454                         [ 'WikiPageTest_testIsCountable',
455                                 CONTENT_MODEL_WIKITEXT,
456                                 '#REDIRECT [[bar]]',
457                                 'comma',
458                                 false
459                         ],
460                         [ 'WikiPageTest_testIsCountable',
461                                 CONTENT_MODEL_WIKITEXT,
462                                 '#REDIRECT [[bar]]',
463                                 'link',
464                                 false
465                         ],
466
467                         // not a content namespace
468                         [ 'Talk:WikiPageTest_testIsCountable',
469                                 CONTENT_MODEL_WIKITEXT,
470                                 'Foo',
471                                 'any',
472                                 false
473                         ],
474                         [ 'Talk:WikiPageTest_testIsCountable',
475                                 CONTENT_MODEL_WIKITEXT,
476                                 'Foo, bar',
477                                 'comma',
478                                 false
479                         ],
480                         [ 'Talk:WikiPageTest_testIsCountable',
481                                 CONTENT_MODEL_WIKITEXT,
482                                 'Foo [[bar]]',
483                                 'link',
484                                 false
485                         ],
486
487                         // not a content namespace, different model
488                         [ 'MediaWiki:WikiPageTest_testIsCountable.js',
489                                 null,
490                                 'Foo',
491                                 'any',
492                                 false
493                         ],
494                         [ 'MediaWiki:WikiPageTest_testIsCountable.js',
495                                 null,
496                                 'Foo, bar',
497                                 'comma',
498                                 false
499                         ],
500                         [ 'MediaWiki:WikiPageTest_testIsCountable.js',
501                                 null,
502                                 'Foo [[bar]]',
503                                 'link',
504                                 false
505                         ],
506                 ];
507         }
508
509         /**
510          * @dataProvider provideIsCountable
511          * @covers WikiPage::isCountable
512          */
513         public function testIsCountable( $title, $model, $text, $mode, $expected ) {
514                 global $wgContentHandlerUseDB;
515
516                 $this->setMwGlobals( 'wgArticleCountMethod', $mode );
517
518                 $title = Title::newFromText( $title );
519
520                 if ( !$wgContentHandlerUseDB
521                         && $model
522                         && ContentHandler::getDefaultModelFor( $title ) != $model
523                 ) {
524                         $this->markTestSkipped( "Can not use non-default content model $model for "
525                                 . $title->getPrefixedDBkey() . " with \$wgContentHandlerUseDB disabled." );
526                 }
527
528                 $page = $this->createPage( $title, $text, $model );
529
530                 $editInfo = $page->prepareContentForEdit( $page->getContent() );
531
532                 $v = $page->isCountable();
533                 $w = $page->isCountable( $editInfo );
534
535                 $this->assertEquals(
536                         $expected,
537                         $v,
538                         "isCountable( null ) returned unexpected value " . var_export( $v, true )
539                                 . " instead of " . var_export( $expected, true )
540                         . " in mode `$mode` for text \"$text\""
541                 );
542
543                 $this->assertEquals(
544                         $expected,
545                         $w,
546                         "isCountable( \$editInfo ) returned unexpected value " . var_export( $v, true )
547                                 . " instead of " . var_export( $expected, true )
548                         . " in mode `$mode` for text \"$text\""
549                 );
550         }
551
552         public static function provideGetParserOutput() {
553                 return [
554                         [
555                                 CONTENT_MODEL_WIKITEXT,
556                                 "hello ''world''\n",
557                                 "<div class=\"mw-parser-output\"><p>hello <i>world</i></p></div>"
558                         ],
559                         // @todo more...?
560                 ];
561         }
562
563         /**
564          * @dataProvider provideGetParserOutput
565          * @covers WikiPage::getParserOutput
566          */
567         public function testGetParserOutput( $model, $text, $expectedHtml ) {
568                 $page = $this->createPage( 'WikiPageTest_testGetParserOutput', $text, $model );
569
570                 $opt = $page->makeParserOptions( 'canonical' );
571                 $po = $page->getParserOutput( $opt );
572                 $text = $po->getText();
573
574                 $text = trim( preg_replace( '/<!--.*?-->/sm', '', $text ) ); # strip injected comments
575                 $text = preg_replace( '!\s*(</p>|</div>)!sm', '\1', $text ); # don't let tidy confuse us
576
577                 $this->assertEquals( $expectedHtml, $text );
578
579                 return $po;
580         }
581
582         /**
583          * @covers WikiPage::getParserOutput
584          */
585         public function testGetParserOutput_nonexisting() {
586                 static $count = 0;
587                 $count++;
588
589                 $page = new WikiPage( new Title( "WikiPageTest_testGetParserOutput_nonexisting_$count" ) );
590
591                 $opt = new ParserOptions();
592                 $po = $page->getParserOutput( $opt );
593
594                 $this->assertFalse( $po, "getParserOutput() shall return false for non-existing pages." );
595         }
596
597         /**
598          * @covers WikiPage::getParserOutput
599          */
600         public function testGetParserOutput_badrev() {
601                 $page = $this->createPage( 'WikiPageTest_testGetParserOutput', "dummy", CONTENT_MODEL_WIKITEXT );
602
603                 $opt = new ParserOptions();
604                 $po = $page->getParserOutput( $opt, $page->getLatest() + 1234 );
605
606                 // @todo would be neat to also test deleted revision
607
608                 $this->assertFalse( $po, "getParserOutput() shall return false for non-existing revisions." );
609         }
610
611         public static $sections =
612
613                 "Intro
614
615 == stuff ==
616 hello world
617
618 == test ==
619 just a test
620
621 == foo ==
622 more stuff
623 ";
624
625         public function dataReplaceSection() {
626                 // NOTE: assume the Help namespace to contain wikitext
627                 return [
628                         [ 'Help:WikiPageTest_testReplaceSection',
629                                 CONTENT_MODEL_WIKITEXT,
630                                 self::$sections,
631                                 "0",
632                                 "No more",
633                                 null,
634                                 trim( preg_replace( '/^Intro/sm', 'No more', self::$sections ) )
635                         ],
636                         [ 'Help:WikiPageTest_testReplaceSection',
637                                 CONTENT_MODEL_WIKITEXT,
638                                 self::$sections,
639                                 "",
640                                 "No more",
641                                 null,
642                                 "No more"
643                         ],
644                         [ 'Help:WikiPageTest_testReplaceSection',
645                                 CONTENT_MODEL_WIKITEXT,
646                                 self::$sections,
647                                 "2",
648                                 "== TEST ==\nmore fun",
649                                 null,
650                                 trim( preg_replace( '/^== test ==.*== foo ==/sm',
651                                         "== TEST ==\nmore fun\n\n== foo ==",
652                                         self::$sections ) )
653                         ],
654                         [ 'Help:WikiPageTest_testReplaceSection',
655                                 CONTENT_MODEL_WIKITEXT,
656                                 self::$sections,
657                                 "8",
658                                 "No more",
659                                 null,
660                                 trim( self::$sections )
661                         ],
662                         [ 'Help:WikiPageTest_testReplaceSection',
663                                 CONTENT_MODEL_WIKITEXT,
664                                 self::$sections,
665                                 "new",
666                                 "No more",
667                                 "New",
668                                 trim( self::$sections ) . "\n\n== New ==\n\nNo more"
669                         ],
670                 ];
671         }
672
673         /**
674          * @dataProvider dataReplaceSection
675          * @covers WikiPage::replaceSectionContent
676          */
677         public function testReplaceSectionContent( $title, $model, $text, $section,
678                 $with, $sectionTitle, $expected
679         ) {
680                 $page = $this->createPage( $title, $text, $model );
681
682                 $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
683                 $c = $page->replaceSectionContent( $section, $content, $sectionTitle );
684
685                 $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
686         }
687
688         /**
689          * @dataProvider dataReplaceSection
690          * @covers WikiPage::replaceSectionAtRev
691          */
692         public function testReplaceSectionAtRev( $title, $model, $text, $section,
693                 $with, $sectionTitle, $expected
694         ) {
695                 $page = $this->createPage( $title, $text, $model );
696                 $baseRevId = $page->getLatest();
697
698                 $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
699                 $c = $page->replaceSectionAtRev( $section, $content, $sectionTitle, $baseRevId );
700
701                 $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
702         }
703
704         /* @todo FIXME: fix this!
705         public function testGetUndoText() {
706         $this->markTestSkippedIfNoDiff3();
707
708         $text = "one";
709         $page = $this->createPage( "WikiPageTest_testGetUndoText", $text );
710         $rev1 = $page->getRevision();
711
712         $text .= "\n\ntwo";
713         $page->doEditContent(
714                 ContentHandler::makeContent( $text, $page->getTitle() ),
715                 "adding section two"
716         );
717         $rev2 = $page->getRevision();
718
719         $text .= "\n\nthree";
720         $page->doEditContent(
721                 ContentHandler::makeContent( $text, $page->getTitle() ),
722                 "adding section three"
723         );
724         $rev3 = $page->getRevision();
725
726         $text .= "\n\nfour";
727         $page->doEditContent(
728                 ContentHandler::makeContent( $text, $page->getTitle() ),
729                 "adding section four"
730         );
731         $rev4 = $page->getRevision();
732
733         $text .= "\n\nfive";
734         $page->doEditContent(
735                 ContentHandler::makeContent( $text, $page->getTitle() ),
736                 "adding section five"
737         );
738         $rev5 = $page->getRevision();
739
740         $text .= "\n\nsix";
741         $page->doEditContent(
742                 ContentHandler::makeContent( $text, $page->getTitle() ),
743                 "adding section six"
744         );
745         $rev6 = $page->getRevision();
746
747         $undo6 = $page->getUndoText( $rev6 );
748         if ( $undo6 === false ) $this->fail( "getUndoText failed for rev6" );
749         $this->assertEquals( "one\n\ntwo\n\nthree\n\nfour\n\nfive", $undo6 );
750
751         $undo3 = $page->getUndoText( $rev4, $rev2 );
752         if ( $undo3 === false ) $this->fail( "getUndoText failed for rev4..rev2" );
753         $this->assertEquals( "one\n\ntwo\n\nfive", $undo3 );
754
755         $undo2 = $page->getUndoText( $rev2 );
756         if ( $undo2 === false ) $this->fail( "getUndoText failed for rev2" );
757         $this->assertEquals( "one\n\nfive", $undo2 );
758         }
759          */
760
761         /**
762          * @covers WikiPage::getOldestRevision
763          */
764         public function testGetOldestRevision() {
765                 $page = $this->newPage( "WikiPageTest_testGetOldestRevision" );
766                 $page->doEditContent(
767                         new WikitextContent( 'one' ),
768                         "first edit",
769                         EDIT_NEW
770                 );
771                 $rev1 = $page->getRevision();
772
773                 $page = new WikiPage( $page->getTitle() );
774                 $page->doEditContent(
775                         new WikitextContent( 'two' ),
776                         "second edit",
777                         EDIT_UPDATE
778                 );
779
780                 $page = new WikiPage( $page->getTitle() );
781                 $page->doEditContent(
782                         new WikitextContent( 'three' ),
783                         "third edit",
784                         EDIT_UPDATE
785                 );
786
787                 // sanity check
788                 $this->assertNotEquals(
789                         $rev1->getId(),
790                         $page->getRevision()->getId(),
791                         '$page->getRevision()->getId()'
792                 );
793
794                 // actual test
795                 $this->assertEquals(
796                         $rev1->getId(),
797                         $page->getOldestRevision()->getId(),
798                         '$page->getOldestRevision()->getId()'
799                 );
800         }
801
802         /**
803          * @todo FIXME: this is a better rollback test than the one below, but it
804          * keeps failing in jenkins for some reason.
805          */
806         public function broken_testDoRollback() {
807                 $admin = new User();
808                 $admin->setName( "Admin" );
809
810                 $text = "one";
811                 $page = $this->newPage( "WikiPageTest_testDoRollback" );
812                 $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
813                         "section one", EDIT_NEW, false, $admin );
814
815                 $user1 = new User();
816                 $user1->setName( "127.0.1.11" );
817                 $text .= "\n\ntwo";
818                 $page = new WikiPage( $page->getTitle() );
819                 $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
820                         "adding section two", 0, false, $user1 );
821
822                 $user2 = new User();
823                 $user2->setName( "127.0.2.13" );
824                 $text .= "\n\nthree";
825                 $page = new WikiPage( $page->getTitle() );
826                 $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
827                         "adding section three", 0, false, $user2 );
828
829                 # we are having issues with doRollback spuriously failing. Apparently
830                 # the last revision somehow goes missing or not committed under some
831                 # circumstances. So, make sure the last revision has the right user name.
832                 $dbr = wfGetDB( DB_REPLICA );
833                 $this->assertEquals( 3, Revision::countByPageId( $dbr, $page->getId() ) );
834
835                 $page = new WikiPage( $page->getTitle() );
836                 $rev3 = $page->getRevision();
837                 $this->assertEquals( '127.0.2.13', $rev3->getUserText() );
838
839                 $rev2 = $rev3->getPrevious();
840                 $this->assertEquals( '127.0.1.11', $rev2->getUserText() );
841
842                 $rev1 = $rev2->getPrevious();
843                 $this->assertEquals( 'Admin', $rev1->getUserText() );
844
845                 # now, try the actual rollback
846                 $admin->addToDatabase();
847                 $admin->addGroup( "sysop" ); # XXX: make the test user a sysop...
848                 $token = $admin->getEditToken(
849                         [ $page->getTitle()->getPrefixedText(), $user2->getName() ],
850                         null
851                 );
852                 $errors = $page->doRollback(
853                         $user2->getName(),
854                         "testing revert",
855                         $token,
856                         false,
857                         $details,
858                         $admin
859                 );
860
861                 if ( $errors ) {
862                         $this->fail( "Rollback failed:\n" . print_r( $errors, true )
863                                 . ";\n" . print_r( $details, true ) );
864                 }
865
866                 $page = new WikiPage( $page->getTitle() );
867                 $this->assertEquals( $rev2->getSha1(), $page->getRevision()->getSha1(),
868                         "rollback did not revert to the correct revision" );
869                 $this->assertEquals( "one\n\ntwo", $page->getContent()->getNativeData() );
870         }
871
872         /**
873          * @todo FIXME: the above rollback test is better, but it keeps failing in jenkins for some reason.
874          * @covers WikiPage::doRollback
875          */
876         public function testDoRollback() {
877                 $admin = new User();
878                 $admin->setName( "Admin" );
879                 $admin->addToDatabase();
880
881                 $text = "one";
882                 $page = $this->newPage( "WikiPageTest_testDoRollback" );
883                 $page->doEditContent(
884                         ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
885                         "section one",
886                         EDIT_NEW,
887                         false,
888                         $admin
889                 );
890                 $rev1 = $page->getRevision();
891
892                 $user1 = new User();
893                 $user1->setName( "127.0.1.11" );
894                 $text .= "\n\ntwo";
895                 $page = new WikiPage( $page->getTitle() );
896                 $page->doEditContent(
897                         ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
898                         "adding section two",
899                         0,
900                         false,
901                         $user1
902                 );
903
904                 # now, try the rollback
905                 $admin->addGroup( "sysop" ); # XXX: make the test user a sysop...
906                 $token = $admin->getEditToken( 'rollback' );
907                 $errors = $page->doRollback(
908                         $user1->getName(),
909                         "testing revert",
910                         $token,
911                         false,
912                         $details,
913                         $admin
914                 );
915
916                 if ( $errors ) {
917                         $this->fail( "Rollback failed:\n" . print_r( $errors, true )
918                                 . ";\n" . print_r( $details, true ) );
919                 }
920
921                 $page = new WikiPage( $page->getTitle() );
922                 $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
923                         "rollback did not revert to the correct revision" );
924                 $this->assertEquals( "one", $page->getContent()->getNativeData() );
925         }
926
927         /**
928          * @covers WikiPage::doRollback
929          */
930         public function testDoRollbackFailureSameContent() {
931                 $admin = new User();
932                 $admin->setName( "Admin" );
933                 $admin->addToDatabase();
934                 $admin->addGroup( "sysop" ); # XXX: make the test user a sysop...
935
936                 $text = "one";
937                 $page = $this->newPage( "WikiPageTest_testDoRollback" );
938                 $page->doEditContent(
939                         ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
940                         "section one",
941                         EDIT_NEW,
942                         false,
943                         $admin
944                 );
945                 $rev1 = $page->getRevision();
946
947                 $user1 = new User();
948                 $user1->setName( "127.0.1.11" );
949                 $user1->addToDatabase();
950                 $user1->addGroup( "sysop" ); # XXX: make the test user a sysop...
951                 $text .= "\n\ntwo";
952                 $page = new WikiPage( $page->getTitle() );
953                 $page->doEditContent(
954                         ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
955                         "adding section two",
956                         0,
957                         false,
958                         $user1
959                 );
960
961                 # now, do a the rollback from the same user was doing the edit before
962                 $resultDetails = [];
963                 $token = $user1->getEditToken( 'rollback' );
964                 $errors = $page->doRollback(
965                         $user1->getName(),
966                         "testing revert same user",
967                         $token,
968                         false,
969                         $resultDetails,
970                         $admin
971                 );
972
973                 $this->assertEquals( [], $errors, "Rollback failed same user" );
974
975                 # now, try the rollback
976                 $resultDetails = [];
977                 $token = $admin->getEditToken( 'rollback' );
978                 $errors = $page->doRollback(
979                         $user1->getName(),
980                         "testing revert",
981                         $token,
982                         false,
983                         $resultDetails,
984                         $admin
985                 );
986
987                 $this->assertEquals( [ [ 'alreadyrolled', 'WikiPageTest testDoRollback',
988                         '127.0.1.11', 'Admin' ] ], $errors, "Rollback not failed" );
989
990                 $page = new WikiPage( $page->getTitle() );
991                 $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
992                         "rollback did not revert to the correct revision" );
993                 $this->assertEquals( "one", $page->getContent()->getNativeData() );
994         }
995
996         public static function provideGetAutoDeleteReason() {
997                 return [
998                         [
999                                 [],
1000                                 false,
1001                                 false
1002                         ],
1003
1004                         [
1005                                 [
1006                                         [ "first edit", null ],
1007                                 ],
1008                                 "/first edit.*only contributor/",
1009                                 false
1010                         ],
1011
1012                         [
1013                                 [
1014                                         [ "first edit", null ],
1015                                         [ "second edit", null ],
1016                                 ],
1017                                 "/second edit.*only contributor/",
1018                                 true
1019                         ],
1020
1021                         [
1022                                 [
1023                                         [ "first edit", "127.0.2.22" ],
1024                                         [ "second edit", "127.0.3.33" ],
1025                                 ],
1026                                 "/second edit/",
1027                                 true
1028                         ],
1029
1030                         [
1031                                 [
1032                                         [
1033                                                 "first edit: "
1034                                                         . "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam "
1035                                                         . " nonumy eirmod tempor invidunt ut labore et dolore magna "
1036                                                         . "aliquyam erat, sed diam voluptua. At vero eos et accusam "
1037                                                         . "et justo duo dolores et ea rebum. Stet clita kasd gubergren, "
1038                                                         . "no sea  takimata sanctus est Lorem ipsum dolor sit amet.'",
1039                                                 null
1040                                         ],
1041                                 ],
1042                                 '/first edit:.*\.\.\."/',
1043                                 false
1044                         ],
1045
1046                         [
1047                                 [
1048                                         [ "first edit", "127.0.2.22" ],
1049                                         [ "", "127.0.3.33" ],
1050                                 ],
1051                                 "/before blanking.*first edit/",
1052                                 true
1053                         ],
1054
1055                 ];
1056         }
1057
1058         /**
1059          * @dataProvider provideGetAutoDeleteReason
1060          * @covers WikiPage::getAutoDeleteReason
1061          */
1062         public function testGetAutoDeleteReason( $edits, $expectedResult, $expectedHistory ) {
1063                 global $wgUser;
1064
1065                 // NOTE: assume Help namespace to contain wikitext
1066                 $page = $this->newPage( "Help:WikiPageTest_testGetAutoDeleteReason" );
1067
1068                 $c = 1;
1069
1070                 foreach ( $edits as $edit ) {
1071                         $user = new User();
1072
1073                         if ( !empty( $edit[1] ) ) {
1074                                 $user->setName( $edit[1] );
1075                         } else {
1076                                 $user = $wgUser;
1077                         }
1078
1079                         $content = ContentHandler::makeContent( $edit[0], $page->getTitle(), $page->getContentModel() );
1080
1081                         $page->doEditContent( $content, "test edit $c", $c < 2 ? EDIT_NEW : 0, false, $user );
1082
1083                         $c += 1;
1084                 }
1085
1086                 $reason = $page->getAutoDeleteReason( $hasHistory );
1087
1088                 if ( is_bool( $expectedResult ) || is_null( $expectedResult ) ) {
1089                         $this->assertEquals( $expectedResult, $reason );
1090                 } else {
1091                         $this->assertTrue( (bool)preg_match( $expectedResult, $reason ),
1092                                 "Autosummary didn't match expected pattern $expectedResult: $reason" );
1093                 }
1094
1095                 $this->assertEquals( $expectedHistory, $hasHistory,
1096                         "expected \$hasHistory to be " . var_export( $expectedHistory, true ) );
1097
1098                 $page->doDeleteArticle( "done" );
1099         }
1100
1101         public static function providePreSaveTransform() {
1102                 return [
1103                         [ 'hello this is ~~~',
1104                                 "hello this is [[Special:Contributions/127.0.0.1|127.0.0.1]]",
1105                         ],
1106                         [ 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
1107                                 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
1108                         ],
1109                 ];
1110         }
1111
1112         /**
1113          * @covers WikiPage::factory
1114          */
1115         public function testWikiPageFactory() {
1116                 $title = Title::makeTitle( NS_FILE, 'Someimage.png' );
1117                 $page = WikiPage::factory( $title );
1118                 $this->assertEquals( 'WikiFilePage', get_class( $page ) );
1119
1120                 $title = Title::makeTitle( NS_CATEGORY, 'SomeCategory' );
1121                 $page = WikiPage::factory( $title );
1122                 $this->assertEquals( 'WikiCategoryPage', get_class( $page ) );
1123
1124                 $title = Title::makeTitle( NS_MAIN, 'SomePage' );
1125                 $page = WikiPage::factory( $title );
1126                 $this->assertEquals( 'WikiPage', get_class( $page ) );
1127         }
1128
1129         /**
1130          * @dataProvider provideCommentMigrationOnDeletion
1131          * @param int $wstage
1132          * @param int $rstage
1133          */
1134         public function testCommentMigrationOnDeletion( $wstage, $rstage ) {
1135                 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', $wstage );
1136                 $dbr = wfGetDB( DB_REPLICA );
1137
1138                 $page = $this->createPage(
1139                         "WikiPageTest_testCommentMigrationOnDeletion",
1140                         "foo",
1141                         CONTENT_MODEL_WIKITEXT
1142                 );
1143                 $revid = $page->getLatest();
1144                 if ( $wstage > MIGRATION_OLD ) {
1145                         $comment_id = $dbr->selectField(
1146                                 'revision_comment_temp',
1147                                 'revcomment_comment_id',
1148                                 [ 'revcomment_rev' => $revid ],
1149                                 __METHOD__
1150                         );
1151                 }
1152
1153                 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', $rstage );
1154
1155                 $page->doDeleteArticle( "testing deletion" );
1156
1157                 if ( $rstage > MIGRATION_OLD ) {
1158                         // Didn't leave behind any 'revision_comment_temp' rows
1159                         $n = $dbr->selectField(
1160                                 'revision_comment_temp', 'COUNT(*)', [ 'revcomment_rev' => $revid ], __METHOD__
1161                         );
1162                         $this->assertEquals( 0, $n, 'no entry in revision_comment_temp after deletion' );
1163
1164                         // Copied or upgraded the comment_id, as applicable
1165                         $ar_comment_id = $dbr->selectField(
1166                                 'archive',
1167                                 'ar_comment_id',
1168                                 [ 'ar_rev_id' => $revid ],
1169                                 __METHOD__
1170                         );
1171                         if ( $wstage > MIGRATION_OLD ) {
1172                                 $this->assertSame( $comment_id, $ar_comment_id );
1173                         } else {
1174                                 $this->assertNotEquals( 0, $ar_comment_id );
1175                         }
1176                 }
1177
1178                 // Copied rev_comment, if applicable
1179                 if ( $rstage <= MIGRATION_WRITE_BOTH && $wstage <= MIGRATION_WRITE_BOTH ) {
1180                         $ar_comment = $dbr->selectField(
1181                                 'archive',
1182                                 'ar_comment',
1183                                 [ 'ar_rev_id' => $revid ],
1184                                 __METHOD__
1185                         );
1186                         $this->assertSame( 'testing', $ar_comment );
1187                 }
1188         }
1189
1190         public static function provideCommentMigrationOnDeletion() {
1191                 return [
1192                         [ MIGRATION_OLD, MIGRATION_OLD ],
1193                         [ MIGRATION_OLD, MIGRATION_WRITE_BOTH ],
1194                         [ MIGRATION_OLD, MIGRATION_WRITE_NEW ],
1195                         [ MIGRATION_WRITE_BOTH, MIGRATION_OLD ],
1196                         [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_BOTH ],
1197                         [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW ],
1198                         [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
1199                         [ MIGRATION_WRITE_NEW, MIGRATION_WRITE_BOTH ],
1200                         [ MIGRATION_WRITE_NEW, MIGRATION_WRITE_NEW ],
1201                         [ MIGRATION_WRITE_NEW, MIGRATION_NEW ],
1202                         [ MIGRATION_NEW, MIGRATION_WRITE_BOTH ],
1203                         [ MIGRATION_NEW, MIGRATION_WRITE_NEW ],
1204                         [ MIGRATION_NEW, MIGRATION_NEW ],
1205                 ];
1206         }
1207
1208 }