mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
diff: tests for UnifiedFragmentBuilder
This commit is contained in:
committed by
Aleksey Pivovarov
parent
d363904bf9
commit
9844a2ba44
+342
-60
@@ -17,83 +17,365 @@ package com.intellij.diff.tools.fragmented
|
||||
|
||||
import com.intellij.diff.DiffTestCase
|
||||
import com.intellij.diff.comparison.ComparisonPolicy
|
||||
import com.intellij.diff.comparison.iterables.DiffIterable
|
||||
import com.intellij.diff.comparison.iterables.DiffIterableUtil
|
||||
import com.intellij.diff.fragments.LineFragment
|
||||
import com.intellij.diff.fragments.LineFragmentImpl
|
||||
import com.intellij.diff.util.LineRange
|
||||
import com.intellij.diff.util.Range
|
||||
import com.intellij.diff.util.Side
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl
|
||||
import com.intellij.openapi.progress.DumbProgressIndicator
|
||||
import junit.framework.TestCase
|
||||
import java.util.*
|
||||
|
||||
class UnifiedFragmentBuilderTest : DiffTestCase() {
|
||||
fun testEquals() {
|
||||
val builder = createBuilder("A\nB\nC", "A\nB\nC", Side.LEFT)
|
||||
fun testSimple() {
|
||||
Test("", "",
|
||||
".",
|
||||
" ")
|
||||
.run()
|
||||
|
||||
assertTrue(builder.isEqual)
|
||||
assertEquals(builder.text.toString(), "A\nB\nC\n")
|
||||
assertEmpty(builder.changedLines)
|
||||
assertEmpty(builder.blocks)
|
||||
Test("A_B_C", "A_B_C",
|
||||
"A_B_C",
|
||||
" _ _ ")
|
||||
.run()
|
||||
|
||||
// empty document has one line, so it's "modified" case (and not "deleted")
|
||||
Test("A", "",
|
||||
"A_.",
|
||||
"L_R")
|
||||
.run()
|
||||
|
||||
Test("", "A",
|
||||
"._A",
|
||||
"L_R")
|
||||
.run()
|
||||
|
||||
Test("A_", "",
|
||||
"A_.",
|
||||
"L_ ")
|
||||
.run()
|
||||
|
||||
Test("A_", "B",
|
||||
"A_._B",
|
||||
"L_L_R")
|
||||
.run()
|
||||
|
||||
Test("B_", "B",
|
||||
"B_.",
|
||||
" _L")
|
||||
.run()
|
||||
|
||||
Test("_B", "B",
|
||||
"._B",
|
||||
"L_ ")
|
||||
.run()
|
||||
|
||||
Test("A_A", "B",
|
||||
"A_A_B",
|
||||
"L_L_R")
|
||||
.run()
|
||||
|
||||
Test("A_B_C_D", "A_D",
|
||||
"A_B_C_D",
|
||||
" _L_L_ ")
|
||||
.run()
|
||||
|
||||
Test("X_B_C", "A_B_C",
|
||||
"X_A_B_C",
|
||||
"L_R_ _ ")
|
||||
.run()
|
||||
|
||||
Test("A_B_C", "A_B_Y",
|
||||
"A_B_C_Y",
|
||||
" _ _L_R")
|
||||
.run()
|
||||
|
||||
Test("A_B_C_D_E_F", "A_X_C_D_Y_F",
|
||||
"A_B_X_C_D_E_Y_F",
|
||||
" _L_R_ _ _L_R_ ")
|
||||
.run()
|
||||
|
||||
Test("A_B_C_D_E_F", "A_B_X_C_D_F",
|
||||
"A_B_X_C_D_E_F",
|
||||
" _ _R_ _ _L_ ")
|
||||
.run()
|
||||
|
||||
Test("A_B_C_D_E_F", "A_B_X_Y_E_F",
|
||||
"A_B_C_D_X_Y_E_F",
|
||||
" _ _L_L_R_R_ _ ")
|
||||
.run()
|
||||
|
||||
Test("", "",
|
||||
".",
|
||||
" ")
|
||||
.changes()
|
||||
.run()
|
||||
|
||||
Test("A", "B",
|
||||
"A_B",
|
||||
"L_R")
|
||||
.changes(mod(0, 0, 1, 1))
|
||||
.runLeft()
|
||||
|
||||
Test("A", "B",
|
||||
"A",
|
||||
" ")
|
||||
.changes()
|
||||
.runLeft()
|
||||
|
||||
Test("A", "B",
|
||||
"B",
|
||||
" ")
|
||||
.changes()
|
||||
.runRight()
|
||||
}
|
||||
|
||||
fun testWrongEndLineTypoBug() {
|
||||
val builder = createBuilder("A\nB\nC\nD", "A\nD", Side.RIGHT)
|
||||
fun testNonFair() {
|
||||
Test("A_B", "",
|
||||
"A_B",
|
||||
" _ ")
|
||||
.changes()
|
||||
.runLeft()
|
||||
|
||||
assertFalse(builder.isEqual)
|
||||
assertEquals(builder.text.toString(), "A\nB\nC\nD\n")
|
||||
assertEquals(builder.changedLines, listOf(LineRange(1, 3)))
|
||||
Test("A_B", "",
|
||||
".",
|
||||
" ")
|
||||
.changes()
|
||||
.runRight()
|
||||
|
||||
assertEquals(builder.blocks.size, 1)
|
||||
val block = builder.blocks[0]
|
||||
assertEquals(block.line1, 1)
|
||||
assertEquals(block.line2, 3)
|
||||
assertEquals(block.range1.start, 1)
|
||||
assertEquals(block.range1.end, 3)
|
||||
assertEquals(block.range2.start, 3)
|
||||
assertEquals(block.range2.end, 3)
|
||||
Test("A_B", "A_._B",
|
||||
"A_B",
|
||||
" _ ")
|
||||
.changes()
|
||||
.runLeft()
|
||||
|
||||
Test("A_B", "A_._B",
|
||||
"A_._B",
|
||||
" _ _ ")
|
||||
.changes()
|
||||
.runRight()
|
||||
|
||||
Test("_._A_._", "X",
|
||||
"._._A_X_._.",
|
||||
" _ _L_R_ _ ")
|
||||
.changes(mod(2, 0, 1, 1))
|
||||
.runLeft()
|
||||
|
||||
Test("_._A_._", "X",
|
||||
"A_X",
|
||||
"L_R")
|
||||
.changes(mod(2, 0, 1, 1))
|
||||
.runRight()
|
||||
|
||||
Test("A_B_C_D", "X_BC_Y",
|
||||
"A_X_BC_D_Y",
|
||||
"L_R_ _L_R")
|
||||
.changes(mod(0, 0, 1, 1), mod(3, 2, 1, 1))
|
||||
.runRight()
|
||||
|
||||
Test("A_B_C_D", "A_BC_Y",
|
||||
"A_BC_D_Y",
|
||||
" _ _L_R")
|
||||
.changes(mod(3, 2, 1, 1))
|
||||
.runRight()
|
||||
|
||||
Test("AB_C_DE", "A_B_D_E",
|
||||
"AB_C_DE",
|
||||
" _L_ ")
|
||||
.changes(del(1, 2, 1))
|
||||
.runLeft()
|
||||
|
||||
Test("AB_C_DE", "A_B_D_E",
|
||||
"A_B_C_D_E",
|
||||
" _ _L_ _ ")
|
||||
.changes(del(1, 2, 1))
|
||||
.runRight()
|
||||
|
||||
Test("AB_DE", "A_B_C_D_E",
|
||||
"AB_C_DE",
|
||||
" _R_ ")
|
||||
.changes(ins(1, 2, 1))
|
||||
.runLeft()
|
||||
|
||||
Test("AB_DE", "A_B_C_D_E",
|
||||
"A_B_C_D_E",
|
||||
" _ _R_ _ ")
|
||||
.changes(ins(1, 2, 1))
|
||||
.runRight()
|
||||
}
|
||||
|
||||
fun testFirstLineChange() {
|
||||
val builder = createBuilder("X\nB\nC", "A\nB\nC", Side.RIGHT)
|
||||
private inner class Test(val input1: String, val input2: String,
|
||||
val result: String,
|
||||
val lineMapping: String) {
|
||||
private var customFragments: List<LineFragment>? = null
|
||||
|
||||
assertFalse(builder.isEqual)
|
||||
assertEquals(builder.text.toString(), "X\nA\nB\nC\n")
|
||||
assertEquals(builder.changedLines, listOf(LineRange(0, 1), LineRange(1, 2)))
|
||||
fun runLeft() {
|
||||
doRun(Side.LEFT)
|
||||
}
|
||||
|
||||
assertEquals(builder.blocks.size, 1)
|
||||
val block = builder.blocks[0]
|
||||
assertEquals(block.line1, 0)
|
||||
assertEquals(block.line2, 2)
|
||||
assertEquals(block.range1.start, 0)
|
||||
assertEquals(block.range1.end, 1)
|
||||
assertEquals(block.range2.start, 1)
|
||||
assertEquals(block.range2.end, 2)
|
||||
fun runRight() {
|
||||
doRun(Side.RIGHT)
|
||||
}
|
||||
|
||||
fun run() {
|
||||
doRun(Side.LEFT, Side.RIGHT)
|
||||
}
|
||||
|
||||
private fun doRun(vararg sides: Side) {
|
||||
sides.forEach { side ->
|
||||
assert(result.length == lineMapping.length)
|
||||
|
||||
val text1 = processText(input1)
|
||||
val text2 = processText(input2)
|
||||
|
||||
val fragments = if (customFragments != null) customFragments!!
|
||||
else MANAGER.compareLines(text1, text2, ComparisonPolicy.DEFAULT, INDICATOR)
|
||||
|
||||
val builder = UnifiedFragmentBuilder(fragments, DocumentImpl(text1), DocumentImpl(text2), side)
|
||||
builder.exec()
|
||||
|
||||
|
||||
val lineCount1 = input1.count({ it == '_' }) + 1
|
||||
val lineCount2 = input2.count({ it == '_' }) + 1
|
||||
val resultLineCount = result.count({ it == '_' }) + 1
|
||||
val lineIterable = DiffIterableUtil.create(fragments.map { Range(it.startLine1, it.endLine1, it.startLine2, it.endLine2) },
|
||||
lineCount1, lineCount2)
|
||||
|
||||
val expectedText = processText(result)
|
||||
val actualText = processActualText(builder)
|
||||
|
||||
val expectedMapping = processExpectedLineMapping(lineMapping)
|
||||
val actualMapping = processActualLineMapping(builder.blocks, resultLineCount)
|
||||
|
||||
val expectedChangedLines = processExpectedChangedLines(lineMapping)
|
||||
val actualChangedLines = processActualChangedLines(builder.changedLines)
|
||||
|
||||
val expectedMappedLines1 = processExpectedMappedLines(lineIterable, Side.LEFT)
|
||||
val expectedMappedLines2 = processExpectedMappedLines(lineIterable, Side.RIGHT)
|
||||
val actualMappedLines1 = processActualMappedLines(builder.convertor1, resultLineCount)
|
||||
val actualMappedLines2 = processActualMappedLines(builder.convertor2, resultLineCount)
|
||||
|
||||
assertEquals(expectedText, actualText)
|
||||
assertEquals(expectedMapping, actualMapping)
|
||||
assertEquals(expectedChangedLines, actualChangedLines)
|
||||
|
||||
if (customFragments == null) {
|
||||
assertEquals(expectedMappedLines1, actualMappedLines1)
|
||||
assertEquals(expectedMappedLines2, actualMappedLines2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun changes(vararg ranges: Range): Test {
|
||||
customFragments = ranges.map {
|
||||
LineFragmentImpl(it.start1, it.end1, it.start2, it.end2,
|
||||
-1, -1, -1, -1)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
private fun processText(text: String): String {
|
||||
return text.filterNot { it == '.' }.replace('_', '\n')
|
||||
}
|
||||
|
||||
private fun processActualText(builder: UnifiedFragmentBuilder): String {
|
||||
return builder.text.toString().removeSuffix("\n")
|
||||
}
|
||||
|
||||
private fun processExpectedLineMapping(lineMapping: String): LineMapping {
|
||||
val leftSet = BitSet()
|
||||
val rightSet = BitSet()
|
||||
val unchangedSet = BitSet()
|
||||
|
||||
lineMapping.split('_').forEachIndexed { index, line ->
|
||||
if (!line.isEmpty()) {
|
||||
val left = line.all { it == 'L' }
|
||||
val right = line.all { it == 'R' }
|
||||
val unchanged = line.all { it == ' ' }
|
||||
|
||||
if (left) leftSet.set(index)
|
||||
else if (right) rightSet.set(index)
|
||||
else if (unchanged) unchangedSet.set(index)
|
||||
else TestCase.fail()
|
||||
}
|
||||
}
|
||||
|
||||
return LineMapping(leftSet, rightSet, unchangedSet)
|
||||
}
|
||||
|
||||
private fun processActualLineMapping(blocks: List<ChangedBlock>, lineCount: Int): LineMapping {
|
||||
val leftSet = BitSet()
|
||||
val rightSet = BitSet()
|
||||
val unchangedSet = BitSet()
|
||||
|
||||
blocks.forEach {
|
||||
leftSet.set(it.range1.start, it.range1.end)
|
||||
rightSet.set(it.range2.start, it.range2.end)
|
||||
}
|
||||
|
||||
unchangedSet.set(0, lineCount)
|
||||
unchangedSet.andNot(leftSet)
|
||||
unchangedSet.andNot(rightSet)
|
||||
|
||||
return LineMapping(leftSet, rightSet, unchangedSet)
|
||||
}
|
||||
|
||||
private fun processExpectedChangedLines(lineMapping: String): BitSet {
|
||||
val expectedMapping = processExpectedLineMapping(lineMapping)
|
||||
val result = BitSet()
|
||||
result.or(expectedMapping.left)
|
||||
result.or(expectedMapping.right)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun processActualChangedLines(changedRanges: List<LineRange>): BitSet {
|
||||
val result = BitSet()
|
||||
changedRanges.forEach {
|
||||
result.set(it.start, it.end)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun processExpectedMappedLines(iterable: DiffIterable, side: Side): BitSet {
|
||||
val result = BitSet()
|
||||
DiffIterableUtil.iterateAll(iterable).forEach { pair ->
|
||||
val range = pair.first
|
||||
val start = side.select(range.start1, range.start2)
|
||||
val end = side.select(range.end1, range.end2)
|
||||
result.set(start, end)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun processActualMappedLines(convertor: LineNumberConvertor, lineCount: Int): BitSet {
|
||||
val result = BitSet()
|
||||
for (i in -5..lineCount + 5) {
|
||||
val line = convertor.convert(i)
|
||||
if (line != -1) {
|
||||
if (result[line]) TestCase.fail()
|
||||
result.set(line)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun testDeletion() {
|
||||
val builder = createBuilder("A\n", "", Side.LEFT)
|
||||
|
||||
assertFalse(builder.isEqual)
|
||||
assertEquals(builder.text.toString(), "A\n\n")
|
||||
assertEquals(builder.changedLines, listOf(LineRange(0, 1)))
|
||||
|
||||
assertEquals(builder.blocks.size, 1)
|
||||
val block = builder.blocks[0]
|
||||
assertEquals(block.line1, 0)
|
||||
assertEquals(block.line2, 1)
|
||||
assertEquals(block.range1.start, 0)
|
||||
assertEquals(block.range1.end, 1)
|
||||
assertEquals(block.range2.start, 1)
|
||||
assertEquals(block.range2.end, 1)
|
||||
private fun mod(line1: Int, line2: Int, count1: Int, count2: Int): Range {
|
||||
assert(count1 != 0)
|
||||
assert(count2 != 0)
|
||||
return Range(line1, line1 + count1, line2, line2 + count2)
|
||||
}
|
||||
|
||||
|
||||
private fun createBuilder(text1: String, text2: String, side: Side): UnifiedFragmentBuilder {
|
||||
val document1 = DocumentImpl(text1)
|
||||
val document2 = DocumentImpl(text2)
|
||||
|
||||
val fragments = MANAGER.compareLinesInner(document1.charsSequence, document2.charsSequence,
|
||||
ComparisonPolicy.DEFAULT, DumbProgressIndicator.INSTANCE)
|
||||
|
||||
val builder = UnifiedFragmentBuilder(fragments, document1, document2, side)
|
||||
builder.exec()
|
||||
|
||||
return builder
|
||||
private fun del(line1: Int, line2: Int, count1: Int): Range {
|
||||
assert(count1 != 0)
|
||||
return Range(line1, line1 + count1, line2, line2)
|
||||
}
|
||||
|
||||
private fun ins(line1: Int, line2: Int, count2: Int): Range {
|
||||
assert(count2 != 0)
|
||||
return Range(line1, line1, line2, line2 + count2)
|
||||
}
|
||||
|
||||
private data class LineMapping(val left: BitSet, val right: BitSet, val unchanged: BitSet)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user