aboutsummaryrefslogtreecommitdiffstats
path: root/tests/phpunit/includes/diff/TextDiffer
diff options
context:
space:
mode:
authorTim Starling <tstarling@wikimedia.org>2023-07-03 18:27:47 +1000
committerTim Starling <tstarling@wikimedia.org>2023-07-19 12:38:18 +1000
commit2aa87cdf2c21570f170ea5f4961b10e2da35b013 (patch)
treeb32557b1fba5504f5e28379585189487f1484207 /tests/phpunit/includes/diff/TextDiffer
parentb42062e7d068a8b01af3fb72a7885d23379bc0e6 (diff)
downloadmediawikicore-2aa87cdf2c21570f170ea5f4961b10e2da35b013.tar.gz
mediawikicore-2aa87cdf2c21570f170ea5f4961b10e2da35b013.zip
Factor out TextDiffer hierarchy from TextSlotDiffRenderer
* Follow the TODO comment in TextSlotDiffRenderer ::getTextDiffInternal() by moving the code out to three parallel implementations, namely ExternalTextDiffer, PhpTextDiffer and Wikidiff2TextDiffer. * Add a container/factory class ManifoldTextDiffer to glue them together and collate available formats. * Move the inline legend to Wikidiff2TextDiffer. Not the toggle since the ability to toggle depends on the available format, not the current format. * Update the diff cache keys so that ManifoldTextDiffer can store the engine=>format map it used to generate the diff. * Drop support for the second parameter to TextSlotDiffRenderer ::setEngine(), since nothing used it anymore. * Provide a format batch API, since some engines are able to efficiently generate multiple formats. This might be used by DifferenceEngine in future. Needs risky change notification for the cache key change. Bug: T339184 Depends-On: I8a35b9b8ec1622c9a36d2496bdd24f51bc52c85f Change-Id: I5c506e39162855aff53dd420dd8145156739059c
Diffstat (limited to 'tests/phpunit/includes/diff/TextDiffer')
-rw-r--r--tests/phpunit/includes/diff/TextDiffer/ExternalTextDifferTest.php23
-rw-r--r--tests/phpunit/includes/diff/TextDiffer/ManifoldTextDifferTest.php122
-rw-r--r--tests/phpunit/includes/diff/TextDiffer/PhpTextDifferTest.php111
-rw-r--r--tests/phpunit/includes/diff/TextDiffer/TextDifferData.php33
-rw-r--r--tests/phpunit/includes/diff/TextDiffer/Wikidiff2TextDifferTest.php148
-rwxr-xr-xtests/phpunit/includes/diff/TextDiffer/externalDiffTest.sh8
6 files changed, 445 insertions, 0 deletions
diff --git a/tests/phpunit/includes/diff/TextDiffer/ExternalTextDifferTest.php b/tests/phpunit/includes/diff/TextDiffer/ExternalTextDifferTest.php
new file mode 100644
index 000000000000..d5d6bd36592c
--- /dev/null
+++ b/tests/phpunit/includes/diff/TextDiffer/ExternalTextDifferTest.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace phpunit\includes\diff\TextDiffer;
+
+use MediaWiki\Diff\TextDiffer\ExternalTextDiffer;
+use MediaWikiIntegrationTestCase;
+
+/**
+ * @covers \MediaWiki\Diff\TextDiffer\ExternalTextDiffer
+ */
+class ExternalTextDifferTest extends MediaWikiIntegrationTestCase {
+ public function testRender() {
+ if ( !is_executable( '/bin/sh' ) ) {
+ $this->markTestSkipped( 'ExternalTextDiffer can\'t pass extra ' .
+ 'arguments like $wgPhpCli, so it\'s hard to be platform-independent' );
+ }
+ $oldText = 'foo';
+ $newText = 'bar';
+ $differ = new ExternalTextDiffer( __DIR__ . '/externalDiffTest.sh' );
+ $result = $differ->render( $oldText, $newText, 'external' );
+ $this->assertSame( "- foo\n+ bar\n", $result );
+ }
+}
diff --git a/tests/phpunit/includes/diff/TextDiffer/ManifoldTextDifferTest.php b/tests/phpunit/includes/diff/TextDiffer/ManifoldTextDifferTest.php
new file mode 100644
index 000000000000..2a3fc836afb3
--- /dev/null
+++ b/tests/phpunit/includes/diff/TextDiffer/ManifoldTextDifferTest.php
@@ -0,0 +1,122 @@
+<?php
+
+use MediaWiki\Diff\TextDiffer\ManifoldTextDiffer;
+use MediaWiki\Tests\Diff\TextDiffer\TextDifferData;
+
+/**
+ * @covers \MediaWiki\Diff\TextDiffer\ManifoldTextDiffer
+ * @covers \MediaWiki\Diff\TextDiffer\BaseTextDiffer
+ */
+class ManifoldTextDifferTest extends MediaWikiIntegrationTestCase {
+ private function createDiffer( $configVars = [] ) {
+ $services = $this->getServiceContainer();
+ return new ManifoldTextDiffer(
+ RequestContext::getMain(),
+ $services->getLanguageFactory()->getLanguage( 'en' ),
+ $configVars['DiffEngine'] ?? null,
+ $configVars['ExternalDiffEngine'] ?? null
+ );
+ }
+
+ public function testGetName() {
+ $this->assertSame( 'manifold', $this->createDiffer()->getName() );
+ }
+
+ public function testGetFormats() {
+ if ( extension_loaded( 'wikidiff2' ) ) {
+ $formats = [ 'table', 'inline' ];
+ } else {
+ $formats = [ 'table' ];
+ }
+ $this->assertSame(
+ $formats,
+ $this->createDiffer()->getFormats()
+ );
+ }
+
+ public function testHasFormat() {
+ $differ = $this->createDiffer();
+ $this->assertTrue( $differ->hasFormat( 'table' ) );
+ if ( extension_loaded( 'wikidiff2' ) ) {
+ $this->assertTrue( $differ->hasFormat( 'inline' ) );
+ }
+ $this->assertFalse( $differ->hasFormat( 'external' ) );
+ $this->assertFalse( $differ->hasFormat( 'nonexistent' ) );
+ }
+
+ public function testHasFormatExternal() {
+ $differ = $this->createDiffer( [
+ 'ExternalDiffEngine' => __DIR__ . '/externalDiffTest.sh'
+ ] );
+ $this->assertTrue( $differ->hasFormat( 'external' ) );
+ }
+
+ public function testRenderForcePhp() {
+ $differ = $this->createDiffer( [
+ 'DiffEngine' => 'php'
+ ] );
+ $result = $differ->render( 'foo', 'bar', 'table' );
+ $this->assertSame(
+ TextDifferData::PHP_TABLE,
+ $result
+ );
+ }
+
+ /**
+ * @requires extension wikidiff2
+ */
+ public function testRenderUnforcedWikidiff2() {
+ $differ = $this->createDiffer();
+ $result = $differ->render( 'foo', 'bar', 'table' );
+ $this->assertSame(
+ TextDifferData::WIKIDIFF2_TABLE,
+ $result
+ );
+ }
+
+ /**
+ * @requires extension wikidiff2
+ */
+ public function testRenderBatchWikidiff2External() {
+ if ( !is_executable( '/bin/sh' ) ) {
+ $this->markTestSkipped( 'ExternalTextDiffer can\'t pass extra ' .
+ 'arguments like $wgPhpCli, so it\'s hard to be platform-independent' );
+ }
+ $differ = $this->createDiffer( [
+ 'ExternalDiffEngine' => __DIR__ . '/externalDiffTest.sh'
+ ] );
+ $result = $differ->renderBatch( 'foo', 'bar', [ 'table', 'inline', 'external' ] );
+ $this->assertSame(
+ [
+ 'table' => TextDifferData::WIKIDIFF2_TABLE,
+ 'inline' => TextDifferData::WIKIDIFF2_INLINE,
+ 'external' => TextDifferData::EXTERNAL
+ ],
+ $result
+ );
+ }
+
+ public static function provideAddRowWrapper() {
+ return [
+ [ 'table', false ],
+ [ 'external', false ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideAddRowWrapper
+ * @param string $format
+ * @param bool $isWrap
+ */
+ public function testAddRowWrapper( $format, $isWrap ) {
+ $differ = $this->createDiffer( [
+ 'ExternalDiffEngine' => __DIR__ . '/externalDiffTest.sh'
+ ] );
+ $result = $differ->addRowWrapper( $format, 'foo' );
+ if ( $isWrap ) {
+ $this->assertSame( '<tr><td colspan="4">foo</td></tr>', $result );
+ } else {
+ $this->assertSame( 'foo', $result );
+ }
+ }
+}
diff --git a/tests/phpunit/includes/diff/TextDiffer/PhpTextDifferTest.php b/tests/phpunit/includes/diff/TextDiffer/PhpTextDifferTest.php
new file mode 100644
index 000000000000..0573eab5ad4e
--- /dev/null
+++ b/tests/phpunit/includes/diff/TextDiffer/PhpTextDifferTest.php
@@ -0,0 +1,111 @@
+<?php
+
+use MediaWiki\Diff\TextDiffer\PhpTextDiffer;
+use MediaWiki\Tests\Diff\TextDiffer\TextDifferData;
+
+/**
+ * @covers \MediaWiki\Diff\TextDiffer\PhpTextDiffer
+ * @covers \MediaWiki\Diff\TextDiffer\BaseTextDiffer
+ */
+class PhpTextDifferTest extends MediaWikiIntegrationTestCase {
+ private function createDiffer() {
+ $lang = $this->getServiceContainer()
+ ->getLanguageFactory()
+ ->getLanguage( 'en' );
+ $differ = new PhpTextDiffer( $lang );
+
+ $localizer = RequestContext::getMain();
+ $localizer->setLanguage( $lang );
+
+ $differ->setLocalizer( $localizer );
+ return $differ;
+ }
+
+ public function testRender() {
+ $differ = $this->createDiffer();
+ $result = $differ->render( 'foo', 'bar', 'table' );
+ $this->assertSame( TextDifferData::PHP_TABLE, $result );
+ }
+
+ public static function provideRenderBatch() {
+ return [
+ 'empty' => [
+ [],
+ []
+ ],
+ 'one format' => [
+ [ 'table' ],
+ [
+ 'table' => TextDifferData::PHP_TABLE,
+ ]
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideRenderBatch
+ * @param array $formats
+ * @param array $expected
+ */
+ public function testRenderBatch( $formats, $expected ) {
+ $oldText = 'foo';
+ $newText = 'bar';
+ $differ = $this->createDiffer();
+ $result = $differ->renderBatch( $oldText, $newText, $formats );
+ $this->assertSame( $expected, $result );
+ }
+
+ public function testHasFormat() {
+ $differ = $this->createDiffer();
+ $this->assertTrue( $differ->hasFormat( 'table' ) );
+ $this->assertFalse( $differ->hasFormat( 'external' ) );
+ }
+
+ public function testAddModules() {
+ $out = RequestContext::getMain()->getOutput();
+ $differ = $this->createDiffer();
+ $differ->addModules( $out, 'table' );
+ $this->assertSame( [], $out->getModules() );
+ }
+
+ public function testGetCacheKeys() {
+ $differ = $this->createDiffer();
+ $result = $differ->getCacheKeys( [ 'table' ] );
+ $this->assertSame( [], $result );
+ }
+
+ public static function provideLocalize() {
+ return [
+ [ 1, [], 'Line 1:' ],
+ [ 2, [], 'Line 2:' ],
+ [ 1, [ 'reducedLineNumbers' => true ], '' ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideLocalize
+ * @param string $line
+ * @param array $options
+ * @param string $expected
+ */
+ public function testLocalize( $line, $options, $expected ) {
+ $differ = $this->createDiffer();
+ $result = $differ->localize(
+ 'table',
+ "<!--LINE $line-->",
+ $options
+ );
+ $this->assertSame( $expected, $result );
+ }
+
+ public function testGetTablePrefixes() {
+ $this->assertSame( [], $this->createDiffer()->getTablePrefixes( 'table' ) );
+ }
+
+ public function testGetPreferredFormatBatch() {
+ $this->assertSame(
+ [ 'table' ],
+ $this->createDiffer()->getPreferredFormatBatch( 'table' )
+ );
+ }
+}
diff --git a/tests/phpunit/includes/diff/TextDiffer/TextDifferData.php b/tests/phpunit/includes/diff/TextDiffer/TextDifferData.php
new file mode 100644
index 000000000000..cbda305fd66c
--- /dev/null
+++ b/tests/phpunit/includes/diff/TextDiffer/TextDifferData.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace MediaWiki\Tests\Diff\TextDiffer;
+
+class TextDifferData {
+ public const EXTERNAL = "- foo\n+ bar\n";
+
+ public const PHP_TABLE = '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1"><!--LINE 1--></td>
+<td colspan="2" class="diff-lineno"><!--LINE 1--></td></tr>
+<tr><td class="diff-marker" data-marker="−"></td><td class="diff-deletedline diff-side-deleted"><div><del class="diffchange diffchange-inline">foo</del></div></td><td class="diff-marker" data-marker="+"></td><td class="diff-addedline diff-side-added"><div><ins class="diffchange diffchange-inline">bar</ins></div></td></tr>
+';
+
+ public const WIKIDIFF2_TABLE = '<tr>
+ <td colspan="2" class="diff-lineno"><!--LINE 1--></td>
+ <td colspan="2" class="diff-lineno"><!--LINE 1--></td>
+</tr>
+<tr>
+ <td colspan="2" class="diff-empty diff-side-deleted"></td>
+ <td class="diff-marker" data-marker="+"></td>
+ <td class="diff-addedline diff-side-added"><div>bar</div></td>
+</tr>
+<tr>
+ <td class="diff-marker" data-marker="−"></td>
+ <td class="diff-deletedline diff-side-deleted"><div>foo</div></td>
+ <td colspan="2" class="diff-empty diff-side-added"></td>
+</tr>
+';
+
+ public const WIKIDIFF2_INLINE = '<div class="mw-diff-inline-header"><!-- LINES 1,1 --></div>
+<div class="mw-diff-inline-added"><ins>bar</ins></div>
+<div class="mw-diff-inline-deleted"><del>foo</del></div>
+';
+}
diff --git a/tests/phpunit/includes/diff/TextDiffer/Wikidiff2TextDifferTest.php b/tests/phpunit/includes/diff/TextDiffer/Wikidiff2TextDifferTest.php
new file mode 100644
index 000000000000..af389c4ec535
--- /dev/null
+++ b/tests/phpunit/includes/diff/TextDiffer/Wikidiff2TextDifferTest.php
@@ -0,0 +1,148 @@
+<?php
+
+use MediaWiki\Diff\TextDiffer\TextDiffer;
+use MediaWiki\Diff\TextDiffer\Wikidiff2TextDiffer;
+use MediaWiki\Tests\Diff\TextDiffer\TextDifferData;
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * @covers \MediaWiki\Diff\TextDiffer\Wikidiff2TextDiffer
+ */
+class Wikidiff2TextDifferTest extends MediaWikiIntegrationTestCase {
+ private function createDiffer() {
+ $differ = new Wikidiff2TextDiffer();
+ $localizer = RequestContext::getMain();
+ $localizer->setLanguage( 'qqx' );
+ $differ->setLocalizer( $localizer );
+ TestingAccessWrapper::newFromObject( $differ )->haveMoveSupport = true;
+ return $differ;
+ }
+
+ /**
+ * @requires extension wikidiff2
+ */
+ public function testRenderBatch() {
+ $oldText = 'foo';
+ $newText = 'bar';
+ $differ = new Wikidiff2TextDiffer();
+ // Should not need a MessageLocalizer
+ $result = $differ->renderBatch( $oldText, $newText, [ 'table', 'inline' ] );
+ $this->assertSame(
+ [
+ 'table' => TextDifferData::WIKIDIFF2_TABLE,
+ 'inline' => TextDifferData::WIKIDIFF2_INLINE
+ ],
+ $result
+ );
+ }
+
+ public function testGetName() {
+ $differ = new Wikidiff2TextDiffer();
+ $this->assertSame( 'wikidiff2', $differ->getName() );
+ }
+
+ public function testGetFormatContext() {
+ $differ = new Wikidiff2TextDiffer();
+ $this->assertSame( TextDiffer::CONTEXT_ROW, $differ->getFormatContext( 'table' ) );
+ }
+
+ public static function provideGetTablePrefixes() {
+ return [
+ [
+ 'table',
+ 'class="mw-diff-inline-legend oo-ui-element-hidden".*\(diff-inline-tooltip-ins\)'
+ ],
+ [
+ 'inline',
+ 'class="mw-diff-inline-legend".*\(diff-inline-tooltip-ins\)'
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideGetTablePrefixes
+ * @param string $format
+ * @param string $pattern
+ */
+ public function testGetTablePrefixes( $format, $pattern ) {
+ $differ = $this->createDiffer();
+ $result = $differ->getTablePrefixes( $format );
+ $this->assertMatchesRegularExpression(
+ '{' . $pattern . '}s',
+ $result[TextSlotDiffRenderer::INLINE_LEGEND_KEY]
+ );
+ }
+
+ public static function provideLocalize() {
+ return [
+ 'normal table' => [
+ 'table',
+ TextDifferData::WIKIDIFF2_TABLE,
+ [],
+ '<td colspan="2" class="diff-lineno">\(lineno: 1\)</td>'
+ ],
+ 'table with move tooltip' => [
+ 'table',
+ // From wikidiff2 001.phpt
+ '<td class="diff-marker"><a class="mw-diff-movedpara-left" href="#movedpara_7_0_rhs">&#x26AB;</a></td>',
+ [],
+ 'title="\(diff-paragraph-moved-tonew\)"'
+ ],
+ 'table with reduced line numbers' => [
+ 'table',
+ TextDifferData::WIKIDIFF2_TABLE,
+ [ 'reducedLineNumbers' => true ],
+ '<td colspan="2" class="diff-lineno"></td>'
+ ],
+ 'inline tooltip' => [
+ 'inline',
+ TextDifferData::WIKIDIFF2_INLINE,
+ [],
+ '<ins title="\(diff-inline-tooltip-ins\)">'
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideLocalize
+ * @param string $format
+ * @param string $input
+ * @param array $options
+ * @param string $pattern
+ */
+ public function testLocalize( $format, $input, $options, $pattern ) {
+ $differ = $this->createDiffer();
+ $result = $differ->localize( $format, $input, $options );
+ $this->assertMatchesRegularExpression(
+ '{' . $pattern . '}s',
+ $result
+ );
+ }
+
+ public static function provideAddLocalizedTitleTooltips() {
+ 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 provideAddLocalizedTitleTooltips
+ */
+ public function testAddLocalizedTitleTooltips( $input, $expected ) {
+ $differ = TestingAccessWrapper::newFromObject( $this->createDiffer() );
+
+ $this->assertEquals( $expected, $differ->addLocalizedTitleTooltips( 'table', $input ) );
+ }
+
+}
diff --git a/tests/phpunit/includes/diff/TextDiffer/externalDiffTest.sh b/tests/phpunit/includes/diff/TextDiffer/externalDiffTest.sh
new file mode 100755
index 000000000000..c0c3ca7efb7d
--- /dev/null
+++ b/tests/phpunit/includes/diff/TextDiffer/externalDiffTest.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+echo -n "- "
+cat $1
+echo
+echo -n "+ "
+cat $2
+echo