diff --git a/diff/dmp.go b/diff/dmp.go index 40b7846..c2f769a 100644 --- a/diff/dmp.go +++ b/diff/dmp.go @@ -12,7 +12,7 @@ * See included LICENSE file for license details. */ -// Package diffmatchpatch offers robust algorithms to perform the +// Package diffmatchpatch offers robust algorithms to perform the // operations required for synchronizing plain text. package diffmatchpatch @@ -22,7 +22,6 @@ import ( "fmt" "math" "net/url" - "reflect" "regexp" "strconv" "strings" @@ -37,7 +36,6 @@ const ( DiffDelete = -1 DiffInsert = 1 DiffEqual = 0 - max32 = int32(uint32(1<<31) - 1) ) // unescaper unescapes selected chars for compatability with JavaScript's encodeURI. @@ -64,21 +62,10 @@ var ( blanklineStartRegex_ = regexp.MustCompile(`^\r?\n\r?\n`) ) -func concat(old1, old2 []Diff) []Diff { - newslice := make([]Diff, len(old1)+len(old2)) - copy(newslice, old1) - copy(newslice[len(old1):], old2) - return newslice -} - func splice(slice []Diff, index int, amount int, elements ...Diff) []Diff { return append(slice[:index], append(elements, slice[index+amount:]...)...) } -func splice_patch(slice []Patch, index int, amount int, elements ...Patch) []Patch { - return append(slice[:index], append(elements, slice[index+amount:]...)...) -} - func indexOf(str string, pattern string, i int) int { if i > len(str)-1 { return -1 @@ -100,26 +87,18 @@ func indexOf(str string, pattern string, i int) int { } -type DiffMatchPatch struct { - // Number of seconds to map a diff before giving up (0 for infinity). - DiffTimeout float64 - // Cost of an empty edit operation in terms of edit characters. - DiffEditCost int - // How far to search for a match (0 = exact location, 1000+ = broad match). - // A match this many characters away from the expected location will add - // 1.0 to the score (0.0 is a perfect match). - MatchDistance int - // When deleting a large block of text (over ~64 characters), how close do - // the contents have to be to match the expected contents. (0.0 = perfection, - // 1.0 = very loose). Note that Match_Threshold controls how closely the - // end points of a delete need to match. - PatchDeleteThreshold float64 - // Chunk size for context length. - PatchMargin int - // The number of bits in an int. - MatchMaxBits int - // At what point is no match declared (0.0 = perfection, 1.0 = very loose). - MatchThreshold float64 +func min(x, y int) int { + if x < y { + return x + } + return y +} + +func max(x, y int) int { + if x > y { + return x + } + return y } // Diff represents one diff operation @@ -128,12 +107,6 @@ type Diff struct { Text string } -type LinesDiff struct { - chars1 string - chars2 string - lineArray []string -} - // Patch represents one patch operation. type Patch struct { diffs []Diff @@ -146,42 +119,37 @@ type Patch struct { // String emulates GNU diff's format. // Header: @@ -382,8 +481,9 @@ // Indicies are printed as 1-based, not 0-based. -func (patch *Patch) String() string { +func (p *Patch) String() string { var coords1, coords2 string - start1 := int64(patch.start1) - start2 := int64(patch.start2) - if patch.length1 == 0 { - coords1 = strconv.FormatInt(start1, 10) + ",0" - } else if patch.length1 == 1 { - coords1 = strconv.FormatInt(start1+1, 10) + if p.length1 == 0 { + coords1 = strconv.Itoa(p.start1) + ",0" + } else if p.length1 == 1 { + coords1 = strconv.Itoa(p.start1 + 1) } else { - coords1 = strconv.FormatInt(start1+1, 10) + "," + strconv.FormatInt(int64(patch.length1), 10) + coords1 = strconv.Itoa(p.start1+1) + "," + strconv.Itoa(p.length1) } - if patch.length2 == 0 { - coords2 = strconv.FormatInt(start2, 10) + ",0" - } else if patch.length2 == 1 { - coords2 = strconv.FormatInt(start2+1, 10) + if p.length2 == 0 { + coords2 = strconv.Itoa(p.start2) + ",0" + } else if p.length2 == 1 { + coords2 = strconv.Itoa(p.start2 + 1) } else { - coords2 = strconv.FormatInt(start2+1, 10) + "," + strconv.FormatInt(int64(patch.length2), 10) + coords2 = strconv.Itoa(p.start2+1) + "," + strconv.Itoa(p.length2) } var text bytes.Buffer text.WriteString("@@ -" + coords1 + " +" + coords2 + " @@\n") // Escape the body of the patch with %xx notation. - for _, aDiff := range patch.diffs { + for _, aDiff := range p.diffs { switch aDiff.Type { case DiffInsert: text.WriteString("+") - break case DiffDelete: text.WriteString("-") - break case DiffEqual: text.WriteString(" ") - break } text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1)) @@ -191,54 +159,55 @@ func (patch *Patch) String() string { return unescaper.Replace(text.String()) } -func createDMP() DiffMatchPatch { - dmp := DiffMatchPatch{} - // Defaults. - // Set these on your diff_match_patch instance to override the defaults. - +type DiffMatchPatch struct { // Number of seconds to map a diff before giving up (0 for infinity). - dmp.DiffTimeout = 1.0 + DiffTimeout time.Duration // Cost of an empty edit operation in terms of edit characters. - dmp.DiffEditCost = 4 - // At what point is no match declared (0.0 = perfection, 1.0 = very loose). - dmp.MatchThreshold = 0.5 + DiffEditCost int // How far to search for a match (0 = exact location, 1000+ = broad match). // A match this many characters away from the expected location will add // 1.0 to the score (0.0 is a perfect match). - dmp.MatchDistance = 1000 - // When deleting a large block of text (over ~64 characters), how close - // do the contents have to be to match the expected contents. (0.0 = - // perfection, 1.0 = very loose). Note that Match_Threshold controls - // how closely the end points of a delete need to match. - dmp.PatchDeleteThreshold = 0.5 + MatchDistance int + // When deleting a large block of text (over ~64 characters), how close do + // the contents have to be to match the expected contents. (0.0 = perfection, + // 1.0 = very loose). Note that Match_Threshold controls how closely the + // end points of a delete need to match. + PatchDeleteThreshold float64 // Chunk size for context length. - dmp.PatchMargin = 4 - + PatchMargin int // The number of bits in an int. - dmp.MatchMaxBits = 32 + MatchMaxBits int + // At what point is no match declared (0.0 = perfection, 1.0 = very loose). + MatchThreshold float64 +} - return dmp +// New creates a new DiffMatchPatch object with default parameters. +func New() *DiffMatchPatch { + // Defaults. + return &DiffMatchPatch{ + DiffTimeout: time.Second, + DiffEditCost: 4, + MatchThreshold: 0.5, + MatchDistance: 1000, + PatchDeleteThreshold: 0.5, + PatchMargin: 4, + MatchMaxBits: 32, + } } // DiffMain finds the differences between two texts. -func (dmp *DiffMatchPatch) DiffMain(text1 string, text2 string, opt ...interface{}) []Diff { - checklines := true - var deadline int32 - - if opt != nil && len(opt) > 0 { - checklines = opt[0].(bool) - - if len(opt) > 1 { - deadline = opt[1].(int32) - } else { - if dmp.DiffTimeout <= 0 { - deadline = max32 - } else { - deadline = int32(int(time.Now().Unix()) + int(dmp.DiffTimeout*1000)) - } - } +func (dmp *DiffMatchPatch) DiffMain(text1, text2 string, checklines bool) []Diff { + var deadline time.Time + if dmp.DiffTimeout <= 0 { + deadline = time.Now().Add(24 * 365 * time.Hour) + } else { + deadline = time.Now().Add(dmp.DiffTimeout) } + return dmp.diffMain(text1, text2, checklines, deadline) +} +// DiffMain finds the differences between two texts. +func (dmp *DiffMatchPatch) diffMain(text1, text2 string, checklines bool, deadline time.Time) []Diff { diffs := []Diff{} if text1 == text2 { if len(text1) > 0 { @@ -247,25 +216,25 @@ func (dmp *DiffMatchPatch) DiffMain(text1 string, text2 string, opt ...interface return diffs } + // Trim off common prefix (speedup). commonlength := dmp.DiffCommonPrefix(text1, text2) - commonprefix := text1[0:commonlength] + commonprefix := text1[:commonlength] text1 = text1[commonlength:] text2 = text2[commonlength:] // Trim off common suffix (speedup). commonlength = dmp.DiffCommonSuffix(text1, text2) commonsuffix := text1[len(text1)-commonlength:] - - text1 = text1[0 : len(text1)-commonlength] - text2 = text2[0 : len(text2)-commonlength] + text1 = text1[:len(text1)-commonlength] + text2 = text2[:len(text2)-commonlength] // Compute the diff on the middle block. diffs = dmp.diffCompute(text1, text2, checklines, deadline) + // Restore the prefix and suffix. if len(commonprefix) != 0 { diffs = append([]Diff{Diff{DiffEqual, commonprefix}}, diffs...) } - if len(commonsuffix) != 0 { diffs = append(diffs, Diff{DiffEqual, commonsuffix}) } @@ -275,21 +244,18 @@ func (dmp *DiffMatchPatch) DiffMain(text1 string, text2 string, opt ...interface // diffCompute finds the differences between two texts. Assumes that the texts do not // have any common prefix or suffix. -func (dmp *DiffMatchPatch) diffCompute(text1 string, text2 string, checklines bool, deadline int32) []Diff { +func (dmp *DiffMatchPatch) diffCompute(text1, text2 string, checklines bool, deadline time.Time) []Diff { diffs := []Diff{} if len(text1) == 0 { // Just add some text (speedup). return append(diffs, Diff{DiffInsert, text2}) - } - - if len(text2) == 0 { + } else if len(text2) == 0 { // Just delete some text (speedup). - return append(diffs, Diff{DiffInsert, text1}) + return append(diffs, Diff{DiffDelete, text1}) } var longtext, shorttext string - - if utf8.RuneCountInString(text1) > utf8.RuneCountInString(text2) { + if len(text1) > len(text2) { longtext = text1 shorttext = text2 } else { @@ -297,33 +263,27 @@ func (dmp *DiffMatchPatch) diffCompute(text1 string, text2 string, checklines bo shorttext = text1 } - var i = strings.Index(longtext, shorttext) - if i != -1 { + if i := strings.Index(longtext, shorttext); i != -1 { var op int8 = DiffInsert // Swap insertions for deletions if diff is reversed. - if utf8.RuneCountInString(text1) > utf8.RuneCountInString(text2) { + if len(text1) > len(text2) { op = DiffDelete } // Shorter text is inside the longer text (speedup). - diffs = []Diff{ - Diff{op, longtext[0:i]}, + return []Diff{ + Diff{op, longtext[:i]}, Diff{DiffEqual, shorttext}, - Diff{op, longtext[i+utf8.RuneCountInString(shorttext):]}, + Diff{op, longtext[i+len(shorttext):]}, } - - return diffs - } - if utf8.RuneCountInString(shorttext) == 1 { + } else if utf8.RuneCountInString(shorttext) == 1 { // Single character string. // After the previous speedup, the character can't be an equality. return []Diff{ Diff{DiffDelete, text1}, Diff{DiffInsert, text2}, } - } - // Check to see if the problem can be split in two. - hm := dmp.DiffHalfMatch(text1, text2) - if hm != nil { + // Check to see if the problem can be split in two. + } else if hm := dmp.DiffHalfMatch(text1, text2); hm != nil { // A half-match was found, sort out the return data. text1_a := hm[0] text1_b := hm[1] @@ -331,14 +291,11 @@ func (dmp *DiffMatchPatch) diffCompute(text1 string, text2 string, checklines bo text2_b := hm[3] mid_common := hm[4] // Send both pairs off for separate processing. - diffs_a := dmp.DiffMain(text1_a, text2_a, checklines, deadline) - diffs_b := dmp.DiffMain(text1_b, text2_b, checklines, deadline) + diffs_a := dmp.diffMain(text1_a, text2_a, checklines, deadline) + diffs_b := dmp.diffMain(text1_b, text2_b, checklines, deadline) // Merge the results. - // TODO: Concat should accept several arguments - concat1 := concat(diffs_a, []Diff{Diff{DiffEqual, mid_common}}) - return concat(concat1, diffs_b) - } - if checklines && utf8.RuneCountInString(text1) > 100 && utf8.RuneCountInString(text2) > 100 { + return append(diffs_a, append([]Diff{Diff{DiffEqual, mid_common}}, diffs_b...)...) + } else if checklines && len(text1) > 100 && len(text2) > 100 { return dmp.diffLineMode(text1, text2, deadline) } return dmp.DiffBisect(text1, text2, deadline) @@ -346,16 +303,16 @@ func (dmp *DiffMatchPatch) diffCompute(text1 string, text2 string, checklines bo // diffLineMode does a quick line-level diff on both strings, then rediff the parts for // greater accuracy. This speedup can produce non-minimal diffs. -func (dmp *DiffMatchPatch) diffLineMode(text1 string, text2 string, deadline int32) []Diff { +func (dmp *DiffMatchPatch) diffLineMode(text1, text2 string, deadline time.Time) []Diff { // Scan the text on a line-by-line basis first. text1, text2, linearray := dmp.DiffLinesToChars(text1, text2) - diffs := dmp.DiffMain(text1, text2, false, deadline) + diffs := dmp.diffMain(text1, text2, false, deadline) // Convert the diff back to original text. diffs = dmp.DiffCharsToLines(diffs, linearray) // Eliminate freak matches (e.g. blank lines) - dmp.DiffCleanupSemantic(diffs) + diffs = dmp.DiffCleanupSemantic(diffs) // Rediff any replacement blocks, this time character-by-character. // Add a dummy entry at the end. @@ -372,33 +329,28 @@ func (dmp *DiffMatchPatch) diffLineMode(text1 string, text2 string, deadline int case DiffInsert: count_insert++ text_insert += diffs[pointer].Text - break - case DiffDelete: count_delete++ text_delete += diffs[pointer].Text - break - case DiffEqual: // Upon reaching an equality, check for prior redundancies. if count_delete >= 1 && count_insert >= 1 { // Delete the offending records and add the merged ones. - diffs := splice(diffs, pointer-count_delete-count_insert, + diffs = splice(diffs, pointer-count_delete-count_insert, count_delete+count_insert) pointer = pointer - count_delete - count_insert - a := dmp.DiffMain(text_delete, text_insert, false, deadline) + a := dmp.diffMain(text_delete, text_insert, false, deadline) for j := len(a) - 1; j >= 0; j-- { diffs = splice(diffs, pointer, 0, a[j]) } pointer = pointer + len(a) } - //count_insert := 0 - //count_delete := 0 - //text_delete := "" - //text_insert := "" - break + count_insert = 0 + count_delete = 0 + text_delete = "" + text_insert = "" } pointer++ } @@ -409,21 +361,24 @@ func (dmp *DiffMatchPatch) diffLineMode(text1 string, text2 string, deadline int // DiffBisect finds the 'middle snake' of a diff, split the problem in two // and return the recursively constructed diff. // See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. -func (dmp *DiffMatchPatch) DiffBisect(text1 string, text2 string, deadline int32) []Diff { +func (dmp *DiffMatchPatch) DiffBisect(text1, text2 string, deadline time.Time) []Diff { // Cache the text lengths to prevent multiple calls. - text1_length := len(text1) - text2_length := len(text2) + text1_len, text2_len := len(text1), len(text2) - max_d := int(math.Ceil(float64(((text1_length + text2_length) / 2)))) + max_d := (text1_len + text2_len + 1) / 2 v_offset := max_d v_length := 2 * max_d + v1 := make([]int, v_length) v2 := make([]int, v_length) - + for i := range v1 { + v1[i] = -1 + v2[i] = -1 + } v1[v_offset+1] = 0 v2[v_offset+1] = 0 - delta := text1_length - text2_length + delta := text1_len - text2_len // If the total number of characters is odd, then the front path will collide // with the reverse path. front := (delta%2 != 0) @@ -435,13 +390,13 @@ func (dmp *DiffMatchPatch) DiffBisect(text1 string, text2 string, deadline int32 k2end := 0 for d := 0; d < max_d; d++ { // Bail out if deadline is reached. - if int32(time.Now().Unix()) > deadline { + if time.Now().After(deadline) { break } // Walk the front path one step. for k1 := -d + k1start; k1 <= d-k1end; k1 += 2 { - var k1_offset = v_offset + k1 + k1_offset := v_offset + k1 var x1 int if k1 == -d || (k1 != d && v1[k1_offset-1] < v1[k1_offset+1]) { @@ -451,23 +406,27 @@ func (dmp *DiffMatchPatch) DiffBisect(text1 string, text2 string, deadline int32 } y1 := x1 - k1 - for x1 < text1_length && y1 < text2_length && - text1[x1] == text2[y1] { - x1++ - y1++ + for x1 < text1_len && y1 < text2_len { + r1, size := utf8.DecodeRuneInString(text1[x1:]) + r2, _ := utf8.DecodeRuneInString(text2[y1:]) + if r1 != r2 { + break + } + x1 += size + y1 += size } v1[k1_offset] = x1 - if x1 > text1_length { + if x1 > text1_len { // Ran off the right of the graph. k1end += 2 - } else if y1 > text2_length { + } else if y1 > text2_len { // Ran off the bottom of the graph. k1start += 2 } else if front { k2_offset := v_offset + delta - k1 if k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1 { // Mirror x2 onto top-left coordinate system. - x2 := text1_length - v2[k2_offset] + x2 := text1_len - v2[k2_offset] if x1 >= x2 { // Overlap detected. return dmp.diffBisectSplit_(text1, text2, x1, y1, deadline) @@ -485,17 +444,20 @@ func (dmp *DiffMatchPatch) DiffBisect(text1 string, text2 string, deadline int32 x2 = v2[k2_offset-1] + 1 } var y2 = x2 - k2 - for x2 < text1_length && - y2 < text2_length && - (text1[text1_length-x2-1] == text2[text2_length-y2-1]) { - x2++ - y2++ + for x2 < text1_len && y2 < text2_len { + r1, size := utf8.DecodeLastRuneInString(text1[:text1_len-x2]) + r2, _ := utf8.DecodeLastRuneInString(text2[:text2_len-y2]) + if r1 != r2 { + break + } + x2 += size + y2 += size } v2[k2_offset] = x2 - if x2 > text1_length { + if x2 > text1_len { // Ran off the left of the graph. k2end += 2 - } else if y2 > text2_length { + } else if y2 > text2_len { // Ran off the top of the graph. k2start += 2 } else if !front { @@ -504,7 +466,7 @@ func (dmp *DiffMatchPatch) DiffBisect(text1 string, text2 string, deadline int32 x1 := v1[k1_offset] y1 := v_offset + x1 - k1_offset // Mirror x2 onto top-left coordinate system. - x2 = text1_length - x2 + x2 = text1_len - x2 if x1 >= x2 { // Overlap detected. return dmp.diffBisectSplit_(text1, text2, x1, y1, deadline) @@ -521,22 +483,23 @@ func (dmp *DiffMatchPatch) DiffBisect(text1 string, text2 string, deadline int32 } } -func (dmp *DiffMatchPatch) diffBisectSplit_(text1 string, text2 string, x int, y int, deadline int32) []Diff { - text1a := text1[0:x] - text2a := text2[0:y] +func (dmp *DiffMatchPatch) diffBisectSplit_(text1, text2 string, x, y int, + deadline time.Time) []Diff { + text1a := text1[:x] + text2a := text2[:y] text1b := text1[x:] text2b := text2[y:] // Compute both diffs serially. - diffs := dmp.DiffMain(text1a, text2a, false, deadline) - diffsb := dmp.DiffMain(text1b, text2b, false, deadline) + diffs := dmp.diffMain(text1a, text2a, false, deadline) + diffsb := dmp.diffMain(text1b, text2b, false, deadline) return append(diffs, diffsb...) } // DiffLinesToChars split two texts into a list of strings. Reduces the texts to a string of // hashes where each Unicode character represents one line. -func (dmp *DiffMatchPatch) DiffLinesToChars(text1 string, text2 string) (string, string, []string) { +func (dmp *DiffMatchPatch) DiffLinesToChars(text1, text2 string) (string, string, []string) { // '\x00' is a valid character, but various debuggers don't like it. // So we'll insert a junk entry to avoid generating a null character. lineArray := []string{""} // e.g. lineArray[4] == 'Hello\n' @@ -585,62 +548,43 @@ func (dmp *DiffMatchPatch) diffLinesToCharsMunge(text string, lineArray *[]strin // DiffCharsToLines rehydrates the text in a diff from a string of line hashes to real lines of // text. func (dmp *DiffMatchPatch) DiffCharsToLines(diffs []Diff, lineArray []string) []Diff { + hydrated := make([]Diff, 0, len(diffs)) for _, aDiff := range diffs { chars := aDiff.Text text := make([]string, len(chars)) - for y := 0; y < len(chars); y++ { - unicodeCodePoints := []byte(chars) - text[y] = lineArray[unicodeCodePoints[y]] + for i, r := range chars { + text[i] = lineArray[r] } aDiff.Text = strings.Join(text, "") + hydrated = append(hydrated, aDiff) } - return diffs + return hydrated } // DiffCommonPrefix determines the common prefix length of two strings. -func (dmp *DiffMatchPatch) DiffCommonPrefix(text1 string, text2 string) int { - n := int(math.Min(float64(len(text1)), float64(len(text2)))) +func (dmp *DiffMatchPatch) DiffCommonPrefix(text1, text2 string) int { + n := min(len(text1), len(text2)) for i := 0; i < n; i++ { if text1[i] != text2[i] { return i } } return n - - // Binary search. - // Performance analysis: http://neil.fraser.name/news/2007/10/09/ - /* - pointermin := 0 - pointermax := math.Min(len(text1), len(text2)) - pointermid := pointermax - pointerstart := 0 - for pointermin < pointermid { - if text1[pointerstart:pointermid] == - text2[pointerstart:pointermid] { - pointermin = pointermid - pointerstart = pointermin - } else { - pointermax = pointermid - } - pointermid = math.Floor((pointermax-pointermin)/2 + pointermin) - } - return pointermid - */ } // DiffCommonSuffix determines the common suffix length of two strings. -func (dmp *DiffMatchPatch) DiffCommonSuffix(text1 string, text2 string) int { +func (dmp *DiffMatchPatch) DiffCommonSuffix(text1, text2 string) int { text1_length := len(text1) text2_length := len(text2) - n := math.Min(float64(text1_length), float64(text2_length)) - for i := 1; i <= int(n); i++ { + n := min(text1_length, text2_length) + for i := 1; i <= n; i++ { if text1[text1_length-i] != text2[text2_length-i] { return i - 1 } } - return int(n) + return n // Binary search. // Performance analysis: http://neil.fraser.name/news/2007/10/09/ /* @@ -703,7 +647,7 @@ func (dmp *DiffMatchPatch) DiffCommonOverlap(text1 string, text2 string) int { return 0 } -// DiffHalfMatch checks whether the two texts share a substring which is at +// DiffHalfMatch checks whether the two texts share a substring which is at // least half the length of the longer text. This speedup can produce non-minimal diffs. func (dmp *DiffMatchPatch) DiffHalfMatch(text1, text2 string) []string { if dmp.DiffTimeout <= 0 { @@ -720,16 +664,15 @@ func (dmp *DiffMatchPatch) DiffHalfMatch(text1, text2 string) []string { shorttext = text1 } - //TODO if len(longtext) < 4 || len(shorttext)*2 < len(longtext) { return nil // Pointless. } // First check if the second quarter is the seed for a half-match. - hm1 := dmp.diffHalfMatchI(longtext, shorttext, int(math.Ceil(float64(len(longtext)/4)))) + hm1 := dmp.diffHalfMatchI(longtext, shorttext, int(float64(len(longtext)+3)/4)) // Check again based on the third quarter. - hm2 := dmp.diffHalfMatchI(longtext, shorttext, int(math.Ceil(float64(len(longtext)/2)))) + hm2 := dmp.diffHalfMatchI(longtext, shorttext, int(float64(len(longtext)+1)/2)) hm := []string{} if hm1 == nil && hm2 == nil { @@ -813,7 +756,7 @@ func (dmp *DiffMatchPatch) diffHalfMatchI(l string, s string, i int) []string { return nil } -// Diff_cleanupSemantic reduces the number of edits by eliminating +// Diff_cleanupSemantic reduces the number of edits by eliminating // semantically trivial equalities. func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff { changes := false @@ -916,9 +859,10 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff { float64(overlap_length2) >= float64(len(insertion))/2 { // Reverse overlap found. // Insert an equality and swap and trim the surrounding edits. + overlap := Diff{DiffEqual, insertion[overlap_length2:]} diffs = append( diffs[:pointer], - append([]Diff{Diff{DiffEqual, insertion[0:overlap_length2]}}, diffs[pointer:]...)...) + append([]Diff{overlap}, diffs[pointer:]...)...) // diffs.splice(pointer, 0, // [DiffEqual, deletion[0 : overlap_length2)]] diffs[pointer-1].Type = DiffInsert @@ -1065,7 +1009,7 @@ func (dmp *DiffMatchPatch) DiffCleanupSemanticLossless(diffs []Diff) []Diff { return diffs } -// Diff_cleanupEfficiency reduces the number of edits by eliminating +// Diff_cleanupEfficiency reduces the number of edits by eliminating // operationally trivial equalities. func (dmp *DiffMatchPatch) DiffCleanupEfficiency(diffs []Diff) []Diff { changes := false @@ -1396,20 +1340,17 @@ func (dmp *DiffMatchPatch) DiffLevenshtein(diffs []Diff) int { switch aDiff.Type { case DiffInsert: insertions += len(aDiff.Text) - break case DiffDelete: deletions += len(aDiff.Text) - break case DiffEqual: // A deletion and an insertion is one substitution. - levenshtein += int(math.Max(float64(insertions), float64(deletions))) + levenshtein += max(insertions, deletions) insertions = 0 deletions = 0 - break } } - levenshtein += int(math.Max(float64(insertions), float64(deletions))) + levenshtein += max(insertions, deletions) return levenshtein } @@ -1472,43 +1413,38 @@ func (dmp *DiffMatchPatch) DiffFromDelta(text1, delta string) (diffs []Diff, err // operation of this token (delete, insert, equality). param := token[1:] - switch token[0] { + switch op := token[0]; op { case '+': // decode would Diff all "+" to " " param = strings.Replace(param, "+", "%2b", -1) param, _ = url.QueryUnescape(param) diffs = append(diffs, Diff{DiffInsert, param}) - break case '=', '-': n, err := strconv.ParseInt(param, 10, 0) if err != nil { return diffs, err - } - - if n < 0 { + } else if n < 0 { return diffs, errors.New("Negative number in DiffFromDelta: " + param) } - text := text1[pointer : pointer+int(n)] + // remember that string slicing is by byte - we want by rune here. + text := string([]rune(text1)[pointer : pointer+int(n)]) pointer += int(n) - if token[0] == '=' { + if op == '=' { diffs = append(diffs, Diff{DiffEqual, text}) } else { diffs = append(diffs, Diff{DiffDelete, text}) } - break default: // Anything else is an error. return diffs, errors.New("Invalid diff operation in DiffFromDelta: " + string(token[0])) } } - if pointer != len(text1) { - return diffs, errors.New("Delta length (" + string(pointer) + ") smaller than source text length (" + string(len(text1)) + ").") + if pointer != len([]rune(text1)) { + return diffs, fmt.Errorf("Delta length (%v) smaller than source text length (%v)", pointer, len(text1)) } - fmt.Println("") //text1, pointer, n) - return diffs, err } @@ -1516,7 +1452,7 @@ func (dmp *DiffMatchPatch) DiffFromDelta(text1, delta string) (diffs []Diff, err // MatchMain locates the best instance of 'pattern' in 'text' near 'loc'. // Returns -1 if no match found. -func (dmp *DiffMatchPatch) MatchMain(text string, pattern string, loc int) int { +func (dmp *DiffMatchPatch) MatchMain(text, pattern string, loc int) int { // Check for null inputs not needed since null can't be passed in C#. loc = int(math.Max(0, math.Min(float64(loc), float64(len(text))))) @@ -1536,7 +1472,7 @@ func (dmp *DiffMatchPatch) MatchMain(text string, pattern string, loc int) int { // MatchBitap locates the best instance of 'pattern' in 'text' near 'loc' using the // Bitap algorithm. Returns -1 if no match found. -func (dmp *DiffMatchPatch) MatchBitap(text string, pattern string, loc int) int { +func (dmp *DiffMatchPatch) MatchBitap(text, pattern string, loc int) int { // Initialise the alphabet. s := dmp.MatchAlphabet(pattern) @@ -1679,22 +1615,22 @@ func (dmp *DiffMatchPatch) PatchAddContext(patch Patch, text string) Patch { // Look for the first and last matches of pattern in text. If two // different matches are found, increase the pattern length. for strings.Index(text, pattern) != strings.LastIndex(text, pattern) && - len(pattern) < dmp.MatchMaxBits-dmp.PatchMargin-dmp.PatchMargin { + len(pattern) < dmp.MatchMaxBits-2*dmp.PatchMargin { padding += dmp.PatchMargin - maxStart := int(math.Max(0, float64(patch.start2-padding))) - minEnd := int(math.Min(float64(len(text)), float64(patch.start2+patch.length1+padding))) + maxStart := max(0, patch.start2-padding) + minEnd := min(len(text), patch.start2+patch.length1+padding) pattern = text[maxStart:minEnd] } // Add one chunk for good luck. padding += dmp.PatchMargin // Add the prefix. - prefix := text[int(math.Max(0, float64(patch.start2-padding))):int(patch.start2)] + prefix := text[max(0, patch.start2-padding):patch.start2] if len(prefix) != 0 { patch.diffs = append([]Diff{Diff{DiffEqual, prefix}}, patch.diffs...) } // Add the suffix. - suffix := text[patch.start2+patch.length1 : int(math.Min(float64(len(text)), float64(patch.start2+patch.length1+padding)))] + suffix := text[patch.start2+patch.length1 : min(len(text), patch.start2+patch.length1+padding)] if len(suffix) != 0 { patch.diffs = append(patch.diffs, Diff{DiffEqual, suffix}) } @@ -1716,17 +1652,16 @@ func (dmp *DiffMatchPatch) PatchMake(opt ...interface{}) []Patch { return dmp.PatchMake(text1, diffs) } else if len(opt) == 2 { text1 := opt[0].(string) - kind := reflect.TypeOf(opt[1]).Name() - if kind == "string" { - text2 := opt[1].(string) - diffs := dmp.DiffMain(text1, text2, true) + switch t := opt[1].(type) { + case string: + diffs := dmp.DiffMain(text1, t, true) if len(diffs) > 2 { diffs = dmp.DiffCleanupSemantic(diffs) diffs = dmp.DiffCleanupEfficiency(diffs) } return dmp.PatchMake(text1, diffs) - } else if kind == "Diff" { - return dmp.patchMake2(text1, opt[1].([]Diff)) + case []Diff: + return dmp.patchMake2(text1, t) } } else if len(opt) == 3 { return dmp.PatchMake(opt[0], opt[2]) @@ -1752,7 +1687,7 @@ func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch { prepatch_text := text1 postpatch_text := text1 - for _, aDiff := range diffs { + for i, aDiff := range diffs { if len(patch.diffs) == 0 && aDiff.Type != DiffEqual { // A new patch starts here. patch.start1 = char_count1 @@ -1763,27 +1698,24 @@ func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch { case DiffInsert: patch.diffs = append(patch.diffs, aDiff) patch.length2 += len(aDiff.Text) - postpatch_text = postpatch_text[0:char_count2] + + postpatch_text = postpatch_text[:char_count2] + aDiff.Text + postpatch_text[char_count2:] - break case DiffDelete: patch.length1 += len(aDiff.Text) patch.diffs = append(patch.diffs, aDiff) - postpatch_text = postpatch_text[0:char_count2] + postpatch_text[char_count2+len(aDiff.Text):] - break + postpatch_text = postpatch_text[:char_count2] + postpatch_text[char_count2+len(aDiff.Text):] case DiffEqual: if len(aDiff.Text) <= 2*dmp.PatchMargin && - len(patch.diffs) != 0 && aDiff != diffs[len(diffs)-1] { + len(patch.diffs) != 0 && i != len(diffs)-1 { // Small equality inside a patch. patch.diffs = append(patch.diffs, aDiff) patch.length1 += len(aDiff.Text) patch.length2 += len(aDiff.Text) } - if len(aDiff.Text) >= 2*dmp.PatchMargin { // Time for a new patch. if len(patch.diffs) != 0 { - dmp.PatchAddContext(patch, prepatch_text) + patch = dmp.PatchAddContext(patch, prepatch_text) patches = append(patches, patch) patch = Patch{} // Unlike Unidiff, our patch lists have a rolling context. @@ -1794,7 +1726,6 @@ func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch { char_count1 = char_count2 } } - break } // Update the current character count. @@ -1805,16 +1736,17 @@ func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch { char_count2 += len(aDiff.Text) } } + // Pick up the leftover patch if not empty. if len(patch.diffs) != 0 { - dmp.PatchAddContext(patch, prepatch_text) + patch = dmp.PatchAddContext(patch, prepatch_text) patches = append(patches, patch) } return patches } -// PatchDeepCopy returns an array that is identical to a +// PatchDeepCopy returns an array that is identical to a // given an array of patches. func (dmp *DiffMatchPatch) PatchDeepCopy(patches []Patch) []Patch { patchesCopy := []Patch{} @@ -1847,7 +1779,7 @@ func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []b nullPadding := dmp.PatchAddPadding(patches) text = nullPadding + text + nullPadding - dmp.PatchSplitMax(patches) + patches = dmp.PatchSplitMax(patches) x := 0 // delta keeps track of the offset between the expected and actual @@ -1855,7 +1787,7 @@ func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []b // positions 10 and 20, but the first patch was found at 12, delta is 2 // and the second patch has an effective expected position of 22. delta := 0 - results := []bool{} + results := make([]bool, len(patches)) for _, aPatch := range patches { expected_loc := aPatch.start2 + delta text1 := dmp.DiffText1(aPatch.diffs) @@ -1898,7 +1830,7 @@ func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []b // Imperfect match. Run a diff to get a framework of equivalent // indices. diffs := dmp.DiffMain(text1, text2, false) - if len(text1) > dmp.MatchMaxBits && float64(dmp.DiffLevenshtein(diffs)/len(text1)) > dmp.PatchDeleteThreshold { + if len(text1) > dmp.MatchMaxBits && float64(dmp.DiffLevenshtein(diffs))/float64(len(text1)) > dmp.PatchDeleteThreshold { // The end points match, but the content is unacceptably bad. results[x] = false } else { @@ -1937,51 +1869,47 @@ func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string { paddingLength := dmp.PatchMargin nullPadding := "" for x := 1; x <= paddingLength; x++ { - nullPadding += strconv.FormatInt(int64(x), 10) + nullPadding += string(x) } // Bump all the patches forward. - for _, aPatch := range patches { - aPatch.start1 += paddingLength - aPatch.start2 += paddingLength + for i, _ := range patches { + patches[i].start1 += paddingLength + patches[i].start2 += paddingLength } // Add some padding on start of first diff. - patch := patches[0] - diffs := patch.diffs - if len(diffs) == 0 || diffs[0].Type != DiffEqual { + if len(patches[0].diffs) == 0 || patches[0].diffs[0].Type != DiffEqual { // Add nullPadding equality. - diffs = append(diffs, Diff{DiffEqual, nullPadding}) - patch.start1 -= paddingLength // Should be 0. - patch.start2 -= paddingLength // Should be 0. - patch.length1 += paddingLength - patch.length2 += paddingLength - } else if paddingLength > len(diffs[0].Text) { + patches[0].diffs = append([]Diff{Diff{DiffEqual, nullPadding}}, patches[0].diffs...) + patches[0].start1 -= paddingLength // Should be 0. + patches[0].start2 -= paddingLength // Should be 0. + patches[0].length1 += paddingLength + patches[0].length2 += paddingLength + } else if paddingLength > len(patches[0].diffs[0].Text) { // Grow first equality. - firstDiff := diffs[0] - extraLength := paddingLength - len(firstDiff.Text) - firstDiff.Text = nullPadding[len(firstDiff.Text):] + firstDiff.Text - patch.start1 -= extraLength - patch.start2 -= extraLength - patch.length1 += extraLength - patch.length2 += extraLength + extraLength := paddingLength - len(patches[0].diffs[0].Text) + patches[0].diffs[0].Text = nullPadding[len(patches[0].diffs[0].Text):] + patches[0].diffs[0].Text + patches[0].start1 -= extraLength + patches[0].start2 -= extraLength + patches[0].length1 += extraLength + patches[0].length2 += extraLength } // Add some padding on end of last diff. - patch = patches[len(patches)-1] - diffs = patch.diffs - if len(diffs) == 0 || diffs[len(diffs)-1].Type != DiffEqual { + last := len(patches) - 1 + if len(patches[last].diffs) == 0 || patches[last].diffs[len(patches[last].diffs)-1].Type != DiffEqual { // Add nullPadding equality. - diffs = append(diffs, Diff{DiffEqual, nullPadding}) - patch.length1 += paddingLength - patch.length2 += paddingLength - } else if paddingLength > len(diffs[len(diffs)-1].Text) { + patches[last].diffs = append(patches[last].diffs, Diff{DiffEqual, nullPadding}) + patches[last].length1 += paddingLength + patches[last].length2 += paddingLength + } else if paddingLength > len(patches[last].diffs[len(patches[last].diffs)-1].Text) { // Grow last equality. - lastDiff := diffs[len(diffs)-1] + lastDiff := patches[last].diffs[len(patches[last].diffs)-1] extraLength := paddingLength - len(lastDiff.Text) - lastDiff.Text += nullPadding[0:extraLength] - patch.length1 += extraLength - patch.length2 += extraLength + patches[last].diffs[len(patches[last].diffs)-1].Text += nullPadding[:extraLength] + patches[last].length1 += extraLength + patches[last].length2 += extraLength } return nullPadding @@ -1990,7 +1918,7 @@ func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string { // PatchSplitMax looks through the patches and breaks up any which are longer than the // maximum limit of the match algorithm. // Intended to be called only from within patch_apply. -func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) { +func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) []Patch { patch_size := dmp.MatchMaxBits for x := 0; x < len(patches); x++ { if patches[x].length1 <= patch_size { @@ -1998,8 +1926,9 @@ func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) { } bigpatch := patches[x] // Remove the big old patch. - x = x - 1 - patches = splice_patch(patches, x, 1) + patches = append(patches[:x], patches[x+1:]...) + x -= 1 + start1 := bigpatch.start1 start2 := bigpatch.start2 precontext := "" @@ -2022,7 +1951,7 @@ func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) { patch.length2 += len(diff_text) start2 += len(diff_text) patch.diffs = append(patch.diffs, bigpatch.diffs[0]) - bigpatch.diffs = append(bigpatch.diffs[:0], bigpatch.diffs[0:]...) + bigpatch.diffs = bigpatch.diffs[1:] empty = false } else if diff_type == DiffDelete && len(patch.diffs) == 1 && patch.diffs[0].Type == DiffEqual && len(diff_text) > 2*patch_size { // This is a large deletion. Let it pass in one chunk. @@ -2030,11 +1959,10 @@ func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) { start1 += len(diff_text) empty = false patch.diffs = append(patch.diffs, Diff{diff_type, diff_text}) - bigpatch.diffs = append(bigpatch.diffs[:0], bigpatch.diffs[0:]...) + bigpatch.diffs = bigpatch.diffs[1:] } else { // Deletion or equality. Only take as much as we can stomach. - diff_text = diff_text[0:int(math.Min(float64(len(diff_text)), - float64(patch_size-patch.length1-dmp.PatchMargin)))] + diff_text = diff_text[:min(len(diff_text), patch_size-patch.length1-dmp.PatchMargin)] patch.length1 += len(diff_text) start1 += len(diff_text) @@ -2046,7 +1974,7 @@ func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) { } patch.diffs = append(patch.diffs, Diff{diff_type, diff_text}) if diff_text == bigpatch.diffs[0].Text { - bigpatch.diffs = append(bigpatch.diffs[:0], bigpatch.diffs[0:]...) + bigpatch.diffs = bigpatch.diffs[1:] } else { bigpatch.diffs[0].Text = bigpatch.diffs[0].Text[len(diff_text):] @@ -2055,12 +1983,12 @@ func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) { } // Compute the head context for the next patch. precontext = dmp.DiffText2(patch.diffs) - precontext = precontext[int(math.Max(0, float64(len(precontext)-dmp.PatchMargin))):] + precontext = precontext[max(0, len(precontext)-dmp.PatchMargin):] postcontext := "" // Append the end context for this patch. if len(dmp.DiffText1(bigpatch.diffs)) > dmp.PatchMargin { - postcontext = dmp.DiffText1(bigpatch.diffs)[0:dmp.PatchMargin] + postcontext = dmp.DiffText1(bigpatch.diffs)[:dmp.PatchMargin] } else { postcontext = dmp.DiffText1(bigpatch.diffs) } @@ -2075,11 +2003,12 @@ func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) { } } if !empty { - x = x + 1 - splice_patch(patches, x, 0, patch) + x += 1 + patches = append(patches[:x], append([]Patch{patch}, patches[x:]...)...) } } } + return patches } // PatchToText takes a list of patches and returns a textual representation. diff --git a/diff/dmp_test.go b/diff/dmp_test.go index 4522b6b..1ca1713 100644 --- a/diff/dmp_test.go +++ b/diff/dmp_test.go @@ -1,70 +1,98 @@ package diffmatchpatch import ( + "bytes" "fmt" - "github.com/bmizerany/assert" "reflect" + "runtime" "strconv" "testing" "time" + + "github.com/stretchrcom/testify/assert" ) -func softAssert(t *testing.T, cond bool, msg string) { - if !cond { - print("assertion fail: ", msg, "\n") - panic(1) +func caller() string { + if _, _, line, ok := runtime.Caller(2); ok { + return fmt.Sprintf("(actual-line %v) ", line) } + return "" } -func assertSeqEqual(seq1, seq2 interface{}) { - var fail = func(msg string) { - fmt.Println("assertion fail: \n", msg, "\n") - panic(1) +func pretty(diffs []Diff) string { + var w bytes.Buffer + for i, diff := range diffs { + w.WriteString(fmt.Sprintf("%v. ", i)) + switch diff.Type { + case DiffInsert: + w.WriteString("DiffIns") + case DiffDelete: + w.WriteString("DiffDel") + case DiffEqual: + w.WriteString("DiffEql") + default: + w.WriteString("Unknown") + } + w.WriteString(fmt.Sprintf(": %v\n", diff.Text)) } + return w.String() +} +func assertMapEqual(t *testing.T, seq1, seq2 interface{}) { v1 := reflect.ValueOf(seq1) k1 := v1.Kind() v2 := reflect.ValueOf(seq2) k2 := v2.Kind() - if k1 != reflect.Array && k1 != reflect.Slice && k1 != reflect.Map { - fail("Parameters are not slices or Arrays") + if k1 != reflect.Map || k2 != reflect.Map { + t.Fatalf("%v Parameters are not maps", caller()) + } else if v1.Len() != v2.Len() { + t.Fatalf("%v Maps of different length: %v != %v", caller(), v1.Len(), v2.Len()) } - if k2 != reflect.Array && k2 != reflect.Slice && k2 != reflect.Map { - fail("Parameters are not slices or Arrays") - } + keys1, keys2 := v1.MapKeys(), v2.MapKeys() - if v1.Len() != v2.Len() { - fail("Sequences of different length:\n" + string(v1.Len()) + "\n" + string(v2.Len())) + if len(keys1) != len(keys2) { + t.Fatalf("%v Maps of different length", caller()) } - if k1 == reflect.Map && k2 == reflect.Map { - keys1 := v1.MapKeys() - keys2 := v2.MapKeys() - - if len(keys1) != len(keys2) { - fail("Maps of different length") + for _, key1 := range keys1 { + if a, b := v2.MapIndex(key1), v1.MapIndex(key1); a != b { + t.Fatal("%v Different key/value in Map: %v != %v", caller(), a, b) } + } - for _, key1 := range keys1 { - if v2.MapIndex(key1) != v1.MapIndex(key1) { - fail("Different key/value in Map.") - } + for _, key2 := range keys2 { + if a, b := v1.MapIndex(key2), v2.MapIndex(key2); a != b { + t.Fatal("%v Different key/value in Map: %v != %v", caller(), a, b) } + } +} + +func assertDiffEqual(t *testing.T, seq1, seq2 []Diff) { + if a, b := len(seq1), len(seq2); a != b { + t.Errorf("%v\nseq1:\n%v\nseq2:\n%v", caller(), pretty(seq1), pretty(seq2)) + t.Errorf("%v Sequences of different length: %v != %v", caller(), a, b) + return + } - for _, key2 := range keys2 { - if v1.MapIndex(key2) != v2.MapIndex(key2) { - fail("Different key/value in Map.") - } + for i := range seq1 { + if a, b := seq1[i], seq2[i]; a != b { + t.Errorf("%v\nseq1:\n%v\nseq2:\n%v", caller(), pretty(seq1), pretty(seq2)) + t.Errorf("%v %v != %v", caller(), a, b) + return } - } else { - for i := 0; i < v1.Len(); i++ { - if v1.Index(i).String() != v2.Index(i).String() { - fail("[" + v1.Index(i).Kind().String() + "] " + v1.Index(i).String() + - " != [" + v2.Index(i).Kind().String() + "] " + v2.Index(i).String()) - break - } + } +} + +func assertStrEqual(t *testing.T, seq1, seq2 []string) { + if a, b := len(seq1), len(seq2); a != b { + t.Fatalf("%v Sequences of different length: %v != %v", caller(), a, b) + } + + for i := range seq1 { + if a, b := seq1[i], seq2[i]; a != b { + t.Fatalf("%v %v != %v", caller(), a, b) } } } @@ -83,7 +111,7 @@ func diffRebuildtexts(diffs []Diff) []string { } func Test_diffCommonPrefix(t *testing.T) { - dmp := createDMP() + dmp := New() // Detect any common suffix. // Null case. assert.Equal(t, 0, dmp.DiffCommonPrefix("abc", "xyz"), "'abc' and 'xyz' should not be equal") @@ -96,7 +124,7 @@ func Test_diffCommonPrefix(t *testing.T) { } func Test_diffCommonSuffixTest(t *testing.T) { - dmp := createDMP() + dmp := New() // Detect any common suffix. // Null case. assert.Equal(t, 0, dmp.DiffCommonSuffix("abc", "xyz"), "") @@ -109,7 +137,7 @@ func Test_diffCommonSuffixTest(t *testing.T) { } func Test_diffCommonOverlapTest(t *testing.T) { - dmp := createDMP() + dmp := New() // Detect any suffix/prefix overlap. // Null case. assert.Equal(t, 0, dmp.DiffCommonOverlap("", "abcd"), "") @@ -130,60 +158,60 @@ func Test_diffCommonOverlapTest(t *testing.T) { } func Test_diffHalfmatchTest(t *testing.T) { - dmp := createDMP() + dmp := New() dmp.DiffTimeout = 1 // No match. - softAssert(t, dmp.DiffHalfMatch("1234567890", "abcdef") == nil, "") - softAssert(t, dmp.DiffHalfMatch("12345", "23") == nil, "") + assert.True(t, dmp.DiffHalfMatch("1234567890", "abcdef") == nil, "") + assert.True(t, dmp.DiffHalfMatch("12345", "23") == nil, "") // Single Match. - assertSeqEqual( + assertStrEqual(t, []string{"12", "90", "a", "z", "345678"}, dmp.DiffHalfMatch("1234567890", "a345678z")) - assertSeqEqual([]string{"a", "z", "12", "90", "345678"}, dmp.DiffHalfMatch("a345678z", "1234567890")) + assertStrEqual(t, []string{"a", "z", "12", "90", "345678"}, dmp.DiffHalfMatch("a345678z", "1234567890")) - assertSeqEqual([]string{"abc", "z", "1234", "0", "56789"}, dmp.DiffHalfMatch("abc56789z", "1234567890")) + assertStrEqual(t, []string{"abc", "z", "1234", "0", "56789"}, dmp.DiffHalfMatch("abc56789z", "1234567890")) - assertSeqEqual([]string{"a", "xyz", "1", "7890", "23456"}, dmp.DiffHalfMatch("a23456xyz", "1234567890")) + assertStrEqual(t, []string{"a", "xyz", "1", "7890", "23456"}, dmp.DiffHalfMatch("a23456xyz", "1234567890")) // Multiple Matches. - assertSeqEqual([]string{"12123", "123121", "a", "z", "1234123451234"}, dmp.DiffHalfMatch("121231234123451234123121", "a1234123451234z")) + assertStrEqual(t, []string{"12123", "123121", "a", "z", "1234123451234"}, dmp.DiffHalfMatch("121231234123451234123121", "a1234123451234z")) - assertSeqEqual([]string{"", "-=-=-=-=-=", "x", "", "x-=-=-=-=-=-=-="}, dmp.DiffHalfMatch("x-=-=-=-=-=-=-=-=-=-=-=-=", "xx-=-=-=-=-=-=-=")) + assertStrEqual(t, []string{"", "-=-=-=-=-=", "x", "", "x-=-=-=-=-=-=-="}, dmp.DiffHalfMatch("x-=-=-=-=-=-=-=-=-=-=-=-=", "xx-=-=-=-=-=-=-=")) - assertSeqEqual([]string{"-=-=-=-=-=", "", "", "y", "-=-=-=-=-=-=-=y"}, dmp.DiffHalfMatch("-=-=-=-=-=-=-=-=-=-=-=-=y", "-=-=-=-=-=-=-=yy")) + assertStrEqual(t, []string{"-=-=-=-=-=", "", "", "y", "-=-=-=-=-=-=-=y"}, dmp.DiffHalfMatch("-=-=-=-=-=-=-=-=-=-=-=-=y", "-=-=-=-=-=-=-=yy")) // Non-optimal halfmatch. // Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy - assertSeqEqual([]string{"qHillo", "w", "x", "Hulloy", "HelloHe"}, dmp.DiffHalfMatch("qHilloHelloHew", "xHelloHeHulloy")) + assertStrEqual(t, []string{"qHillo", "w", "x", "Hulloy", "HelloHe"}, dmp.DiffHalfMatch("qHilloHelloHew", "xHelloHeHulloy")) // Optimal no halfmatch. dmp.DiffTimeout = 0 - softAssert(t, dmp.DiffHalfMatch("qHilloHelloHew", "xHelloHeHulloy") == nil, "") + assert.True(t, dmp.DiffHalfMatch("qHilloHelloHew", "xHelloHeHulloy") == nil, "") } func Test_diffLinesToChars(t *testing.T) { - dmp := createDMP() + dmp := New() // Convert lines down to characters. tmpVector := []string{"", "alpha\n", "beta\n"} result0, result1, result2 := dmp.DiffLinesToChars("alpha\nbeta\nalpha\n", "beta\nalpha\nbeta\n") assert.Equal(t, "\u0001\u0002\u0001", result0, "") assert.Equal(t, "\u0002\u0001\u0002", result1, "") - assertSeqEqual(tmpVector, result2) + assertStrEqual(t, tmpVector, result2) tmpVector = []string{"", "alpha\r\n", "beta\r\n", "\r\n"} result0, result1, result2 = dmp.DiffLinesToChars("", "alpha\r\nbeta\r\n\r\n\r\n") assert.Equal(t, "", result0, "") assert.Equal(t, "\u0001\u0002\u0003\u0003", result1, "") - assertSeqEqual(tmpVector, result2) + assertStrEqual(t, tmpVector, result2) tmpVector = []string{"", "a", "b"} result0, result1, result2 = dmp.DiffLinesToChars("a", "b") assert.Equal(t, "\u0001", result0, "") assert.Equal(t, "\u0002", result1, "") - assertSeqEqual(tmpVector, result2) + assertStrEqual(t, tmpVector, result2) // More than 256 to reveal any 8-bit limitations. /* @@ -208,30 +236,30 @@ func Test_diffLinesToChars(t *testing.T) { assert.Equal(t, chars, result0) assert.Equal(t, "", result1, "") - assertSeqEqual(tmpVector, result2) + assertDiffEqual(t, tmpVector, result2) */ } func Test_diffCharsToLines(t *testing.T) { - dmp := createDMP() + dmp := New() // Convert chars up to lines. diffs := []Diff{ Diff{DiffEqual, "\u0001\u0002\u0001"}, Diff{DiffInsert, "\u0002\u0001\u0002"}} tmpVector := []string{"", "alpha\n", "beta\n"} - dmp.DiffCharsToLines(diffs, tmpVector) - assertSeqEqual([]Diff{ + actual := dmp.DiffCharsToLines(diffs, tmpVector) + assertDiffEqual(t, []Diff{ Diff{DiffEqual, "alpha\nbeta\nalpha\n"}, - Diff{DiffInsert, "beta\nalpha\nbeta\n"}}, diffs) + Diff{DiffInsert, "beta\nalpha\nbeta\n"}}, actual) // More than 256 to reveal any 8-bit limitations. - n := 300 + n := 257 tmpVector = []string{} lineList := []rune{} charList := []rune{} - for x := 1; x < n+1; x++ { + for x := 1; x <= n; x++ { tmpVector = append(tmpVector, string(x)+"\n") lineList = append(lineList, rune(x), '\n') charList = append(charList, rune(x)) @@ -242,83 +270,83 @@ func Test_diffCharsToLines(t *testing.T) { tmpVector = append([]string{""}, tmpVector...) diffs = []Diff{Diff{DiffDelete, string(charList)}} - dmp.DiffCharsToLines(diffs, tmpVector) - assertSeqEqual([]Diff{ - Diff{DiffDelete, string(lineList)}}, diffs) + actual = dmp.DiffCharsToLines(diffs, tmpVector) + assertDiffEqual(t, []Diff{ + Diff{DiffDelete, string(lineList)}}, actual) } func Test_diffCleanupMerge(t *testing.T) { - dmp := createDMP() + dmp := New() // Cleanup a messy diff. // Null case. diffs := []Diff{} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{}, diffs) + assertDiffEqual(t, []Diff{}, diffs) // No Diff case. diffs = []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffInsert, "c"}} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffInsert, "c"}}, diffs) + assertDiffEqual(t, []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffInsert, "c"}}, diffs) // Merge equalities. diffs = []Diff{Diff{DiffEqual, "a"}, Diff{DiffEqual, "b"}, Diff{DiffEqual, "c"}} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{Diff{DiffEqual, "abc"}}, diffs) + assertDiffEqual(t, []Diff{Diff{DiffEqual, "abc"}}, diffs) // Merge deletions. diffs = []Diff{Diff{DiffDelete, "a"}, Diff{DiffDelete, "b"}, Diff{DiffDelete, "c"}} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{Diff{DiffDelete, "abc"}}, diffs) + assertDiffEqual(t, []Diff{Diff{DiffDelete, "abc"}}, diffs) // Merge insertions. diffs = []Diff{Diff{DiffInsert, "a"}, Diff{DiffInsert, "b"}, Diff{DiffInsert, "c"}} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{Diff{DiffInsert, "abc"}}, diffs) + assertDiffEqual(t, []Diff{Diff{DiffInsert, "abc"}}, diffs) // Merge interweave. diffs = []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "b"}, Diff{DiffDelete, "c"}, Diff{DiffInsert, "d"}, Diff{DiffEqual, "e"}, Diff{DiffEqual, "f"}} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{Diff{DiffDelete, "ac"}, Diff{DiffInsert, "bd"}, Diff{DiffEqual, "ef"}}, diffs) + assertDiffEqual(t, []Diff{Diff{DiffDelete, "ac"}, Diff{DiffInsert, "bd"}, Diff{DiffEqual, "ef"}}, diffs) // Prefix and suffix detection. diffs = []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "abc"}, Diff{DiffDelete, "dc"}} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "c"}}, diffs) + assertDiffEqual(t, []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "c"}}, diffs) // Prefix and suffix detection with equalities. diffs = []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "a"}, Diff{DiffInsert, "abc"}, Diff{DiffDelete, "dc"}, Diff{DiffEqual, "y"}} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{Diff{DiffEqual, "xa"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "cy"}}, diffs) + assertDiffEqual(t, []Diff{Diff{DiffEqual, "xa"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "cy"}}, diffs) // Slide edit left. diffs = []Diff{Diff{DiffEqual, "a"}, Diff{DiffInsert, "ba"}, Diff{DiffEqual, "c"}} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{Diff{DiffInsert, "ab"}, Diff{DiffEqual, "ac"}}, diffs) + assertDiffEqual(t, []Diff{Diff{DiffInsert, "ab"}, Diff{DiffEqual, "ac"}}, diffs) // Slide edit right. diffs = []Diff{Diff{DiffEqual, "c"}, Diff{DiffInsert, "ab"}, Diff{DiffEqual, "a"}} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{Diff{DiffEqual, "ca"}, Diff{DiffInsert, "ba"}}, diffs) + assertDiffEqual(t, []Diff{Diff{DiffEqual, "ca"}, Diff{DiffInsert, "ba"}}, diffs) // Slide edit left recursive. diffs = []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffEqual, "c"}, Diff{DiffDelete, "ac"}, Diff{DiffEqual, "x"}} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{Diff{DiffDelete, "abc"}, Diff{DiffEqual, "acx"}}, diffs) + assertDiffEqual(t, []Diff{Diff{DiffDelete, "abc"}, Diff{DiffEqual, "acx"}}, diffs) // Slide edit right recursive. diffs = []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "ca"}, Diff{DiffEqual, "c"}, Diff{DiffDelete, "b"}, Diff{DiffEqual, "a"}} diffs = dmp.DiffCleanupMerge(diffs) - assertSeqEqual([]Diff{Diff{DiffEqual, "xca"}, Diff{DiffDelete, "cba"}}, diffs) + assertDiffEqual(t, []Diff{Diff{DiffEqual, "xca"}, Diff{DiffDelete, "cba"}}, diffs) } func Test_diffCleanupSemanticLossless(t *testing.T) { - dmp := createDMP() + dmp := New() // Slide diffs to match logical boundaries. // Null case. diffs := []Diff{} dmp.DiffCleanupSemanticLossless(diffs) - assertSeqEqual([]Diff{}, diffs) + assertDiffEqual(t, []Diff{}, diffs) // Blank lines. diffs = []Diff{ @@ -329,7 +357,7 @@ func Test_diffCleanupSemanticLossless(t *testing.T) { dmp.DiffCleanupSemanticLossless(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffEqual, "AAA\r\n\r\n"}, Diff{DiffInsert, "BBB\r\nDDD\r\n\r\n"}, Diff{DiffEqual, "BBB\r\nEEE"}}, diffs) @@ -342,7 +370,7 @@ func Test_diffCleanupSemanticLossless(t *testing.T) { dmp.DiffCleanupSemanticLossless(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffEqual, "AAA\r\n"}, Diff{DiffInsert, "BBB DDD\r\n"}, Diff{DiffEqual, "BBB EEE"}}, diffs) @@ -355,7 +383,7 @@ func Test_diffCleanupSemanticLossless(t *testing.T) { dmp.DiffCleanupSemanticLossless(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffEqual, "The "}, Diff{DiffInsert, "cow and the "}, Diff{DiffEqual, "cat."}}, diffs) @@ -368,7 +396,7 @@ func Test_diffCleanupSemanticLossless(t *testing.T) { dmp.DiffCleanupSemanticLossless(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffEqual, "The-"}, Diff{DiffInsert, "cow-and-the-"}, Diff{DiffEqual, "cat."}}, diffs) @@ -381,7 +409,7 @@ func Test_diffCleanupSemanticLossless(t *testing.T) { dmp.DiffCleanupSemanticLossless(diffs) - /*assertSeqEqual([]Diff{ + /*assertDiffEqual(t, []Diff{ Diff{DiffDelete, "a"}, Diff{DiffEqual, "aax"}}, diffs)*/ @@ -393,7 +421,7 @@ func Test_diffCleanupSemanticLossless(t *testing.T) { dmp.DiffCleanupSemanticLossless(diffs) /* - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffEqual, "xaa"}, Diff{DiffDelete, "a"}}, diffs) */ @@ -405,19 +433,19 @@ func Test_diffCleanupSemanticLossless(t *testing.T) { dmp.DiffCleanupSemanticLossless(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffEqual, "The xxx."}, Diff{DiffInsert, " The zzz."}, Diff{DiffEqual, " The yyy."}}, diffs) } func Test_diffCleanupSemantic(t *testing.T) { - dmp := createDMP() + dmp := New() // Cleanup semantically trivial equalities. // Null case. diffs := []Diff{} diffs = dmp.DiffCleanupSemantic(diffs) - assertSeqEqual([]Diff{}, diffs) + assertDiffEqual(t, []Diff{}, diffs) // No elimination #1. diffs = []Diff{ @@ -426,7 +454,7 @@ func Test_diffCleanupSemantic(t *testing.T) { Diff{DiffEqual, "12"}, Diff{DiffDelete, "e"}} diffs = dmp.DiffCleanupSemantic(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "ab"}, Diff{DiffInsert, "cd"}, Diff{DiffEqual, "12"}, @@ -439,7 +467,7 @@ func Test_diffCleanupSemantic(t *testing.T) { Diff{DiffEqual, "1234"}, Diff{DiffDelete, "wxyz"}} diffs = dmp.DiffCleanupSemantic(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "abc"}, Diff{DiffInsert, "ABC"}, Diff{DiffEqual, "1234"}, @@ -451,7 +479,7 @@ func Test_diffCleanupSemantic(t *testing.T) { Diff{DiffEqual, "b"}, Diff{DiffDelete, "c"}} diffs = dmp.DiffCleanupSemantic(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "abc"}, Diff{DiffInsert, "b"}}, diffs) @@ -463,7 +491,7 @@ func Test_diffCleanupSemantic(t *testing.T) { Diff{DiffEqual, "f"}, Diff{DiffInsert, "g"}} diffs = dmp.DiffCleanupSemantic(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "abcdef"}, Diff{DiffInsert, "cdfg"}}, diffs) @@ -479,7 +507,7 @@ func Test_diffCleanupSemantic(t *testing.T) { Diff{DiffDelete, "B"}, Diff{DiffInsert, "2"}} diffs = dmp.DiffCleanupSemantic(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "AB_AB"}, Diff{DiffInsert, "1A2_1A2"}}, diffs) @@ -489,7 +517,7 @@ func Test_diffCleanupSemantic(t *testing.T) { Diff{DiffDelete, "ow and the c"}, Diff{DiffEqual, "at."}} diffs = dmp.DiffCleanupSemantic(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffEqual, "The "}, Diff{DiffDelete, "cow and the "}, Diff{DiffEqual, "cat."}}, diffs) @@ -499,7 +527,7 @@ func Test_diffCleanupSemantic(t *testing.T) { Diff{DiffDelete, "abcxx"}, Diff{DiffInsert, "xxdef"}} diffs = dmp.DiffCleanupSemantic(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "abcxx"}, Diff{DiffInsert, "xxdef"}}, diffs) @@ -508,7 +536,7 @@ func Test_diffCleanupSemantic(t *testing.T) { Diff{DiffDelete, "abcxxx"}, Diff{DiffInsert, "xxxdef"}} diffs = dmp.DiffCleanupSemantic(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "abc"}, Diff{DiffEqual, "xxx"}, Diff{DiffInsert, "def"}}, diffs) @@ -518,7 +546,7 @@ func Test_diffCleanupSemantic(t *testing.T) { Diff{DiffDelete, "xxxabc"}, Diff{DiffInsert, "defxxx"}} diffs = dmp.DiffCleanupSemantic(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffInsert, "def"}, Diff{DiffEqual, "xxx"}, Diff{DiffDelete, "abc"}}, diffs) @@ -531,7 +559,7 @@ func Test_diffCleanupSemantic(t *testing.T) { Diff{DiffDelete, "A3"}, Diff{DiffInsert, "3BC"}} diffs = dmp.DiffCleanupSemantic(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "abcd"}, Diff{DiffEqual, "1212"}, Diff{DiffInsert, "efghi"}, @@ -542,13 +570,13 @@ func Test_diffCleanupSemantic(t *testing.T) { } func Test_diffCleanupEfficiency(t *testing.T) { - dmp := createDMP() + dmp := New() // Cleanup operationally trivial equalities. dmp.DiffEditCost = 4 // Null case. diffs := []Diff{} diffs = dmp.DiffCleanupEfficiency(diffs) - assertSeqEqual([]Diff{}, diffs) + assertDiffEqual(t, []Diff{}, diffs) // No elimination. diffs = []Diff{ @@ -558,7 +586,7 @@ func Test_diffCleanupEfficiency(t *testing.T) { Diff{DiffDelete, "cd"}, Diff{DiffInsert, "34"}} diffs = dmp.DiffCleanupEfficiency(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "ab"}, Diff{DiffInsert, "12"}, Diff{DiffEqual, "wxyz"}, @@ -573,7 +601,7 @@ func Test_diffCleanupEfficiency(t *testing.T) { Diff{DiffDelete, "cd"}, Diff{DiffInsert, "34"}} diffs = dmp.DiffCleanupEfficiency(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "abxyzcd"}, Diff{DiffInsert, "12xyz34"}}, diffs) @@ -584,7 +612,7 @@ func Test_diffCleanupEfficiency(t *testing.T) { Diff{DiffDelete, "cd"}, Diff{DiffInsert, "34"}} diffs = dmp.DiffCleanupEfficiency(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "xcd"}, Diff{DiffInsert, "12x34"}}, diffs) @@ -598,7 +626,7 @@ func Test_diffCleanupEfficiency(t *testing.T) { Diff{DiffDelete, "cd"}, Diff{DiffInsert, "56"}} diffs = dmp.DiffCleanupEfficiency(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "abxyzcd"}, Diff{DiffInsert, "12xy34z56"}}, diffs) @@ -611,7 +639,7 @@ func Test_diffCleanupEfficiency(t *testing.T) { Diff{DiffDelete, "cd"}, Diff{DiffInsert, "34"}} diffs = dmp.DiffCleanupEfficiency(diffs) - assertSeqEqual([]Diff{ + assertDiffEqual(t, []Diff{ Diff{DiffDelete, "abwxyzcd"}, Diff{DiffInsert, "12wxyz34"}}, diffs) dmp.DiffEditCost = 4 @@ -619,7 +647,7 @@ func Test_diffCleanupEfficiency(t *testing.T) { /* func Test_diffPrettyHtml(t *testing.T) { - dmp := createDMP() + dmp := New() // Pretty print. diffs := []Diff{ Diff{DiffEqual, "a\n"}, @@ -630,7 +658,7 @@ func Test_diffPrettyHtml(t *testing.T) { }*/ func Test_diffText(t *testing.T) { - dmp := createDMP() + dmp := New() // Compute the source and destination texts. diffs := []Diff{ Diff{DiffEqual, "jump"}, @@ -645,7 +673,7 @@ func Test_diffText(t *testing.T) { } func Test_diffDelta(t *testing.T) { - dmp := createDMP() + dmp := New() // Convert a diff into delta string. diffs := []Diff{ Diff{DiffEqual, "jump"}, @@ -665,18 +693,18 @@ func Test_diffDelta(t *testing.T) { // Convert delta string into a diff. _seq1, err := dmp.DiffFromDelta(text1, delta) - assertSeqEqual(diffs, _seq1) + assertDiffEqual(t, diffs, _seq1) // Generates error (19 < 20). _, err = dmp.DiffFromDelta(text1+"x", delta) if err == nil { - panic(1) //assert.Fail("diff_fromDelta: Too long."); + t.Fatal("diff_fromDelta: Too long.") } // Generates error (19 > 18). _, err = dmp.DiffFromDelta(text1[1:], delta) if err == nil { - panic(1) //assert.Fail("diff_fromDelta: Too short."); + t.Fatal("diff_fromDelta: Too short.") } // Generates error (%c3%xy invalid Unicode). @@ -699,8 +727,11 @@ func Test_diffDelta(t *testing.T) { // Lowercase, due to UrlEncode uses lower. assert.Equal(t, "=7\t-7\t+%DA%82 %02 %5C %7C", delta) - _res1, _ := dmp.DiffFromDelta(text1, delta) - assertSeqEqual(diffs, _res1) + _res1, err := dmp.DiffFromDelta(text1, delta) + if err != nil { + t.Fatal(err) + } + assertDiffEqual(t, diffs, _res1) // Verify pool of unchanged characters. diffs = []Diff{ @@ -713,11 +744,11 @@ func Test_diffDelta(t *testing.T) { // Convert delta string into a diff. _res2, _ := dmp.DiffFromDelta("", delta) - assertSeqEqual(diffs, _res2) + assertDiffEqual(t, diffs, _res2) } func Test_diffXIndex(t *testing.T) { - dmp := createDMP() + dmp := New() // Translate a location in text1 to text2. diffs := []Diff{ Diff{DiffDelete, "a"}, @@ -733,7 +764,7 @@ func Test_diffXIndex(t *testing.T) { } func Test_diffLevenshtein(t *testing.T) { - dmp := createDMP() + dmp := New() diffs := []Diff{ Diff{DiffDelete, "abc"}, Diff{DiffInsert, "1234"}, @@ -754,13 +785,13 @@ func Test_diffLevenshtein(t *testing.T) { } func Test_diffBisect(t *testing.T) { - dmp := createDMP() - // Normal. - a := "cat" - b := "map" - // Since the resulting diff hasn't been normalized, it would be ok if - // the insertion and deletion pairs are swapped. - // If the order changes, tweak this test as required. + dmp := New() + // Normal. + a := "cat" + b := "map" + // Since the resulting diff hasn't been normalized, it would be ok if + // the insertion and deletion pairs are swapped. + // If the order changes, tweak this test as required. diffs := []Diff{ Diff{DiffDelete, "c"}, Diff{DiffInsert, "m"}, @@ -768,41 +799,39 @@ func Test_diffBisect(t *testing.T) { Diff{DiffDelete, "t"}, Diff{DiffInsert, "p"}} - fmt.Println(dmp.DiffBisect(a, b, int32(time.Date(9999, time.December, 31, 23, 59, 59, 59, time.UTC).Unix()))) - - assertSeqEqual(diffs, dmp.DiffBisect(a, b, int32(time.Date(9999, time.December, 31, 23, 59, 59, 59, time.UTC).Unix()))) //TODO + assertDiffEqual(t, diffs, dmp.DiffBisect(a, b, time.Date(9999, time.December, 31, 23, 59, 59, 59, time.UTC))) - // Timeout. - diffs = []Diff{Diff{DiffDelete, "cat"}, Diff{DiffInsert, "map"}} - assertSeqEqual(diffs, dmp.DiffBisect(a, b, int32(time.Date(0001, time.January, 01, 00, 00, 00, 00, time.UTC).Unix()))) //TODO + // Timeout. + diffs = []Diff{Diff{DiffDelete, "cat"}, Diff{DiffInsert, "map"}} + assertDiffEqual(t, diffs, dmp.DiffBisect(a, b, time.Date(0001, time.January, 01, 00, 00, 00, 00, time.UTC))) } func Test_diffMain(t *testing.T) { - dmp := createDMP() + dmp := New() // Perform a trivial diff. diffs := []Diff{} - assertSeqEqual(diffs, dmp.DiffMain("", "", false)) + assertDiffEqual(t, diffs, dmp.DiffMain("", "", false)) diffs = []Diff{Diff{DiffEqual, "abc"}} - assertSeqEqual(diffs, dmp.DiffMain("abc", "abc", false)) + assertDiffEqual(t, diffs, dmp.DiffMain("abc", "abc", false)) diffs = []Diff{Diff{DiffEqual, "ab"}, Diff{DiffInsert, "123"}, Diff{DiffEqual, "c"}} - assertSeqEqual(diffs, dmp.DiffMain("abc", "ab123c", false)) + assertDiffEqual(t, diffs, dmp.DiffMain("abc", "ab123c", false)) diffs = []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "123"}, Diff{DiffEqual, "bc"}} - assertSeqEqual(diffs, dmp.DiffMain("a123bc", "abc", false)) + assertDiffEqual(t, diffs, dmp.DiffMain("a123bc", "abc", false)) diffs = []Diff{Diff{DiffEqual, "a"}, Diff{DiffInsert, "123"}, Diff{DiffEqual, "b"}, Diff{DiffInsert, "456"}, Diff{DiffEqual, "c"}} - assertSeqEqual(diffs, dmp.DiffMain("abc", "a123b456c", false)) + assertDiffEqual(t, diffs, dmp.DiffMain("abc", "a123b456c", false)) diffs = []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "123"}, Diff{DiffEqual, "b"}, Diff{DiffDelete, "456"}, Diff{DiffEqual, "c"}} - assertSeqEqual(diffs, dmp.DiffMain("a123b456c", "abc", false)) + assertDiffEqual(t, diffs, dmp.DiffMain("a123b456c", "abc", false)) // Perform a real diff. // Switch off the timeout. dmp.DiffTimeout = 0 diffs = []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "b"}} - assertSeqEqual(diffs, dmp.DiffMain("a", "b", false)) + assertDiffEqual(t, diffs, dmp.DiffMain("a", "b", false)) diffs = []Diff{ Diff{DiffDelete, "Apple"}, @@ -810,8 +839,7 @@ func Test_diffMain(t *testing.T) { Diff{DiffEqual, "s are a"}, Diff{DiffInsert, "lso"}, Diff{DiffEqual, " fruit."}} - - assertSeqEqual(diffs, dmp.DiffMain("Apples are a fruit.", "Bananas are also fruit.", false)) + assertDiffEqual(t, diffs, dmp.DiffMain("Apples are a fruit.", "Bananas are also fruit.", false)) diffs = []Diff{ Diff{DiffDelete, "a"}, @@ -819,81 +847,102 @@ func Test_diffMain(t *testing.T) { Diff{DiffEqual, "x"}, Diff{DiffDelete, "\t"}, Diff{DiffInsert, "\u0000"}} + assertDiffEqual(t, diffs, dmp.DiffMain("ax\t", "\u0680x\u0000", false)) + diffs = []Diff{ + Diff{DiffDelete, "1"}, + Diff{DiffEqual, "a"}, + Diff{DiffDelete, "y"}, + Diff{DiffEqual, "b"}, + Diff{DiffDelete, "2"}, + Diff{DiffInsert, "xab"}} + assertDiffEqual(t, diffs, dmp.DiffMain("1ayb2", "abxab", false)) - assertSeqEqual(diffs, dmp.DiffMain("ax\t", "\u0680x\u0000", false)) - diffs = []Diff{Diff{DiffDelete, "1"}, Diff{DiffEqual, "a"}, Diff{DiffDelete, "y"}, Diff{DiffEqual, "b"}, Diff{DiffDelete, "2"}, Diff{DiffInsert, "xab"}} - assertSeqEqual(diffs, dmp.DiffMain("1ayb2", "abxab", false)) - - diffs = []Diff{Diff{DiffInsert, "xaxcx"}, Diff{DiffEqual, "abc"}, Diff{DiffDelete, "y"}} - assertSeqEqual(diffs, dmp.DiffMain("abcy", "xaxcxabc", false)) + diffs = []Diff{ + Diff{DiffInsert, "xaxcx"}, + Diff{DiffEqual, "abc"}, Diff{DiffDelete, "y"}} + assertDiffEqual(t, diffs, dmp.DiffMain("abcy", "xaxcxabc", false)) - diffs = []Diff{Diff{DiffDelete, "ABCD"}, Diff{DiffEqual, "a"}, Diff{DiffDelete, "="}, Diff{DiffInsert, "-"}, Diff{DiffEqual, "bcd"}, Diff{DiffDelete, "="}, Diff{DiffInsert, "-"}, Diff{DiffEqual, "efghijklmnopqrs"}, Diff{DiffDelete, "EFGHIJKLMNOefg"}} - assertSeqEqual(diffs, dmp.DiffMain("ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", "a-bcd-efghijklmnopqrs", false)) + diffs = []Diff{ + Diff{DiffDelete, "ABCD"}, + Diff{DiffEqual, "a"}, + Diff{DiffDelete, "="}, + Diff{DiffInsert, "-"}, + Diff{DiffEqual, "bcd"}, + Diff{DiffDelete, "="}, + Diff{DiffInsert, "-"}, + Diff{DiffEqual, "efghijklmnopqrs"}, + Diff{DiffDelete, "EFGHIJKLMNOefg"}} + assertDiffEqual(t, diffs, dmp.DiffMain("ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", "a-bcd-efghijklmnopqrs", false)) - diffs = []Diff{Diff{DiffInsert, " "}, Diff{DiffEqual, "a"}, Diff{DiffInsert, "nd"}, Diff{DiffEqual, " [[Pennsylvania]]"}, Diff{DiffDelete, " and [[New"}} - assertSeqEqual(diffs, dmp.DiffMain("a [[Pennsylvania]] and [[New", " and [[Pennsylvania]]", false)) + diffs = []Diff{ + Diff{DiffInsert, " "}, + Diff{DiffEqual, "a"}, + Diff{DiffInsert, "nd"}, + Diff{DiffEqual, " [[Pennsylvania]]"}, + Diff{DiffDelete, " and [[New"}} + assertDiffEqual(t, diffs, dmp.DiffMain("a [[Pennsylvania]] and [[New", " and [[Pennsylvania]]", false)) - dmp.DiffTimeout = 0.1 // 100ms + dmp.DiffTimeout = 100 * time.Millisecond // 100ms a := "`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.\n" b := "I am the very model of a modern major general,\nI've information vegetable, animal, and mineral,\nI know the kings of England, and I quote the fights historical,\nFrom Marathon to Waterloo, in order categorical.\n" // Increase the text lengths by 1024 times to ensure a timeout. - for x := 0; x < 10; x++ { + for x := 0; x < 13; x++ { a = a + a b = b + b } - startTime := time.Now().Unix() - startTime *= 1000 - dmp.DiffMain(a, b) - endTime := time.Now().Unix() - endTime *= 1000 + + startTime := time.Now() + dmp.DiffMain(a, b, true) + endTime := time.Now() + delta := endTime.Sub(startTime) // Test that we took at least the timeout period. - softAssert(t, dmp.DiffTimeout*1000 <= float64(endTime-startTime), "") + assert.True(t, delta >= dmp.DiffTimeout, fmt.Sprintf("%v !>= %v", delta, dmp.DiffTimeout)) // Test that we didn't take forever (be forgiving). // Theoretically this test could fail very occasionally if the // OS task swaps or locks up for a second at the wrong moment. - softAssert(t, dmp.DiffTimeout*1000*2 > float64(endTime-startTime), "") + assert.True(t, delta < (dmp.DiffTimeout*2), fmt.Sprintf("%v !< %v", delta, dmp.DiffTimeout*2)) dmp.DiffTimeout = 0 // Test the linemode speedup. // Must be long to pass the 100 char cutoff. a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n" b = "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n" - assertSeqEqual(dmp.DiffMain(a, b, true), dmp.DiffMain(a, b, false)) + assertDiffEqual(t, dmp.DiffMain(a, b, true), dmp.DiffMain(a, b, false)) a = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" b = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij" - assertSeqEqual(dmp.DiffMain(a, b, true), dmp.DiffMain(a, b, false)) + assertDiffEqual(t, dmp.DiffMain(a, b, true), dmp.DiffMain(a, b, false)) a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n" b = "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n" texts_linemode := diffRebuildtexts(dmp.DiffMain(a, b, true)) texts_textmode := diffRebuildtexts(dmp.DiffMain(a, b, false)) - assertSeqEqual(texts_textmode, texts_linemode) + assertStrEqual(t, texts_textmode, texts_linemode) // Test null inputs -- not needed because nulls can't be passed in C#. } func Test_match_alphabet(t *testing.T) { - dmp := createDMP() + dmp := New() // Initialise the bitmasks for Bitap. bitmask := map[byte]int{ 'a': 4, 'b': 2, 'c': 1, } - assertSeqEqual(bitmask, dmp.MatchAlphabet("abc")) + assertMapEqual(t, bitmask, dmp.MatchAlphabet("abc")) bitmask = map[byte]int{ 'a': 37, 'b': 18, 'c': 8, } - assertSeqEqual(bitmask, dmp.MatchAlphabet("abcaba")) + assertMapEqual(t, bitmask, dmp.MatchAlphabet("abcaba")) } func Test_match_bitap(t *testing.T) { - dmp := createDMP() + dmp := New() // Bitap algorithm. dmp.MatchDistance = 100 @@ -940,7 +989,7 @@ func Test_match_bitap(t *testing.T) { } func Test_MatchMain(t *testing.T) { - dmp := createDMP() + dmp := New() // Full match. assert.Equal(t, 0, dmp.MatchMain("abcdef", "abcdef", 1000), "MatchMain: Equality.") @@ -982,10 +1031,10 @@ func Test_patch_patchObj(t *testing.T) { } func Test_patch_fromText(t *testing.T) { - dmp := createDMP() + dmp := New() _v1, _ := dmp.PatchFromText("") - softAssert(t, len(_v1) == 0, "patch_fromText: #0.") + assert.True(t, len(_v1) == 0, "patch_fromText: #0.") strp := "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n" _v2, _ := dmp.PatchFromText(strp) assert.Equal(t, strp, _v2[0].String(), "patch_fromText: #1.") @@ -1001,11 +1050,11 @@ func Test_patch_fromText(t *testing.T) { // Generates error. _, err := dmp.PatchFromText("Bad\nPatch\n") - softAssert(t, err != nil, "There should be an error") + assert.True(t, err != nil, "There should be an error") } func Test_patch_toText(t *testing.T) { - dmp := createDMP() + dmp := New() strp := "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n" var patches []Patch patches, _ = dmp.PatchFromText(strp) @@ -1019,7 +1068,7 @@ func Test_patch_toText(t *testing.T) { } func Test_patch_addContext(t *testing.T) { - dmp := createDMP() + dmp := New() dmp.PatchMargin = 4 var p Patch _p, _ := dmp.PatchFromText("@@ -21,4 +21,10 @@\n-jump\n+somersault\n") @@ -1044,7 +1093,7 @@ func Test_patch_addContext(t *testing.T) { } func Test_patch_make(t *testing.T) { - dmp := createDMP() + dmp := New() var patches []Patch patches = dmp.PatchMake("", "") assert.Equal(t, "", dmp.PatchToText(patches), "patch_make: Null case.") @@ -1071,7 +1120,7 @@ func Test_patch_make(t *testing.T) { assert.Equal(t, expectedPatch, dmp.PatchToText(patches), "patch_make: Text1+Text2+Diff inputs (deprecated).") patches = dmp.PatchMake("`1234567890-=[]\\;',./", "~!@#$%^&*()_+{}|:\"<>?") - assert.Equal(t, "@@ -1,21 +1,21 @@\n-%601234567890-=%5b%5d%5c;',./\n+~!@#$%25%5e&*()_+%7b%7d%7c:%22%3c%3e?\n", + assert.Equal(t, "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n", dmp.PatchToText(patches), "patch_toText: Character encoding.") @@ -1080,7 +1129,7 @@ func Test_patch_make(t *testing.T) { Diff{DiffInsert, "~!@#$%^&*()_+{}|:\"<>?"}} _p1, _ := dmp.PatchFromText("@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n") - assertSeqEqual(diffs, + assertDiffEqual(t, diffs, _p1[0].diffs, ) @@ -1092,17 +1141,15 @@ func Test_patch_make(t *testing.T) { expectedPatch = "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n" patches = dmp.PatchMake(text1, text2) assert.Equal(t, expectedPatch, dmp.PatchToText(patches), "patch_make: Long string with repeats.") - - // Test null inputs -- not needed because nulls can't be passed in C#. } func Test_PatchSplitMax(t *testing.T) { // Assumes that Match_MaxBits is 32. - dmp := createDMP() + dmp := New() var patches []Patch patches = dmp.PatchMake("abcdefghijklmnopqrstuvwxyz01234567890", "XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0") - dmp.PatchSplitMax(patches) + patches = dmp.PatchSplitMax(patches) assert.Equal(t, "@@ -1,32 +1,46 @@\n+X\n ab\n+X\n cd\n+X\n ef\n+X\n gh\n+X\n ij\n+X\n kl\n+X\n mn\n+X\n op\n+X\n qr\n+X\n st\n+X\n uv\n+X\n wx\n+X\n yz\n+X\n 012345\n@@ -25,13 +39,18 @@\n zX01\n+X\n 23\n+X\n 45\n+X\n 67\n+X\n 89\n+X\n 0\n", dmp.PatchToText(patches)) patches = dmp.PatchMake("abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz", "abcdefuvwxyz") @@ -1111,7 +1158,7 @@ func Test_PatchSplitMax(t *testing.T) { assert.Equal(t, oldToText, dmp.PatchToText(patches)) patches = dmp.PatchMake("1234567890123456789012345678901234567890123456789012345678901234567890", "abc") - dmp.PatchSplitMax(patches) + patches = dmp.PatchSplitMax(patches) assert.Equal(t, "@@ -1,32 +1,4 @@\n-1234567890123456789012345678\n 9012\n@@ -29,32 +1,4 @@\n-9012345678901234567890123456\n 7890\n@@ -57,14 +1,3 @@\n-78901234567890\n+abc\n", dmp.PatchToText(patches)) patches = dmp.PatchMake("abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1", "abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1") @@ -1120,12 +1167,16 @@ func Test_PatchSplitMax(t *testing.T) { } func Test_PatchAddPadding(t *testing.T) { - dmp := createDMP() + dmp := New() var patches []Patch patches = dmp.PatchMake("", "test") - assert.Equal(t, "@@ -0,0 +1,4 @@\n+test\n", + pass := assert.Equal(t, "@@ -0,0 +1,4 @@\n+test\n", dmp.PatchToText(patches), "PatchAddPadding: Both edges full.") + if !pass { + t.FailNow() + } + dmp.PatchAddPadding(patches) assert.Equal(t, "@@ -1,8 +1,12 @@\n %01%02%03%04\n+test\n %01%02%03%04\n", dmp.PatchToText(patches), @@ -1151,7 +1202,7 @@ func Test_PatchAddPadding(t *testing.T) { } func Test_patchApply(t *testing.T) { - dmp := createDMP() + dmp := New() dmp.MatchDistance = 1000 dmp.MatchThreshold = 0.5 dmp.PatchDeleteThreshold = 0.5 @@ -1159,43 +1210,46 @@ func Test_patchApply(t *testing.T) { patches = dmp.PatchMake("", "") results0, results1 := dmp.PatchApply(patches, "Hello world.") boolArray := results1 - resultStr := results0 + "\t" + string(len(boolArray)) - assert.Equal(t, "Hello world.\t0", resultStr, "patch_apply: Null case.") + resultStr := fmt.Sprintf("%v\t%v", results0, len(boolArray)) + pass := assert.Equal(t, "Hello world.\t0", resultStr, "patch_apply: Null case.") + if !pass { + t.FailNow() + } patches = dmp.PatchMake("The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.") results0, results1 = dmp.PatchApply(patches, "The quick brown fox jumps over the lazy dog.") boolArray = results1 resultStr = results0 + "\t" + strconv.FormatBool(boolArray[0]) + "\t" + strconv.FormatBool(boolArray[1]) - assert.Equal(t, "That quick brown fox jumped over a lazy dog.\tTrue\tTrue", resultStr, "patch_apply: Exact match.") + assert.Equal(t, "That quick brown fox jumped over a lazy dog.\ttrue\ttrue", resultStr, "patch_apply: Exact match.") results0, results1 = dmp.PatchApply(patches, "The quick red rabbit jumps over the tired tiger.") boolArray = results1 resultStr = results0 + "\t" + strconv.FormatBool(boolArray[0]) + "\t" + strconv.FormatBool(boolArray[1]) - assert.Equal(t, "That quick red rabbit jumped over a tired tiger.\tTrue\tTrue", resultStr, "patch_apply: Partial match.") + assert.Equal(t, "That quick red rabbit jumped over a tired tiger.\ttrue\ttrue", resultStr, "patch_apply: Partial match.") results0, results1 = dmp.PatchApply(patches, "I am the very model of a modern major general.") boolArray = results1 resultStr = results0 + "\t" + strconv.FormatBool(boolArray[0]) + "\t" + strconv.FormatBool(boolArray[1]) - assert.Equal(t, "I am the very model of a modern major general.\tFalse\tFalse", resultStr, "patch_apply: Failed match.") + assert.Equal(t, "I am the very model of a modern major general.\tfalse\tfalse", resultStr, "patch_apply: Failed match.") patches = dmp.PatchMake("x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy") results0, results1 = dmp.PatchApply(patches, "x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y") boolArray = results1 resultStr = results0 + "\t" + strconv.FormatBool(boolArray[0]) + "\t" + strconv.FormatBool(boolArray[1]) - assert.Equal(t, "xabcy\tTrue\tTrue", resultStr, "patch_apply: Big delete, small Diff.") + assert.Equal(t, "xabcy\ttrue\ttrue", resultStr, "patch_apply: Big delete, small Diff.") patches = dmp.PatchMake("x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy") results0, results1 = dmp.PatchApply(patches, "x12345678901234567890---------------++++++++++---------------12345678901234567890y") boolArray = results1 resultStr = results0 + "\t" + strconv.FormatBool(boolArray[0]) + "\t" + strconv.FormatBool(boolArray[1]) - assert.Equal(t, "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y\tFalse\tTrue", resultStr, "patch_apply: Big delete, big Diff 1.") + assert.Equal(t, "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y\tfalse\ttrue", resultStr, "patch_apply: Big delete, big Diff 1.") dmp.PatchDeleteThreshold = 0.6 patches = dmp.PatchMake("x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy") results0, results1 = dmp.PatchApply(patches, "x12345678901234567890---------------++++++++++---------------12345678901234567890y") boolArray = results1 resultStr = results0 + "\t" + strconv.FormatBool(boolArray[0]) + "\t" + strconv.FormatBool(boolArray[1]) - assert.Equal(t, "xabcy\tTrue\tTrue", resultStr, "patch_apply: Big delete, big Diff 2.") + assert.Equal(t, "xabcy\ttrue\ttrue", resultStr, "patch_apply: Big delete, big Diff 2.") dmp.PatchDeleteThreshold = 0.5 dmp.MatchThreshold = 0.0 @@ -1204,7 +1258,7 @@ func Test_patchApply(t *testing.T) { results0, results1 = dmp.PatchApply(patches, "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890") boolArray = results1 resultStr = results0 + "\t" + strconv.FormatBool(boolArray[0]) + "\t" + strconv.FormatBool(boolArray[1]) - assert.Equal(t, "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890\tFalse\tTrue", resultStr, "patch_apply: Compensate for failed patch.") + assert.Equal(t, "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890\tfalse\ttrue", resultStr, "patch_apply: Compensate for failed patch.") dmp.MatchThreshold = 0.5 dmp.MatchDistance = 1000 @@ -1222,17 +1276,33 @@ func Test_patchApply(t *testing.T) { results0, results1 = dmp.PatchApply(patches, "") boolArray = results1 resultStr = results0 + "\t" + strconv.FormatBool(boolArray[0]) - assert.Equal(t, "test\tTrue", resultStr, "patch_apply: Edge exact match.") + assert.Equal(t, "test\ttrue", resultStr, "patch_apply: Edge exact match.") patches = dmp.PatchMake("XY", "XtestY") results0, results1 = dmp.PatchApply(patches, "XY") boolArray = results1 resultStr = results0 + "\t" + strconv.FormatBool(boolArray[0]) - assert.Equal(t, "XtestY\tTrue", resultStr, "patch_apply: Near edge exact match.") + assert.Equal(t, "XtestY\ttrue", resultStr, "patch_apply: Near edge exact match.") patches = dmp.PatchMake("y", "y123") results0, results1 = dmp.PatchApply(patches, "x") boolArray = results1 resultStr = results0 + "\t" + strconv.FormatBool(boolArray[0]) - assert.Equal(t, "x123\tTrue", resultStr, "patch_apply: Edge partial match.") + assert.Equal(t, "x123\ttrue", resultStr, "patch_apply: Edge partial match.") +} + +func Benchmark_DiffMain(bench *testing.B) { + dmp := New() + dmp.DiffTimeout = time.Second + a := "`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.\n" + b := "I am the very model of a modern major general,\nI've information vegetable, animal, and mineral,\nI know the kings of England, and I quote the fights historical,\nFrom Marathon to Waterloo, in order categorical.\n" + // Increase the text lengths by 1024 times to ensure a timeout. + for x := 0; x < 10; x++ { + a = a + a + b = b + b + } + + for i := 0; i < bench.N; i++ { + dmp.DiffMain(a, b, true) + } } diff --git a/diff/stack.go b/diff/stack.go index d005335..d28ae45 100644 --- a/diff/stack.go +++ b/diff/stack.go @@ -36,7 +36,7 @@ func (s *Stack) Pop() (value interface{}) { return nil } -// Peek returns the value of the element on the top of the stack +// Peek returns the value of the element on the top of the stack // but don't remove it. If the stack is empty, return nil func (s *Stack) Peek() (value interface{}) { if s.size > 0 {