From 291698b483bf7360b258233383166d3bf11c833e Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 2 Sep 2015 20:52:56 +0300 Subject: [PATCH] merge: tests --- .../diff/util/TextDiffTypeFactory.java | 5 + .../tests/com/intellij/diff/Assertions.kt | 6 + .../tests/com/intellij/diff/DiffTestCase.kt | 220 +++++++ .../intellij/diff/comparison/AutoTestCase.kt | 87 --- .../comparison/ComparisonMergeUtilTest.kt | 185 ++++++ .../comparison/ComparisonMergeUtilTestBase.kt | 189 ++++++ .../diff/comparison/ComparisonUtilAutoTest.kt | 95 +-- .../diff/comparison/ComparisonUtilTest.kt | 8 +- .../diff/comparison/ComparisonUtilTestBase.kt | 72 +- .../com/intellij/diff/merge/MergeAutoTest.kt | 122 ++++ .../com/intellij/diff/merge/MergeTest.kt | 614 ++++++++++++++++++ .../com/intellij/diff/merge/MergeTestBase.kt | 430 ++++++++++++ .../LineNumberConvertorCorrectorTest.kt | 4 +- .../UnifiedFragmentBuilderAutoTest.kt | 65 +- 14 files changed, 1823 insertions(+), 279 deletions(-) create mode 100644 platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt delete mode 100644 platform/diff-impl/tests/com/intellij/diff/comparison/AutoTestCase.kt create mode 100644 platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonMergeUtilTest.kt create mode 100644 platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonMergeUtilTestBase.kt create mode 100644 platform/diff-impl/tests/com/intellij/diff/merge/MergeAutoTest.kt create mode 100644 platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt create mode 100644 platform/diff-impl/tests/com/intellij/diff/merge/MergeTestBase.kt diff --git a/platform/diff-impl/src/com/intellij/diff/util/TextDiffTypeFactory.java b/platform/diff-impl/src/com/intellij/diff/util/TextDiffTypeFactory.java index b1f456c9a65a..c115e2149d35 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/TextDiffTypeFactory.java +++ b/platform/diff-impl/src/com/intellij/diff/util/TextDiffTypeFactory.java @@ -107,6 +107,11 @@ public class TextDiffTypeFactory { public Color getMarkerColor(@Nullable Editor editor) { return getAttributes(editor).getErrorStripeColor(); } + + @Override + public String toString() { + return myName; + } } private static final double MIDDLE_COLOR_FACTOR = 0.6; diff --git a/platform/diff-impl/tests/com/intellij/diff/Assertions.kt b/platform/diff-impl/tests/com/intellij/diff/Assertions.kt index 9379774ac2a2..f5ed932eeb2a 100644 --- a/platform/diff-impl/tests/com/intellij/diff/Assertions.kt +++ b/platform/diff-impl/tests/com/intellij/diff/Assertions.kt @@ -15,7 +15,9 @@ */ package com.intellij.diff +import com.intellij.openapi.util.text.StringUtil import com.intellij.testFramework.UsefulTestCase +import junit.framework.ComparisonFailure import junit.framework.TestCase public fun assertTrue(actual: Boolean, message: String = "") { @@ -30,6 +32,10 @@ public fun assertEquals(expected: Any?, actual: Any?, message: String = "") { TestCase.assertEquals(message, expected, actual) } +public fun assertEquals(expected: CharSequence?, actual: CharSequence?, message: String = "") { + if (!StringUtil.equals(expected, actual)) throw ComparisonFailure(message, expected?.toString(), actual?.toString()) +} + public fun assertEmpty(collection: Collection<*>, message: String = "") { UsefulTestCase.assertEmpty(message, collection) } diff --git a/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt b/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt new file mode 100644 index 000000000000..9c9555e9ac51 --- /dev/null +++ b/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt @@ -0,0 +1,220 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff + +import com.intellij.diff.comparison.ComparisonManagerImpl +import com.intellij.diff.util.ThreeSide +import com.intellij.openapi.editor.Document +import com.intellij.openapi.progress.DumbProgressIndicator +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.util.Couple +import com.intellij.openapi.util.registry.Registry +import com.intellij.openapi.util.text.StringUtil +import com.intellij.testFramework.UsefulTestCase +import com.intellij.util.containers.HashMap +import com.intellij.util.text.CharSequenceSubSequence +import java.util.ArrayList +import java.util.BitSet +import java.util.Random +import java.util.concurrent.atomic.AtomicLong + +public abstract class DiffTestCase : UsefulTestCase() { + companion object { + private val REGISTRY = Registry.get("diff.verify.iterable") + + private val DEFAULT_CHAR_COUNT = 12 + private val DEFAULT_CHAR_TABLE: Map = { + val map = HashMap() + listOf('\n', '\n', '\t', ' ', ' ', '.', '<', '!').forEachIndexed { i, c -> map.put(i, c) } + map + }() + } + + public val RNG: Random = Random() + private var gotSeedException = false + + private var oldRegistryValue: Boolean = false + + public val INDICATOR: ProgressIndicator = DumbProgressIndicator.INSTANCE + public val MANAGER: ComparisonManagerImpl = ComparisonManagerImpl() + + + override fun setUp() { + super.setUp() + oldRegistryValue = REGISTRY.asBoolean() + REGISTRY.setValue(true) + } + + override fun tearDown() { + REGISTRY.setValue(oldRegistryValue) + super.tearDown() + } + + + public fun assertEqualsCharSequences(chunk1: CharSequence, chunk2: CharSequence, ignoreSpaces: Boolean, skipLastNewline: Boolean) { + if (ignoreSpaces) { + assertTrue(StringUtil.equalsIgnoreWhitespaces(chunk1, chunk2)) + } else { + if (skipLastNewline) { + if (StringUtil.equals(chunk1, chunk2)) return + if (StringUtil.equals(stripNewline(chunk1), chunk2)) return + if (StringUtil.equals(chunk1, stripNewline(chunk2))) return + assertTrue(false) + } else { + assertTrue(StringUtil.equals(chunk1, chunk2)) + } + } + } + + // + // Parsing + // + + public fun textToReadableFormat(text: CharSequence?): String { + if (text == null) return "null" + return "'" + text.toString().replace('\n', '*').replace('\t', '+') + "'" + } + + public fun parseSource(string: CharSequence): String = string.toString().replace('_', '\n') + + public fun parseMatching(before: String, after: String): Couple { + return Couple.of(parseMatching(before), parseMatching(after)) + } + + public fun parseMatching(matching: String): BitSet { + val set = BitSet() + matching.filterNot { it == '.' }.forEachIndexed { i, c -> if (c != ' ') set.set(i) } + return set + } + + // + // Misc + // + + public fun getLineCount(document: Document): Int { + return Math.max(1, document.getLineCount()) + } + + public fun Int.until(a: Int): IntRange = this..a - 1 + + // + // AutoTests + // + + public fun doAutoTest(seed: Long, runs: Int, test: (DebugData) -> Unit) { + RNG.setSeed(seed) + + var lastSeed: Long = -1 + val debugData = DebugData() + + for (i in 1..runs) { + if (i % 1000 == 0) println(i) + + try { + lastSeed = getCurrentSeed() + + test(debugData) + debugData.reset() + } catch (e: Throwable) { + println("Seed: " + seed) + println("Runs: " + runs) + println("I: " + i) + println("Current seed: " + lastSeed) + debugData.dump() + throw e + } + } + } + + public fun generateText(maxLength: Int, charCount: Int, predefinedChars: Map): String { + val length = RNG.nextInt(maxLength + 1) + val builder = StringBuilder(length) + + for (i in 1..length) { + val rnd = RNG.nextInt(charCount) + val char = predefinedChars.get(rnd) ?: (rnd + 97).toChar() + builder.append(char) + } + return builder.toString() + } + + public fun generateText(maxLength: Int): String { + return generateText(maxLength, DEFAULT_CHAR_COUNT, DEFAULT_CHAR_TABLE) + } + + public fun getCurrentSeed(): Long { + if (gotSeedException) return -1 + try { + val seedField = RNG.javaClass.getDeclaredField("seed") + seedField.setAccessible(true) + val seedFieldValue = seedField.get(RNG) as AtomicLong + return seedFieldValue.get() xor 0x5DEECE66DL + } catch (e: Exception) { + gotSeedException = true + System.err.println("Can't get random seed: " + e.getMessage()) + return -1 + } + } + + private fun stripNewline(text: CharSequence): CharSequence? { + return when (StringUtil.endsWithChar(text, '\n') ) { + true -> CharSequenceSubSequence(text, 0, text.length() - 1) + false -> null + } + } + + public class DebugData() { + private val data: MutableList> = ArrayList>() + + public fun put(key: String, value: Any) { + data.add(Pair(key, value)) + } + + public fun reset() { + data.clear() + } + + public fun dump() { + data.forEach { println(it.first + ": " + it.second) } + } + } + + // + // Helpers + // + + public data open class Trio(val data1: T, val data2: T, val data3: T) { + companion object { + public fun from(f: (ThreeSide) -> V): Trio = Trio(f(ThreeSide.LEFT), f(ThreeSide.BASE), f(ThreeSide.RIGHT)) + } + + public fun map(f: (T) -> V): Trio = Trio(f(data1), f(data2), f(data3)) + + public fun map(f: (T, ThreeSide) -> V): Trio = Trio(f(data1, ThreeSide.LEFT), f(data2, ThreeSide.BASE), f(data3, ThreeSide.RIGHT)) + + public fun forEach(f: (T, ThreeSide) -> Unit): Unit { + f(data1, ThreeSide.LEFT) + f(data2, ThreeSide.BASE) + f(data3, ThreeSide.RIGHT) + } + + public fun invoke(side: ThreeSide): T = side.select(data1, data2, data3) + + override fun toString(): String { + return "($data1, $data2, $data3)" + } + } +} \ No newline at end of file diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/AutoTestCase.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/AutoTestCase.kt deleted file mode 100644 index c668441b1e1b..000000000000 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/AutoTestCase.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.diff.comparison - -import com.intellij.diff.assertTrue -import com.intellij.openapi.util.text.StringUtil -import com.intellij.testFramework.UsefulTestCase -import com.intellij.util.text.CharSequenceSubSequence -import java.util.Random -import java.util.concurrent.atomic.AtomicLong - -public abstract class AutoTestCase : UsefulTestCase() { - protected val RNG: Random = Random() - - private var gotSeedException = false - - protected fun generateText(maxLength: Int, charCount: Int, predefinedChars: Map): String { - val length = RNG.nextInt(maxLength + 1) - val builder = StringBuilder(length) - - for (i in 1..length) { - val rnd = RNG.nextInt(charCount) - val char = predefinedChars.get(rnd) ?: (rnd + 97).toChar(); - builder.append(char) - } - return builder.toString() - } - - protected fun getCurrentSeed(): Long { - if (gotSeedException) return -1 - try { - val seedField = RNG.javaClass.getDeclaredField("seed") - seedField.setAccessible(true) - val seedFieldValue = seedField.get(RNG) as AtomicLong - return seedFieldValue.get() xor 0x5DEECE66DL - } - catch (e: Exception) { - gotSeedException = true - System.err.println("Can't get random seed: " + e.getMessage()) - return -1 - } - } - - public fun textToReadableFormat(text: CharSequence?): String { - if (text == null) return "null" - return "'" + text.toString().replace('\n', '*').replace('\t', '+') + "'" - } - - public fun assertEqualsCharSequences(chunk1: CharSequence, chunk2: CharSequence, ignoreSpaces: Boolean, skipLastNewline: Boolean) { - if (ignoreSpaces) { - assertTrue(StringUtil.equalsIgnoreWhitespaces(chunk1, chunk2)) - } - else { - if (skipLastNewline) { - if (StringUtil.equals(chunk1, chunk2)) return - if (StringUtil.equals(stripNewline(chunk1), chunk2)) return - if (StringUtil.equals(chunk1, stripNewline(chunk2))) return - assertTrue(false) - } - else { - assertTrue(StringUtil.equals(chunk1, chunk2)) - } - } - } - - private fun stripNewline(text: CharSequence): CharSequence? { - return when (StringUtil.endsWithChar(text, '\n') ) { - true -> CharSequenceSubSequence(text, 0, text.length() - 1) - false -> null - } - } - - protected fun Int.until(a: Int): IntRange = this..a - 1 -} diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonMergeUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonMergeUtilTest.kt new file mode 100644 index 000000000000..3ac42d55049f --- /dev/null +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonMergeUtilTest.kt @@ -0,0 +1,185 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.comparison + +public class ComparisonMergeUtilTest : ComparisonMergeUtilTestBase() { + public fun testSimpleCases() { + chars { + ("" - "" - "") + ("" - "" - "").matching() + changes() + test() + } + + chars { + ("xyz" - "xyz" - "xyz") + (" " - " " - " ").matching() + changes() + test() + } + + chars { + ("" - "" - "x") + ("" - "" - "-").matching() + changes(mod(0, 0, 0, 0, 0, 1)) + test() + } + + chars { + ("x" - "yx" - "x") + (" " - "- " - " ").matching() + test() + } + + chars { + ("x" - "xy" - "x") + (" " - " -" - " ").matching() + test() + } + + chars { + ("xyz" - "xYz" - "xyz") + (" - " - " - " - " - ").matching() + test() + } + + chars { + ("xyz" - "XyZ" - "xyz") + ("- -" - "- -" - "- -").matching() + test() + } + } + + public fun testConflictTYpes() { + chars { + ("abcd" - "abcd" - "abXcd") + (" " - " " - " - ").matching() + test() + } + + chars { + ("abXcd" - "abcd" - "abXcd") + (" - " - " " - " - ").matching() + test() + } + + chars { + ("abcd" - "abXcd" - "abXcd") + (" " - " - " - " - ").matching() + test() + } + + chars { + ("abcd" - "abXcd" - "abcd") + (" " - " - " - " ").matching() + test() + } + + chars { + ("abcd" - "abXcd" - "abYcd") + (" " - " - " - " - ").matching() + test() + } + + chars { + ("abXcd" - "abXcd" - "abYcd") + (" - " - " - " - " - ").matching() + test() + } + + chars { + ("abYcd" - "abXcd" - "abYcd") + (" - " - " - " - " - ").matching() + test() + } + + chars { + ("abYcd" - "abXcd" - "abZcd") + (" - " - " - " - " - ").matching() + test() + } + + chars { + ("abXcd" - "abcd" - "abYcd") + (" - " - " " - " - ").matching() + test() + } + } + + public fun testBoundaryConflicts() { + chars { + ("abcd" - "abcd" - "abcdx") + (" " - " " - " -").matching() + test() + } + + chars { + ("abcd" - "abcdx" - "abcdx") + (" " - " -" - " -").matching() + test() + } + + chars { + ("abcd" - "abcdx" - "abcdy") + (" " - " -" - " -").matching() + test() + } + + chars { + ("abcdz" - "abcdx" - "abcdy") + (" -" - " -" - " -").matching() + test() + } + + chars { + ("xabcd" - "abcd" - "abcd") + ("- " - " " - " ").matching() + test() + } + + chars { + ("xabcd" - "yabcd" - "abcd") + ("- " - "- " - " ").matching() + test() + } + + chars { + ("abcd" - "yabcd" - "abcd") + (" " - "- " - " ").matching() + test() + } + } + + public fun testMultipleChanges() { + chars { + ("XXbXcXXeX" - "XyXzXXnXkX" - "XqXXeXrXX") + (" - - - " - " - - - - " - " - - - ").matching() + test() + } + + chars { + ("Ax" - "z" - "zA") + ("--" - "-" - "--").matching() + test() + } + + chars { + ("ayz" - "xyz" - "xyq") + ("- -" - "- -" - "- -").matching() + test() + } + } +} diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonMergeUtilTestBase.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonMergeUtilTestBase.kt new file mode 100644 index 000000000000..479592742c96 --- /dev/null +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonMergeUtilTestBase.kt @@ -0,0 +1,189 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.comparison + +import com.intellij.diff.DiffTestCase +import com.intellij.diff.DiffTestCase.Trio +import com.intellij.diff.assertEquals +import com.intellij.diff.assertOrderedEquals +import com.intellij.diff.assertTrue +import com.intellij.diff.fragments.MergeLineFragment +import com.intellij.diff.util.IntPair +import com.intellij.diff.util.ThreeSide +import com.intellij.openapi.editor.Document +import com.intellij.openapi.editor.impl.DocumentImpl +import com.intellij.openapi.util.Couple +import com.intellij.util.containers.ContainerUtil +import java.util.BitSet + +public abstract class ComparisonMergeUtilTestBase : DiffTestCase() { + private fun doCharTest(texts: Trio, expected: List?, matchings: Trio?) { + val iterable1 = ByChar.compare(texts.data2.getCharsSequence(), texts.data1.getCharsSequence(), INDICATOR) + val iterable2 = ByChar.compare(texts.data2.getCharsSequence(), texts.data3.getCharsSequence(), INDICATOR) + + val fragments = ComparisonMergeUtil.buildFair(iterable1, iterable2, INDICATOR) + val actual = convertDiffFragments(fragments) + + checkConsistency(actual, texts) + if (matchings != null) checkDiffMatching(actual, matchings) + if (expected != null) checkDiffChanges(actual, expected) + } + + private fun checkConsistency(actual: List, texts: Trio) { + var lasts = Trio(-1, -1, -1) + + for (change in actual) { + val starts = change.starts + val ends = change.ends + + var empty = true + var squashed = true + ThreeSide.values().forEach { + val start = starts(it) + val end = ends(it) + val last = lasts(it) + + assertTrue(last <= start) + assertTrue(start <= end) + empty = empty && (start == end) + squashed = squashed && (start == last) + } + + assertTrue(!empty) + assertTrue(!squashed) + + lasts = ends + } + } + + private fun checkDiffChanges(actual: List, expected: List) { + assertOrderedEquals(actual, expected) + } + + private fun checkDiffMatching(changes: List, matchings: Trio) { + val sets = Trio(BitSet(), BitSet(), BitSet()) + + for (change in changes) { + sets.forEach({ set: BitSet, side: ThreeSide -> set.set(change.start(side), change.end(side)) }) + } + + assertEquals(matchings.data1, sets.data1) + assertEquals(matchings.data2, sets.data2) + assertEquals(matchings.data3, sets.data3) + } + + private fun convertDiffFragments(fragments: List): List { + return fragments.map { + Change( + it.getStartLine(ThreeSide.LEFT), it.getEndLine(ThreeSide.LEFT), + it.getStartLine(ThreeSide.BASE), it.getEndLine(ThreeSide.BASE), + it.getStartLine(ThreeSide.RIGHT), it.getEndLine(ThreeSide.RIGHT)) + } + } + + + private enum class TestType { + CHAR + } + + public inner class MergeTestBuilder(val type: TestType) { + private var isExecuted: Boolean = false + + private var texts: Trio? = null + + private var changes: List? = null + private var matching: Trio? = null + + public fun assertExecuted() { + assertTrue(isExecuted) + } + + public fun test() { + isExecuted = true + + assertTrue(changes != null || matching != null) + + when (type) { + TestType.CHAR -> doCharTest(texts!!, changes, matching) + } + } + + + public fun String.minus(v: String): Couple { + return Couple(this, v) + } + + public fun Couple.minus(v: String): Helper { + return Helper(Trio(this.first, this.second, v)) + } + + public inner class Helper(val texts: Trio) { + init { + val builder = this@MergeTestBuilder + if (builder.texts == null) { + builder.texts = texts.map { it -> DocumentImpl(it) } + } + } + + public fun matching() { + matching = texts.map { it -> parseMatching(it) } + } + } + + + public fun changes(vararg expected: Change): Unit { + changes = ContainerUtil.list(*expected) + } + + + public fun mod(line1: Int, line2: Int, line3: Int, count1: Int, count2: Int, count3: Int): Change { + return Change(line1, line1 + count1, line2, line2 + count2, line3, line3 + count3) + } + } + + public fun chars(f: MergeTestBuilder.() -> Unit) { + doTest(TestType.CHAR, f) + } + + private fun doTest(type: TestType, f: MergeTestBuilder.() -> Unit) { + val builder = MergeTestBuilder(type) + builder.f() + builder.assertExecuted() + } + + + public data class Change(start1: Int, end1: Int, start2: Int, end2: Int, start3: Int, end3: Int) + : Trio(IntPair(start1, end1), IntPair(start2, end2), IntPair(start3, end3)) { + + val start1 = start(ThreeSide.LEFT) + val start2 = start(ThreeSide.BASE) + val start3 = start(ThreeSide.RIGHT) + + val end1 = end(ThreeSide.LEFT) + val end2 = end(ThreeSide.BASE) + val end3 = end(ThreeSide.RIGHT) + + val starts = Trio(start1, start2, start3) + val ends = Trio(end1, end2, end3) + + public fun start(side: ThreeSide): Int = this(side).val1 + public fun end(side: ThreeSide): Int = this(side).val2 + + override fun toString(): String { + return "($start1, $end1) - ($start2, $end2) - ($start3, $end3)" + } + } +} diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt index c5230067c55e..c7eb4ee5a282 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt @@ -15,6 +15,7 @@ */ package com.intellij.diff.comparison +import com.intellij.diff.DiffTestCase import com.intellij.diff.assertEquals import com.intellij.diff.assertFalse import com.intellij.diff.assertTrue @@ -23,27 +24,11 @@ import com.intellij.diff.fragments.LineFragment import com.intellij.diff.util.DiffUtil import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.impl.DocumentImpl -import com.intellij.openapi.progress.DumbProgressIndicator import com.intellij.openapi.util.Couple -import com.intellij.openapi.util.Ref import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.text.StringUtil -import com.intellij.util.containers.HashMap - -public class ComparisonUtilAutoTest : AutoTestCase() { - private var oldRegistryValue: Boolean = false - - override fun setUp() { - super.setUp() - oldRegistryValue = REGISTRY.asBoolean() - REGISTRY.setValue(true) - } - - override fun tearDown() { - REGISTRY.setValue(oldRegistryValue) - super.tearDown() - } +public class ComparisonUtilAutoTest : DiffTestCase() { public fun testChar() { doTestChar(System.currentTimeMillis(), 30, 30) } @@ -72,7 +57,7 @@ public class ComparisonUtilAutoTest : AutoTestCase() { val sequence2 = text2.getCharsSequence() val fragments = MANAGER.compareLinesInner(sequence1, sequence2, policy, INDICATOR) - debugData.set(fragments) + debugData.put("Fragments", fragments) checkResultLine(text1, text2, fragments, policy, true) } @@ -86,10 +71,10 @@ public class ComparisonUtilAutoTest : AutoTestCase() { val sequence2 = text2.getCharsSequence() val fragments = MANAGER.compareLinesInner(sequence1, sequence2, policy, INDICATOR) - debugData.set(fragments) + debugData.put("Fragments", fragments) val squashedFragments = MANAGER.squash(fragments) - debugData.set(listOf(fragments, squashedFragments)) + debugData.put("Squashed Fragments", squashedFragments) checkResultLine(text1, text2, squashedFragments, policy, false) } @@ -103,10 +88,10 @@ public class ComparisonUtilAutoTest : AutoTestCase() { val sequence2 = text2.getCharsSequence() val fragments = MANAGER.compareLinesInner(sequence1, sequence2, policy, INDICATOR) - debugData.set(fragments) + debugData.put("Fragments", fragments) val processed = MANAGER.processBlocks(fragments, sequence1, sequence2, policy, true, true) - debugData.set(listOf(fragments, processed)) + debugData.put("Processed Fragments", processed) checkResultLine(text1, text2, processed, policy, false) } @@ -120,7 +105,7 @@ public class ComparisonUtilAutoTest : AutoTestCase() { val sequence2 = text2.getCharsSequence() val fragments = MANAGER.compareChars(sequence1, sequence2, policy, INDICATOR) - debugData.set(fragments) + debugData.put("Fragments", fragments) checkResultChar(sequence1, sequence2, fragments, policy) } @@ -134,54 +119,30 @@ public class ComparisonUtilAutoTest : AutoTestCase() { val sequence2 = text2.getCharsSequence() val fragments = MANAGER.compareWords(sequence1, sequence2, policy, INDICATOR) - debugData.set(fragments) + debugData.put("Fragments", fragments) checkResultWord(sequence1, sequence2, fragments, policy) } } private fun doTest(seed: Long, runs: Int, maxLength: Int, policies: List, - test: (Document, Document, ComparisonPolicy, Ref) -> Unit) { - RNG.setSeed(seed) + test: (Document, Document, ComparisonPolicy, DiffTestCase.DebugData) -> Unit) { + doAutoTest(seed, runs) { debugData -> + debugData.put("MaxLength", maxLength) - var policy: ComparisonPolicy? = null - var lastSeed: Long = -1; - val debugData = Ref() + val text1 = DocumentImpl(generateText(maxLength)) + val text2 = DocumentImpl(generateText(maxLength)) - for (i in 1..runs) { - if (i % 1000 == 0) println(i) - var text1: Document? = null - var text2: Document? = null - try { - lastSeed = getCurrentSeed() + debugData.put("Text1", textToReadableFormat(text1.getCharsSequence())) + debugData.put("Text2", textToReadableFormat(text2.getCharsSequence())) - text1 = generateText(maxLength) - text2 = generateText(maxLength) - - for (comparisonPolicy in policies) { - policy = comparisonPolicy - test(text1, text2, comparisonPolicy, debugData) - } - } - catch (e: Throwable) { - println("Seed: " + seed) - println("Runs: " + runs) - println("MaxLength: " + maxLength) - println("Policy: " + policy!!) - println("I: " + i) - println("Current seed: " + lastSeed) - println("Text1: " + textToReadableFormat(text1?.getCharsSequence())) - println("Text2: " + textToReadableFormat(text2?.getCharsSequence())) - println("Debug Data: " + debugData.get()) - throw e + for (comparisonPolicy in policies) { + debugData.put("Policy", comparisonPolicy) + test(text1, text2, comparisonPolicy, debugData) } } } - private fun generateText(maxLength: Int): Document { - return DocumentImpl(generateText(maxLength, CHAR_COUNT, CHAR_TABLE)) - } - private fun checkResultLine(text1: Document, text2: Document, fragments: List, policy: ComparisonPolicy, allowNonSquashed: Boolean) { checkLineConsistency(text1, text2, fragments, allowNonSquashed) @@ -357,22 +318,4 @@ public class ComparisonUtilAutoTest : AutoTestCase() { private fun Document.subsequence(start: Int, end: Int): CharSequence { return this.getCharsSequence().subSequence(start, end) } - - private fun getLineCount(document: Document): Int { - return Math.max(1, document.getLineCount()) - } - - companion object { - private val REGISTRY = Registry.get("diff.verify.iterable"); - - private val INDICATOR = DumbProgressIndicator.INSTANCE - private val MANAGER = ComparisonManagerImpl() - - private val CHAR_COUNT = 12 - private val CHAR_TABLE: Map = { - val map = HashMap() - listOf('\n', '\n', '\t', ' ', ' ', '.', '<', '!').forEachIndexed { i, c -> map.put(i, c) } - map - }() - } } diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTest.kt index 2ae19f30b3fe..09175a6c89fc 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTest.kt @@ -15,8 +15,8 @@ */ package com.intellij.diff.comparison +import com.intellij.diff.assertEquals import com.intellij.testFramework.UsefulTestCase -import junit.framework.TestCase public class ComparisonUtilTest : UsefulTestCase() { public fun testTrimEquals() { @@ -61,12 +61,12 @@ public class ComparisonUtilTest : UsefulTestCase() { } private fun doTestTrim(expected: Boolean, string1: String, string2: String) { - doTest(expected, string1, string2, ComparisonPolicy.TRIM_WHITESPACES); + doTest(expected, string1, string2, ComparisonPolicy.TRIM_WHITESPACES) } private fun doTest(expected: Boolean, string1: String, string2: String, policy: ComparisonPolicy) { - val result = MANAGER.isEquals(string1, string2, policy); - TestCase.assertEquals("---\n" + string1 + "\n---\n" + string2 + "\n---", expected, result); + val result = MANAGER.isEquals(string1, string2, policy) + assertEquals(expected, result, "---\n" + string1 + "\n---\n" + string2 + "\n---") } companion object { diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt index 18252ee2a188..35a79c7792f1 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt @@ -15,39 +15,16 @@ */ package com.intellij.diff.comparison -import com.intellij.diff.assertEquals -import com.intellij.diff.assertNull -import com.intellij.diff.assertOrderedEquals -import com.intellij.diff.assertTrue +import com.intellij.diff.* import com.intellij.diff.fragments.DiffFragment import com.intellij.diff.fragments.LineFragment import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.impl.DocumentImpl -import com.intellij.openapi.progress.DumbProgressIndicator import com.intellij.openapi.util.Couple -import com.intellij.openapi.util.registry.Registry -import com.intellij.testFramework.UsefulTestCase import com.intellij.util.containers.ContainerUtil import java.util.BitSet -public abstract class ComparisonUtilTestBase : UsefulTestCase() { - private var oldRegistryValue: Boolean = false - - override fun setUp() { - super.setUp() - oldRegistryValue = REGISTRY.asBoolean() - REGISTRY.setValue(true) - } - - override fun tearDown() { - REGISTRY.setValue(oldRegistryValue) - super.tearDown() - } - - // - // Impl - // - +public abstract class ComparisonUtilTestBase : DiffTestCase() { private fun doLineTest(before: Document, after: Document, expected: List?, policy: ComparisonPolicy) { val fragments = MANAGER.compareLines(before.getCharsSequence(), after.getCharsSequence(), policy, INDICATOR) checkConsistency(fragments, before, after) @@ -174,10 +151,6 @@ public abstract class ComparisonUtilTestBase : UsefulTestCase() { } } - private fun getLineCount(document: Document): Int { - return Math.max(1, document.getLineCount()) - } - // // Test Builder // @@ -187,7 +160,7 @@ public abstract class ComparisonUtilTestBase : UsefulTestCase() { } public inner class TestBuilder(private val type: TestType) { - private var isExecuted: Boolean = false; + private var isExecuted: Boolean = false private var before: Document? = null private var after: Document? = null @@ -200,18 +173,18 @@ public abstract class ComparisonUtilTestBase : UsefulTestCase() { private var trimMatching: Couple? = null private var ignoreMatching: Couple? = null - private var shouldSquash: Boolean = false; - private var shouldTrim: Boolean = false; + private var shouldSquash: Boolean = false + private var shouldTrim: Boolean = false private fun changes(policy: ComparisonPolicy): List? = when (policy) { - ComparisonPolicy.IGNORE_WHITESPACES -> ignoreChanges ?: trimChanges ?: defaultChanges; - ComparisonPolicy.TRIM_WHITESPACES -> trimChanges ?: defaultChanges; + ComparisonPolicy.IGNORE_WHITESPACES -> ignoreChanges ?: trimChanges ?: defaultChanges + ComparisonPolicy.TRIM_WHITESPACES -> trimChanges ?: defaultChanges ComparisonPolicy.DEFAULT -> defaultChanges } private fun matchings(policy: ComparisonPolicy): Couple? = when (policy) { - ComparisonPolicy.IGNORE_WHITESPACES -> ignoreMatching ?: trimMatching ?: defaultMatching; - ComparisonPolicy.TRIM_WHITESPACES -> trimMatching ?: defaultMatching; + ComparisonPolicy.IGNORE_WHITESPACES -> ignoreMatching ?: trimMatching ?: defaultMatching + ComparisonPolicy.TRIM_WHITESPACES -> trimMatching ?: defaultMatching ComparisonPolicy.DEFAULT -> defaultMatching } @@ -221,7 +194,7 @@ public abstract class ComparisonUtilTestBase : UsefulTestCase() { private fun run(policy: ComparisonPolicy) { try { - isExecuted = true; + isExecuted = true val change = changes(policy) val matchings = matchings(policy) @@ -239,22 +212,10 @@ public abstract class ComparisonUtilTestBase : UsefulTestCase() { } catch (e: Throwable) { println("Policy: " + policy.name()) - throw e; + throw e } } - private fun parseSource(string: String): String = string.replace('_', '\n') - - private fun parseMatching(before: String, after: String): Couple { - return Couple.of(parseMatching(before), parseMatching(after)) - } - - private fun parseMatching(matching: String): BitSet { - val set = BitSet() - matching.filterNot { it == '.' }.forEachIndexed { i, c -> if (c != ' ') set.set(i) } - return set - } - public fun testAll() { testDefault() @@ -339,8 +300,8 @@ public abstract class ComparisonUtilTestBase : UsefulTestCase() { public fun postprocess(squash: Boolean, trim: Boolean): Unit { - shouldSquash = squash; - shouldTrim = trim; + shouldSquash = squash + shouldTrim = trim } } @@ -372,11 +333,4 @@ public abstract class ComparisonUtilTestBase : UsefulTestCase() { return "(" + start1 + ", " + end1 + ") - (" + start2 + ", " + end2 + ")" } } - - companion object { - private val REGISTRY = Registry.get("diff.verify.iterable"); - - private val INDICATOR = DumbProgressIndicator.INSTANCE - private val MANAGER = ComparisonManagerImpl() - } } diff --git a/platform/diff-impl/tests/com/intellij/diff/merge/MergeAutoTest.kt b/platform/diff-impl/tests/com/intellij/diff/merge/MergeAutoTest.kt new file mode 100644 index 000000000000..ceadaddbb64e --- /dev/null +++ b/platform/diff-impl/tests/com/intellij/diff/merge/MergeAutoTest.kt @@ -0,0 +1,122 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.merge + +import com.intellij.diff.DiffTestCase +import com.intellij.diff.assertEquals +import com.intellij.diff.assertTrue +import com.intellij.diff.merge.MergeTestBase.TestBuilder +import com.intellij.diff.util.Side +import com.intellij.diff.util.ThreeSide +import kotlin.test.fail + +public class MergeAutoTest : MergeTestBase() { + companion object { + private val MODIFICATION_CYCLE_COUNT = 5 + private val MODIFICATION_CYCLE_SIZE = 3 + } + + public fun testUndo() { + doUndoTest(System.currentTimeMillis(), 10, 300) + } + + private fun doUndoTest(seed: Long, runs: Int, maxLength: Int) { + doTest(seed, runs, maxLength) { text1, text2, text3, debugData -> + testN(text1, text2, text3) { + if (changes.size() == 0) { + assertEquals(text1, text2) + assertEquals(text1, text3) + assertEquals(text2, text3) + return@testN + } + + for (m in 1..MODIFICATION_CYCLE_COUNT) { + checkUndo(MODIFICATION_CYCLE_SIZE) { + for (n in 1..MODIFICATION_CYCLE_SIZE) { + val operation = RNG.nextInt(3) + when (operation) { + 0 -> doApply() + 1 -> doIgnore() + 2 -> doReplace() + else -> fail() + } + checkChangesRangeOrdering(changes) + } + } + } + } + } + } + + private fun TestBuilder.doApply(): Unit { + val index = RNG.nextInt(changes.size()) + val change = changes.get(index) + + val side = Side.fromLeft(RNG.nextBoolean()) + val modifier = RNG.nextBoolean() + + command(change) { viewer.replaceChange(change, side, modifier) } + } + + private fun TestBuilder.doIgnore(): Unit { + val index = RNG.nextInt(changes.size()) + val change = changes.get(index) + + val side = Side.fromLeft(RNG.nextBoolean()) + val modifier = RNG.nextBoolean() + + command(change) { viewer.ignoreChange(change, side, modifier) } + } + + private fun TestBuilder.doReplace(): Unit { + val length = document.getTextLength() + + var index1: Int = 0; + var index2: Int = 0; + if (length != 0) { + index1 = RNG.nextInt(length) + index2 = index1 + RNG.nextInt(length - index1) + } + + val newText = generateText(30) + + write { document.replaceString(index1, index2, newText) } + } + + private fun doTest(seed: Long, runs: Int, maxLength: Int, test: (String, String, String, DiffTestCase.DebugData) -> Unit) { + doAutoTest(seed, runs) { debugData -> + debugData.put("MaxLength", maxLength) + + val text1 = generateText(maxLength) + val text2 = generateText(maxLength) + val text3 = generateText(maxLength) + + debugData.put("Text1", textToReadableFormat(text1)) + debugData.put("Text2", textToReadableFormat(text2)) + debugData.put("Text3", textToReadableFormat(text3)) + + test(text1, text2, text3, debugData) + } + } + + private fun checkChangesRangeOrdering(changes: List) { + for (i in 1..changes.size() - 1) { + val lastEnd = changes.get(i - 1).getEndLine(ThreeSide.BASE) + val start = changes.get(i).getStartLine(ThreeSide.BASE) + assertTrue(lastEnd <= start, "lastEnd: $lastEnd, start: $start") + } + } +} diff --git a/platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt b/platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt new file mode 100644 index 000000000000..264fa392e3e9 --- /dev/null +++ b/platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt @@ -0,0 +1,614 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.merge + +import com.intellij.diff.merge.MergeTestBase.SidesState.BOTH +import com.intellij.diff.merge.MergeTestBase.SidesState.LEFT +import com.intellij.diff.merge.MergeTestBase.SidesState.NONE +import com.intellij.diff.merge.MergeTestBase.SidesState.RIGHT +import com.intellij.diff.util.Side +import com.intellij.diff.util.TextDiffType.CONFLICT +import com.intellij.diff.util.TextDiffType.DELETED +import com.intellij.diff.util.TextDiffType.INSERTED +import com.intellij.diff.util.TextDiffType.MODIFIED +import com.intellij.openapi.diff.DiffBundle + +public class MergeTest : MergeTestBase() { + public fun testChangeTypes() { + test ("", "", "", 0) { + } + + test ("x", "x", "x", 0) { + } + + test ("x_y_z a x", "x_y_z a x", "x_y_z a x", 0) { + } + + test1 ("x", "x", "y") { + 0.assertType(MODIFIED, RIGHT) + 0.assertContent("x", 0, 1) + 0.assertResolved(NONE) + } + + test1 ("x", "y", "x") { + 0.assertType(MODIFIED, BOTH) + 0.assertContent("y", 0, 1) + 0.assertResolved(NONE) + } + + test2 ("x_Y", "Y", "Y_z") { + 0.assertType(INSERTED, LEFT) + 0.assertContent("", 0, 0) + 0.assertResolved(NONE) + + 1.assertType(INSERTED, RIGHT) + 1.assertContent("", 1, 1) + 1.assertResolved(NONE) + } + + test2 ("Y_z", "x_Y_z", "x_Y") { + 0.assertType(DELETED, LEFT) + 0.assertContent("x", 0, 1) + 0.assertResolved(NONE) + + 1.assertType(DELETED, RIGHT) + 1.assertContent("z", 2, 3) + 1.assertResolved(NONE) + } + + test1 ("X_Z", "X_y_Z", "X_Z") { + 0.assertType(DELETED, BOTH) + 0.assertContent("y", 1, 2) + 0.assertResolved(NONE) + } + + test1 ("X_y_Z", "X_Z", "X_y_Z") { + 0.assertType(INSERTED, BOTH) + 0.assertContent("", 1, 1) + 0.assertResolved(NONE) + } + + test1("x", "y", "z") { + 0.assertType(CONFLICT, BOTH) + 0.assertContent("y", 0, 1) + 0.assertResolved(NONE) + } + + test1 ("z_Y", "x_Y", "Y") { + 0.assertType(CONFLICT, BOTH) + 0.assertContent("x", 0, 1) + 0.assertResolved(NONE) + } + + test1 ("z_Y", "x_Y", "k_x_Y") { + 0.assertType(CONFLICT, BOTH) + 0.assertContent("x", 0, 1) + 0.assertResolved(NONE) + } + + test1 ("x_Y", "Y", "z_Y") { + 0.assertType(CONFLICT, BOTH) + 0.assertContent("", 0, 0) + 0.assertResolved(NONE) + } + + test1 ("x_Y", "Y", "z_x_Y") { + 0.assertType(CONFLICT, BOTH) + 0.assertContent("", 0, 0) + 0.assertResolved(NONE) + } + + test1 ("x_Y", "x_z_Y", "z_Y") { + 0.assertType(CONFLICT, BOTH) + 0.assertContent("x_z", 0, 2) + 0.assertResolved(NONE) + } + } + + public fun testLastLine() { + test1 ("x", "x_", "x") { + 0.assertType(DELETED, BOTH) + 0.assertContent("", 1, 2) + 0.assertResolved(NONE) + } + + test1 ("x_", "x_", "x") { + 0.assertType(DELETED, RIGHT) + 0.assertContent("", 1, 2) + 0.assertResolved(NONE) + } + + test1 ("x_", "x_", "x_y") { + 0.assertType(MODIFIED,RIGHT) + 0.assertContent("", 1, 2) + 0.assertResolved(NONE) + } + + test1 ("x", "x_", "x_y") { + 0.assertType(CONFLICT,BOTH) + 0.assertContent("", 1, 2) + 0.assertResolved(NONE) + } + + test1 ("x_", "x", "x_y") { + 0.assertType(CONFLICT,BOTH) + 0.assertContent("", 1, 1) + 0.assertResolved(NONE) + } + } + + public fun testModifications() { + test1 ("x", "x", "y") { + 0.apply(Side.RIGHT) + 0.assertResolved(BOTH) + 0.assertContent("y") + + assertContent("y") + } + + test1 ("x", "x", "y") { + 0.apply(Side.LEFT) + 0.assertResolved(BOTH) + 0.assertContent("x") + + assertContent("x") + } + + test1 ("X_x_Z", "X_x_Z", "X_y_Z") { + 0.apply(Side.RIGHT) + 0.assertResolved(BOTH) + 0.assertContent("y") + + assertContent("X_y_Z") + } + + test1 ("z", "x", "y") { + 0.apply(Side.RIGHT) + 0.assertResolved(RIGHT) + 0.assertContent("y") + + 0.apply(Side.LEFT) + 0.assertResolved(BOTH) + 0.assertContent("y_z") + + assertContent("y_z") + } + + test1 ("z", "x", "y") { + 0.apply(Side.LEFT) + 0.assertResolved(LEFT) + 0.assertContent("z") + + 0.apply(Side.RIGHT) + 0.assertResolved(BOTH) + 0.assertContent("z_y") + + assertContent("z_y") + } + + test1 ("X", "X", "X_y") { + 0.apply(Side.RIGHT) + 0.assertResolved(BOTH) + 0.assertContent("y") + + assertContent("X_y") + } + + test1 ("X", "X", "X_y") { + 0.apply(Side.LEFT) + 0.assertResolved(BOTH) + 0.assertContent("") + + assertContent("X") + } + + test1 ("X_z", "X", "X_y") { + 0.apply(Side.RIGHT) + 0.assertResolved(RIGHT) + 0.assertContent("y") + + 0.apply(Side.LEFT) + 0.assertResolved(BOTH) + 0.assertContent("y_z") + + assertContent("X_y_z") + } + + test1 ("X_z", "X_y", "X") { + 0.apply(Side.LEFT) + 0.assertResolved(BOTH) + 0.assertContent("z") + + assertContent("X_z") + } + + test1 ("X_z", "X_y", "X") { + 0.apply(Side.RIGHT) + 0.assertResolved(RIGHT) + 0.assertContent("") + + 0.apply(Side.LEFT) + 0.assertResolved(BOTH) + 0.assertContent("z") + + assertContent("X_z") + } + } + + public fun testModificationsIgnore() { + test1 ("x", "x", "y") { + 0.ignore(Side.RIGHT) + 0.assertResolved(BOTH) + 0.assertContent("x") + + assertContent("x") + } + + test1 ("x", "x", "y") { + 0.ignore(Side.LEFT) + 0.assertResolved(BOTH) + 0.assertContent("x") + + assertContent("x") + } + + test1 ("z", "x", "y") { + 0.ignore(Side.RIGHT) + 0.assertResolved(RIGHT) + 0.assertContent("x") + + 0.ignore(Side.LEFT) + 0.assertResolved(BOTH) + 0.assertContent("x") + + assertContent("x") + } + + test1 ("z", "x", "y") { + 0.apply(Side.RIGHT) + 0.assertResolved(RIGHT) + 0.assertContent("y") + + 0.ignore(Side.LEFT) + 0.assertResolved(BOTH) + 0.assertContent("y") + + assertContent("y") + } + + test1 ("z", "x", "y") { + 0.ignore(Side.RIGHT) + 0.assertResolved(RIGHT) + 0.assertContent("x") + + 0.apply(Side.LEFT) + 0.assertResolved(BOTH) + 0.assertContent("z") + + assertContent("z") + } + + test1 ("X_z", "X_y", "X") { + 0.ignore(Side.RIGHT) + 0.assertResolved(RIGHT) + 0.assertContent("y") + + 0.apply(Side.LEFT) + 0.assertResolved(BOTH) + 0.assertContent("z") + + assertContent("X_z") + } + + test1 ("X_z", "X_y", "X") { + 0.ignore(Side.LEFT) + 0.assertResolved(LEFT) + 0.assertContent("y") + + 0.apply(Side.RIGHT) + 0.assertResolved(BOTH) + 0.assertContent("") + + assertContent("X") + } + } + + public fun testModificationsModifiers() { + test1 ("x", "x", "y") { + 0.apply(Side.RIGHT, true) + 0.assertResolved(BOTH) + 0.assertContent("y") + + assertContent("y") + } + + test1 ("x", "x", "y") { + 0.ignore(Side.RIGHT, true) + 0.assertResolved(BOTH) + 0.assertContent("x") + + assertContent("x") + } + + test1 ("z", "x", "y") { + 0.apply(Side.RIGHT, true) + 0.assertResolved(BOTH) + 0.assertContent("y") + + assertContent("y") + } + + test1 ("z", "x", "y") { + 0.ignore(Side.RIGHT, true) + 0.assertResolved(BOTH) + 0.assertContent("x") + + assertContent("x") + } + } + + public fun testUndoSimple() { + test1 ("x", "y", "z") { + checkUndo(1) { + 0.apply(Side.RIGHT) + } + } + + test1 ("x", "y", "z") { + checkUndo(1) { + 0.apply(Side.RIGHT, true) + } + } + + test1 ("x", "y", "z") { + checkUndo(1) { + 0.ignore(Side.RIGHT) + } + } + + test1 ("x", "y", "z") { + checkUndo(1) { + 0.ignore(Side.RIGHT, true) + } + } + + test1 ("x", "y", "z") { + checkUndo(2) { + 0.apply(Side.RIGHT) + 0.apply(Side.LEFT) + } + } + + test1 ("x", "y", "z") { + checkUndo(2) { + 0.apply(Side.RIGHT) + 0.apply(Side.RIGHT) + } + } + + test1 ("X", "X_y", "X") { + checkUndo(1) { + 0.apply(Side.RIGHT) + } + } + + test1 ("X", "X", "X_y") { + checkUndo(1) { + 0.apply(Side.LEFT) + } + } + + test1 ("X", "X", "X_y") { + checkUndo(1) { + 0.apply(Side.RIGHT) + } + } + + test1 ("X_z", "X_y", "X") { + checkUndo(1) { + 0.apply(Side.LEFT) + } + } + + test1 ("X_z", "X_y", "X") { + checkUndo(1) { + 0.apply(Side.RIGHT) + } + } + + test2 ("y_X", "X", "X_z") { + checkUndo(1) { + 0.apply(Side.LEFT) + } + checkUndo(1) { + 1.apply(Side.RIGHT) + } + + assertContent("y_X_z") + } + } + + public fun testRangeModification() { + test1("X_x_y_z_Y", "X_a_b_c_Y", "X_x_y_z_Y") { + 0.assertContent("a_b_c", 1, 4) + checkUndo(1) { replaceText(!2 - 0, !2 - 1, "D") } + 0.assertContent("a_D_c", 1, 4) + } + + test1("X_x_y_z_Y", "X_a_b_c_Y", "X_x_y_z_Y") { + 0.assertContent("a_b_c", 1, 4) + checkUndo(1) { replaceText(!2 - -1, !2 - 2, "D") } + 0.assertContent("aDc", 1, 2) + } + + test1("X_x_y_z_Y", "X_a_b_c_Y", "X_x_y_z_Y") { + 0.assertContent("a_b_c", 1, 4) + checkUndo(1) { replaceText("c", "u_x") } + 0.assertContent("a_b_u_x", 1, 5) + } + + test1("X_x_y_z_Y", "X_a_b_c_Y", "X_x_y_z_Y") { + 0.assertContent("a_b_c", 1, 4) + checkUndo(1) { deleteText("c_") } + 0.assertContent("a_b", 1, 3) + } + + test1("X_x_y_z_Y", "X_a_b_c_Y", "X_x_y_z_Y") { + 0.assertContent("a_b_c", 1, 4) + checkUndo(1) { insertTextBefore("b", "q") } + 0.assertContent("a_qb_c", 1, 4) + } + + test1("X_x_y_z_Y", "X_a_b_c_Y", "X_x_y_z_Y") { + 0.assertContent("a_b_c", 1, 4) + checkUndo(1) { insertTextAfter("b", "q") } + 0.assertContent("a_bq_c", 1, 4) + } + + test1("X_x_y_z_Y", "X_a_b_c_Y", "X_x_y_z_Y") { + 0.assertContent("a_b_c", 1, 4) + checkUndo(1) { insertText(0, "a_b_c_") } + 0.assertContent("a_b_c", 4, 7) + } + + test1("A_X_x_y_z_Y", "A_X_a_b_c_Y", "A_X_x_y_z_Y") { + 0.assertContent("a_b_c", 2, 5) + checkUndo(1) { replaceText("X_a_b", "q_w_e") } + 0.assertContent("c") + } + + test1("X_x_y_z_Y_A", "X_a_b_c_Y_A", "X_x_y_z_Y_A") { + 0.assertContent("a_b_c") + checkUndo(1) { replaceText("c_Y", "q") } + 0.assertContent("a_b") + } + + test1("A_X_x_Y_A", "A_X_b_Y_A", "A_X_x_z_Y_A") { + 0.assertContent("b") + checkUndo(1) { replaceText("X_b_Y", "q") } + + 0.assertContent("", 2, 2) + assertContent("A_q_A") + } + } + + public fun testNonConflictsActions() { + val applyAllTitle = DiffBundle.message("merge.dialog.apply.all.non.conflicting.changes.action.name") + val applyLeftTitle = DiffBundle.message("merge.dialog.apply.left.non.conflicting.changes.action.name") + val applyRightTitle = DiffBundle.message("merge.dialog.apply.right.non.conflicting.changes.action.name") + + val text1 = + "1 ======\n" + + "insert left\n" + + "2 ======\n" + + "remove right\n" + + "3 ======\n" + + "new both\n" + + "4 ======\n" + + "modify both\n" + + "5 ======\n" + + "modify\n" + + "6 ======\n" + + "7 ======" + val text2 = + "1 ======\n" + + "2 ======\n" + + "remove right\n" + + "3 ======\n" + + "4 ======\n" + + "modify\n" + + "5 ======\n" + + "modify\n" + + "6 ======\n" + + "delete modify\n" + + "7 ======" + val text3 = + "1 ======\n" + + "2 ======\n" + + "3 ======\n" + + "new both\n" + + "4 ======\n" + + "modify both\n" + + "5 ======\n" + + "modify right\n" + + "6 ======\n" + + "modify\n" + + "7 ======" + + testN(text1, text2, text3) { + checkUndo(1) { + runActionByTitle(applyAllTitle) + } + + assertChangesCount(1) + assertContent( + "1 ======\n" + + "insert left\n" + + "2 ======\n" + + "3 ======\n" + + "new both\n" + + "4 ======\n" + + "modify both\n" + + "5 ======\n" + + "modify right\n" + + "6 ======\n" + + "delete modify\n" + + "7 ======") + } + + testN(text1, text2, text3) { + checkUndo(1) { + runActionByTitle(applyLeftTitle) + } + + assertChangesCount(3) + assertContent( + "1 ======\n" + + "insert left\n" + + "2 ======\n" + + "remove right\n" + + "3 ======\n" + + "new both\n" + + "4 ======\n" + + "modify both\n" + + "5 ======\n" + + "modify\n" + + "6 ======\n" + + "delete modify\n" + + "7 ======") + } + + testN(text1, text2, text3) { + checkUndo(1) { + runActionByTitle(applyRightTitle) + } + + assertChangesCount(2) + assertContent( + "1 ======\n" + + "2 ======\n" + + "3 ======\n" + + "new both\n" + + "4 ======\n" + + "modify both\n" + + "5 ======\n" + + "modify right\n" + + "6 ======\n" + + "delete modify\n" + + "7 ======") + } + } +} diff --git a/platform/diff-impl/tests/com/intellij/diff/merge/MergeTestBase.kt b/platform/diff-impl/tests/com/intellij/diff/merge/MergeTestBase.kt new file mode 100644 index 000000000000..e516f124cf88 --- /dev/null +++ b/platform/diff-impl/tests/com/intellij/diff/merge/MergeTestBase.kt @@ -0,0 +1,430 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.merge + +import com.intellij.diff.DiffContentFactoryImpl +import com.intellij.diff.DiffTestCase +import com.intellij.diff.DiffTestCase.Trio +import com.intellij.diff.assertEquals +import com.intellij.diff.assertTrue +import com.intellij.diff.contents.DocumentContent +import com.intellij.diff.merge.MergeTestBase.SidesState.BOTH +import com.intellij.diff.merge.MergeTestBase.SidesState.LEFT +import com.intellij.diff.merge.MergeTestBase.SidesState.NONE +import com.intellij.diff.merge.MergeTestBase.SidesState.RIGHT +import com.intellij.diff.merge.TextMergeTool.TextMergeViewer +import com.intellij.diff.merge.TextMergeTool.TextMergeViewer.MyThreesideViewer +import com.intellij.diff.util.DiffUtil +import com.intellij.diff.util.Side +import com.intellij.diff.util.TextDiffType +import com.intellij.diff.util.ThreeSide +import com.intellij.openapi.actionSystem.ActionPlaces +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.command.CommandProcessor +import com.intellij.openapi.command.undo.UndoManager +import com.intellij.openapi.editor.Document +import com.intellij.openapi.editor.ex.EditorEx +import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Couple +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.text.StringUtil +import com.intellij.testFramework.fixtures.IdeaProjectTestFixture +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory +import com.intellij.util.ui.UIUtil + +public abstract class MergeTestBase : DiffTestCase() { + private var projectFixture: IdeaProjectTestFixture? = null + private var project: Project? = null + + override fun setUp() { + super.setUp() + projectFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getTestName(true)).getFixture() + projectFixture!!.setUp() + project = projectFixture!!.getProject() + } + + override fun tearDown() { + projectFixture?.tearDown() + project = null + super.tearDown() + } + + public fun test1(left: String, base: String, right: String, f: TestBuilder.() -> Unit) { + test(left, base, right, 1, f) + } + + public fun test2(left: String, base: String, right: String, f: TestBuilder.() -> Unit) { + test(left, base, right, 2, f) + } + + public fun testN(left: String, base: String, right: String, f: TestBuilder.() -> Unit) { + test(left, base, right, -1, f) + } + + public fun test(left: String, base: String, right: String, changesCount: Int, f: TestBuilder.() -> Unit) { + val contentFactory = DiffContentFactoryImpl() + val leftContent: DocumentContent = contentFactory.create(parseSource(left)) + val baseContent: DocumentContent = contentFactory.create(parseSource(base)) + val rightContent: DocumentContent = contentFactory.create(parseSource(right)) + val outputContent: DocumentContent = contentFactory.create(parseSource("")) + outputContent.getDocument().setReadOnly(false) + + val context = MockMergeContext(project) + val request = MockMergeRequest(leftContent, baseContent, rightContent, outputContent) + + val viewer = TextMergeTool.INSTANCE.createComponent(context, request) as TextMergeViewer + try { + val toolbar = viewer.init() + UIUtil.dispatchAllInvocationEvents() + + val builder = TestBuilder(viewer, toolbar.toolbarActions ?: emptyList()) + builder.assertChangesCount(changesCount) + builder.f() + } finally { + Disposer.dispose(viewer) + } + } + + public inner class TestBuilder(public val mergeViewer: TextMergeViewer, private val actions: List) { + public val viewer: MyThreesideViewer = mergeViewer.getViewer() + public val changes: List = viewer.getAllChanges() + public val editor: EditorEx = viewer.getEditor(ThreeSide.BASE) + public val document: Document = editor.getDocument() + + private val textEditor = TextEditorProvider.getInstance().getTextEditor(editor); + private val undoManager = UndoManager.getInstance(project!!) + + public fun change(num: Int): TextMergeChange { + if (changes.size() < num) throw Exception("changes: ${changes.size()}, index: $num") + return changes.get(num) + } + + public fun activeChanges(): List = viewer.getChanges() + + // + // Actions + // + + public fun runActionByTitle(name: String): Boolean { + val action = actions.filter { name.equals(it.getTemplatePresentation().getText()) } + assertTrue(action.size() == 1, action.toString()) + return runAction(action.get(0)) + } + + private fun runAction(action: AnAction): Boolean { + val actionEvent = AnActionEvent.createFromAnAction(action, null, ActionPlaces.MAIN_MENU, editor.getDataContext()) + action.update(actionEvent) + val success = actionEvent.getPresentation().isEnabledAndVisible() + if (success) action.actionPerformed(actionEvent) + return success + } + + // + // Modification + // + + public fun command(affected: TextMergeChange, f: () -> Unit): Unit { + command(listOf(affected), f) + } + + public fun command(affected: List? = null, f: () -> Unit): Unit { + viewer.executeMergeCommand(null, affected, f) + UIUtil.dispatchAllInvocationEvents() + } + + public fun write(f: () -> Unit): Unit { + ApplicationManager.getApplication().runWriteAction({ CommandProcessor.getInstance().executeCommand(project, f, null, null) }) + UIUtil.dispatchAllInvocationEvents() + } + + public fun Int.ignore(side: Side, modifier: Boolean = false) { + val change = change(this) + command(change) { viewer.ignoreChange(change, side, modifier) } + } + + public fun Int.apply(side: Side, modifier: Boolean = false) { + val change = change(this) + command(change) { viewer.replaceChange(change, side, modifier) } + } + + // + // Text modification + // + + public fun insertText(offset: Int, newContent: CharSequence) { + replaceText(offset, offset, newContent) + } + + public fun deleteText(startOffset: Int, endOffset: Int) { + replaceText(startOffset, endOffset, "") + } + + public fun replaceText(startOffset: Int, endOffset: Int, newContent: CharSequence) { + write { document.replaceString(startOffset, endOffset, parseSource(newContent)) } + } + + public fun insertText(offset: LineCol, newContent: CharSequence) { + replaceText(offset.toOffset(), offset.toOffset(), newContent) + } + + public fun deleteText(startOffset: LineCol, endOffset: LineCol) { + replaceText(startOffset.toOffset(), endOffset.toOffset(), "") + } + + public fun replaceText(startOffset: LineCol, endOffset: LineCol, newContent: CharSequence) { + write { replaceText(startOffset.toOffset(), endOffset.toOffset(), newContent) } + } + + public fun replaceText(oldContent: CharSequence, newContent: CharSequence) { + write { + val range = findRange(parseSource(oldContent)) + replaceText(range.first, range.second, newContent) + } + } + + public fun deleteText(oldContent: CharSequence) { + write { + val range = findRange(parseSource(oldContent)) + replaceText(range.first, range.second, "") + } + } + + public fun insertTextBefore(oldContent: CharSequence, newContent: CharSequence) { + write { insertText(findRange(parseSource(oldContent)).first, newContent) } + } + + public fun insertTextAfter(oldContent: CharSequence, newContent: CharSequence) { + write { insertText(findRange(parseSource(oldContent)).second, newContent) } + } + + private fun findRange(oldContent: CharSequence): Couple { + val text = document.getImmutableCharSequence() + val index1 = StringUtil.indexOf(text, oldContent) + assertTrue(index1 >= 0, "content - '\n$oldContent\n'\ntext - '\n$text'") + val index2 = StringUtil.indexOf(text, oldContent, index1 + 1) + assertTrue(index2 == -1, "content - '\n$oldContent\n'\ntext - '\n$text'") + return Couple(index1, index1 + oldContent.length()) + } + + // + // Undo + // + + public fun undo(count: Int = 1) { + if (count == -1) { + while (undoManager.isUndoAvailable(textEditor)) { + undoManager.undo(textEditor) + } + } + else { + for (i in 1..count) { + assertTrue(undoManager.isUndoAvailable(textEditor)) + undoManager.undo(textEditor) + } + } + } + + public fun redo(count: Int = 1) { + if (count == -1) { + while (undoManager.isRedoAvailable(textEditor)) { + undoManager.redo(textEditor) + } + } + else { + for (i in 1..count) { + assertTrue(undoManager.isRedoAvailable(textEditor)) + undoManager.redo(textEditor) + } + } + } + + public fun checkUndo(count: Int = -1, f: TestBuilder.() -> Unit) { + val initialState = ViewerState.recordState(viewer) + f() + UIUtil.dispatchAllInvocationEvents() + + val afterState = ViewerState.recordState(viewer) + undo(count) + UIUtil.dispatchAllInvocationEvents() + + val undoState = ViewerState.recordState(viewer) + redo(count) + UIUtil.dispatchAllInvocationEvents() + + val redoState = ViewerState.recordState(viewer) + + assertEquals(initialState, undoState) + assertEquals(afterState, redoState) + } + + // + // Checks + // + + public fun assertChangesCount(expected: Int) { + if (expected == -1) return + val actual = activeChanges().size() + assertEquals(expected, actual) + } + + public fun Int.assertType(type: TextDiffType, changeType: SidesState) { + assertType(type) + assertType(changeType) + } + + public fun Int.assertType(type: TextDiffType) { + val change = change(this) + assertEquals(change.getDiffType(), type) + } + + public fun Int.assertType(changeType: SidesState) { + assertTrue(changeType != NONE) + val change = change(this) + val actual = change.getType() + val isLeftChange = changeType != RIGHT + val isRightChange = changeType != LEFT + assertEquals(Pair(isLeftChange, isRightChange), Pair(actual.isLeftChange(), actual.isRightChange())) + } + + public fun Int.assertResolved(type: SidesState) { + val change = change(this) + val isLeftResolved = type == LEFT || type == BOTH + val isRightResolved = type == RIGHT || type == BOTH + assertEquals(Pair(isLeftResolved, isRightResolved), Pair(change.isResolved(Side.LEFT), change.isResolved(Side.RIGHT))) + } + + public fun Int.assertRange(start: Int, end: Int) { + val change = change(this) + assertEquals(Pair(start, end), Pair(change.getStartLine(ThreeSide.BASE), change.getEndLine(ThreeSide.BASE))) + } + + public fun Int.assertContent(expected: String, start: Int, end: Int) { + assertContent(expected) + assertRange(start, end) + } + + public fun Int.assertContent(expected: String) { + val change = change(this) + val document = editor.getDocument() + val actual = DiffUtil.getLinesContent(document, change.getStartLine(ThreeSide.BASE), change.getEndLine(ThreeSide.BASE)) + assertEquals(parseSource(expected), actual) + } + + public fun assertContent(expected: String) { + val actual = viewer.getEditor(ThreeSide.BASE).getDocument().getImmutableCharSequence() + assertEquals(parseSource(expected), actual) + } + + // + // Helpers + // + + public fun Int.not(): LineColHelper = LineColHelper(this) + public fun LineColHelper.minus(col: Int): LineCol = LineCol(this.line, col) + + public inner class LineColHelper(val line: Int) { + } + + public inner data class LineCol(val line: Int, val col: Int) { + public fun toOffset(): Int = editor.getDocument().getLineStartOffset(line) + col + } + } + + private class MockMergeContext(private val myProject: Project?) : MergeContext() { + override fun getProject(): Project? = myProject + + override fun isFocused(): Boolean = false + + override fun requestFocus() { + } + + override fun finishMerge(result: MergeResult) { + } + } + + private class MockMergeRequest(val left: DocumentContent, + val base: DocumentContent, + val right: DocumentContent, + val output: DocumentContent) : TextMergeRequest() { + override fun getTitle(): String? = null + + override fun applyResult(result: MergeResult) { + } + + override fun getContents(): List = listOf(left, base, right) + + override fun getOutputContent(): DocumentContent = output + + override fun getContentTitles(): List = listOf(null, null, null) + } + + public enum class SidesState { + LEFT, RIGHT, BOTH, NONE + } + + private data class ViewerState private constructor(private val content: CharSequence, + private val changes: List) { + companion object { + public fun recordState(viewer: MyThreesideViewer): ViewerState { + val content = viewer.getEditor(ThreeSide.BASE).getDocument().getImmutableCharSequence() + val changes = viewer.getAllChanges().map { recordChangeState(viewer, it) } + return ViewerState(content, changes) + } + + private fun recordChangeState(viewer: MyThreesideViewer, change: TextMergeChange): ChangeState { + val document = viewer.getEditor(ThreeSide.BASE).getDocument(); + val content = DiffUtil.getLinesContent(document, change.getStartLine(ThreeSide.BASE), change.getEndLine(ThreeSide.BASE)) + + val resolved = if (change.isResolved()) BOTH else if (change.isResolved(Side.LEFT)) LEFT else if (change.isResolved(Side.RIGHT)) RIGHT else NONE + + val starts = Trio.from { change.getStartLine(it) } + val ends = Trio.from { change.getStartLine(it) } + + return ChangeState(content, starts, ends, resolved) + } + } + + override fun equals(other: Any?): Boolean { + if (this identityEquals other) return true + if (other !is ViewerState) return false + + if (!StringUtil.equals(content, other.content)) return false + if (!changes.equals(other.changes)) return false + return true + } + + override fun hashCode(): Int = StringUtil.hashCode(content) + + private data class ChangeState(private val content: CharSequence, + private val starts: Trio, + private val ends: Trio, + private val resolved: SidesState) { + override fun equals(other: Any?): Boolean { + if (this identityEquals other) return true + if (other !is ChangeState) return false + + if (!StringUtil.equals(content, other.content)) return false + if (!starts.equals(other.starts)) return false + if (!ends.equals(other.ends)) return false + if (!resolved.equals(other.resolved)) return false + return true + } + } + } +} diff --git a/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/LineNumberConvertorCorrectorTest.kt b/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/LineNumberConvertorCorrectorTest.kt index 9e84b4f9846b..210f0af33772 100644 --- a/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/LineNumberConvertorCorrectorTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/LineNumberConvertorCorrectorTest.kt @@ -179,12 +179,12 @@ public class LineNumberConvertorCorrectorTest : UsefulTestCase() { public fun printMatchings() { for (i in 0..length * 2 - 1) { val value = convertor.convert1(i) - if (value != -1) println("L: " + i + " - " + value) + if (value != -1) println("L: $i - $value") } for (i in 0..length * 2 - 1) { val value = convertor.convert2(i) - if (value != -1) println("R: " + i + " - " + value) + if (value != -1) println("R: $i - $value") } } } diff --git a/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt b/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt index 97dd02f20421..6c021ef6e2b8 100644 --- a/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt @@ -15,10 +15,9 @@ */ package com.intellij.diff.tools.fragmented +import com.intellij.diff.DiffTestCase import com.intellij.diff.assertEquals import com.intellij.diff.assertTrue -import com.intellij.diff.comparison.AutoTestCase -import com.intellij.diff.comparison.ComparisonManagerImpl import com.intellij.diff.comparison.ComparisonPolicy import com.intellij.diff.util.LineRange import com.intellij.diff.util.Side @@ -28,48 +27,28 @@ import com.intellij.openapi.progress.DumbProgressIndicator import com.intellij.openapi.util.text.StringUtil import com.intellij.util.containers.HashMap -public class UnifiedFragmentBuilderAutoTest : AutoTestCase() { +public class UnifiedFragmentBuilderAutoTest : DiffTestCase() { public fun test() { doTest(System.currentTimeMillis(), 30, 30) } public fun doTest(seed: Long, runs: Int, maxLength: Int) { - RNG.setSeed(seed) + doAutoTest(seed, runs) { debugData -> + debugData.put("MaxLength", maxLength) - var policy: ComparisonPolicy? = null - var masterSide: Side? = null - var lastSeed: Long = -1; + var text1 = DocumentImpl(generateText(maxLength)) + var text2 = DocumentImpl(generateText(maxLength)) - for (i in 1..runs) { - if (i % 1000 == 0) println(i) - var text1: Document? = null - var text2: Document? = null - try { - lastSeed = getCurrentSeed() + debugData.put("Text1", textToReadableFormat(text1.getCharsSequence())) + debugData.put("Text2", textToReadableFormat(text2.getCharsSequence())) - text1 = generateText(maxLength) - text2 = generateText(maxLength) - - for (side in Side.values()) { - for (comparisonPolicy in ComparisonPolicy.values()) { - policy = comparisonPolicy - masterSide = side - doTest(text1, text2, policy, masterSide) - } + for (side in Side.values()) { + for (comparisonPolicy in ComparisonPolicy.values()) { + debugData.put("Policy", comparisonPolicy) + debugData.put("Current side", side) + doTest(text1, text2, comparisonPolicy, side) } } - catch (e: Throwable) { - println("Seed: " + seed) - println("Runs: " + runs) - println("MaxLength: " + maxLength) - println("Policy: " + policy!!) - println("Current side: " + masterSide!!) - println("I: " + i) - println("Current seed: " + lastSeed) - println("Text1: " + textToReadableFormat(text1?.getCharsSequence())) - println("Text2: " + textToReadableFormat(text2?.getCharsSequence())) - throw e - } } } @@ -158,8 +137,7 @@ public class UnifiedFragmentBuilderAutoTest : AutoTestCase() { if (c1 == c2) { index1++ index2++ - } - else { + } else { index1++ } } @@ -173,19 +151,4 @@ public class UnifiedFragmentBuilderAutoTest : AutoTestCase() { } return false } - - private fun generateText(maxLength: Int): Document { - return DocumentImpl(generateText(maxLength, CHAR_COUNT, CHAR_TABLE)) - } - - companion object { - private val MANAGER = ComparisonManagerImpl() - - private val CHAR_COUNT = 12 - private val CHAR_TABLE: Map = { - val map = HashMap() - listOf('\n', '\n', '\t', ' ', ' ', '.', '<', '!').forEachIndexed { i, c -> map.put(i, c) } - map - }() - } }