<?php
+use MediaWiki\Revision\MutableRevisionRecord;
+use MediaWiki\Revision\RevisionRecord;
+use MediaWiki\Revision\SlotRecord;
use Wikimedia\TestingAccessWrapper;
/**
public function testLoadRevisionData() {
$cases = $this->getLoadRevisionDataCases();
- foreach ( $cases as $case ) {
- list( $expectedOld, $expectedNew, $old, $new, $message ) = $case;
+ foreach ( $cases as $testName => $case ) {
+ list( $expectedOld, $expectedNew, $expectedRet, $old, $new ) = $case;
$diffEngine = new DifferenceEngine( $this->context, $old, $new, 2, true, false );
- $diffEngine->loadRevisionData();
+ $ret = $diffEngine->loadRevisionData();
+ $ret2 = $diffEngine->loadRevisionData();
- $this->assertEquals( $diffEngine->getOldid(), $expectedOld, $message );
- $this->assertEquals( $diffEngine->getNewid(), $expectedNew, $message );
+ $this->assertEquals( $expectedOld, $diffEngine->getOldid(), $testName );
+ $this->assertEquals( $expectedNew, $diffEngine->getNewid(), $testName );
+ $this->assertEquals( $expectedRet, $ret, $testName );
+ $this->assertEquals( $expectedRet, $ret2, $testName );
}
}
$revs = self::$revisions;
return [
- [ $revs[2], $revs[3], $revs[3], 'prev', 'diff=prev' ],
- [ $revs[2], $revs[3], $revs[2], 'next', 'diff=next' ],
- [ $revs[1], $revs[3], $revs[1], $revs[3], 'diff=' . $revs[3] ],
- [ $revs[1], $revs[3], $revs[1], 0, 'diff=0' ]
+ 'diff=prev' => [ $revs[2], $revs[3], true, $revs[3], 'prev' ],
+ 'diff=next' => [ $revs[2], $revs[3], true, $revs[2], 'next' ],
+ 'diff=' . $revs[3] => [ $revs[1], $revs[3], true, $revs[1], $revs[3] ],
+ 'diff=0' => [ $revs[1], $revs[3], true, $revs[1], 0 ],
+ 'diff=prev&oldid=<first>' => [ false, $revs[0], true, $revs[0], 'prev' ],
+ 'invalid' => [ 123456789, $revs[1], false, 123456789, $revs[1] ],
];
}
$this->assertEquals( $expected, $diffEngine->addLocalisedTitleTooltips( $input ) );
}
+ /**
+ * @dataProvider provideGenerateContentDiffBody
+ */
+ public function testGenerateContentDiffBody(
+ array $oldContentArgs, array $newContentArgs, $expectedDiff
+ ) {
+ $this->mergeMwGlobalArrayValue( 'wgContentHandlers', [
+ 'testing-nontext' => DummyNonTextContentHandler::class,
+ ] );
+ $oldContent = ContentHandler::makeContent( ...$oldContentArgs );
+ $newContent = ContentHandler::makeContent( ...$newContentArgs );
+
+ $differenceEngine = new DifferenceEngine();
+ $diff = $differenceEngine->generateContentDiffBody( $oldContent, $newContent );
+ $this->assertSame( $expectedDiff, $this->getPlainDiff( $diff ) );
+ }
+
+ public static function provideGenerateContentDiffBody() {
+ $content1 = [ 'xxx', null, CONTENT_MODEL_TEXT ];
+ $content2 = [ 'yyy', null, CONTENT_MODEL_TEXT ];
+
+ return [
+ 'self-diff' => [ $content1, $content1, '' ],
+ 'text diff' => [ $content1, $content2, '-xxx+yyy' ],
+ ];
+ }
+
+ public function testGenerateTextDiffBody() {
+ $oldText = "aaa\nbbb\nccc";
+ $newText = "aaa\nxxx\nccc";
+ $expectedDiff = " aaa aaa\n-bbb+xxx\n ccc ccc";
+
+ $differenceEngine = new DifferenceEngine();
+ $diff = $differenceEngine->generateTextDiffBody( $oldText, $newText );
+ $this->assertSame( $expectedDiff, $this->getPlainDiff( $diff ) );
+ }
+
+ public function testSetContent() {
+ $oldContent = ContentHandler::makeContent( 'xxx', null, CONTENT_MODEL_TEXT );
+ $newContent = ContentHandler::makeContent( 'yyy', null, CONTENT_MODEL_TEXT );
+
+ $differenceEngine = new DifferenceEngine();
+ $differenceEngine->setContent( $oldContent, $newContent );
+ $diff = $differenceEngine->getDiffBody();
+ $this->assertSame( "Line 1:\nLine 1:\n-xxx+yyy", $this->getPlainDiff( $diff ) );
+ }
+
+ public function testSetRevisions() {
+ $main1 = SlotRecord::newUnsaved( SlotRecord::MAIN,
+ ContentHandler::makeContent( 'xxx', null, CONTENT_MODEL_TEXT ) );
+ $main2 = SlotRecord::newUnsaved( SlotRecord::MAIN,
+ ContentHandler::makeContent( 'yyy', null, CONTENT_MODEL_TEXT ) );
+ $rev1 = $this->getRevisionRecord( $main1 );
+ $rev2 = $this->getRevisionRecord( $main2 );
+
+ $differenceEngine = new DifferenceEngine();
+ $differenceEngine->setRevisions( $rev1, $rev2 );
+ $this->assertSame( $rev1, $differenceEngine->getOldRevision() );
+ $this->assertSame( $rev2, $differenceEngine->getNewRevision() );
+ $this->assertSame( true, $differenceEngine->loadRevisionData() );
+ $this->assertSame( true, $differenceEngine->loadText() );
+
+ $differenceEngine->setRevisions( null, $rev2 );
+ $this->assertSame( null, $differenceEngine->getOldRevision() );
+ }
+
+ /**
+ * @dataProvider provideGetDiffBody
+ */
+ public function testGetDiffBody(
+ RevisionRecord $oldRevision = null, RevisionRecord $newRevision = null, $expectedDiff
+ ) {
+ if ( $expectedDiff instanceof Exception ) {
+ $this->setExpectedException( get_class( $expectedDiff ), $expectedDiff->getMessage() );
+ }
+ $differenceEngine = new DifferenceEngine();
+ $differenceEngine->setRevisions( $oldRevision, $newRevision );
+ if ( $expectedDiff instanceof Exception ) {
+ return;
+ }
+
+ $diff = $differenceEngine->getDiffBody();
+ $this->assertSame( $expectedDiff, $this->getPlainDiff( $diff ) );
+ }
+
+ public function provideGetDiffBody() {
+ $main1 = SlotRecord::newUnsaved( SlotRecord::MAIN,
+ ContentHandler::makeContent( 'xxx', null, CONTENT_MODEL_TEXT ) );
+ $main2 = SlotRecord::newUnsaved( SlotRecord::MAIN,
+ ContentHandler::makeContent( 'yyy', null, CONTENT_MODEL_TEXT ) );
+ $slot1 = SlotRecord::newUnsaved( 'slot',
+ ContentHandler::makeContent( 'aaa', null, CONTENT_MODEL_TEXT ) );
+ $slot2 = SlotRecord::newUnsaved( 'slot',
+ ContentHandler::makeContent( 'bbb', null, CONTENT_MODEL_TEXT ) );
+
+ return [
+ 'revision vs. null' => [
+ null,
+ $this->getRevisionRecord( $main1, $slot1 ),
+ '',
+ ],
+ 'revision vs. itself' => [
+ $this->getRevisionRecord( $main1, $slot1 ),
+ $this->getRevisionRecord( $main1, $slot1 ),
+ '',
+ ],
+ 'different text in one slot' => [
+ $this->getRevisionRecord( $main1, $slot1 ),
+ $this->getRevisionRecord( $main1, $slot2 ),
+ "slotLine 1:\nLine 1:\n-aaa+bbb",
+ ],
+ 'different text in two slots' => [
+ $this->getRevisionRecord( $main1, $slot1 ),
+ $this->getRevisionRecord( $main2, $slot2 ),
+ "Line 1:\nLine 1:\n-xxx+yyy\nslotLine 1:\nLine 1:\n-aaa+bbb",
+ ],
+ 'new slot' => [
+ $this->getRevisionRecord( $main1 ),
+ $this->getRevisionRecord( $main1, $slot1 ),
+ "slotLine 1:\nLine 1:\n- +aaa",
+ ],
+ ];
+ }
+
+ public function testRecursion() {
+ // Set up a ContentHandler which will return a wrapped DifferenceEngine as
+ // SlotDiffRenderer, then pass it a content which uses the same ContentHandler.
+ // This tests the anti-recursion logic in DifferenceEngine::generateContentDiffBody.
+
+ $customDifferenceEngine = $this->getMockBuilder( DifferenceEngine::class )
+ ->enableProxyingToOriginalMethods()
+ ->getMock();
+ $customContentHandler = $this->getMockBuilder( ContentHandler::class )
+ ->setConstructorArgs( [ 'foo', [] ] )
+ ->setMethods( [ 'createDifferenceEngine' ] )
+ ->getMockForAbstractClass();
+ $customContentHandler->expects( $this->any() )
+ ->method( 'createDifferenceEngine' )
+ ->willReturn( $customDifferenceEngine );
+ /** @var ContentHandler $customContentHandler */
+ $customContent = $this->getMockBuilder( Content::class )
+ ->setMethods( [ 'getContentHandler' ] )
+ ->getMockForAbstractClass();
+ $customContent->expects( $this->any() )
+ ->method( 'getContentHandler' )
+ ->willReturn( $customContentHandler );
+ /** @var Content $customContent */
+ $customContent2 = clone $customContent;
+
+ $slotDiffRenderer = $customContentHandler->getSlotDiffRenderer( RequestContext::getMain() );
+ $this->setExpectedException( Exception::class,
+ ': could not maintain backwards compatibility. Please use a SlotDiffRenderer.' );
+ $slotDiffRenderer->getDiff( $customContent, $customContent2 );
+ }
+
+ /**
+ * Convert a HTML diff to a human-readable format and hopefully make the test less fragile.
+ * @param string diff
+ * @return string
+ */
+ private function getPlainDiff( $diff ) {
+ $replacements = [
+ html_entity_decode( ' ' ) => ' ',
+ html_entity_decode( '−' ) => '-',
+ ];
+ return str_replace( array_keys( $replacements ), array_values( $replacements ),
+ trim( strip_tags( $diff ), "\n" ) );
+ }
+
+ /**
+ * @param int $id
+ * @return Title
+ */
+ private function getMockTitle( $id = 23 ) {
+ $mock = $this->getMockBuilder( Title::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mock->expects( $this->any() )
+ ->method( 'getDBkey' )
+ ->will( $this->returnValue( __CLASS__ ) );
+ $mock->expects( $this->any() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( $id ) );
+
+ return $mock;
+ }
+
+ /**
+ * @param SlotRecord[] $slots
+ * @return MutableRevisionRecord
+ */
+ private function getRevisionRecord( ...$slots ) {
+ $title = $this->getMockTitle();
+ $revision = new MutableRevisionRecord( $title );
+ foreach ( $slots as $slot ) {
+ $revision->setSlot( $slot );
+ }
+ return $revision;
+ }
+
}