package com.diodesoftware.scb.diff;
import java.util.*;
import junit.framework.TestCase;
import org.apache.log4j.Logger;
public class TestDiff extends TestCase
{
private static Logger log = Logger.getLogger(TestDiff.class);
public TestDiff(String name)
{
super(name);
}
public void testStrings1()
{
Object[] a = new Object[] {
"a",
"b",
"c",
"e",
"h",
"j",
"l",
"m",
"n",
"p"
};
Object[] b = new Object[] {
"b",
"c",
"d",
"e",
"f",
"j",
"k",
"l",
"m",
"build",
"s",
"t"
};
Difference[] expected = new Difference[] {
new Difference(0, 0, 0, -1),
new Difference(3, -1, 2, 2),
new Difference(4, 4, 4, 4),
new Difference(6, -1, 6, 6),
new Difference(8, 9, 9, 11),
};
runDiff(a, b, expected);
}
public void testStrings2()
{
Object[] a = new Object[] {
"a",
"b",
"c",
"d"
};
Object[] b = new Object[] {
"c",
"d"
};
Difference[] expected = new Difference[] {
new Difference(0, 1, 0, -1),
};
runDiff(a, b, expected);
}
public void testStrings3()
{
Object[] a = new Object[] {
"a", "b", "c", "d", "x", "y", "z"
};
Object[] b = new Object[] {
"c", "d"
};
Difference[] expected = new Difference[] {
new Difference(0, 1, 0, -1),
new Difference(4, 6, 2, -1)
};
runDiff(a, b, expected);
}
public void testStrings4()
{
Object[] a = new Object[] {
"a",
"b",
"c",
"d",
"e"
};
Object[] b = new Object[] {
"a",
"x",
"y",
"b",
"c",
"j",
"e",
};
Difference[] expected = new Difference[] {
new Difference(1, -1, 1, 2),
new Difference(3, 3, 5, 5),
};
runDiff(a, b, expected);
}
public void testInteger()
{
Object[] a = new Integer[] {
new Integer(1),
new Integer(2),
new Integer(3),
};
Object[] b = new Integer[] {
new Integer(2),
new Integer(3),
};
Difference[] expected = new Difference[] {
new Difference(0, 0, 0, -1)
};
runDiff(a, b, expected);
}
static class NoncomparableObject
{
private String x;
public NoncomparableObject(String x)
{
this.x = x;
}
public boolean equals(Object obj)
{
return compareTo(obj) == 0;
}
public int compareTo(Object obj)
{
if (obj instanceof NoncomparableObject) {
NoncomparableObject other = (NoncomparableObject)obj;
return x.compareTo(other.x);
}
else {
return -1;
}
}
public String toString()
{
return getClass().getName() + " \"" + x + "\"";
}
}
public void testComparator()
{
Object[] a = new NoncomparableObject[] {
new NoncomparableObject("a"),
new NoncomparableObject("b"),
new NoncomparableObject("c"),
new NoncomparableObject("g"),
new NoncomparableObject("h1"),
};
Object[] b = new NoncomparableObject[] {
new NoncomparableObject("b"),
new NoncomparableObject("c"),
new NoncomparableObject("d"),
new NoncomparableObject("e"),
new NoncomparableObject("f"),
new NoncomparableObject("g"),
new NoncomparableObject("h2"),
};
Difference[] expected = new Difference[] {
new Difference(0, 0, 0, -1),
new Difference(3, -1 , 2, 4),
new Difference(4, 4, 6, 6),
};
Comparator comparator = new Comparator() {
public int compare(Object x, Object y)
{
if (x == null) {
if (y == null) {
return 0;
}
else {
return -1;
}
}
else if (y == null) {
return 1;
}
else if (x instanceof NoncomparableObject && y instanceof NoncomparableObject) {
NoncomparableObject xno = (NoncomparableObject)x;
NoncomparableObject yno = (NoncomparableObject)y;
return xno.x.compareTo(yno.x);
}
else {
return -1;
}
}
public boolean equals(Object x, Object y)
{
return compare(x, y) == 0;
}
};
runDiff(new Diff(a, b, comparator), expected);
}
static class ComparableObject implements Comparable
{
private String x;
public ComparableObject(String x)
{
this.x = x;
}
public boolean equals(Object obj)
{
return compareTo(obj) == 0;
}
public int compareTo(Object obj)
{
if (obj instanceof ComparableObject) {
ComparableObject other = (ComparableObject)obj;
return x.compareTo(other.x);
}
else {
return -1;
}
}
public String toString()
{
return getClass().getName() + " \"" + x + "\"";
}
}
public void testComparableObject()
{
Object[] a = new ComparableObject[] {
new ComparableObject("a"),
new ComparableObject("b"),
new ComparableObject("c"),
new ComparableObject("g"),
new ComparableObject("h1"),
};
Object[] b = new ComparableObject[] {
new ComparableObject("b"),
new ComparableObject("c"),
new ComparableObject("d"),
new ComparableObject("e"),
new ComparableObject("f"),
new ComparableObject("g"),
new ComparableObject("h2"),
};
Difference[] expected = new Difference[] {
new Difference(0, 0, 0, -1),
new Difference(3, -1, 2, 4),
new Difference(4, 4, 6, 6),
};
runDiff(a, b, expected);
}
public void testLongArray()
{
Object[] a = new Object[] {
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
};
Object[] b = new Object[] {
"a",
"b",
"p", // insert
"q", // ...
"build", // ...
"s", // ...
"t", // ...
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"u", // change
"l",
};
Difference[] expected = new Difference[] {
new Difference(2, -1, 2, 6),
new Difference(10, 10, 15, 15),
};
runDiff(a, b, expected);
}
public void testRepeated()
{
Object[] a = new Object[] {
"a",
"a",
"a",
"a",
"b",
"b",
"b",
"a",
"a",
"a",
"a",
"b",
"b",
"b",
"a",
"a",
"a",
"a",
"b",
"b",
"b",
"a",
"a",
"a",
"a",
"b",
"b",
"b",
};
Object[] b = new Object[] {
"a",
"a",
"a",
"a",
"b",
"b",
"b",
"a",
"b",
"b",
"b",
"a",
"a",
"a",
"a",
};
Difference[] expected = new Difference[] {
new Difference(8, 10, 8, -1),
new Difference(18, 27, 15, -1),
};
runDiff(a, b, expected);
}
public void testReallyBig() {
Object[] a = new Object[] {
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"A",
"H",
"I",
"J",
"D",
"K",
"L",
"C",
"G",
"M",
"H",
"N",
"J",
"I",
"K",
"O",
"C",
"G",
"M",
"P",
"Q",
"J",
"R",
"K",
"S",
"C",
"C",
"F",
"G",
"D",
"T",
"N",
"G",
"M",
"U",
"V",
"J",
"Q",
"K",
"W",
"C",
"G",
"M",
"X",
"C",
"V",
"K",
"Y",
"C",
"G",
"G",
"A",
"Z",
"AA",
"J",
"C",
"Z",
"G",
"V",
"K",
"BB",
"C",
"G",
"M",
"CC",
"DD",
"J",
"EE",
"K",
"FF",
"C",
"AA",
"G",
"M",
"GG",
"K",
"HH",
"C",
"DD",
"G",
"M",
"II",
"II",
"II",
};
Object[] b = new Object[] {
"A",
"B",
"C",
"JJ",
"G",
"A",
"II",
"KK",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"A",
"H",
"I",
"J",
"D",
"K",
"L",
"C",
"G",
"M",
"H",
"N",
"J",
"I",
"K",
"O",
"C",
"G",
"M",
"P",
"Q",
"J",
"R",
"K",
"S",
"C",
"C",
"F",
"G",
"D",
"T",
"N",
"G",
"M",
"U",
"V",
"J",
"Q",
"K",
"W",
"C",
"G",
"M",
"X",
"C",
"V",
"K",
"Y",
"C",
"G",
"G",
"A",
"Z",
"AA",
"J",
"C",
"Z",
"G",
"V",
"K",
"BB",
"C",
"G",
"M",
"CC",
"DD",
"J",
"EE",
"K",
"FF",
"C",
"AA",
"G",
"M",
"GG",
"K",
"HH",
"C",
"DD",
"G",
"M",
"II",
"II",
"II",
"II",
};
Difference[] expected = new Difference[] {
new Difference(3, -1, 3, 10),
new Difference(88, -1, 96, 96),
};
runDiff(a, b, expected);
}
// This test doesn't yet work. The problem is that LCSes are logically
// incorrect, with regard to source code. That is, after a failed mapping
// (i.e., null), the "next" LCS is the *previous* LCS, not the current one,
// that is, starting at the next element after the null. Not sure when this
// is an artifact of the algorithm, or whether it is "correct" and my
// expectations aren't.
public void misleading_diffs_testFromZipDiff() {
Object[] a = new Object[] {
"{",
"ZipEntry",
"e",
"=",
"entry",
";",
"if",
"(",
"e",
"!=",
"null",
")",
"{",
"switch",
"(",
"e",
".",
"method",
")",
"{",
"case",
"DEFLATED",
":",
"if",
"(",
"(",
"e",
".",
"flag",
"&",
"8",
")",
"==",
"0",
")",
"{",
"if",
"(",
"e",
".",
"size",
"!=",
"def",
".",
"getTotalIn",
"(",
")",
")",
"{",
"throw",
"new",
"ZipException",
"(",
"\"invalid entry size (expected \"",
"+",
"e",
".",
"size",
"+",
"\" but got \"",
"+",
"def",
".",
"getTotalIn",
"(",
")",
"+",
"\" bytes)\"",
")",
";",
"}",
"if",
"(",
"e",
".",
"csize",
"!=",
"def",
".",
"getTotalOut",
"(",
")",
")",
"{",
"throw",
"new",
"ZipException",
"(",
"\"invalid entry compressed size (expected \"",
"+",
"e",
".",
"csize",
"+",
"\" but got \"",
"+",
"def",
".",
"getTotalOut",
"(",
")",
"+",
"\" bytes)\"",
")",
";",
"}",
"if",
"(",
"e",
".",
"crc",
"!=",
"crc",
".",
"getValue",
"(",
")",
")",
"{",
"throw",
"new",
"ZipException",
"(",
"\"invalid entry CRC-32 (expected 0x\"",
"+",
"Long",
".",
"toHexString",
"(",
"e",
".",
"crc",
")",
"+",
"\" but got 0x\"",
"+",
"Long",
".",
"toHexString",
"(",
"crc",
".",
"getValue",
"(",
")",
")",
"+",
"\")\"",
")",
";",
"}",
"}",
"else",
"{",
"e",
".",
"size",
"=",
"def",
".",
"getTotalIn",
"(",
")",
";",
"e",
".",
"csize",
"=",
"def",
".",
"getTotalOut",
"(",
")",
";",
"e",
".",
"crc",
"=",
"crc",
".",
"getValue",
"(",
")",
";",
"writeEXT",
"(",
"e",
")",
";",
"}",
"def",
".",
"reset",
"(",
")",
";",
"written",
"+=",
"e",
".",
"csize",
";",
"break",
";",
"}",
"}",
"}",
};
Object[] b = new Object[] {
"{",
"ZipEntry",
"e",
"=",
"entry",
";",
"if",
"(",
"e",
"!=",
"null",
")",
"{",
"switch",
"(",
"e",
".",
"method",
")",
"{",
"case",
"DEFLATED",
":",
"if",
"(",
"(",
"e",
".",
"flag",
"&",
"8",
")",
"==",
"0",
")",
"{",
"if",
"(",
"e",
".",
"size",
"!=",
"def",
".",
"getBytesRead",
"(",
")",
")",
"{",
"throw",
"new",
"ZipException",
"(",
"\"invalid entry size (expected \"",
"+",
"e",
".",
"size",
"+",
"\" but got \"",
"+",
"def",
".",
"getBytesRead",
"(",
")",
"+",
"\" bytes)\"",
")",
";",
"}",
"if",
"(",
"e",
".",
"csize",
"!=",
"def",
".",
"getBytesWritten",
"(",
")",
")",
"{",
"throw",
"new",
"ZipException",
"(",
"\"invalid entry compressed size (expected \"",
"+",
"e",
".",
"csize",
"+",
"\" but got \"",
"+",
"def",
".",
"getBytesWritten",
"(",
")",
"+",
"\" bytes)\"",
")",
";",
"}",
"if",
"(",
"e",
".",
"crc",
"!=",
"crc",
".",
"getValue",
"(",
")",
")",
"{",
"throw",
"new",
"ZipException",
"(",
"\"invalid entry CRC-32 (expected 0x\"",
"+",
"Long",
".",
"toHexString",
"(",
"e",
".",
"crc",
")",
"+",
"\" but got 0x\"",
"+",
"Long",
".",
"toHexString",
"(",
"crc",
".",
"getValue",
"(",
")",
")",
"+",
"\")\"",
")",
";",
"}",
"}",
"else",
"{",
"e",
".",
"size",
"=",
"def",
".",
"getBytesRead",
"(",
")",
";",
"e",
".",
"csize",
"=",
"def",
".",
"getBytesWritten",
"(",
")",
";",
"e",
".",
"crc",
"=",
"crc",
".",
"getValue",
"(",
")",
";",
"writeEXT",
"(",
"e",
")",
";",
"}",
"def",
".",
"reset",
"(",
")",
";",
"written",
"+=",
"e",
".",
"csize",
";",
"break",
";",
"}",
"}",
"}",
};
Difference[] expected = new Difference[] {
new Difference(3, -1, 3, 10),
new Difference(88, -1, 96, 96),
};
runDiff(a, b, expected);
}
public void testBlanks()
{
// many thanks to Oliver Koll for finding and submitting this.
Object[] a = new Object[] {
"same",
"same",
"same",
"",
"same",
"del", // delete
"", // ...
"del" // ...
};
Object[] b = new Object[] {
"ins", // insert
"", // ...
"same",
"same",
"same",
"",
"same"
};
Difference[] expected = new Difference[] {
new Difference(0, -1, 0, 1),
new Difference(5, 7, 7, -1),
};
runDiff(a, b, expected);
}
protected void assertLCS(Object[] a, Object[] b, Integer[] expected)
{
log("a", a);
log("b", b);
log("expected", expected);
Diff diff = new Diff(a, b);
Integer[] lcses = diff.getLongestCommonSubsequences();
log("lcses", lcses);
assertEquals("length of output", expected.length, lcses.length);
for (int ei = 0; ei < expected.length; ++ei) {
Integer exp = expected[ei];
Integer lcs = lcses[ei];
assertEquals("expected[" + ei +"]", exp, lcs);
}
}
protected void runDiff(Object[] a, Object[] b, Difference[] expected)
{
log("a", a);
log("b", b);
Diff diff = new Diff(a, b);
runDiff(diff, expected);
}
protected void runDiff(Diff diff, Difference[] expected)
{
log("expected", expected);
// Integer[] lcs = diff.getLongestCommonSubsequences();
// tr.Ace.log("lcs", lcs);
List diffOut = diff.diff();
log("diffOut", diffOut);
assertEquals("length of output", expected.length, diffOut.size());
for (int ei = 0; ei < expected.length; ++ei) {
Difference d = (Difference)diffOut.get(ei);
Difference exp = expected[ei];
assertEquals("expected[" + ei +"]", exp, d);
}
}
private static void log(String a, List b){
for(Object o : b)
log.debug(a + " " + o);
}
private static void log(String a, Object[] oa){
for(Object o : oa)
log.debug(a + " " + o);
}
}