123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- <?php
- use MediaWiki\Storage\MutableRevisionRecord;
- use MediaWiki\Storage\RevisionRecord;
- use MediaWiki\Storage\SlotRecord;
- use Wikimedia\TestingAccessWrapper;
- /**
- * @covers DifferenceEngine
- *
- * @todo tests for the rest of DifferenceEngine!
- *
- * @group Database
- * @group Diff
- *
- * @author Katie Filbert < aude.wiki@gmail.com >
- */
- class DifferenceEngineTest extends MediaWikiTestCase {
- protected $context;
- private static $revisions;
- protected function setUp() {
- parent::setUp();
- $title = $this->getTitle();
- $this->context = new RequestContext();
- $this->context->setTitle( $title );
- if ( !self::$revisions ) {
- self::$revisions = $this->doEdits();
- }
- }
- /**
- * @return Title
- */
- protected function getTitle() {
- $namespace = $this->getDefaultWikitextNS();
- return Title::newFromText( 'Kitten', $namespace );
- }
- /**
- * @return int[] Revision ids
- */
- protected function doEdits() {
- $title = $this->getTitle();
- $page = WikiPage::factory( $title );
- $strings = [ "it is a kitten", "two kittens", "three kittens", "four kittens" ];
- $revisions = [];
- foreach ( $strings as $string ) {
- $content = ContentHandler::makeContent( $string, $title );
- $page->doEditContent( $content, 'edit page' );
- $revisions[] = $page->getLatest();
- }
- return $revisions;
- }
- public function testMapDiffPrevNext() {
- $cases = $this->getMapDiffPrevNextCases();
- foreach ( $cases as $case ) {
- list( $expected, $old, $new, $message ) = $case;
- $diffEngine = new DifferenceEngine( $this->context, $old, $new, 2, true, false );
- $diffMap = $diffEngine->mapDiffPrevNext( $old, $new );
- $this->assertEquals( $expected, $diffMap, $message );
- }
- }
- private function getMapDiffPrevNextCases() {
- $revs = self::$revisions;
- return [
- [ [ $revs[1], $revs[2] ], $revs[2], 'prev', 'diff=prev' ],
- [ [ $revs[2], $revs[3] ], $revs[2], 'next', 'diff=next' ],
- [ [ $revs[1], $revs[3] ], $revs[1], $revs[3], 'diff=' . $revs[3] ]
- ];
- }
- public function testLoadRevisionData() {
- $cases = $this->getLoadRevisionDataCases();
- foreach ( $cases as $testName => $case ) {
- list( $expectedOld, $expectedNew, $expectedRet, $old, $new ) = $case;
- $diffEngine = new DifferenceEngine( $this->context, $old, $new, 2, true, false );
- $ret = $diffEngine->loadRevisionData();
- $ret2 = $diffEngine->loadRevisionData();
- $this->assertEquals( $expectedOld, $diffEngine->getOldid(), $testName );
- $this->assertEquals( $expectedNew, $diffEngine->getNewid(), $testName );
- $this->assertEquals( $expectedRet, $ret, $testName );
- $this->assertEquals( $expectedRet, $ret2, $testName );
- }
- }
- private function getLoadRevisionDataCases() {
- $revs = self::$revisions;
- return [
- '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] ],
- ];
- }
- public function testGetOldid() {
- $revs = self::$revisions;
- $diffEngine = new DifferenceEngine( $this->context, $revs[1], $revs[2], 2, true, false );
- $this->assertEquals( $revs[1], $diffEngine->getOldid(), 'diff get old id' );
- }
- public function testGetNewid() {
- $revs = self::$revisions;
- $diffEngine = new DifferenceEngine( $this->context, $revs[1], $revs[2], 2, true, false );
- $this->assertEquals( $revs[2], $diffEngine->getNewid(), 'diff get new id' );
- }
- public function provideLocaliseTitleTooltipsTestData() {
- return [
- 'moved paragraph left shoud get new location title' => [
- '<a class="mw-diff-movedpara-left">⚫</a>',
- '<a class="mw-diff-movedpara-left" title="(diff-paragraph-moved-tonew)">⚫</a>',
- ],
- 'moved paragraph right shoud get old location title' => [
- '<a class="mw-diff-movedpara-right">⚫</a>',
- '<a class="mw-diff-movedpara-right" title="(diff-paragraph-moved-toold)">⚫</a>',
- ],
- 'nothing changed when key not hit' => [
- '<a class="mw-diff-movedpara-rightis">⚫</a>',
- '<a class="mw-diff-movedpara-rightis">⚫</a>',
- ],
- ];
- }
- /**
- * @dataProvider provideLocaliseTitleTooltipsTestData
- */
- public function testAddLocalisedTitleTooltips( $input, $expected ) {
- $this->setContentLang( 'qqx' );
- $diffEngine = TestingAccessWrapper::newFromObject( new DifferenceEngine() );
- $this->assertEquals( $expected, $diffEngine->addLocalisedTitleTooltips( $input ) );
- }
- /**
- * @dataProvider provideGenerateContentDiffBody
- */
- public function testGenerateContentDiffBody(
- Content $oldContent, Content $newContent, $expectedDiff
- ) {
- // Set $wgExternalDiffEngine to something bogus to try to force use of
- // the PHP engine rather than wikidiff2.
- $this->setMwGlobals( [
- 'wgExternalDiffEngine' => '/dev/null',
- ] );
- $differenceEngine = new DifferenceEngine();
- $diff = $differenceEngine->generateContentDiffBody( $oldContent, $newContent );
- $this->assertSame( $expectedDiff, $this->getPlainDiff( $diff ) );
- }
- public function provideGenerateContentDiffBody() {
- $this->mergeMwGlobalArrayValue( 'wgContentHandlers', [
- 'testing-nontext' => DummyNonTextContentHandler::class,
- ] );
- $content1 = ContentHandler::makeContent( 'xxx', null, CONTENT_MODEL_TEXT );
- $content2 = ContentHandler::makeContent( 'yyy', null, CONTENT_MODEL_TEXT );
- return [
- 'self-diff' => [ $content1, $content1, '' ],
- 'text diff' => [ $content1, $content2, '-xxx+yyy' ],
- ];
- }
- public function testGenerateTextDiffBody() {
- // Set $wgExternalDiffEngine to something bogus to try to force use of
- // the PHP engine rather than wikidiff2.
- $this->setMwGlobals( [
- 'wgExternalDiffEngine' => '/dev/null',
- ] );
- $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() {
- // Set $wgExternalDiffEngine to something bogus to try to force use of
- // the PHP engine rather than wikidiff2.
- $this->setMwGlobals( [
- 'wgExternalDiffEngine' => '/dev/null',
- ] );
- $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( 'main',
- ContentHandler::makeContent( 'xxx', null, CONTENT_MODEL_TEXT ) );
- $main2 = SlotRecord::newUnsaved( '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
- ) {
- // Set $wgExternalDiffEngine to something bogus to try to force use of
- // the PHP engine rather than wikidiff2.
- $this->setMwGlobals( [
- 'wgExternalDiffEngine' => '/dev/null',
- ] );
- 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( 'main',
- ContentHandler::makeContent( 'xxx', null, CONTENT_MODEL_TEXT ) );
- $main2 = SlotRecord::newUnsaved( '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 $customContentHandler ContentHandler */
- $customContent = $this->getMockBuilder( Content::class )
- ->setMethods( [ 'getContentHandler' ] )
- ->getMockForAbstractClass();
- $customContent->expects( $this->any() )
- ->method( 'getContentHandler' )
- ->willReturn( $customContentHandler );
- /** @var $customContent Content */
- $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;
- }
- }
|