Merge "Bound the cache size of numberofWatchingusers()"
[lhc/web/wiklou.git] / tests / phpunit / includes / EditPageTest.php
1 <?php
2
3 /**
4 * @group Editing
5 *
6 * @group Database
7 * ^--- tell jenkins this test needs the database
8 *
9 * @group medium
10 * ^--- tell phpunit that these test cases may take longer than 2 seconds.
11 */
12 class EditPageTest extends MediaWikiLangTestCase {
13
14 /**
15 * @dataProvider provideExtractSectionTitle
16 * @covers EditPage::extractSectionTitle
17 */
18 public function testExtractSectionTitle( $section, $title ) {
19 $extracted = EditPage::extractSectionTitle( $section );
20 $this->assertEquals( $title, $extracted );
21 }
22
23 public static function provideExtractSectionTitle() {
24 return array(
25 array(
26 "== Test ==\n\nJust a test section.",
27 "Test"
28 ),
29 array(
30 "An initial section, no header.",
31 false
32 ),
33 array(
34 "An initial section with a fake heder (bug 32617)\n\n== Test == ??\nwtf",
35 false
36 ),
37 array(
38 "== Section ==\nfollowed by a fake == Non-section == ??\nnoooo",
39 "Section"
40 ),
41 array(
42 "== Section== \t\r\n followed by whitespace (bug 35051)",
43 'Section',
44 ),
45 );
46 }
47
48 protected function forceRevisionDate( WikiPage $page, $timestamp ) {
49 $dbw = wfGetDB( DB_MASTER );
50
51 $dbw->update( 'revision',
52 array( 'rev_timestamp' => $dbw->timestamp( $timestamp ) ),
53 array( 'rev_id' => $page->getLatest() ) );
54
55 $page->clear();
56 }
57
58 /**
59 * User input text is passed to rtrim() by edit page. This is a simple
60 * wrapper around assertEquals() which calls rrtrim() to normalize the
61 * expected and actual texts.
62 * @param string $expected
63 * @param string $actual
64 * @param string $msg
65 */
66 protected function assertEditedTextEquals( $expected, $actual, $msg = '' ) {
67 return $this->assertEquals( rtrim( $expected ), rtrim( $actual ), $msg );
68 }
69
70 /**
71 * Performs an edit and checks the result.
72 *
73 * @param string|Title $title The title of the page to edit
74 * @param string|null $baseText Some text to create the page with before attempting the edit.
75 * @param User|string|null $user The user to perform the edit as.
76 * @param array $edit An array of request parameters used to define the edit to perform.
77 * Some well known fields are:
78 * * wpTextbox1: the text to submit
79 * * wpSummary: the edit summary
80 * * wpEditToken: the edit token (will be inserted if not provided)
81 * * wpEdittime: timestamp of the edit's base revision (will be inserted
82 * if not provided)
83 * * wpStarttime: timestamp when the edit started (will be inserted if not provided)
84 * * wpSectionTitle: the section to edit
85 * * wpMinorEdit: mark as minor edit
86 * * wpWatchthis: whether to watch the page
87 * @param int|null $expectedCode The expected result code (EditPage::AS_XXX constants).
88 * Set to null to skip the check.
89 * @param string|null $expectedText The text expected to be on the page after the edit.
90 * Set to null to skip the check.
91 * @param string|null $message An optional message to show along with any error message.
92 *
93 * @return WikiPage The page that was just edited, useful for getting the edit's rev_id, etc.
94 */
95 protected function assertEdit( $title, $baseText, $user = null, array $edit,
96 $expectedCode = null, $expectedText = null, $message = null
97 ) {
98 if ( is_string( $title ) ) {
99 $ns = $this->getDefaultWikitextNS();
100 $title = Title::newFromText( $title, $ns );
101 }
102 $this->assertNotNull( $title );
103
104 if ( is_string( $user ) ) {
105 $user = User::newFromName( $user );
106
107 if ( $user->getId() === 0 ) {
108 $user->addToDatabase();
109 }
110 }
111
112 $page = WikiPage::factory( $title );
113
114 if ( $baseText !== null ) {
115 $content = ContentHandler::makeContent( $baseText, $title );
116 $page->doEditContent( $content, "base text for test" );
117 $this->forceRevisionDate( $page, '20120101000000' );
118
119 //sanity check
120 $page->clear();
121 $currentText = ContentHandler::getContentText( $page->getContent() );
122
123 # EditPage rtrim() the user input, so we alter our expected text
124 # to reflect that.
125 $this->assertEditedTextEquals( $baseText, $currentText );
126 }
127
128 if ( $user == null ) {
129 $user = $GLOBALS['wgUser'];
130 } else {
131 $this->setMwGlobals( 'wgUser', $user );
132 }
133
134 if ( !isset( $edit['wpEditToken'] ) ) {
135 $edit['wpEditToken'] = $user->getEditToken();
136 }
137
138 if ( !isset( $edit['wpEdittime'] ) ) {
139 $edit['wpEdittime'] = $page->exists() ? $page->getTimestamp() : '';
140 }
141
142 if ( !isset( $edit['wpStarttime'] ) ) {
143 $edit['wpStarttime'] = wfTimestampNow();
144 }
145
146 $req = new FauxRequest( $edit, true ); // session ??
147
148 $article = new Article( $title );
149 $article->getContext()->setTitle( $title );
150 $ep = new EditPage( $article );
151 $ep->setContextTitle( $title );
152 $ep->importFormData( $req );
153
154 $bot = isset( $edit['bot'] ) ? (bool)$edit['bot'] : false;
155
156 // this is where the edit happens!
157 // Note: don't want to use EditPage::AttemptSave, because it messes with $wgOut
158 // and throws exceptions like PermissionsError
159 $status = $ep->internalAttemptSave( $result, $bot );
160
161 if ( $expectedCode !== null ) {
162 // check edit code
163 $this->assertEquals( $expectedCode, $status->value,
164 "Expected result code mismatch. $message" );
165 }
166
167 $page = WikiPage::factory( $title );
168
169 if ( $expectedText !== null ) {
170 // check resulting page text
171 $content = $page->getContent();
172 $text = ContentHandler::getContentText( $content );
173
174 # EditPage rtrim() the user input, so we alter our expected text
175 # to reflect that.
176 $this->assertEditedTextEquals( $expectedText, $text,
177 "Expected article text mismatch. $message" );
178 }
179
180 return $page;
181 }
182
183 /**
184 * @todo split into a dataprovider and test method
185 * @covers EditPage
186 */
187 public function testCreatePage() {
188 $this->assertEdit(
189 'EditPageTest_testCreatePage',
190 null,
191 null,
192 array(
193 'wpTextbox1' => "Hello World!",
194 ),
195 EditPage::AS_SUCCESS_NEW_ARTICLE,
196 "Hello World!",
197 "expected article being created"
198 )->doDeleteArticleReal( 'EditPageTest_testCreatePage' );
199
200 $this->assertEdit(
201 'EditPageTest_testCreatePage',
202 null,
203 null,
204 array(
205 'wpTextbox1' => "",
206 ),
207 EditPage::AS_BLANK_ARTICLE,
208 null,
209 "expected article not being created if empty"
210 );
211
212 $this->assertEdit(
213 'MediaWiki:January',
214 null,
215 'UTSysop',
216 array(
217 'wpTextbox1' => "Not January",
218 ),
219 EditPage::AS_SUCCESS_NEW_ARTICLE,
220 "Not January",
221 "expected MediaWiki: page being created"
222 )->doDeleteArticleReal( 'EditPageTest_testCreatePage' );
223
224 $this->assertEdit(
225 'MediaWiki:EditPageTest_testCreatePage',
226 null,
227 'UTSysop',
228 array(
229 'wpTextbox1' => "",
230 ),
231 EditPage::AS_BLANK_ARTICLE,
232 null,
233 "expected not-registered MediaWiki: page not being created if empty"
234 );
235
236 $this->assertEdit(
237 'MediaWiki:January',
238 null,
239 'UTSysop',
240 array(
241 'wpTextbox1' => "",
242 ),
243 EditPage::AS_SUCCESS_NEW_ARTICLE,
244 "",
245 "expected registered MediaWiki: page being created even if empty"
246 )->doDeleteArticleReal( 'EditPageTest_testCreatePage' );
247
248 $this->assertEdit(
249 'MediaWiki:Ipb-default-expiry',
250 null,
251 'UTSysop',
252 array(
253 'wpTextbox1' => "",
254 ),
255 EditPage::AS_BLANK_ARTICLE,
256 "",
257 "expected registered MediaWiki: page whose default content is empty not being created if empty"
258 );
259
260 $this->assertEdit(
261 'MediaWiki:January',
262 null,
263 'UTSysop',
264 array(
265 'wpTextbox1' => "January",
266 ),
267 EditPage::AS_BLANK_ARTICLE,
268 null,
269 "expected MediaWiki: page not being created if text equals default message"
270 );
271
272 $this->assertEdit(
273 'EditPageTest_testCreatePage',
274 null,
275 null,
276 array(
277 'wpTextbox1' => "",
278 'wpIgnoreBlankArticle' => 1,
279 ),
280 EditPage::AS_SUCCESS_NEW_ARTICLE,
281 "",
282 "expected empty article being created"
283 )->doDeleteArticleReal( 'EditPageTest_testCreatePage' );
284 }
285
286 public function testUpdatePage() {
287 $text = "one";
288 $edit = array(
289 'wpTextbox1' => $text,
290 'wpSummary' => 'first update',
291 );
292
293 $page = $this->assertEdit( 'EditPageTest_testUpdatePage', "zero", null, $edit,
294 EditPage::AS_SUCCESS_UPDATE, $text,
295 "expected successfull update with given text" );
296
297 $this->forceRevisionDate( $page, '20120101000000' );
298
299 $text = "two";
300 $edit = array(
301 'wpTextbox1' => $text,
302 'wpSummary' => 'second update',
303 );
304
305 $this->assertEdit( 'EditPageTest_testUpdatePage', null, null, $edit,
306 EditPage::AS_SUCCESS_UPDATE, $text,
307 "expected successfull update with given text" );
308 }
309
310 public static function provideSectionEdit() {
311 $text = 'Intro
312
313 == one ==
314 first section.
315
316 == two ==
317 second section.
318 ';
319
320 $sectionOne = '== one ==
321 hello
322 ';
323
324 $newSection = '== new section ==
325
326 hello
327 ';
328
329 $textWithNewSectionOne = preg_replace(
330 '/== one ==.*== two ==/ms',
331 "$sectionOne\n== two ==", $text
332 );
333
334 $textWithNewSectionAdded = "$text\n$newSection";
335
336 return array(
337 array( #0
338 $text,
339 '',
340 'hello',
341 'replace all',
342 'hello'
343 ),
344
345 array( #1
346 $text,
347 '1',
348 $sectionOne,
349 'replace first section',
350 $textWithNewSectionOne,
351 ),
352
353 array( #2
354 $text,
355 'new',
356 'hello',
357 'new section',
358 $textWithNewSectionAdded,
359 ),
360 );
361 }
362
363 /**
364 * @dataProvider provideSectionEdit
365 * @covers EditPage
366 */
367 public function testSectionEdit( $base, $section, $text, $summary, $expected ) {
368 $edit = array(
369 'wpTextbox1' => $text,
370 'wpSummary' => $summary,
371 'wpSection' => $section,
372 );
373
374 $this->assertEdit( 'EditPageTest_testSectionEdit', $base, null, $edit,
375 EditPage::AS_SUCCESS_UPDATE, $expected,
376 "expected successfull update of section" );
377 }
378
379 public static function provideAutoMerge() {
380 $tests = array();
381
382 $tests[] = array( #0: plain conflict
383 "Elmo", # base edit user
384 "one\n\ntwo\n\nthree\n",
385 array( #adam's edit
386 'wpStarttime' => 1,
387 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
388 ),
389 array( #berta's edit
390 'wpStarttime' => 2,
391 'wpTextbox1' => "(one)\n\ntwo\n\nthree\n",
392 ),
393 EditPage::AS_CONFLICT_DETECTED, # expected code
394 "ONE\n\ntwo\n\nthree\n", # expected text
395 'expected edit conflict', # message
396 );
397
398 $tests[] = array( #1: successful merge
399 "Elmo", # base edit user
400 "one\n\ntwo\n\nthree\n",
401 array( #adam's edit
402 'wpStarttime' => 1,
403 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
404 ),
405 array( #berta's edit
406 'wpStarttime' => 2,
407 'wpTextbox1' => "one\n\ntwo\n\nTHREE\n",
408 ),
409 EditPage::AS_SUCCESS_UPDATE, # expected code
410 "ONE\n\ntwo\n\nTHREE\n", # expected text
411 'expected automatic merge', # message
412 );
413
414 $text = "Intro\n\n";
415 $text .= "== first section ==\n\n";
416 $text .= "one\n\ntwo\n\nthree\n\n";
417 $text .= "== second section ==\n\n";
418 $text .= "four\n\nfive\n\nsix\n\n";
419
420 // extract the first section.
421 $section = preg_replace( '/.*(== first section ==.*)== second section ==.*/sm', '$1', $text );
422
423 // generate expected text after merge
424 $expected = str_replace( 'one', 'ONE', str_replace( 'three', 'THREE', $text ) );
425
426 $tests[] = array( #2: merge in section
427 "Elmo", # base edit user
428 $text,
429 array( #adam's edit
430 'wpStarttime' => 1,
431 'wpTextbox1' => str_replace( 'one', 'ONE', $section ),
432 'wpSection' => '1'
433 ),
434 array( #berta's edit
435 'wpStarttime' => 2,
436 'wpTextbox1' => str_replace( 'three', 'THREE', $section ),
437 'wpSection' => '1'
438 ),
439 EditPage::AS_SUCCESS_UPDATE, # expected code
440 $expected, # expected text
441 'expected automatic section merge', # message
442 );
443
444 // see whether it makes a difference who did the base edit
445 $testsWithAdam = array_map( function ( $test ) {
446 $test[0] = 'Adam'; // change base edit user
447 return $test;
448 }, $tests );
449
450 $testsWithBerta = array_map( function ( $test ) {
451 $test[0] = 'Berta'; // change base edit user
452 return $test;
453 }, $tests );
454
455 return array_merge( $tests, $testsWithAdam, $testsWithBerta );
456 }
457
458 /**
459 * @dataProvider provideAutoMerge
460 * @covers EditPage
461 */
462 public function testAutoMerge( $baseUser, $text, $adamsEdit, $bertasEdit,
463 $expectedCode, $expectedText, $message = null
464 ) {
465 $this->checkHasDiff3();
466
467 //create page
468 $ns = $this->getDefaultWikitextNS();
469 $title = Title::newFromText( 'EditPageTest_testAutoMerge', $ns );
470 $page = WikiPage::factory( $title );
471
472 if ( $page->exists() ) {
473 $page->doDeleteArticle( "clean slate for testing" );
474 }
475
476 $baseEdit = array(
477 'wpTextbox1' => $text,
478 );
479
480 $page = $this->assertEdit( 'EditPageTest_testAutoMerge', null,
481 $baseUser, $baseEdit, null, null, __METHOD__ );
482
483 $this->forceRevisionDate( $page, '20120101000000' );
484
485 $edittime = $page->getTimestamp();
486
487 // start timestamps for conflict detection
488 if ( !isset( $adamsEdit['wpStarttime'] ) ) {
489 $adamsEdit['wpStarttime'] = 1;
490 }
491
492 if ( !isset( $bertasEdit['wpStarttime'] ) ) {
493 $bertasEdit['wpStarttime'] = 2;
494 }
495
496 $starttime = wfTimestampNow();
497 $adamsTime = wfTimestamp(
498 TS_MW,
499 (int)wfTimestamp( TS_UNIX, $starttime ) + (int)$adamsEdit['wpStarttime']
500 );
501 $bertasTime = wfTimestamp(
502 TS_MW,
503 (int)wfTimestamp( TS_UNIX, $starttime ) + (int)$bertasEdit['wpStarttime']
504 );
505
506 $adamsEdit['wpStarttime'] = $adamsTime;
507 $bertasEdit['wpStarttime'] = $bertasTime;
508
509 $adamsEdit['wpSummary'] = 'Adam\'s edit';
510 $bertasEdit['wpSummary'] = 'Bertas\'s edit';
511
512 $adamsEdit['wpEdittime'] = $edittime;
513 $bertasEdit['wpEdittime'] = $edittime;
514
515 // first edit
516 $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Adam', $adamsEdit,
517 EditPage::AS_SUCCESS_UPDATE, null, "expected successfull update" );
518
519 // second edit
520 $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Berta', $bertasEdit,
521 $expectedCode, $expectedText, $message );
522 }
523 }