7 * @covers ApiComparePages
9 class ApiComparePagesTest
extends ApiTestCase
{
11 protected static $repl = [];
13 protected function setUp() {
16 // Set $wgExternalDiffEngine to something bogus to try to force use of
17 // the PHP engine rather than wikidiff2.
18 $this->setMwGlobals( [
19 'wgExternalDiffEngine' => '/dev/null',
23 protected function addPage( $page, $text, $model = CONTENT_MODEL_WIKITEXT
) {
24 $title = Title
::newFromText( 'ApiComparePagesTest ' . $page );
25 $content = ContentHandler
::makeContent( $text, $title, $model );
27 $page = WikiPage
::factory( $title );
28 $user = static::getTestSysop()->getUser();
29 $status = $page->doEditContent(
30 $content, 'Test for ApiComparePagesTest: ' . $text, 0, false, $user
32 if ( !$status->isOK() ) {
33 $this->fail( "Failed to create $title: " . $status->getWikiText( false, false, 'en' ) );
35 return $status->value
['revision']->getId();
38 public function addDBDataOnce() {
39 $user = static::getTestSysop()->getUser();
40 self
::$repl['creator'] = $user->getName();
41 self
::$repl['creatorid'] = $user->getId();
43 self
::$repl['revA1'] = $this->addPage( 'A', 'A 1' );
44 self
::$repl['revA2'] = $this->addPage( 'A', 'A 2' );
45 self
::$repl['revA3'] = $this->addPage( 'A', 'A 3' );
46 self
::$repl['revA4'] = $this->addPage( 'A', 'A 4' );
47 self
::$repl['pageA'] = Title
::newFromText( 'ApiComparePagesTest A' )->getArticleId();
49 self
::$repl['revB1'] = $this->addPage( 'B', 'B 1' );
50 self
::$repl['revB2'] = $this->addPage( 'B', 'B 2' );
51 self
::$repl['revB3'] = $this->addPage( 'B', 'B 3' );
52 self
::$repl['revB4'] = $this->addPage( 'B', 'B 4' );
53 self
::$repl['pageB'] = Title
::newFromText( 'ApiComparePagesTest B' )->getArticleId();
55 self
::$repl['revC1'] = $this->addPage( 'C', 'C 1' );
56 self
::$repl['revC2'] = $this->addPage( 'C', 'C 2' );
57 self
::$repl['revC3'] = $this->addPage( 'C', 'C 3' );
58 self
::$repl['pageC'] = Title
::newFromText( 'ApiComparePagesTest C' )->getArticleId();
60 $id = $this->addPage( 'D', 'D 1' );
61 self
::$repl['pageD'] = Title
::newFromText( 'ApiComparePagesTest D' )->getArticleId();
62 wfGetDB( DB_MASTER
)->delete( 'revision', [ 'rev_id' => $id ] );
64 self
::$repl['revE1'] = $this->addPage( 'E', 'E 1' );
65 self
::$repl['revE2'] = $this->addPage( 'E', 'E 2' );
66 self
::$repl['revE3'] = $this->addPage( 'E', 'E 3' );
67 self
::$repl['revE4'] = $this->addPage( 'E', 'E 4' );
68 self
::$repl['pageE'] = Title
::newFromText( 'ApiComparePagesTest E' )->getArticleId();
69 wfGetDB( DB_MASTER
)->update(
70 'page', [ 'page_latest' => 0 ], [ 'page_id' => self
::$repl['pageE'] ]
73 self
::$repl['revF1'] = $this->addPage( 'F', "== Section 1 ==\nF 1.1\n\n== Section 2 ==\nF 1.2" );
74 self
::$repl['pageF'] = Title
::newFromText( 'ApiComparePagesTest F' )->getArticleId();
76 WikiPage
::factory( Title
::newFromText( 'ApiComparePagesTest C' ) )
77 ->doDeleteArticleReal( 'Test for ApiComparePagesTest' );
79 RevisionDeleter
::createList(
81 RequestContext
::getMain(),
82 Title
::newFromText( 'ApiComparePagesTest B' ),
83 [ self
::$repl['revB2'] ]
86 Revision
::DELETED_TEXT
=> 1,
87 Revision
::DELETED_USER
=> 1,
88 Revision
::DELETED_COMMENT
=> 1,
90 'comment' => 'Test for ApiComparePages',
93 RevisionDeleter
::createList(
95 RequestContext
::getMain(),
96 Title
::newFromText( 'ApiComparePagesTest B' ),
97 [ self
::$repl['revB3'] ]
100 Revision
::DELETED_USER
=> 1,
101 Revision
::DELETED_COMMENT
=> 1,
102 Revision
::DELETED_RESTRICTED
=> 1,
104 'comment' => 'Test for ApiComparePages',
107 Title
::clearCaches(); // Otherwise it has the wrong latest revision for some reason
110 protected function doReplacements( &$value ) {
111 if ( is_string( $value ) ) {
112 if ( preg_match( '/^{{REPL:(.+?)}}$/', $value, $m ) ) {
113 $value = self
::$repl[$m[1]];
115 $value = preg_replace_callback( '/{{REPL:(.+?)}}/', function ( $m ) {
116 return isset( self
::$repl[$m[1]] ) ? self
::$repl[$m[1]] : $m[0];
119 } elseif ( is_array( $value ) ||
is_object( $value ) ) {
120 foreach ( $value as &$v ) {
121 $this->doReplacements( $v );
128 * @dataProvider provideDiff
130 public function testDiff( $params, $expect, $exceptionCode = false, $sysop = false ) {
131 $this->doReplacements( $params );
134 'action' => 'compare',
138 ?
static::getTestSysop()->getUser()
139 : static::getTestUser()->getUser();
140 if ( $exceptionCode ) {
142 $this->doApiRequest( $params, null, false, $user );
143 $this->fail( 'Expected exception not thrown' );
144 } catch ( ApiUsageException
$ex ) {
145 $this->assertTrue( $this->apiExceptionHasCode( $ex, $exceptionCode ),
146 "Exception with code $exceptionCode" );
149 $apiResult = $this->doApiRequest( $params, null, false, $user );
150 $apiResult = $apiResult[0];
151 $this->doReplacements( $expect );
152 $this->assertEquals( $expect, $apiResult );
156 public static function provideDiff() {
157 // phpcs:disable Generic.Files.LineLength.TooLong
159 'Basic diff, titles' => [
161 'fromtitle' => 'ApiComparePagesTest A',
162 'totitle' => 'ApiComparePagesTest B',
166 'fromid' => '{{REPL:pageA}}',
167 'fromrevid' => '{{REPL:revA4}}',
169 'fromtitle' => 'ApiComparePagesTest A',
170 'toid' => '{{REPL:pageB}}',
171 'torevid' => '{{REPL:revB4}}',
173 'totitle' => 'ApiComparePagesTest B',
174 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
175 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
176 . '<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",
180 'Basic diff, page IDs' => [
182 'fromid' => '{{REPL:pageA}}',
183 'toid' => '{{REPL:pageB}}',
187 'fromid' => '{{REPL:pageA}}',
188 'fromrevid' => '{{REPL:revA4}}',
190 'fromtitle' => 'ApiComparePagesTest A',
191 'toid' => '{{REPL:pageB}}',
192 'torevid' => '{{REPL:revB4}}',
194 'totitle' => 'ApiComparePagesTest B',
195 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
196 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
197 . '<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",
201 'Basic diff, revision IDs' => [
203 'fromrev' => '{{REPL:revA2}}',
204 'torev' => '{{REPL:revA3}}',
208 'fromid' => '{{REPL:pageA}}',
209 'fromrevid' => '{{REPL:revA2}}',
211 'fromtitle' => 'ApiComparePagesTest A',
212 'toid' => '{{REPL:pageA}}',
213 'torevid' => '{{REPL:revA3}}',
215 'totitle' => 'ApiComparePagesTest A',
216 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
217 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
218 . '<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",
222 'Basic diff, deleted revision ID as sysop' => [
224 'fromrev' => '{{REPL:revA2}}',
225 'torev' => '{{REPL:revC2}}',
229 'fromid' => '{{REPL:pageA}}',
230 'fromrevid' => '{{REPL:revA2}}',
232 'fromtitle' => 'ApiComparePagesTest A',
234 'torevid' => '{{REPL:revC2}}',
236 'totitle' => 'ApiComparePagesTest C',
238 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
239 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
240 . '<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",
245 'Basic diff, revdel as sysop' => [
247 'fromrev' => '{{REPL:revA2}}',
248 'torev' => '{{REPL:revB2}}',
252 'fromid' => '{{REPL:pageA}}',
253 'fromrevid' => '{{REPL:revA2}}',
255 'fromtitle' => 'ApiComparePagesTest A',
256 'toid' => '{{REPL:pageB}}',
257 'torevid' => '{{REPL:revB2}}',
259 'totitle' => 'ApiComparePagesTest B',
260 'totexthidden' => true,
261 'touserhidden' => true,
262 'tocommenthidden' => true,
263 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
264 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
265 . '<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",
270 'Basic diff, text' => [
272 'fromtext' => 'From text',
273 'fromcontentmodel' => 'wikitext',
274 'totext' => 'To text {{subst:PAGENAME}}',
275 'tocontentmodel' => 'wikitext',
279 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
280 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
281 . '<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",
285 'Basic diff, text 2' => [
287 'fromtext' => 'From text',
288 'totext' => 'To text {{subst:PAGENAME}}',
289 'tocontentmodel' => 'wikitext',
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">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",
299 'Basic diff, guessed model' => [
301 'fromtext' => 'From text',
302 'totext' => 'To text',
307 'warnings' => 'No content model could be determined, assuming wikitext.',
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</div></td></tr>' . "\n",
317 'Basic diff, text with title and PST' => [
319 'fromtext' => 'From text',
321 'totext' => 'To text {{subst:PAGENAME}}',
326 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
327 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
328 . '<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",
332 'Basic diff, text with page ID and PST' => [
334 'fromtext' => 'From text',
335 'toid' => '{{REPL:pageB}}',
336 'totext' => 'To text {{subst:PAGENAME}}',
341 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
342 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
343 . '<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",
347 'Basic diff, text with revision and PST' => [
349 'fromtext' => 'From text',
350 'torev' => '{{REPL:revB2}}',
351 'totext' => 'To text {{subst:PAGENAME}}',
356 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
357 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
358 . '<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",
362 'Basic diff, text with deleted revision and PST' => [
364 'fromtext' => 'From text',
365 'torev' => '{{REPL:revC2}}',
366 'totext' => 'To text {{subst:PAGENAME}}',
371 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
372 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
373 . '<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",
378 'Basic diff, test with sections' => [
380 'fromtitle' => 'ApiComparePagesTest F',
382 'totext' => "== Section 1 ==\nTo text\n\n== Section 2 ==\nTo text?",
387 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
388 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
389 . '<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"
390 . '<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",
391 'fromid' => '{{REPL:pageF}}',
392 'fromrevid' => '{{REPL:revF1}}',
394 'fromtitle' => 'ApiComparePagesTest F',
398 'Diff with all props' => [
400 'fromrev' => '{{REPL:revB1}}',
401 'torev' => '{{REPL:revB3}}',
402 'totitle' => 'ApiComparePagesTest B',
403 'prop' => 'diff|diffsize|rel|ids|title|user|comment|parsedcomment|size'
407 'fromid' => '{{REPL:pageB}}',
408 'fromrevid' => '{{REPL:revB1}}',
410 'fromtitle' => 'ApiComparePagesTest B',
412 'fromuser' => '{{REPL:creator}}',
413 'fromuserid' => '{{REPL:creatorid}}',
414 'fromcomment' => 'Test for ApiComparePagesTest: B 1',
415 'fromparsedcomment' => 'Test for ApiComparePagesTest: B 1',
416 'toid' => '{{REPL:pageB}}',
417 'torevid' => '{{REPL:revB3}}',
419 'totitle' => 'ApiComparePagesTest B',
421 'touserhidden' => true,
422 'tocommenthidden' => true,
423 'tosuppressed' => true,
424 'next' => '{{REPL:revB4}}',
426 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
427 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
428 . '<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",
432 'Diff with all props as sysop' => [
434 'fromrev' => '{{REPL:revB2}}',
435 'torev' => '{{REPL:revB3}}',
436 'totitle' => 'ApiComparePagesTest B',
437 'prop' => 'diff|diffsize|rel|ids|title|user|comment|parsedcomment|size'
441 'fromid' => '{{REPL:pageB}}',
442 'fromrevid' => '{{REPL:revB2}}',
444 'fromtitle' => 'ApiComparePagesTest B',
446 'fromtexthidden' => true,
447 'fromuserhidden' => true,
448 'fromuser' => '{{REPL:creator}}',
449 'fromuserid' => '{{REPL:creatorid}}',
450 'fromcommenthidden' => true,
451 'fromcomment' => 'Test for ApiComparePagesTest: B 2',
452 'fromparsedcomment' => 'Test for ApiComparePagesTest: B 2',
453 'toid' => '{{REPL:pageB}}',
454 'torevid' => '{{REPL:revB3}}',
456 'totitle' => 'ApiComparePagesTest B',
458 'touserhidden' => true,
459 'tocommenthidden' => true,
460 'tosuppressed' => true,
461 'prev' => '{{REPL:revB1}}',
462 'next' => '{{REPL:revB4}}',
464 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
465 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
466 . '<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",
471 'Relative diff, cur' => [
473 'fromrev' => '{{REPL:revA2}}',
474 'torelative' => 'cur',
479 'fromid' => '{{REPL:pageA}}',
480 'fromrevid' => '{{REPL:revA2}}',
481 'toid' => '{{REPL:pageA}}',
482 'torevid' => '{{REPL:revA4}}',
486 'Relative diff, next' => [
488 'fromrev' => '{{REPL:revE2}}',
489 'torelative' => 'next',
494 'fromid' => '{{REPL:pageE}}',
495 'fromrevid' => '{{REPL:revE2}}',
496 'toid' => '{{REPL:pageE}}',
497 'torevid' => '{{REPL:revE3}}',
498 'prev' => '{{REPL:revE1}}',
499 'next' => '{{REPL:revE4}}',
503 'Relative diff, prev' => [
505 'fromrev' => '{{REPL:revE3}}',
506 'torelative' => 'prev',
511 'fromid' => '{{REPL:pageE}}',
512 'fromrevid' => '{{REPL:revE2}}',
513 'toid' => '{{REPL:pageE}}',
514 'torevid' => '{{REPL:revE3}}',
515 'prev' => '{{REPL:revE1}}',
516 'next' => '{{REPL:revE4}}',
521 'Error, missing title' => [
523 'fromtitle' => 'ApiComparePagesTest X',
524 'totitle' => 'ApiComparePagesTest B',
529 'Error, invalid title' => [
531 'fromtitle' => '<bad>',
532 'totitle' => 'ApiComparePagesTest B',
537 'Error, missing page ID' => [
540 'totitle' => 'ApiComparePagesTest B',
545 'Error, page with missing revision' => [
547 'fromtitle' => 'ApiComparePagesTest D',
548 'totitle' => 'ApiComparePagesTest B',
553 'Error, page with no revision' => [
555 'fromtitle' => 'ApiComparePagesTest E',
556 'totitle' => 'ApiComparePagesTest B',
561 'Error, bad rev ID' => [
563 'fromrev' => 8817900,
564 'totitle' => 'ApiComparePagesTest B',
569 'Error, deleted revision ID, non-sysop' => [
571 'fromrev' => '{{REPL:revA2}}',
572 'torev' => '{{REPL:revC2}}',
577 'Error, revision-deleted content' => [
579 'fromrev' => '{{REPL:revA2}}',
580 'torev' => '{{REPL:revB2}}',
585 'Error, text with no title and PST' => [
587 'fromtext' => 'From text',
588 'totext' => 'To text {{subst:PAGENAME}}',
594 'Error, test with invalid from section ID' => [
596 'fromtitle' => 'ApiComparePagesTest F',
598 'totext' => "== Section 1 ==\nTo text\n\n== Section 2 ==\nTo text?",
604 'Error, test with invalid to section ID' => [
606 'fromtitle' => 'ApiComparePagesTest F',
608 'totext' => "== Section 1 ==\nTo text\n\n== Section 2 ==\nTo text?",
614 'Error, Relative diff, no from revision' => [
617 'torelative' => 'cur',
621 'compare-relative-to-nothing'
623 'Error, Relative diff, cur with no current revision' => [
625 'fromrev' => '{{REPL:revE2}}',
626 'torelative' => 'cur',
632 'Error, Relative diff, next revdeleted' => [
634 'fromrev' => '{{REPL:revB1}}',
635 'torelative' => 'next',
641 'Error, Relative diff, prev revdeleted' => [
643 'fromrev' => '{{REPL:revB3}}',
644 'torelative' => 'prev',