1<?php
2
3use MediaWiki\Revision\RevisionRecord;
4
5/**
6 * @group API
7 * @group Database
8 * @group medium
9 * @covers ApiComparePages
10 */
11class ApiComparePagesTest extends ApiTestCase {
12
13	protected static $repl = [];
14
15	protected function addPage( $page, $text, $model = CONTENT_MODEL_WIKITEXT ) {
16		$title = Title::newFromText( 'ApiComparePagesTest ' . $page );
17		$content = ContentHandler::makeContent( $text, $title, $model );
18
19		$page = WikiPage::factory( $title );
20		$user = static::getTestSysop()->getUser();
21		$status = $page->doEditContent(
22			$content, 'Test for ApiComparePagesTest: ' . $text, 0, false, $user
23		);
24		if ( !$status->isOK() ) {
25			$this->fail( "Failed to create $title: " . $status->getWikiText( false, false, 'en' ) );
26		}
27		return $status->value['revision-record']->getId();
28	}
29
30	public function addDBDataOnce() {
31		$user = static::getTestSysop()->getUser();
32		self::$repl['creator'] = $user->getName();
33		self::$repl['creatorid'] = $user->getId();
34
35		self::$repl['revA1'] = $this->addPage( 'A', 'A 1' );
36		self::$repl['revA2'] = $this->addPage( 'A', 'A 2' );
37		self::$repl['revA3'] = $this->addPage( 'A', 'A 3' );
38		self::$repl['revA4'] = $this->addPage( 'A', 'A 4' );
39		self::$repl['pageA'] = Title::newFromText( 'ApiComparePagesTest A' )->getArticleID();
40
41		self::$repl['revB1'] = $this->addPage( 'B', 'B 1' );
42		self::$repl['revB2'] = $this->addPage( 'B', 'B 2' );
43		self::$repl['revB3'] = $this->addPage( 'B', 'B 3' );
44		self::$repl['revB4'] = $this->addPage( 'B', 'B 4' );
45		self::$repl['pageB'] = Title::newFromText( 'ApiComparePagesTest B' )->getArticleID();
46		foreach ( [
47			self::$repl['revB1'] => '20010101011101',
48			self::$repl['revB2'] => '20020202022202',
49			self::$repl['revB3'] => '20030303033303',
50			self::$repl['revB4'] => '20040404044404',
51		] as $id => $ts ) {
52			$this->db->update(
53				'revision',
54				[ 'rev_timestamp' => $this->db->timestamp( $ts ) ],
55				[ 'rev_id' => $id ],
56				__METHOD__
57			);
58		}
59
60		self::$repl['revC1'] = $this->addPage( 'C', 'C 1' );
61		self::$repl['revC2'] = $this->addPage( 'C', 'C 2' );
62		self::$repl['revC3'] = $this->addPage( 'C', 'C 3' );
63		self::$repl['pageC'] = Title::newFromText( 'ApiComparePagesTest C' )->getArticleID();
64
65		$id = $this->addPage( 'D', 'D 1' );
66		self::$repl['pageD'] = Title::newFromText( 'ApiComparePagesTest D' )->getArticleID();
67		wfGetDB( DB_MASTER )->delete( 'revision', [ 'rev_id' => $id ] );
68
69		self::$repl['revE1'] = $this->addPage( 'E', 'E 1' );
70		self::$repl['revE2'] = $this->addPage( 'E', 'E 2' );
71		self::$repl['revE3'] = $this->addPage( 'E', 'E 3' );
72		self::$repl['revE4'] = $this->addPage( 'E', 'E 4' );
73		self::$repl['pageE'] = Title::newFromText( 'ApiComparePagesTest E' )->getArticleID();
74		wfGetDB( DB_MASTER )->update(
75			'page', [ 'page_latest' => 0 ], [ 'page_id' => self::$repl['pageE'] ]
76		);
77
78		self::$repl['revF1'] = $this->addPage( 'F', "== Section 1 ==\nF 1.1\n\n== Section 2 ==\nF 1.2" );
79		self::$repl['pageF'] = Title::newFromText( 'ApiComparePagesTest F' )->getArticleID();
80
81		self::$repl['revG1'] = $this->addPage( 'G', "== Section 1 ==\nG 1.1", CONTENT_MODEL_TEXT );
82		self::$repl['pageG'] = Title::newFromText( 'ApiComparePagesTest G' )->getArticleID();
83
84		WikiPage::factory( Title::newFromText( 'ApiComparePagesTest C' ) )
85			->doDeleteArticleReal( 'Test for ApiComparePagesTest', $user );
86
87		RevisionDeleter::createList(
88			'revision',
89			RequestContext::getMain(),
90			Title::newFromText( 'ApiComparePagesTest B' ),
91			[ self::$repl['revB2'] ]
92		)->setVisibility( [
93			'value' => [
94				RevisionRecord::DELETED_TEXT => 1,
95				RevisionRecord::DELETED_USER => 1,
96				RevisionRecord::DELETED_COMMENT => 1,
97			],
98			'comment' => 'Test for ApiComparePages',
99		] );
100
101		RevisionDeleter::createList(
102			'revision',
103			RequestContext::getMain(),
104			Title::newFromText( 'ApiComparePagesTest B' ),
105			[ self::$repl['revB3'] ]
106		)->setVisibility( [
107			'value' => [
108				RevisionRecord::DELETED_USER => 1,
109				RevisionRecord::DELETED_COMMENT => 1,
110				RevisionRecord::DELETED_RESTRICTED => 1,
111			],
112			'comment' => 'Test for ApiComparePages',
113		] );
114
115		Title::clearCaches(); // Otherwise it has the wrong latest revision for some reason
116	}
117
118	protected function doReplacements( &$value ) {
119		if ( is_string( $value ) ) {
120			if ( preg_match( '/^{{REPL:(.+?)}}$/', $value, $m ) ) {
121				$value = self::$repl[$m[1]];
122			} else {
123				$value = preg_replace_callback( '/{{REPL:(.+?)}}/', function ( $m ) {
124					return self::$repl[$m[1]] ?? $m[0];
125				}, $value );
126			}
127		} elseif ( is_array( $value ) || is_object( $value ) ) {
128			foreach ( $value as &$v ) {
129				$this->doReplacements( $v );
130			}
131			unset( $v );
132		}
133	}
134
135	/**
136	 * @dataProvider provideDiff
137	 */
138	public function testDiff( $params, $expect, $exceptionCode = false, $sysop = false ) {
139		$this->setMwGlobals( [ 'wgDiffEngine' => 'php' ] );
140
141		$this->doReplacements( $params );
142
143		$params += [
144			'action' => 'compare',
145			'errorformat' => 'none',
146		];
147
148		$user = $sysop
149			? static::getTestSysop()->getUser()
150			: static::getTestUser()->getUser();
151		if ( $exceptionCode ) {
152			try {
153				$this->doApiRequest( $params, null, false, $user );
154				$this->fail( 'Expected exception not thrown' );
155			} catch ( ApiUsageException $ex ) {
156				$this->assertTrue( $this->apiExceptionHasCode( $ex, $exceptionCode ),
157					"Exception with code $exceptionCode" );
158			}
159		} else {
160			$apiResult = $this->doApiRequest( $params, null, false, $user );
161			$apiResult = $apiResult[0];
162			$this->doReplacements( $expect );
163			$this->assertEquals( $expect, $apiResult );
164		}
165	}
166
167	private static function makeDeprecationWarnings( ...$params ) {
168		$warn = [];
169		foreach ( $params as $p ) {
170			$warn[] = [
171				'code' => 'deprecation',
172				'data' => [ 'feature' => "action=compare&{$p}" ],
173				'module' => 'compare',
174			];
175			if ( count( $warn ) === 1 ) {
176				$warn[] = [
177					'code' => 'deprecation-help',
178					'module' => 'main',
179				];
180			}
181		}
182
183		return $warn;
184	}
185
186	public static function provideDiff() {
187		// phpcs:disable Generic.Files.LineLength.TooLong
188		return [
189			'Basic diff, titles' => [
190				[
191					'fromtitle' => 'ApiComparePagesTest A',
192					'totitle' => 'ApiComparePagesTest B',
193				],
194				[
195					'compare' => [
196						'fromid' => '{{REPL:pageA}}',
197						'fromrevid' => '{{REPL:revA4}}',
198						'fromns' => 0,
199						'fromtitle' => 'ApiComparePagesTest A',
200						'toid' => '{{REPL:pageB}}',
201						'torevid' => '{{REPL:revB4}}',
202						'tons' => 0,
203						'totitle' => 'ApiComparePagesTest B',
204						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
205							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
206							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">A </del>4</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">B </ins>4</div></td></tr>' . "\n",
207					]
208				],
209			],
210			'Basic diff, page IDs' => [
211				[
212					'fromid' => '{{REPL:pageA}}',
213					'toid' => '{{REPL:pageB}}',
214				],
215				[
216					'compare' => [
217						'fromid' => '{{REPL:pageA}}',
218						'fromrevid' => '{{REPL:revA4}}',
219						'fromns' => 0,
220						'fromtitle' => 'ApiComparePagesTest A',
221						'toid' => '{{REPL:pageB}}',
222						'torevid' => '{{REPL:revB4}}',
223						'tons' => 0,
224						'totitle' => 'ApiComparePagesTest B',
225						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
226							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
227							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">A </del>4</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">B </ins>4</div></td></tr>' . "\n",
228					]
229				],
230			],
231			'Basic diff, revision IDs' => [
232				[
233					'fromrev' => '{{REPL:revA2}}',
234					'torev' => '{{REPL:revA3}}',
235				],
236				[
237					'compare' => [
238						'fromid' => '{{REPL:pageA}}',
239						'fromrevid' => '{{REPL:revA2}}',
240						'fromns' => 0,
241						'fromtitle' => 'ApiComparePagesTest A',
242						'toid' => '{{REPL:pageA}}',
243						'torevid' => '{{REPL:revA3}}',
244						'tons' => 0,
245						'totitle' => 'ApiComparePagesTest A',
246						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
247							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
248							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>A <del class="diffchange diffchange-inline">2</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>A <ins class="diffchange diffchange-inline">3</ins></div></td></tr>' . "\n",
249					]
250				],
251			],
252			'Basic diff, deleted revision ID as sysop' => [
253				[
254					'fromrev' => '{{REPL:revA2}}',
255					'torev' => '{{REPL:revC2}}',
256				],
257				[
258					'compare' => [
259						'fromid' => '{{REPL:pageA}}',
260						'fromrevid' => '{{REPL:revA2}}',
261						'fromns' => 0,
262						'fromtitle' => 'ApiComparePagesTest A',
263						'toid' => 0,
264						'torevid' => '{{REPL:revC2}}',
265						'tons' => 0,
266						'totitle' => 'ApiComparePagesTest C',
267						'toarchive' => true,
268						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
269							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
270							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">A </del>2</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">C </ins>2</div></td></tr>' . "\n",
271					]
272				],
273				false, true
274			],
275			'Basic diff, revdel as sysop' => [
276				[
277					'fromrev' => '{{REPL:revA2}}',
278					'torev' => '{{REPL:revB2}}',
279				],
280				[
281					'compare' => [
282						'fromid' => '{{REPL:pageA}}',
283						'fromrevid' => '{{REPL:revA2}}',
284						'fromns' => 0,
285						'fromtitle' => 'ApiComparePagesTest A',
286						'toid' => '{{REPL:pageB}}',
287						'torevid' => '{{REPL:revB2}}',
288						'tons' => 0,
289						'totitle' => 'ApiComparePagesTest B',
290						'totexthidden' => true,
291						'touserhidden' => true,
292						'tocommenthidden' => true,
293						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
294							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
295							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">A </del>2</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">B </ins>2</div></td></tr>' . "\n",
296					]
297				],
298				false, true
299			],
300			'Basic diff, text' => [
301				[
302					'fromslots' => 'main',
303					'fromtext-main' => 'From text',
304					'fromcontentmodel-main' => 'wikitext',
305					'toslots' => 'main',
306					'totext-main' => 'To text {{subst:PAGENAME}}',
307					'tocontentmodel-main' => 'wikitext',
308				],
309				[
310					'compare' => [
311						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
312							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
313							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">{{subst:PAGENAME}}</ins></div></td></tr>' . "\n",
314					]
315				],
316			],
317			'Basic diff, text 2' => [
318				[
319					'fromslots' => 'main',
320					'fromtext-main' => 'From text',
321					'toslots' => 'main',
322					'totext-main' => 'To text {{subst:PAGENAME}}',
323					'tocontentmodel-main' => 'wikitext',
324				],
325				[
326					'compare' => [
327						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
328							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
329							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">{{subst:PAGENAME}}</ins></div></td></tr>' . "\n",
330					]
331				],
332			],
333			'Basic diff, guessed model' => [
334				[
335					'fromslots' => 'main',
336					'fromtext-main' => 'From text',
337					'toslots' => 'main',
338					'totext-main' => 'To text',
339				],
340				[
341					'warnings' => [ [ 'code' => 'compare-nocontentmodel', 'module' => 'compare' ] ],
342					'compare' => [
343						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
344							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
345							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text</div></td></tr>' . "\n",
346					]
347				],
348			],
349			'Basic diff, text with title and PST' => [
350				[
351					'fromslots' => 'main',
352					'fromtext-main' => 'From text',
353					'totitle' => 'Test',
354					'toslots' => 'main',
355					'totext-main' => 'To text {{subst:PAGENAME}}',
356					'topst' => true,
357				],
358				[
359					'compare' => [
360						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
361							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
362							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">Test</ins></div></td></tr>' . "\n",
363					]
364				],
365			],
366			'Basic diff, text with page ID and PST' => [
367				[
368					'fromslots' => 'main',
369					'fromtext-main' => 'From text',
370					'toid' => '{{REPL:pageB}}',
371					'toslots' => 'main',
372					'totext-main' => 'To text {{subst:PAGENAME}}',
373					'topst' => true,
374				],
375				[
376					'compare' => [
377						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
378							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
379							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest B</ins></div></td></tr>' . "\n",
380					]
381				],
382			],
383			'Basic diff, text with revision and PST' => [
384				[
385					'fromslots' => 'main',
386					'fromtext-main' => 'From text',
387					'torev' => '{{REPL:revB2}}',
388					'toslots' => 'main',
389					'totext-main' => 'To text {{subst:PAGENAME}}',
390					'topst' => true,
391				],
392				[
393					'compare' => [
394						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
395							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
396							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest B</ins></div></td></tr>' . "\n",
397					]
398				],
399			],
400			'Basic diff, text with deleted revision and PST' => [
401				[
402					'fromslots' => 'main',
403					'fromtext-main' => 'From text',
404					'torev' => '{{REPL:revC2}}',
405					'toslots' => 'main',
406					'totext-main' => 'To text {{subst:PAGENAME}}',
407					'topst' => true,
408				],
409				[
410					'compare' => [
411						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
412							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
413							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest C</ins></div></td></tr>' . "\n",
414					]
415				],
416				false, true
417			],
418			'Basic diff, test with sections' => [
419				[
420					'fromtitle' => 'ApiComparePagesTest F',
421					'fromslots' => 'main',
422					'fromtext-main' => "== Section 2 ==\nFrom text?",
423					'fromsection-main' => 2,
424					'totitle' => 'ApiComparePagesTest F',
425					'toslots' => 'main',
426					'totext-main' => "== Section 1 ==\nTo text?",
427					'tosection-main' => 1,
428				],
429				[
430					'compare' => [
431						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
432							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
433							. '<tr><td class=\'diff-marker\'> </td><td class=\'diff-context\'><div>== Section 1 ==</div></td><td class=\'diff-marker\'> </td><td class=\'diff-context\'><div>== Section 1 ==</div></td></tr>' . "\n"
434							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">F 1.1</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To text?</ins></div></td></tr>' . "\n"
435							. '<tr><td class=\'diff-marker\'> </td><td class=\'diff-context\'></td><td class=\'diff-marker\'> </td><td class=\'diff-context\'></td></tr>' . "\n"
436							. '<tr><td class=\'diff-marker\'> </td><td class=\'diff-context\'><div>== Section 2 ==</div></td><td class=\'diff-marker\'> </td><td class=\'diff-context\'><div>== Section 2 ==</div></td></tr>' . "\n"
437							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From text?</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">F 1.2</ins></div></td></tr>' . "\n",
438					]
439				],
440			],
441			'Diff with all props' => [
442				[
443					'fromrev' => '{{REPL:revB1}}',
444					'torev' => '{{REPL:revB3}}',
445					'totitle' => 'ApiComparePagesTest B',
446					'prop' => 'diff|diffsize|rel|ids|title|user|comment|parsedcomment|size|timestamp'
447				],
448				[
449					'compare' => [
450						'fromid' => '{{REPL:pageB}}',
451						'fromrevid' => '{{REPL:revB1}}',
452						'fromns' => 0,
453						'fromtitle' => 'ApiComparePagesTest B',
454						'fromsize' => 3,
455						'fromuser' => '{{REPL:creator}}',
456						'fromuserid' => '{{REPL:creatorid}}',
457						'fromcomment' => 'Test for ApiComparePagesTest: B 1',
458						'fromparsedcomment' => 'Test for ApiComparePagesTest: B 1',
459						'fromtimestamp' => '2001-01-01T01:11:01Z',
460						'toid' => '{{REPL:pageB}}',
461						'torevid' => '{{REPL:revB3}}',
462						'tons' => 0,
463						'totitle' => 'ApiComparePagesTest B',
464						'tosize' => 3,
465						'touserhidden' => true,
466						'tocommenthidden' => true,
467						'tosuppressed' => true,
468						'totimestamp' => '2003-03-03T03:33:03Z',
469						'next' => '{{REPL:revB4}}',
470						'diffsize' => 391,
471						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
472							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
473							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>B <del class="diffchange diffchange-inline">1</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>B <ins class="diffchange diffchange-inline">3</ins></div></td></tr>' . "\n",
474					]
475				],
476			],
477			'Diff with all props as sysop' => [
478				[
479					'fromrev' => '{{REPL:revB2}}',
480					'torev' => '{{REPL:revB3}}',
481					'totitle' => 'ApiComparePagesTest B',
482					'prop' => 'diff|diffsize|rel|ids|title|user|comment|parsedcomment|size|timestamp'
483				],
484				[
485					'compare' => [
486						'fromid' => '{{REPL:pageB}}',
487						'fromrevid' => '{{REPL:revB2}}',
488						'fromns' => 0,
489						'fromtitle' => 'ApiComparePagesTest B',
490						'fromsize' => 3,
491						'fromtexthidden' => true,
492						'fromuserhidden' => true,
493						'fromuser' => '{{REPL:creator}}',
494						'fromuserid' => '{{REPL:creatorid}}',
495						'fromcommenthidden' => true,
496						'fromcomment' => 'Test for ApiComparePagesTest: B 2',
497						'fromparsedcomment' => 'Test for ApiComparePagesTest: B 2',
498						'fromtimestamp' => '2002-02-02T02:22:02Z',
499						'toid' => '{{REPL:pageB}}',
500						'torevid' => '{{REPL:revB3}}',
501						'tons' => 0,
502						'totitle' => 'ApiComparePagesTest B',
503						'tosize' => 3,
504						'touserhidden' => true,
505						'tocommenthidden' => true,
506						'tosuppressed' => true,
507						'totimestamp' => '2003-03-03T03:33:03Z',
508						'prev' => '{{REPL:revB1}}',
509						'next' => '{{REPL:revB4}}',
510						'diffsize' => 391,
511						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
512							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
513							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>B <del class="diffchange diffchange-inline">2</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>B <ins class="diffchange diffchange-inline">3</ins></div></td></tr>' . "\n",
514					]
515				],
516				false, true
517			],
518			'Text diff with all props' => [
519				[
520					'fromrev' => '{{REPL:revB1}}',
521					'toslots' => 'main',
522					'totext-main' => 'To text {{subst:PAGENAME}}',
523					'tocontentmodel-main' => 'wikitext',
524					'prop' => 'diff|diffsize|rel|ids|title|user|comment|parsedcomment|size|timestamp'
525				],
526				[
527					'compare' => [
528						'fromid' => '{{REPL:pageB}}',
529						'fromrevid' => '{{REPL:revB1}}',
530						'fromns' => 0,
531						'fromtitle' => 'ApiComparePagesTest B',
532						'fromsize' => 3,
533						'fromuser' => '{{REPL:creator}}',
534						'fromuserid' => '{{REPL:creatorid}}',
535						'fromcomment' => 'Test for ApiComparePagesTest: B 1',
536						'fromparsedcomment' => 'Test for ApiComparePagesTest: B 1',
537						'fromtimestamp' => '2001-01-01T01:11:01Z',
538						'diffsize' => 414,
539						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
540							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
541							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">B 1</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To text {{subst:PAGENAME}}</ins></div></td></tr>' . "\n",
542					]
543				],
544			],
545			'Relative diff, cur' => [
546				[
547					'fromrev' => '{{REPL:revA2}}',
548					'torelative' => 'cur',
549					'prop' => 'ids',
550				],
551				[
552					'compare' => [
553						'fromid' => '{{REPL:pageA}}',
554						'fromrevid' => '{{REPL:revA2}}',
555						'toid' => '{{REPL:pageA}}',
556						'torevid' => '{{REPL:revA4}}',
557					]
558				],
559			],
560			'Relative diff, next' => [
561				[
562					'fromrev' => '{{REPL:revE2}}',
563					'torelative' => 'next',
564					'prop' => 'ids|rel',
565				],
566				[
567					'compare' => [
568						'fromid' => '{{REPL:pageE}}',
569						'fromrevid' => '{{REPL:revE2}}',
570						'toid' => '{{REPL:pageE}}',
571						'torevid' => '{{REPL:revE3}}',
572						'prev' => '{{REPL:revE1}}',
573						'next' => '{{REPL:revE4}}',
574					]
575				],
576			],
577			'Relative diff, prev' => [
578				[
579					'fromrev' => '{{REPL:revE3}}',
580					'torelative' => 'prev',
581					'prop' => 'ids|rel',
582				],
583				[
584					'compare' => [
585						'fromid' => '{{REPL:pageE}}',
586						'fromrevid' => '{{REPL:revE2}}',
587						'toid' => '{{REPL:pageE}}',
588						'torevid' => '{{REPL:revE3}}',
589						'prev' => '{{REPL:revE1}}',
590						'next' => '{{REPL:revE4}}',
591					]
592				],
593			],
594			'Relative diff, no prev' => [
595				[
596					'fromrev' => '{{REPL:revA1}}',
597					'torelative' => 'prev',
598					'prop' => 'ids|rel|diff|title|user|comment',
599				],
600				[
601					'warnings' => [
602						[
603							'code' => 'compare-no-prev',
604							'module' => 'compare',
605						],
606					],
607					'compare' => [
608						'toid' => '{{REPL:pageA}}',
609						'torevid' => '{{REPL:revA1}}',
610						'tons' => 0,
611						'totitle' => 'ApiComparePagesTest A',
612						'touser' => '{{REPL:creator}}',
613						'touserid' => '{{REPL:creatorid}}',
614						'tocomment' => 'Test for ApiComparePagesTest: A 1',
615						'toparsedcomment' => 'Test for ApiComparePagesTest: A 1',
616						'next' => '{{REPL:revA2}}',
617						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
618							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
619							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div> </div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">A 1</ins></div></td></tr>' . "\n",
620					],
621				],
622			],
623			'Relative diff, no next' => [
624				[
625					'fromrev' => '{{REPL:revA4}}',
626					'torelative' => 'next',
627					'prop' => 'ids|rel|diff|title|user|comment',
628				],
629				[
630					'warnings' => [
631						[
632							'code' => 'compare-no-next',
633							'module' => 'compare',
634						],
635					],
636					'compare' => [
637						'fromid' => '{{REPL:pageA}}',
638						'fromrevid' => '{{REPL:revA4}}',
639						'fromns' => 0,
640						'fromtitle' => 'ApiComparePagesTest A',
641						'fromuser' => '{{REPL:creator}}',
642						'fromuserid' => '{{REPL:creatorid}}',
643						'fromcomment' => 'Test for ApiComparePagesTest: A 4',
644						'fromparsedcomment' => 'Test for ApiComparePagesTest: A 4',
645						'prev' => '{{REPL:revA3}}',
646						'body' => '',
647					],
648				],
649			],
650			'Diff for specific slots' => [
651				// @todo Use a page with multiple slots here
652				[
653					'fromrev' => '{{REPL:revA1}}',
654					'torev' => '{{REPL:revA3}}',
655					'prop' => 'diff',
656					'slots' => 'main',
657				],
658				[
659					'compare' => [
660						'bodies' => [
661							'main' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
662								. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
663								. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>A <del class="diffchange diffchange-inline">1</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>A <ins class="diffchange diffchange-inline">3</ins></div></td></tr>' . "\n",
664						],
665					],
666				],
667			],
668			// @todo Add a test for diffing with a deleted slot. Deleting 'main' doesn't work.
669
670			'Basic diff, deprecated text' => [
671				[
672					'fromtext' => 'From text',
673					'fromcontentmodel' => 'wikitext',
674					'totext' => 'To text {{subst:PAGENAME}}',
675					'tocontentmodel' => 'wikitext',
676				],
677				[
678					'warnings' => self::makeDeprecationWarnings( 'fromtext', 'fromcontentmodel', 'totext', 'tocontentmodel' ),
679					'compare' => [
680						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
681							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
682							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">{{subst:PAGENAME}}</ins></div></td></tr>' . "\n",
683					]
684				],
685			],
686			'Basic diff, deprecated text 2' => [
687				[
688					'fromtext' => 'From text',
689					'totext' => 'To text {{subst:PAGENAME}}',
690					'tocontentmodel' => 'wikitext',
691				],
692				[
693					'warnings' => self::makeDeprecationWarnings( 'fromtext', 'totext', 'tocontentmodel' ),
694					'compare' => [
695						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
696							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
697							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">{{subst:PAGENAME}}</ins></div></td></tr>' . "\n",
698					]
699				],
700			],
701			'Basic diff, deprecated text, guessed model' => [
702				[
703					'fromtext' => 'From text',
704					'totext' => 'To text',
705				],
706				[
707					'warnings' => array_merge( self::makeDeprecationWarnings( 'fromtext', 'totext' ), [
708						[ 'code' => 'compare-nocontentmodel', 'module' => 'compare' ],
709					] ),
710					'compare' => [
711						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
712							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
713							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text</div></td></tr>' . "\n",
714					]
715				],
716			],
717			'Basic diff, deprecated text with title and PST' => [
718				[
719					'fromtext' => 'From text',
720					'totitle' => 'Test',
721					'totext' => 'To text {{subst:PAGENAME}}',
722					'topst' => true,
723				],
724				[
725					'warnings' => self::makeDeprecationWarnings( 'fromtext', 'totext' ),
726					'compare' => [
727						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
728							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
729							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">Test</ins></div></td></tr>' . "\n",
730					]
731				],
732			],
733			'Basic diff, deprecated text with page ID and PST' => [
734				[
735					'fromtext' => 'From text',
736					'toid' => '{{REPL:pageB}}',
737					'totext' => 'To text {{subst:PAGENAME}}',
738					'topst' => true,
739				],
740				[
741					'warnings' => self::makeDeprecationWarnings( 'fromtext', 'totext' ),
742					'compare' => [
743						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
744							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
745							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest B</ins></div></td></tr>' . "\n",
746					]
747				],
748			],
749			'Basic diff, deprecated text with revision and PST' => [
750				[
751					'fromtext' => 'From text',
752					'torev' => '{{REPL:revB2}}',
753					'totext' => 'To text {{subst:PAGENAME}}',
754					'topst' => true,
755				],
756				[
757					'warnings' => self::makeDeprecationWarnings( 'fromtext', 'totext' ),
758					'compare' => [
759						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
760							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
761							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest B</ins></div></td></tr>' . "\n",
762					]
763				],
764			],
765			'Basic diff, deprecated text with deleted revision and PST' => [
766				[
767					'fromtext' => 'From text',
768					'torev' => '{{REPL:revC2}}',
769					'totext' => 'To text {{subst:PAGENAME}}',
770					'topst' => true,
771				],
772				[
773					'warnings' => self::makeDeprecationWarnings( 'fromtext', 'totext' ),
774					'compare' => [
775						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
776							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
777							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest C</ins></div></td></tr>' . "\n",
778					]
779				],
780				false, true
781			],
782			'Basic diff, test with deprecated sections' => [
783				[
784					'fromtitle' => 'ApiComparePagesTest F',
785					'fromsection' => 1,
786					'totext' => "== Section 1 ==\nTo text\n\n== Section 2 ==\nTo text?",
787					'tosection' => 2,
788				],
789				[
790					'warnings' => self::makeDeprecationWarnings( 'fromsection', 'totext', 'tosection' ),
791					'compare' => [
792						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
793							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
794							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>== Section <del class="diffchange diffchange-inline">1 </del>==</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>== Section <ins class="diffchange diffchange-inline">2 </ins>==</div></td></tr>' . "\n"
795							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">F 1.1</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To text?</ins></div></td></tr>' . "\n",
796						'fromid' => '{{REPL:pageF}}',
797						'fromrevid' => '{{REPL:revF1}}',
798						'fromns' => '0',
799						'fromtitle' => 'ApiComparePagesTest F',
800					]
801				],
802			],
803			'Basic diff, test with deprecated sections and revdel, non-sysop' => [
804				[
805					'fromrev' => '{{REPL:revB2}}',
806					'fromsection' => 0,
807					'torev' => '{{REPL:revB4}}',
808					'tosection' => 0,
809				],
810				[],
811				'missingcontent'
812			],
813			'Basic diff, test with deprecated sections and revdel, sysop' => [
814				[
815					'fromrev' => '{{REPL:revB2}}',
816					'fromsection' => 0,
817					'torev' => '{{REPL:revB4}}',
818					'tosection' => 0,
819				],
820				[
821					'warnings' => self::makeDeprecationWarnings( 'fromsection', 'tosection' ),
822					'compare' => [
823						'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
824							. '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
825							. '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>B <del class="diffchange diffchange-inline">2</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>B <ins class="diffchange diffchange-inline">4</ins></div></td></tr>' . "\n",
826						'fromid' => '{{REPL:pageB}}',
827						'fromrevid' => '{{REPL:revB2}}',
828						'fromns' => 0,
829						'fromtitle' => 'ApiComparePagesTest B',
830						'fromtexthidden' => true,
831						'fromuserhidden' => true,
832						'fromcommenthidden' => true,
833						'toid' => '{{REPL:pageB}}',
834						'torevid' => '{{REPL:revB4}}',
835						'tons' => 0,
836						'totitle' => 'ApiComparePagesTest B',
837					]
838				],
839				false, true,
840			],
841
842			'Error, missing title' => [
843				[
844					'fromtitle' => 'ApiComparePagesTest X',
845					'totitle' => 'ApiComparePagesTest B',
846				],
847				[],
848				'missingtitle',
849			],
850			'Error, invalid title' => [
851				[
852					'fromtitle' => '<bad>',
853					'totitle' => 'ApiComparePagesTest B',
854				],
855				[],
856				'invalidtitle',
857			],
858			'Error, missing page ID' => [
859				[
860					'fromid' => 8817900,
861					'totitle' => 'ApiComparePagesTest B',
862				],
863				[],
864				'nosuchpageid',
865			],
866			'Error, page with missing revision' => [
867				[
868					'fromtitle' => 'ApiComparePagesTest D',
869					'totitle' => 'ApiComparePagesTest B',
870				],
871				[],
872				'nosuchrevid',
873			],
874			'Error, page with no revision' => [
875				[
876					'fromtitle' => 'ApiComparePagesTest E',
877					'totitle' => 'ApiComparePagesTest B',
878				],
879				[],
880				'nosuchrevid',
881			],
882			'Error, bad rev ID' => [
883				[
884					'fromrev' => 8817900,
885					'totitle' => 'ApiComparePagesTest B',
886				],
887				[],
888				'nosuchrevid',
889			],
890			'Error, deleted revision ID, non-sysop' => [
891				[
892					'fromrev' => '{{REPL:revA2}}',
893					'torev' => '{{REPL:revC2}}',
894				],
895				[],
896				'nosuchrevid',
897			],
898			'Error, deleted revision ID and torelative=prev' => [
899				[
900					'fromrev' => '{{REPL:revC2}}',
901					'torelative' => 'prev',
902				],
903				[],
904				'compare-relative-to-deleted', true
905			],
906			'Error, deleted revision ID and torelative=next' => [
907				[
908					'fromrev' => '{{REPL:revC2}}',
909					'torelative' => 'next',
910				],
911				[],
912				'compare-relative-to-deleted', true
913			],
914			'Deleted revision ID and torelative=cur' => [
915				[
916					'fromrev' => '{{REPL:revC2}}',
917					'torelative' => 'cur',
918				],
919				[],
920				'nosuchrevid', true
921			],
922			'Error, revision-deleted content' => [
923				[
924					'fromrev' => '{{REPL:revA2}}',
925					'torev' => '{{REPL:revB2}}',
926				],
927				[],
928				'missingcontent',
929			],
930			'Error, text with no title and PST' => [
931				[
932					'fromtext' => 'From text',
933					'totext' => 'To text {{subst:PAGENAME}}',
934					'topst' => true,
935				],
936				[],
937				'compare-no-title',
938			],
939			'Error, test with invalid from section ID' => [
940				[
941					'fromtitle' => 'ApiComparePagesTest F',
942					'fromsection' => 5,
943					'totext' => "== Section 1 ==\nTo text\n\n== Section 2 ==\nTo text?",
944					'tosection' => 2,
945				],
946				[],
947				'nosuchfromsection',
948			],
949			'Error, test with invalid to section ID' => [
950				[
951					'fromtitle' => 'ApiComparePagesTest F',
952					'fromsection' => 1,
953					'totext' => "== Section 1 ==\nTo text\n\n== Section 2 ==\nTo text?",
954					'tosection' => 5,
955				],
956				[],
957				'nosuchtosection',
958			],
959			'Error, Relative diff, no from revision' => [
960				[
961					'fromtext' => 'Foo',
962					'torelative' => 'cur',
963					'prop' => 'ids',
964				],
965				[],
966				'compare-relative-to-nothing'
967			],
968			'Error, Relative diff, cur with no current revision' => [
969				[
970					'fromrev' => '{{REPL:revE2}}',
971					'torelative' => 'cur',
972					'prop' => 'ids',
973				],
974				[],
975				'nosuchrevid'
976			],
977			'Error, Relative diff, next revdeleted' => [
978				[
979					'fromrev' => '{{REPL:revB1}}',
980					'torelative' => 'next',
981					'prop' => 'ids',
982				],
983				[],
984				'missingcontent'
985			],
986			'Error, Relative diff, prev revdeleted' => [
987				[
988					'fromrev' => '{{REPL:revB3}}',
989					'torelative' => 'prev',
990					'prop' => 'ids',
991				],
992				[],
993				'missingcontent'
994			],
995			'Error, section diff with no revision' => [
996				[
997					'fromtitle' => 'ApiComparePagesTest F',
998					'toslots' => 'main',
999					'totext-main' => "== Section 1 ==\nTo text?",
1000					'tosection-main' => 1,
1001				],
1002				[],
1003				'compare-notorevision',
1004			],
1005			'Error, section diff with revdeleted revision' => [
1006				[
1007					'fromtitle' => 'ApiComparePagesTest F',
1008					'torev' => '{{REPL:revB2}}',
1009					'toslots' => 'main',
1010					'totext-main' => "== Section 1 ==\nTo text?",
1011					'tosection-main' => 1,
1012				],
1013				[],
1014				'missingcontent',
1015			],
1016			'Error, section diff with a content model not supporting sections' => [
1017				[
1018					'fromtitle' => 'ApiComparePagesTest G',
1019					'torev' => '{{REPL:revG1}}',
1020					'toslots' => 'main',
1021					'totext-main' => "== Section 1 ==\nTo text?",
1022					'tosection-main' => 1,
1023				],
1024				[],
1025				'sectionsnotsupported',
1026			],
1027			'Error, section diff with bad content model' => [
1028				[
1029					'fromtitle' => 'ApiComparePagesTest F',
1030					'torev' => '{{REPL:revF1}}',
1031					'toslots' => 'main',
1032					'totext-main' => "== Section 1 ==\nTo text?",
1033					'tosection-main' => 1,
1034					'tocontentmodel-main' => CONTENT_MODEL_TEXT,
1035				],
1036				[],
1037				'sectionreplacefailed',
1038			],
1039			'Error, deleting the main slot' => [
1040				[
1041					'fromtitle' => 'ApiComparePagesTest A',
1042					'totitle' => 'ApiComparePagesTest A',
1043					'toslots' => 'main',
1044				],
1045				[],
1046				'compare-maintextrequired',
1047			],
1048			// @todo Add a test for using 'tosection-foo' without 'totext-foo' (can't do it with main)
1049		];
1050		// phpcs:enable
1051	}
1052}
1053