| package difflib |
| |
| import ( |
| "bytes" |
| "fmt" |
| "math" |
| "reflect" |
| "strings" |
| "testing" |
| ) |
| |
| func assertAlmostEqual(t *testing.T, a, b float64, places int) { |
| if math.Abs(a-b) > math.Pow10(-places) { |
| t.Errorf("%.7f != %.7f", a, b) |
| } |
| } |
| |
| func assertEqual(t *testing.T, a, b interface{}) { |
| if !reflect.DeepEqual(a, b) { |
| t.Errorf("%v != %v", a, b) |
| } |
| } |
| |
| func splitChars(s string) []string { |
| chars := make([]string, 0, len(s)) |
| // Assume ASCII inputs |
| for i := 0; i != len(s); i++ { |
| chars = append(chars, string(s[i])) |
| } |
| return chars |
| } |
| |
| func TestGetOptCodes(t *testing.T) { |
| a := "qabxcd" |
| b := "abycdf" |
| s := NewMatcher(splitChars(a), splitChars(b)) |
| w := &bytes.Buffer{} |
| for _, op := range s.GetOpCodes() { |
| fmt.Fprintf(w, "%s a[%d:%d], (%s) b[%d:%d] (%s)\n", string(op.Tag), |
| op.I1, op.I2, a[op.I1:op.I2], op.J1, op.J2, b[op.J1:op.J2]) |
| } |
| result := string(w.Bytes()) |
| expected := `d a[0:1], (q) b[0:0] () |
| e a[1:3], (ab) b[0:2] (ab) |
| r a[3:4], (x) b[2:3] (y) |
| e a[4:6], (cd) b[3:5] (cd) |
| i a[6:6], () b[5:6] (f) |
| ` |
| if expected != result { |
| t.Errorf("unexpected op codes: \n%s", result) |
| } |
| } |
| |
| func TestGroupedOpCodes(t *testing.T) { |
| a := []string{} |
| for i := 0; i != 39; i++ { |
| a = append(a, fmt.Sprintf("%02d", i)) |
| } |
| b := []string{} |
| b = append(b, a[:8]...) |
| b = append(b, " i") |
| b = append(b, a[8:19]...) |
| b = append(b, " x") |
| b = append(b, a[20:22]...) |
| b = append(b, a[27:34]...) |
| b = append(b, " y") |
| b = append(b, a[35:]...) |
| s := NewMatcher(a, b) |
| w := &bytes.Buffer{} |
| for _, g := range s.GetGroupedOpCodes(-1) { |
| fmt.Fprintf(w, "group\n") |
| for _, op := range g { |
| fmt.Fprintf(w, " %s, %d, %d, %d, %d\n", string(op.Tag), |
| op.I1, op.I2, op.J1, op.J2) |
| } |
| } |
| result := string(w.Bytes()) |
| expected := `group |
| e, 5, 8, 5, 8 |
| i, 8, 8, 8, 9 |
| e, 8, 11, 9, 12 |
| group |
| e, 16, 19, 17, 20 |
| r, 19, 20, 20, 21 |
| e, 20, 22, 21, 23 |
| d, 22, 27, 23, 23 |
| e, 27, 30, 23, 26 |
| group |
| e, 31, 34, 27, 30 |
| r, 34, 35, 30, 31 |
| e, 35, 38, 31, 34 |
| ` |
| if expected != result { |
| t.Errorf("unexpected op codes: \n%s", result) |
| } |
| } |
| |
| func rep(s string, count int) string { |
| return strings.Repeat(s, count) |
| } |
| |
| func TestWithAsciiOneInsert(t *testing.T) { |
| sm := NewMatcher(splitChars(rep("b", 100)), |
| splitChars("a"+rep("b", 100))) |
| assertEqual(t, sm.GetOpCodes(), |
| []OpCode{{'i', 0, 0, 0, 1}, {'e', 0, 100, 1, 101}}) |
| assertEqual(t, len(sm.bPopular), 0) |
| |
| sm = NewMatcher(splitChars(rep("b", 100)), |
| splitChars(rep("b", 50)+"a"+rep("b", 50))) |
| assertEqual(t, sm.GetOpCodes(), |
| []OpCode{{'e', 0, 50, 0, 50}, {'i', 50, 50, 50, 51}, {'e', 50, 100, 51, 101}}) |
| assertEqual(t, len(sm.bPopular), 0) |
| } |
| |
| func TestWithAsciiOnDelete(t *testing.T) { |
| sm := NewMatcher(splitChars(rep("a", 40)+"c"+rep("b", 40)), |
| splitChars(rep("a", 40)+rep("b", 40))) |
| assertEqual(t, sm.GetOpCodes(), |
| []OpCode{{'e', 0, 40, 0, 40}, {'d', 40, 41, 40, 40}, {'e', 41, 81, 40, 80}}) |
| } |
| |
| func TestSFBugsComparingEmptyLists(t *testing.T) { |
| groups := NewMatcher(nil, nil).GetGroupedOpCodes(-1) |
| assertEqual(t, len(groups), 0) |
| diff := UnifiedDiff{ |
| FromFile: "Original", |
| ToFile: "Current", |
| Context: 3, |
| } |
| result, err := GetUnifiedDiffString(diff) |
| assertEqual(t, err, nil) |
| assertEqual(t, result, "") |
| } |
| |
| func TestOutputFormatRangeFormatUnified(t *testing.T) { |
| // Per the diff spec at http://www.unix.org/single_unix_specification/ |
| // |
| // Each <range> field shall be of the form: |
| // %1d", <beginning line number> if the range contains exactly one line, |
| // and: |
| // "%1d,%1d", <beginning line number>, <number of lines> otherwise. |
| // If a range is empty, its beginning line number shall be the number of |
| // the line just before the range, or 0 if the empty range starts the file. |
| fm := formatRangeUnified |
| assertEqual(t, fm(3, 3), "3,0") |
| assertEqual(t, fm(3, 4), "4") |
| assertEqual(t, fm(3, 5), "4,2") |
| assertEqual(t, fm(3, 6), "4,3") |
| assertEqual(t, fm(0, 0), "0,0") |
| } |
| |
| func TestOutputFormatRangeFormatContext(t *testing.T) { |
| // Per the diff spec at http://www.unix.org/single_unix_specification/ |
| // |
| // The range of lines in file1 shall be written in the following format |
| // if the range contains two or more lines: |
| // "*** %d,%d ****\n", <beginning line number>, <ending line number> |
| // and the following format otherwise: |
| // "*** %d ****\n", <ending line number> |
| // The ending line number of an empty range shall be the number of the preceding line, |
| // or 0 if the range is at the start of the file. |
| // |
| // Next, the range of lines in file2 shall be written in the following format |
| // if the range contains two or more lines: |
| // "--- %d,%d ----\n", <beginning line number>, <ending line number> |
| // and the following format otherwise: |
| // "--- %d ----\n", <ending line number> |
| fm := formatRangeContext |
| assertEqual(t, fm(3, 3), "3") |
| assertEqual(t, fm(3, 4), "4") |
| assertEqual(t, fm(3, 5), "4,5") |
| assertEqual(t, fm(3, 6), "4,6") |
| assertEqual(t, fm(0, 0), "0") |
| } |
| |
| func TestOutputFormatTabDelimiter(t *testing.T) { |
| diff := UnifiedDiff{ |
| A: splitChars("one"), |
| B: splitChars("two"), |
| FromFile: "Original", |
| FromDate: "2005-01-26 23:30:50", |
| ToFile: "Current", |
| ToDate: "2010-04-12 10:20:52", |
| Eol: "\n", |
| } |
| ud, err := GetUnifiedDiffString(diff) |
| assertEqual(t, err, nil) |
| assertEqual(t, SplitLines(ud)[:2], []string{ |
| "--- Original\t2005-01-26 23:30:50\n", |
| "+++ Current\t2010-04-12 10:20:52\n", |
| }) |
| } |
| |
| func TestOutputFormatNoTrailingTabOnEmptyFiledate(t *testing.T) { |
| diff := UnifiedDiff{ |
| A: splitChars("one"), |
| B: splitChars("two"), |
| FromFile: "Original", |
| ToFile: "Current", |
| Eol: "\n", |
| } |
| ud, err := GetUnifiedDiffString(diff) |
| assertEqual(t, err, nil) |
| assertEqual(t, SplitLines(ud)[:2], []string{"--- Original\n", "+++ Current\n"}) |
| } |
| |
| func TestOmitFilenames(t *testing.T) { |
| diff := UnifiedDiff{ |
| A: SplitLines("o\nn\ne\n"), |
| B: SplitLines("t\nw\no\n"), |
| Eol: "\n", |
| } |
| ud, err := GetUnifiedDiffString(diff) |
| assertEqual(t, err, nil) |
| assertEqual(t, SplitLines(ud), []string{ |
| "@@ -0,0 +1,2 @@\n", |
| "+t\n", |
| "+w\n", |
| "@@ -2,2 +3,0 @@\n", |
| "-n\n", |
| "-e\n", |
| "\n", |
| }) |
| } |
| |
| func TestSplitLines(t *testing.T) { |
| allTests := []struct { |
| input string |
| want []string |
| }{ |
| {"foo", []string{"foo\n"}}, |
| {"foo\nbar", []string{"foo\n", "bar\n"}}, |
| {"foo\nbar\n", []string{"foo\n", "bar\n", "\n"}}, |
| } |
| for _, test := range allTests { |
| assertEqual(t, SplitLines(test.input), test.want) |
| } |
| } |
| |
| func benchmarkSplitLines(b *testing.B, count int) { |
| str := strings.Repeat("foo\n", count) |
| |
| b.ResetTimer() |
| |
| n := 0 |
| for i := 0; i < b.N; i++ { |
| n += len(SplitLines(str)) |
| } |
| } |
| |
| func BenchmarkSplitLines100(b *testing.B) { |
| benchmarkSplitLines(b, 100) |
| } |
| |
| func BenchmarkSplitLines10000(b *testing.B) { |
| benchmarkSplitLines(b, 10000) |
| } |