From f87c80119cf0dcaf811a742fd7f0ca5a6413d7fa Mon Sep 17 00:00:00 2001 From: gkampitakis Date: Sat, 3 Sep 2022 00:39:53 +0100 Subject: [PATCH] feat: update repo and dependencies Release new forked version with no security issue --- .github/workflows/go.yml | 53 +++ .travis.yml | 30 -- Makefile | 53 +-- README.md | 10 +- diffmatchpatch/benchutil_test.go | 10 +- diffmatchpatch/diff.go | 117 +++-- diffmatchpatch/diff_test.go | 679 ++++++++++++++++-------------- diffmatchpatch/diffmatchpatch.go | 7 +- diffmatchpatch/match.go | 3 +- diffmatchpatch/match_test.go | 22 +- diffmatchpatch/patch.go | 35 +- diffmatchpatch/patch_test.go | 376 +++++++++++++---- diffmatchpatch/stringutil.go | 11 +- diffmatchpatch/stringutil_test.go | 8 +- go.mod | 12 +- go.sum | 21 - golangci.yml | 24 ++ scripts/lint.sh | 22 - 18 files changed, 903 insertions(+), 590 deletions(-) create mode 100644 .github/workflows/go.yml delete mode 100644 .travis.yml create mode 100644 golangci.yml delete mode 100755 scripts/lint.sh diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..0d0ea13 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,53 @@ +name: Go + +on: + pull_request: + paths-ignore: + - "images/**" + - "**/*.md" + branches: + - main + push: + paths-ignore: + - "images/**" + - "**/*.md" + branches: + - main + +jobs: + lint: + name: Run linting + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v3 + with: + go-version: 1.19.x + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: "latest" + args: -c ./golangci.yml + - name: Format lint + run: | + make install-tools && make format && git diff --quiet + test: + name: Run tests + runs-on: ubuntu-latest + strategy: + matrix: + go: + [ + "1.16.x", + "1.17.x", + "1.18.x", + "1.19.x", + ] + steps: + - uses: actions/checkout@v3 + - name: Setup go + uses: actions/setup-go@v3 + with: + go-version: ${{matrix.go}} + - name: Run Tests + run: make test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9032a0f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: go -arch: - - amd64 - - ppc64le -os: - - linux - - osx - -go: - - 1.13 - - 1.14 - - 1.15 - -sudo: false - -env: - global: - # Coveralls.io - - secure: OGYOsFNXNarEZ5yA4/M6ZdVguD0jL8vXgXrbLzjcpkKcq8ObHSCtNINoUlnNf6l6Z92kPnuV+LSm7jKTojBlov4IwgiY1ACbvg921SdjxYkg1AiwHTRTLR1g/esX8RdaBpJ0TOcXOFFsYMRVvl5sxxtb0tXSuUrT+Ch4SUCY7X8= - -install: - - make install-dependencies - - make install-tools - - make install - -script: - - make lint - - make test-with-coverage - - gover - - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then goveralls -coverprofile=gover.coverprofile -service=travis-ci -repotoken $COVERALLS_TOKEN; fi diff --git a/Makefile b/Makefile index 710bc83..17c9c1b 100644 --- a/Makefile +++ b/Makefile @@ -1,44 +1,23 @@ -.PHONY: all clean clean-coverage install install-dependencies install-tools lint test test-verbose test-with-coverage +.PHONY: install-tools lint test test-verbose format benchmark -export ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) -export PKG := github.com/sergi/go-diff -export ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) - -$(eval $(ARGS):;@:) # turn arguments into do-nothing targets -export ARGS - -ifdef ARGS - PKG_TEST := $(ARGS) -else - PKG_TEST := $(PKG)/... -endif - -all: install-tools install-dependencies install lint test - -clean: - go clean -i $(PKG)/... - go clean -i -race $(PKG)/... -clean-coverage: - find $(ROOT_DIR) | grep .coverprofile | xargs rm -install: - go install -v $(PKG)/... -install-dependencies: - go get -t -v $(PKG)/... - go build -v $(PKG)/... install-tools: # Install linting tools - go get -u -v golang.org/x/lint/... - go get -u -v github.com/kisielk/errcheck/... + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.49.0 + go install mvdan.cc/gofumpt@latest + go install github.com/segmentio/golines@latest - # Install code coverage tools - go get -u -v github.com/onsi/ginkgo/ginkgo/... - go get -u -v github.com/modocache/gover/... - go get -u -v github.com/mattn/goveralls/... lint: - $(ROOT_DIR)/scripts/lint.sh + golangci-lint run -c ./golangci.yml ./... + +format: + gofumpt -l -w -extra . + golines . -w + test: - go test -race -test.timeout 120s $(PKG_TEST) + go test -race -test.timeout 120s -count=1 ./... + test-verbose: - go test -race -test.timeout 120s -v $(PKG_TEST) -test-with-coverage: - ginkgo -r -cover -race -skipPackage="testdata" + go test -race -test.timeout 120s -v -cover -count=1 ./... + +benchmark: + go test -bench=. -benchmem ./... diff --git a/README.md b/README.md index 597437b..2150478 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# go-diff [![GoDoc](https://godoc.org/github.com/sergi/go-diff?status.png)](https://godoc.org/github.com/sergi/go-diff/diffmatchpatch) [![Build Status](https://travis-ci.org/sergi/go-diff.svg?branch=master)](https://travis-ci.org/sergi/go-diff) [![Coverage Status](https://coveralls.io/repos/sergi/go-diff/badge.png?branch=master)](https://coveralls.io/r/sergi/go-diff?branch=master) +# go-diff [![GoDoc](https://godoc.org/github.com/gkampitakis/go-diff?status.png)](https://godoc.org/github.com/gkampitakis/go-diff/diffmatchpatch) go-diff offers algorithms to perform operations required for synchronizing plain text: @@ -9,7 +9,7 @@ go-diff offers algorithms to perform operations required for synchronizing plain ## Installation ```bash -go get -u github.com/sergi/go-diff/... +go get -u github.com/gkampitakis/go-diff/... ``` ## Usage @@ -22,7 +22,7 @@ package main import ( "fmt" - "github.com/sergi/go-diff/diffmatchpatch" + "github.com/gkampitakis/go-diff/diffmatchpatch" ) const ( @@ -41,11 +41,11 @@ func main() { ## Found a bug or are you missing a feature in go-diff? -Please make sure to have the latest version of go-diff. If the problem still persists go through the [open issues](https://github.com/sergi/go-diff/issues) in the tracker first. If you cannot find your request just open up a [new issue](https://github.com/sergi/go-diff/issues/new). +Please make sure to have the latest version of go-diff. If the problem still persists go through the [open issues](https://github.com/gkampitakis/go-diff/issues) in the tracker first. If you cannot find your request just open up a [new issue](https://github.com/gkampitakis/go-diff/issues/new). ## How to contribute? -You want to contribute to go-diff? GREAT! If you are here because of a bug you want to fix or a feature you want to add, you can just read on. Otherwise we have a list of [open issues in the tracker](https://github.com/sergi/go-diff/issues). Just choose something you think you can work on and discuss your plans in the issue by commenting on it. +You want to contribute to go-diff? GREAT! If you are here because of a bug you want to fix or a feature you want to add, you can just read on. Otherwise we have a list of [open issues in the tracker](https://github.com/gkampitakis/go-diff/issues). Just choose something you think you can work on and discuss your plans in the issue by commenting on it. Please make sure that every behavioral change is accompanied by test cases. Additionally, every contribution must pass the `lint` and `test` Makefile targets which can be run using the following commands in the repository root directory. diff --git a/diffmatchpatch/benchutil_test.go b/diffmatchpatch/benchutil_test.go index b8e404d..17949fe 100644 --- a/diffmatchpatch/benchutil_test.go +++ b/diffmatchpatch/benchutil_test.go @@ -8,18 +8,16 @@ package diffmatchpatch -import ( - "io/ioutil" -) +import "os" const testdataPath = "../testdata/" -func speedtestTexts() (s1 string, s2 string) { - d1, err := ioutil.ReadFile(testdataPath + "speedtest1.txt") +func speedtestTexts() (s1, s2 string) { + d1, err := os.ReadFile(testdataPath + "speedtest1.txt") if err != nil { panic(err) } - d2, err := ioutil.ReadFile(testdataPath + "speedtest2.txt") + d2, err := os.ReadFile(testdataPath + "speedtest2.txt") if err != nil { panic(err) } diff --git a/diffmatchpatch/diff.go b/diffmatchpatch/diff.go index 2a9f2dc..0f0f88f 100644 --- a/diffmatchpatch/diff.go +++ b/diffmatchpatch/diff.go @@ -34,7 +34,7 @@ const ( DiffInsert Operation = 1 // DiffEqual item represents an equal diff. DiffEqual Operation = 0 - //IndexSeparator is used to seperate the array indexes in an index string + // IndexSeparator is used to seperate the array indexes in an index string IndexSeparator = "," ) @@ -45,7 +45,7 @@ type Diff struct { } // splice removes amount elements from slice at index index, replacing them with elements. -func splice(slice []Diff, index int, amount int, elements ...Diff) []Diff { +func splice(slice []Diff, index, amount int, elements ...Diff) []Diff { if len(elements) == amount { // Easy case: overwrite the relevant items. copy(slice[index:], elements) @@ -97,7 +97,11 @@ func (dmp *DiffMatchPatch) DiffMainRunes(text1, text2 []rune, checklines bool) [ return dmp.diffMainRunes(text1, text2, checklines, deadline) } -func (dmp *DiffMatchPatch) diffMainRunes(text1, text2 []rune, checklines bool, deadline time.Time) []Diff { +func (dmp *DiffMatchPatch) diffMainRunes( + text1, text2 []rune, + checklines bool, + deadline time.Time, +) []Diff { if runesEqual(text1, text2) { var diffs []Diff if len(text1) > 0 { @@ -131,8 +135,13 @@ func (dmp *DiffMatchPatch) diffMainRunes(text1, text2 []rune, checklines bool, d return dmp.DiffCleanupMerge(diffs) } -// diffCompute finds the differences between two rune slices. Assumes that the texts do not have any common prefix or suffix. -func (dmp *DiffMatchPatch) diffCompute(text1, text2 []rune, checklines bool, deadline time.Time) []Diff { +// diffCompute finds the differences between two rune slices. +// Assumes that the texts do not have any common prefix or suffix. +func (dmp *DiffMatchPatch) diffCompute( + text1, text2 []rune, + checklines bool, + deadline time.Time, +) []Diff { diffs := []Diff{} if len(text1) == 0 { // Just add some text (speedup). @@ -159,9 +168,9 @@ func (dmp *DiffMatchPatch) diffCompute(text1, text2 []rune, checklines bool, dea } // Shorter text is inside the longer text (speedup). return []Diff{ - Diff{op, string(longtext[:i])}, - Diff{DiffEqual, string(shorttext)}, - Diff{op, string(longtext[i+len(shorttext):])}, + {op, string(longtext[:i])}, + {DiffEqual, string(shorttext)}, + {op, string(longtext[i+len(shorttext):])}, } } else if len(shorttext) == 1 { // Single character string. @@ -192,7 +201,8 @@ func (dmp *DiffMatchPatch) diffCompute(text1, text2 []rune, checklines bool, dea return dmp.diffBisect(text1, text2, deadline) } -// diffLineMode does a quick line-level diff on both []runes, then rediff the parts for greater accuracy. This speedup can produce non-minimal diffs. +// diffLineMode does a quick line-level diff on both []runes, +// then rediff the parts for greater accuracy. This speedup can produce non-minimal diffs. func (dmp *DiffMatchPatch) diffLineMode(text1, text2 []rune, deadline time.Time) []Diff { // Scan the text on a line-by-line basis first. text1, text2, linearray := dmp.DiffLinesToRunes(string(text1), string(text2)) @@ -258,7 +268,9 @@ func (dmp *DiffMatchPatch) DiffBisect(text1, text2 string, deadline time.Time) [ return dmp.diffBisect([]rune(text1), []rune(text2), deadline) } -// diffBisect finds the 'middle snake' of a diff, splits the problem in two and returns the recursively constructed diff. +// diffBisect finds the 'middle snake' of a diff, splits the problem in two and returns +// the recursively constructed diff. +// // See Myers's 1986 paper: An O(ND) Difference Algorithm and Its Variations. func (dmp *DiffMatchPatch) diffBisect(runes1, runes2 []rune, deadline time.Time) []Diff { // Cache the text lengths to prevent multiple calls. @@ -338,7 +350,7 @@ func (dmp *DiffMatchPatch) diffBisect(runes1, runes2 []rune, deadline time.Time) } else { x2 = v2[k2Offset-1] + 1 } - var y2 = x2 - k2 + y2 := x2 - k2 for x2 < runes1Len && y2 < runes2Len { if runes1[runes1Len-x2-1] != runes2[runes2Len-y2-1] { break @@ -376,7 +388,8 @@ func (dmp *DiffMatchPatch) diffBisect(runes1, runes2 []rune, deadline time.Time) } func (dmp *DiffMatchPatch) diffBisectSplit(runes1, runes2 []rune, x, y int, - deadline time.Time) []Diff { + deadline time.Time, +) []Diff { runes1a := runes1[:x] runes2a := runes2[:y] runes1b := runes1[x:] @@ -389,7 +402,8 @@ func (dmp *DiffMatchPatch) diffBisectSplit(runes1, runes2 []rune, x, y int, return append(diffs, diffsb...) } -// DiffLinesToChars splits two texts into a list of strings, and educes the texts to a string of hashes where each Unicode character represents one line. +// DiffLinesToChars splits two texts into a list of strings, and educes the texts to +// a string of hashes where each Unicode character represents one line. // It's slightly faster to call DiffLinesToRunes first, followed by DiffMainRunes. func (dmp *DiffMatchPatch) DiffLinesToChars(text1, text2 string) (string, string, []string) { chars1, chars2, lineArray := dmp.diffLinesToStrings(text1, text2) @@ -462,7 +476,7 @@ func commonSuffixLength(text1, text2 []rune) int { } // DiffCommonOverlap determines if the suffix of one string is the prefix of another. -func (dmp *DiffMatchPatch) DiffCommonOverlap(text1 string, text2 string) int { +func (dmp *DiffMatchPatch) DiffCommonOverlap(text1, text2 string) int { // Cache the text lengths to prevent multiple calls. text1Length := len(text1) text2Length := len(text2) @@ -482,7 +496,9 @@ func (dmp *DiffMatchPatch) DiffCommonOverlap(text1 string, text2 string) int { return textLength } - // Start by looking for a single character match and increase length until no match is found. Performance analysis: http://neil.fraser.name/news/2010/11/04/ + // Start by looking for a single character match and increase length until no match is found. + // + // Performance analysis: http://neil.fraser.name/news/2010/11/04/ best := 0 length := 1 for { @@ -501,7 +517,8 @@ func (dmp *DiffMatchPatch) DiffCommonOverlap(text1 string, text2 string) int { return best } -// 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. +// 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 { // Unused in this code, but retained for interface compatibility. runeSlices := dmp.diffHalfMatch([]rune(text1), []rune(text2)) @@ -541,7 +558,7 @@ func (dmp *DiffMatchPatch) diffHalfMatch(text1, text2 []rune) [][]rune { // Check again based on the third quarter. hm2 := dmp.diffHalfMatchI(longtext, shorttext, int(float64(len(longtext)+1)/2)) - hm := [][]rune{} + var hm [][]rune if hm1 == nil && hm2 == nil { return nil } else if hm2 == nil { @@ -565,8 +582,9 @@ func (dmp *DiffMatchPatch) diffHalfMatch(text1, text2 []rune) [][]rune { return [][]rune{hm[2], hm[3], hm[0], hm[1], hm[4]} } -// diffHalfMatchI checks if a substring of shorttext exist within longtext such that the substring is at least half the length of longtext? -// Returns a slice containing the prefix of longtext, the suffix of longtext, the prefix of shorttext, the suffix of shorttext and the common middle, or null if there was no match. +// diffHalfMatchI checks if a substring of shorttext exist within longtext such that the substring +// is at least half the length of longtext? Returns a slice containing the prefix of longtext, the suffix of longtext, +// the prefix of shorttext, the suffix of shorttext and the common middle, or null if there was no match. func (dmp *DiffMatchPatch) diffHalfMatchI(l, s []rune, i int) [][]rune { var bestCommonA []rune var bestCommonB []rune @@ -697,8 +715,7 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff { // Overlap found. Insert an equality and trim the surrounding edits. diffs = splice(diffs, pointer, 0, Diff{DiffEqual, insertion[:overlapLength1]}) - diffs[pointer-1].Text = - deletion[0 : len(deletion)-overlapLength1] + diffs[pointer-1].Text = deletion[0 : len(deletion)-overlapLength1] diffs[pointer+1].Text = insertion[overlapLength1:] pointer++ } @@ -729,7 +746,6 @@ var ( whitespaceRegex = regexp.MustCompile(`\s`) linebreakRegex = regexp.MustCompile(`[\r\n]`) blanklineEndRegex = regexp.MustCompile(`\n\r?\n$`) - blanklineStartRegex = regexp.MustCompile(`^\r?\n\r?\n`) ) // diffCleanupSemanticScore computes a score representing whether the internal boundary falls on logical boundaries. @@ -740,7 +756,9 @@ func diffCleanupSemanticScore(one, two string) int { return 6 } - // Each port of this function behaves slightly differently due to subtle differences in each language's definition of things like 'whitespace'. Since this function's purpose is largely cosmetic, the choice has been made to use each language's native features rather than force total conformity. + // Each port of this function behaves slightly differently due to subtle differences in each language's + // definition of things like 'whitespace'. Since this function's purpose is largely cosmetic, + // the choice has been made to use each language's native features rather than force total conformity. rune1, _ := utf8.DecodeLastRuneInString(one) rune2, _ := utf8.DecodeRuneInString(two) char1 := string(rune1) @@ -774,8 +792,10 @@ func diffCleanupSemanticScore(one, two string) int { return 0 } -// DiffCleanupSemanticLossless looks for single edits surrounded on both sides by equalities which can be shifted sideways to align the edit to a word boundary. -// E.g: The cat came. -> The cat came. +// DiffCleanupSemanticLossless looks for single edits surrounded on both sides by equalities +// which can be shifted sideways to align the edit to a word boundary. +// +// e.g: The cat came. -> The cat came. func (dmp *DiffMatchPatch) DiffCleanupSemanticLossless(diffs []Diff) []Diff { pointer := 1 @@ -976,12 +996,10 @@ func (dmp *DiffMatchPatch) DiffCleanupMerge(diffs []Diff) []Diff { countInsert++ textInsert = append(textInsert, []rune(diffs[pointer].Text)...) pointer++ - break case DiffDelete: countDelete++ textDelete = append(textDelete, []rune(diffs[pointer].Text)...) pointer++ - break case DiffEqual: // Upon reaching an equality, check for prior redundancies. if countDelete+countInsert > 1 { @@ -1043,7 +1061,6 @@ func (dmp *DiffMatchPatch) DiffCleanupMerge(diffs []Diff) []Diff { countDelete = 0 textDelete = nil textInsert = nil - break } } @@ -1051,7 +1068,8 @@ func (dmp *DiffMatchPatch) DiffCleanupMerge(diffs []Diff) []Diff { diffs = diffs[0 : len(diffs)-1] // Remove the dummy entry at the end. } - // Second pass: look for single edits surrounded on both sides by equalities which can be shifted sideways to eliminate an equality. E.g: ABAC -> ABAC + // Second pass: look for single edits surrounded on both sides by equalities which can be shifted + // sideways to eliminate an equality. E.g: ABAC -> ABAC changes := false pointer = 1 // Intentionally ignore the first and last element (don't need checking). @@ -1069,8 +1087,7 @@ func (dmp *DiffMatchPatch) DiffCleanupMerge(diffs []Diff) []Diff { } else if strings.HasPrefix(diffs[pointer].Text, diffs[pointer+1].Text) { // Shift the edit over the next equality. diffs[pointer-1].Text += diffs[pointer+1].Text - diffs[pointer].Text = - diffs[pointer].Text[len(diffs[pointer+1].Text):] + diffs[pointer+1].Text + diffs[pointer].Text = diffs[pointer].Text[len(diffs[pointer+1].Text):] + diffs[pointer+1].Text diffs = splice(diffs, pointer+1, 1) changes = true } @@ -1168,7 +1185,7 @@ func (dmp *DiffMatchPatch) DiffPrettyText(diffs []Diff) string { // DiffText1 computes and returns the source text (all equalities and deletions). func (dmp *DiffMatchPatch) DiffText1(diffs []Diff) string { - //StringBuilder text = new StringBuilder() + // StringBuilder text = new StringBuilder() var text bytes.Buffer for _, aDiff := range diffs { @@ -1215,8 +1232,12 @@ func (dmp *DiffMatchPatch) DiffLevenshtein(diffs []Diff) int { return levenshtein } -// DiffToDelta crushes the diff into an encoded string which describes the operations required to transform text1 into text2. -// E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. Operations are tab-separated. Inserted text is escaped using %xx notation. +// DiffToDelta crushes the diff into an encoded string which describes the operations +// required to transform text1 into text2. +// +// e.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. +// +// Operations are tab-separated. Inserted text is escaped using %xx notation. func (dmp *DiffMatchPatch) DiffToDelta(diffs []Diff) string { var text bytes.Buffer for _, aDiff := range diffs { @@ -1225,17 +1246,14 @@ func (dmp *DiffMatchPatch) DiffToDelta(diffs []Diff) string { _, _ = text.WriteString("+") _, _ = text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1)) _, _ = text.WriteString("\t") - break case DiffDelete: _, _ = text.WriteString("-") _, _ = text.WriteString(strconv.Itoa(utf8.RuneCountInString(aDiff.Text))) _, _ = text.WriteString("\t") - break case DiffEqual: _, _ = text.WriteString("=") _, _ = text.WriteString(strconv.Itoa(utf8.RuneCountInString(aDiff.Text))) _, _ = text.WriteString("\t") - break } } delta := text.String() @@ -1247,8 +1265,9 @@ func (dmp *DiffMatchPatch) DiffToDelta(diffs []Diff) string { return delta } -// DiffFromDelta given the original text1, and an encoded string which describes the operations required to transform text1 into text2, comAdde the full diff. -func (dmp *DiffMatchPatch) DiffFromDelta(text1 string, delta string) (diffs []Diff, err error) { +// DiffFromDelta given the original text1, and an encoded string which describes the operations +// required to transform text1 into text2, comAdde the full diff. +func (dmp *DiffMatchPatch) DiffFromDelta(text1, delta string) (diffs []Diff, err error) { i := 0 runes := []rune(text1) @@ -1258,7 +1277,8 @@ func (dmp *DiffMatchPatch) DiffFromDelta(text1 string, delta string) (diffs []Di continue } - // Each token begins with a one character parameter which specifies the operation of this token (delete, insert, equality). + // Each token begins with a one character parameter which specifies the operation + // of this token (delete, insert, equality). param := token[1:] switch op := token[0]; op { @@ -1297,12 +1317,16 @@ func (dmp *DiffMatchPatch) DiffFromDelta(text1 string, delta string) (diffs []Di } default: // Anything else is an error. - return nil, errors.New("Invalid diff operation in DiffFromDelta: " + string(token[0])) + return nil, errors.New("invalid diff operation in DiffFromDelta: " + string(token[0])) } } if i != len(runes) { - return nil, fmt.Errorf("Delta length (%v) is different from source text length (%v)", i, len(text1)) + return nil, fmt.Errorf( + "delta length (%v) is different from source text length (%v)", + i, + len(text1), + ) } return diffs, nil @@ -1310,10 +1334,11 @@ func (dmp *DiffMatchPatch) DiffFromDelta(text1 string, delta string) (diffs []Di // diffLinesToStrings splits two texts into a list of strings. Each string represents one line. func (dmp *DiffMatchPatch) diffLinesToStrings(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. + // '\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' - //Each string has the index of lineArray which it points to + // Each string has the index of lineArray which it points to strIndexArray1 := dmp.diffLinesToStringsMunge(text1, &lineArray) strIndexArray2 := dmp.diffLinesToStringsMunge(text2, &lineArray) @@ -1322,7 +1347,9 @@ func (dmp *DiffMatchPatch) diffLinesToStrings(text1, text2 string) (string, stri // diffLinesToStringsMunge splits a text into an array of strings, and reduces the texts to a []string. func (dmp *DiffMatchPatch) diffLinesToStringsMunge(text string, lineArray *[]string) []uint32 { - // Walk the text, pulling out a substring for each line. text.split('\n') would would temporarily double our memory footprint. Modifying text would create many large strings to garbage collect. + // Walk the text, pulling out a substring for each line. text.split('\n') + // would would temporarily double our memory footprint. + // Modifying text would create many large strings to garbage collect. lineHash := map[string]int{} // e.g. lineHash['Hello\n'] == 4 lineStart := 0 lineEnd := -1 diff --git a/diffmatchpatch/diff_test.go b/diffmatchpatch/diff_test.go index acb97e3..f8f7982 100644 --- a/diffmatchpatch/diff_test.go +++ b/diffmatchpatch/diff_test.go @@ -9,40 +9,22 @@ package diffmatchpatch import ( - "bytes" "fmt" - "io/ioutil" + "io" "os" + "reflect" "strconv" "strings" "testing" "time" "unicode/utf8" - - "github.com/stretchr/testify/assert" ) -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)) +func assertEqual(t *testing.T, expected, actual interface{}, msg ...string) { + t.Helper() + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Not equal: \nexpected: %v\nactual: %v\n - %s\n", expected, actual, msg[0]) } - - return w.String() } func diffRebuildTexts(diffs []Diff) []string { @@ -78,17 +60,18 @@ func TestDiffCommonPrefix(t *testing.T) { {"Whole", "1234", "1234xyz", 4}, } { actual := dmp.DiffCommonPrefix(tc.Text1, tc.Text2) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } } func BenchmarkDiffCommonPrefix(b *testing.B) { s := "ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ" - dmp := New() + b.ResetTimer() + for i := 0; i < b.N; i++ { - dmp.DiffCommonPrefix(s, s) + SinkInt = dmp.DiffCommonPrefix(s, s) } } @@ -106,7 +89,7 @@ func TestCommonPrefixLength(t *testing.T) { {"1234", "1234xyz", 4}, } { actual := commonPrefixLength([]rune(tc.Text1), []rune(tc.Text2)) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } @@ -128,7 +111,7 @@ func TestDiffCommonSuffix(t *testing.T) { {"Whole", "1234", "xyz1234", 4}, } { actual := dmp.DiffCommonSuffix(tc.Text1, tc.Text2) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } } @@ -136,7 +119,6 @@ var SinkInt int // exported sink var to avoid compiler optimizations in benchmar func BenchmarkDiffCommonSuffix(b *testing.B) { s := "ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ" - dmp := New() b.ResetTimer() @@ -153,9 +135,10 @@ func BenchmarkCommonLength(b *testing.B) { }{ {name: "empty", x: nil, y: []rune{}}, {name: "short", x: []rune("AABCC"), y: []rune("AA-CC")}, - {name: "long", - x: []rune(strings.Repeat("A", 1000) + "B" + strings.Repeat("C", 1000)), - y: []rune(strings.Repeat("A", 1000) + "-" + strings.Repeat("C", 1000)), + { + name: "long", + x: []rune(strings.Repeat("A", 1000) + "B" + strings.Repeat("C", 1000)), + y: []rune(strings.Repeat("A", 1000) + "-" + strings.Repeat("C", 1000)), }, } b.Run("prefix", func(b *testing.B) { @@ -193,7 +176,7 @@ func TestCommonSuffixLength(t *testing.T) { {"123", "a3", 1}, } { actual := commonSuffixLength([]rune(tc.Text1), []rune(tc.Text2)) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } @@ -218,7 +201,7 @@ func TestDiffCommonOverlap(t *testing.T) { {"Unicode", "fi", "\ufb01i", 0}, } { actual := dmp.DiffCommonOverlap(tc.Text1, tc.Text2) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } } @@ -253,7 +236,7 @@ func TestDiffHalfMatch(t *testing.T) { {"qHilloHelloHew", "xHelloHeHulloy", []string{"qHillo", "w", "x", "Hulloy", "HelloHe"}}, } { actual := dmp.DiffHalfMatch(tc.Text1, tc.Text2) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } dmp.DiffTimeout = 0 @@ -263,20 +246,25 @@ func TestDiffHalfMatch(t *testing.T) { {"qHilloHelloHew", "xHelloHeHulloy", nil}, } { actual := dmp.DiffHalfMatch(tc.Text1, tc.Text2) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } +var SinkSliceString []string + func BenchmarkDiffHalfMatch(b *testing.B) { - s1, s2 := speedtestTexts() + var r []string + s1, s2 := speedtestTexts() dmp := New() b.ResetTimer() for i := 0; i < b.N; i++ { - dmp.DiffHalfMatch(s1, s2) + r = dmp.DiffHalfMatch(s1, s2) } + + SinkSliceString = r } func TestDiffBisectSplit(t *testing.T) { @@ -294,7 +282,7 @@ func TestDiffBisectSplit(t *testing.T) { []rune(tc.Text2), 7, 6, time.Now().Add(time.Hour)) for _, d := range diffs { - assert.True(t, utf8.ValidString(d.Text)) + assertEqual(t, true, utf8.ValidString(d.Text)) } // TODO define the expected outcome @@ -320,9 +308,9 @@ func TestDiffLinesToChars(t *testing.T) { {"alpha\nbeta\nalpha", "", "1,2,3", "", []string{"", "alpha\n", "beta\n", "alpha"}}, } { actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(tc.Text1, tc.Text2) - assert.Equal(t, tc.ExpectedChars1, actualChars1, fmt.Sprintf("Test case #%d, %#v", i, tc)) - assert.Equal(t, tc.ExpectedChars2, actualChars2, fmt.Sprintf("Test case #%d, %#v", i, tc)) - assert.Equal(t, tc.ExpectedLines, actualLines, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.ExpectedChars1, actualChars1, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.ExpectedChars2, actualChars2, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.ExpectedLines, actualLines, fmt.Sprintf("Test case #%d, %#v", i, tc)) } // More than 256 to reveal any 8-bit limitations. @@ -337,19 +325,18 @@ func TestDiffLinesToChars(t *testing.T) { } lines := strings.Join(lineList, "") chars := strings.Join(charList[:], ",") - assert.Equal(t, n, len(strings.Split(chars, ","))) + assertEqual(t, n, len(strings.Split(chars, ","))) actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(lines, "") - assert.Equal(t, chars, actualChars1) - assert.Equal(t, "", actualChars2) - assert.Equal(t, lineList, actualLines) + assertEqual(t, chars, actualChars1) + assertEqual(t, "", actualChars2) + assertEqual(t, lineList, actualLines) } func TestDiffCharsToLines(t *testing.T) { type TestCase struct { - Diffs []Diff - Lines []string - + Diffs []Diff + Lines []string Expected []Diff } @@ -370,7 +357,7 @@ func TestDiffCharsToLines(t *testing.T) { }, } { actual := dmp.DiffCharsToLines(tc.Diffs, tc.Lines) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } // More than 256 to reveal any 8-bit limitations. @@ -383,19 +370,17 @@ func TestDiffCharsToLines(t *testing.T) { lineList = append(lineList, strconv.Itoa(x)+"\n") charList = append(charList, strconv.Itoa(x)) } - assert.Equal(t, n, len(charList)) + assertEqual(t, n, len(charList)) chars := strings.Join(charList[:], ",") - actual := dmp.DiffCharsToLines([]Diff{Diff{DiffDelete, chars}}, lineList) - assert.Equal(t, []Diff{Diff{DiffDelete, strings.Join(lineList, "")}}, actual) + actual := dmp.DiffCharsToLines([]Diff{{DiffDelete, chars}}, lineList) + assertEqual(t, []Diff{{DiffDelete, strings.Join(lineList, "")}}, actual) } func TestDiffCleanupMerge(t *testing.T) { type TestCase struct { - Name string - - Diffs []Diff - + Name string + Diffs []Diff Expected []Diff } @@ -409,76 +394,74 @@ func TestDiffCleanupMerge(t *testing.T) { }, { "No Diff case", - []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffInsert, "c"}}, - []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffInsert, "c"}}, + []Diff{{DiffEqual, "a"}, {DiffDelete, "b"}, {DiffInsert, "c"}}, + []Diff{{DiffEqual, "a"}, {DiffDelete, "b"}, {DiffInsert, "c"}}, }, { "Merge equalities", - []Diff{Diff{DiffEqual, "a"}, Diff{DiffEqual, "b"}, Diff{DiffEqual, "c"}}, - []Diff{Diff{DiffEqual, "abc"}}, + []Diff{{DiffEqual, "a"}, {DiffEqual, "b"}, {DiffEqual, "c"}}, + []Diff{{DiffEqual, "abc"}}, }, { "Merge deletions", - []Diff{Diff{DiffDelete, "a"}, Diff{DiffDelete, "b"}, Diff{DiffDelete, "c"}}, - []Diff{Diff{DiffDelete, "abc"}}, + []Diff{{DiffDelete, "a"}, {DiffDelete, "b"}, {DiffDelete, "c"}}, + []Diff{{DiffDelete, "abc"}}, }, { "Merge insertions", - []Diff{Diff{DiffInsert, "a"}, Diff{DiffInsert, "b"}, Diff{DiffInsert, "c"}}, - []Diff{Diff{DiffInsert, "abc"}}, + []Diff{{DiffInsert, "a"}, {DiffInsert, "b"}, {DiffInsert, "c"}}, + []Diff{{DiffInsert, "abc"}}, }, { "Merge interweave", - []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "b"}, Diff{DiffDelete, "c"}, Diff{DiffInsert, "d"}, Diff{DiffEqual, "e"}, Diff{DiffEqual, "f"}}, - []Diff{Diff{DiffDelete, "ac"}, Diff{DiffInsert, "bd"}, Diff{DiffEqual, "ef"}}, + []Diff{{DiffDelete, "a"}, {DiffInsert, "b"}, {DiffDelete, "c"}, {DiffInsert, "d"}, {DiffEqual, "e"}, {DiffEqual, "f"}}, + []Diff{{DiffDelete, "ac"}, {DiffInsert, "bd"}, {DiffEqual, "ef"}}, }, { "Prefix and suffix detection", - []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "abc"}, Diff{DiffDelete, "dc"}}, - []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "c"}}, + []Diff{{DiffDelete, "a"}, {DiffInsert, "abc"}, {DiffDelete, "dc"}}, + []Diff{{DiffEqual, "a"}, {DiffDelete, "d"}, {DiffInsert, "b"}, {DiffEqual, "c"}}, }, { "Prefix and suffix detection with equalities", - []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "a"}, Diff{DiffInsert, "abc"}, Diff{DiffDelete, "dc"}, Diff{DiffEqual, "y"}}, - []Diff{Diff{DiffEqual, "xa"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "cy"}}, + []Diff{{DiffEqual, "x"}, {DiffDelete, "a"}, {DiffInsert, "abc"}, {DiffDelete, "dc"}, {DiffEqual, "y"}}, + []Diff{{DiffEqual, "xa"}, {DiffDelete, "d"}, {DiffInsert, "b"}, {DiffEqual, "cy"}}, }, { "Same test as above but with unicode (\u0101 will appear in diffs with at least 257 unique lines)", - []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "\u0101"}, Diff{DiffInsert, "\u0101bc"}, Diff{DiffDelete, "dc"}, Diff{DiffEqual, "y"}}, - []Diff{Diff{DiffEqual, "x\u0101"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "cy"}}, + []Diff{{DiffEqual, "x"}, {DiffDelete, "\u0101"}, {DiffInsert, "\u0101bc"}, {DiffDelete, "dc"}, {DiffEqual, "y"}}, + []Diff{{DiffEqual, "x\u0101"}, {DiffDelete, "d"}, {DiffInsert, "b"}, {DiffEqual, "cy"}}, }, { "Slide edit left", - []Diff{Diff{DiffEqual, "a"}, Diff{DiffInsert, "ba"}, Diff{DiffEqual, "c"}}, - []Diff{Diff{DiffInsert, "ab"}, Diff{DiffEqual, "ac"}}, + []Diff{{DiffEqual, "a"}, {DiffInsert, "ba"}, {DiffEqual, "c"}}, + []Diff{{DiffInsert, "ab"}, {DiffEqual, "ac"}}, }, { "Slide edit right", - []Diff{Diff{DiffEqual, "c"}, Diff{DiffInsert, "ab"}, Diff{DiffEqual, "a"}}, - []Diff{Diff{DiffEqual, "ca"}, Diff{DiffInsert, "ba"}}, + []Diff{{DiffEqual, "c"}, {DiffInsert, "ab"}, {DiffEqual, "a"}}, + []Diff{{DiffEqual, "ca"}, {DiffInsert, "ba"}}, }, { "Slide edit left recursive", - []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffEqual, "c"}, Diff{DiffDelete, "ac"}, Diff{DiffEqual, "x"}}, - []Diff{Diff{DiffDelete, "abc"}, Diff{DiffEqual, "acx"}}, + []Diff{{DiffEqual, "a"}, {DiffDelete, "b"}, {DiffEqual, "c"}, {DiffDelete, "ac"}, {DiffEqual, "x"}}, + []Diff{{DiffDelete, "abc"}, {DiffEqual, "acx"}}, }, { "Slide edit right recursive", - []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "ca"}, Diff{DiffEqual, "c"}, Diff{DiffDelete, "b"}, Diff{DiffEqual, "a"}}, - []Diff{Diff{DiffEqual, "xca"}, Diff{DiffDelete, "cba"}}, + []Diff{{DiffEqual, "x"}, {DiffDelete, "ca"}, {DiffEqual, "c"}, {DiffDelete, "b"}, {DiffEqual, "a"}}, + []Diff{{DiffEqual, "xca"}, {DiffDelete, "cba"}}, }, } { actual := dmp.DiffCleanupMerge(tc.Diffs) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } } func TestDiffCleanupSemanticLossless(t *testing.T) { type TestCase struct { - Name string - - Diffs []Diff - + Name string + Diffs []Diff Expected []Diff } @@ -493,130 +476,128 @@ func TestDiffCleanupSemanticLossless(t *testing.T) { { "Blank lines", []Diff{ - Diff{DiffEqual, "AAA\r\n\r\nBBB"}, - Diff{DiffInsert, "\r\nDDD\r\n\r\nBBB"}, - Diff{DiffEqual, "\r\nEEE"}, + {DiffEqual, "AAA\r\n\r\nBBB"}, + {DiffInsert, "\r\nDDD\r\n\r\nBBB"}, + {DiffEqual, "\r\nEEE"}, }, []Diff{ - Diff{DiffEqual, "AAA\r\n\r\n"}, - Diff{DiffInsert, "BBB\r\nDDD\r\n\r\n"}, - Diff{DiffEqual, "BBB\r\nEEE"}, + {DiffEqual, "AAA\r\n\r\n"}, + {DiffInsert, "BBB\r\nDDD\r\n\r\n"}, + {DiffEqual, "BBB\r\nEEE"}, }, }, { "Line boundaries", []Diff{ - Diff{DiffEqual, "AAA\r\nBBB"}, - Diff{DiffInsert, " DDD\r\nBBB"}, - Diff{DiffEqual, " EEE"}, + {DiffEqual, "AAA\r\nBBB"}, + {DiffInsert, " DDD\r\nBBB"}, + {DiffEqual, " EEE"}, }, []Diff{ - Diff{DiffEqual, "AAA\r\n"}, - Diff{DiffInsert, "BBB DDD\r\n"}, - Diff{DiffEqual, "BBB EEE"}, + {DiffEqual, "AAA\r\n"}, + {DiffInsert, "BBB DDD\r\n"}, + {DiffEqual, "BBB EEE"}, }, }, { "Word boundaries", []Diff{ - Diff{DiffEqual, "The c"}, - Diff{DiffInsert, "ow and the c"}, - Diff{DiffEqual, "at."}, + {DiffEqual, "The c"}, + {DiffInsert, "ow and the c"}, + {DiffEqual, "at."}, }, []Diff{ - Diff{DiffEqual, "The "}, - Diff{DiffInsert, "cow and the "}, - Diff{DiffEqual, "cat."}, + {DiffEqual, "The "}, + {DiffInsert, "cow and the "}, + {DiffEqual, "cat."}, }, }, { "Alphanumeric boundaries", []Diff{ - Diff{DiffEqual, "The-c"}, - Diff{DiffInsert, "ow-and-the-c"}, - Diff{DiffEqual, "at."}, + {DiffEqual, "The-c"}, + {DiffInsert, "ow-and-the-c"}, + {DiffEqual, "at."}, }, []Diff{ - Diff{DiffEqual, "The-"}, - Diff{DiffInsert, "cow-and-the-"}, - Diff{DiffEqual, "cat."}, + {DiffEqual, "The-"}, + {DiffInsert, "cow-and-the-"}, + {DiffEqual, "cat."}, }, }, { "Hitting the start", []Diff{ - Diff{DiffEqual, "a"}, - Diff{DiffDelete, "a"}, - Diff{DiffEqual, "ax"}, + {DiffEqual, "a"}, + {DiffDelete, "a"}, + {DiffEqual, "ax"}, }, []Diff{ - Diff{DiffDelete, "a"}, - Diff{DiffEqual, "aax"}, + {DiffDelete, "a"}, + {DiffEqual, "aax"}, }, }, { "Hitting the end", []Diff{ - Diff{DiffEqual, "xa"}, - Diff{DiffDelete, "a"}, - Diff{DiffEqual, "a"}, + {DiffEqual, "xa"}, + {DiffDelete, "a"}, + {DiffEqual, "a"}, }, []Diff{ - Diff{DiffEqual, "xaa"}, - Diff{DiffDelete, "a"}, + {DiffEqual, "xaa"}, + {DiffDelete, "a"}, }, }, { "Sentence boundaries", []Diff{ - Diff{DiffEqual, "The xxx. The "}, - Diff{DiffInsert, "zzz. The "}, - Diff{DiffEqual, "yyy."}, + {DiffEqual, "The xxx. The "}, + {DiffInsert, "zzz. The "}, + {DiffEqual, "yyy."}, }, []Diff{ - Diff{DiffEqual, "The xxx."}, - Diff{DiffInsert, " The zzz."}, - Diff{DiffEqual, " The yyy."}, + {DiffEqual, "The xxx."}, + {DiffInsert, " The zzz."}, + {DiffEqual, " The yyy."}, }, }, { "UTF-8 strings", []Diff{ - Diff{DiffEqual, "The ♕. The "}, - Diff{DiffInsert, "♔. The "}, - Diff{DiffEqual, "♖."}, + {DiffEqual, "The ♕. The "}, + {DiffInsert, "♔. The "}, + {DiffEqual, "♖."}, }, []Diff{ - Diff{DiffEqual, "The ♕."}, - Diff{DiffInsert, " The ♔."}, - Diff{DiffEqual, " The ♖."}, + {DiffEqual, "The ♕."}, + {DiffInsert, " The ♔."}, + {DiffEqual, " The ♖."}, }, }, { "Rune boundaries", []Diff{ - Diff{DiffEqual, "♕♕"}, - Diff{DiffInsert, "♔♔"}, - Diff{DiffEqual, "♖♖"}, + {DiffEqual, "♕♕"}, + {DiffInsert, "♔♔"}, + {DiffEqual, "♖♖"}, }, []Diff{ - Diff{DiffEqual, "♕♕"}, - Diff{DiffInsert, "♔♔"}, - Diff{DiffEqual, "♖♖"}, + {DiffEqual, "♕♕"}, + {DiffInsert, "♔♔"}, + {DiffEqual, "♖♖"}, }, }, } { actual := dmp.DiffCleanupSemanticLossless(tc.Diffs) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } } func TestDiffCleanupSemantic(t *testing.T) { type TestCase struct { - Name string - - Diffs []Diff - + Name string + Diffs []Diff Expected []Diff } @@ -863,22 +844,26 @@ func TestDiffCleanupSemantic(t *testing.T) { }, } { actual := dmp.DiffCleanupSemantic(tc.Diffs) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } } +var SinkSliceDiff []Diff + func BenchmarkDiffCleanupSemantic(b *testing.B) { - s1, s2 := speedtestTexts() + var r []Diff + s1, s2 := speedtestTexts() dmp := New() - diffs := dmp.DiffMain(s1, s2, false) b.ResetTimer() for i := 0; i < b.N; i++ { - dmp.DiffCleanupSemantic(diffs) + r = dmp.DiffCleanupSemantic(diffs) } + + SinkSliceDiff = r } func TestDiffCleanupEfficiency(t *testing.T) { @@ -902,66 +887,66 @@ func TestDiffCleanupEfficiency(t *testing.T) { { "No elimination", []Diff{ - Diff{DiffDelete, "ab"}, - Diff{DiffInsert, "12"}, - Diff{DiffEqual, "wxyz"}, - Diff{DiffDelete, "cd"}, - Diff{DiffInsert, "34"}, + {DiffDelete, "ab"}, + {DiffInsert, "12"}, + {DiffEqual, "wxyz"}, + {DiffDelete, "cd"}, + {DiffInsert, "34"}, }, []Diff{ - Diff{DiffDelete, "ab"}, - Diff{DiffInsert, "12"}, - Diff{DiffEqual, "wxyz"}, - Diff{DiffDelete, "cd"}, - Diff{DiffInsert, "34"}, + {DiffDelete, "ab"}, + {DiffInsert, "12"}, + {DiffEqual, "wxyz"}, + {DiffDelete, "cd"}, + {DiffInsert, "34"}, }, }, { "Four-edit elimination", []Diff{ - Diff{DiffDelete, "ab"}, - Diff{DiffInsert, "12"}, - Diff{DiffEqual, "xyz"}, - Diff{DiffDelete, "cd"}, - Diff{DiffInsert, "34"}, + {DiffDelete, "ab"}, + {DiffInsert, "12"}, + {DiffEqual, "xyz"}, + {DiffDelete, "cd"}, + {DiffInsert, "34"}, }, []Diff{ - Diff{DiffDelete, "abxyzcd"}, - Diff{DiffInsert, "12xyz34"}, + {DiffDelete, "abxyzcd"}, + {DiffInsert, "12xyz34"}, }, }, { "Three-edit elimination", []Diff{ - Diff{DiffInsert, "12"}, - Diff{DiffEqual, "x"}, - Diff{DiffDelete, "cd"}, - Diff{DiffInsert, "34"}, + {DiffInsert, "12"}, + {DiffEqual, "x"}, + {DiffDelete, "cd"}, + {DiffInsert, "34"}, }, []Diff{ - Diff{DiffDelete, "xcd"}, - Diff{DiffInsert, "12x34"}, + {DiffDelete, "xcd"}, + {DiffInsert, "12x34"}, }, }, { "Backpass elimination", []Diff{ - Diff{DiffDelete, "ab"}, - Diff{DiffInsert, "12"}, - Diff{DiffEqual, "xy"}, - Diff{DiffInsert, "34"}, - Diff{DiffEqual, "z"}, - Diff{DiffDelete, "cd"}, - Diff{DiffInsert, "56"}, + {DiffDelete, "ab"}, + {DiffInsert, "12"}, + {DiffEqual, "xy"}, + {DiffInsert, "34"}, + {DiffEqual, "z"}, + {DiffDelete, "cd"}, + {DiffInsert, "56"}, }, []Diff{ - Diff{DiffDelete, "abxyzcd"}, - Diff{DiffInsert, "12xy34z56"}, + {DiffDelete, "abxyzcd"}, + {DiffInsert, "12xy34z56"}, }, }, } { actual := dmp.DiffCleanupEfficiency(tc.Diffs) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } dmp.DiffEditCost = 5 @@ -970,20 +955,20 @@ func TestDiffCleanupEfficiency(t *testing.T) { { "High cost elimination", []Diff{ - Diff{DiffDelete, "ab"}, - Diff{DiffInsert, "12"}, - Diff{DiffEqual, "wxyz"}, - Diff{DiffDelete, "cd"}, - Diff{DiffInsert, "34"}, + {DiffDelete, "ab"}, + {DiffInsert, "12"}, + {DiffEqual, "wxyz"}, + {DiffDelete, "cd"}, + {DiffInsert, "34"}, }, []Diff{ - Diff{DiffDelete, "abwxyzcd"}, - Diff{DiffInsert, "12wxyz34"}, + {DiffDelete, "abwxyzcd"}, + {DiffInsert, "12wxyz34"}, }, }, } { actual := dmp.DiffCleanupEfficiency(tc.Diffs) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } } @@ -1003,12 +988,12 @@ func TestDiffPrettyHtml(t *testing.T) { {DiffDelete, "b"}, {DiffInsert, "c&d"}, }, - - Expected: "
<B>b</B>c&d", + Expected: "
<B>b<" + + "/B>c&d", }, } { actual := dmp.DiffPrettyHtml(tc.Diffs) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } @@ -1033,7 +1018,7 @@ func TestDiffPrettyText(t *testing.T) { }, } { actual := dmp.DiffPrettyText(tc.Diffs) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } @@ -1064,10 +1049,10 @@ func TestDiffText(t *testing.T) { }, } { actualText1 := dmp.DiffText1(tc.Diffs) - assert.Equal(t, tc.ExpectedText1, actualText1, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.ExpectedText1, actualText1, fmt.Sprintf("Test case #%d, %#v", i, tc)) actualText2 := dmp.DiffText2(tc.Diffs) - assert.Equal(t, tc.ExpectedText2, actualText2, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.ExpectedText2, actualText2, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } @@ -1084,80 +1069,126 @@ func TestDiffDelta(t *testing.T) { dmp := New() for i, tc := range []TestCase{ - {"Delta shorter than text", "jumps over the lazyx", "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", "Delta length (19) is different from source text length (20)"}, - {"Delta longer than text", "umps over the lazy", "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", "Delta length (19) is different from source text length (18)"}, - {"Invalid URL escaping", "", "+%c3%xy", "invalid URL escape \"%xy\""}, - {"Invalid UTF-8 sequence", "", "+%c3xy", "invalid UTF-8 token: \"\\xc3xy\""}, - {"Invalid diff operation", "", "a", "Invalid diff operation in DiffFromDelta: a"}, - {"Invalid diff syntax", "", "-", "strconv.ParseInt: parsing \"\": invalid syntax"}, - {"Negative number in delta", "", "--1", "Negative number in DiffFromDelta: -1"}, - {"Empty case", "", "", ""}, + { + "Delta shorter than text", + "jumps over the lazyx", + "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", + "delta length (19) is different from source text length (20)", + }, + { + "Delta longer than text", + "umps over the lazy", + "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", + "delta length (19) is different from source text length (18)", + }, + { + "Invalid URL escaping", + "", + "+%c3%xy", + "invalid URL escape \"%xy\"", + }, + { + "Invalid UTF-8 sequence", + "", + "+%c3xy", + "invalid UTF-8 token: \"\\xc3xy\"", + }, + { + "Invalid diff operation", + "", + "a", + "invalid diff operation in DiffFromDelta: a", + }, + { + "Invalid diff syntax", + "", + "-", + "strconv.ParseInt: parsing \"\": invalid syntax", + }, + { + "Negative number in delta", + "", + "--1", + "Negative number in DiffFromDelta: -1", + }, + { + "Empty case", + "", + "", + "", + }, } { diffs, err := dmp.DiffFromDelta(tc.Text, tc.Delta) msg := fmt.Sprintf("Test case #%d, %s", i, tc.Name) if tc.ErrorMessagePrefix == "" { - assert.Nil(t, err, msg) - assert.Nil(t, diffs, msg) + assertEqual(t, nil, err, msg) + assertEqual(t, 0, len(diffs), msg) } else { e := err.Error() if strings.HasPrefix(e, tc.ErrorMessagePrefix) { e = tc.ErrorMessagePrefix } - assert.Nil(t, diffs, msg) - assert.Equal(t, tc.ErrorMessagePrefix, e, msg) + assertEqual(t, 0, len(diffs), msg) + assertEqual(t, tc.ErrorMessagePrefix, e, msg) } } // Convert a diff into delta string. diffs := []Diff{ - Diff{DiffEqual, "jump"}, - Diff{DiffDelete, "s"}, - Diff{DiffInsert, "ed"}, - Diff{DiffEqual, " over "}, - Diff{DiffDelete, "the"}, - Diff{DiffInsert, "a"}, - Diff{DiffEqual, " lazy"}, - Diff{DiffInsert, "old dog"}, + {DiffEqual, "jump"}, + {DiffDelete, "s"}, + {DiffInsert, "ed"}, + {DiffEqual, " over "}, + {DiffDelete, "the"}, + {DiffInsert, "a"}, + {DiffEqual, " lazy"}, + {DiffInsert, "old dog"}, } text1 := dmp.DiffText1(diffs) - assert.Equal(t, "jumps over the lazy", text1) + assertEqual(t, "jumps over the lazy", text1) delta := dmp.DiffToDelta(diffs) - assert.Equal(t, "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", delta) + assertEqual(t, "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", delta) // Convert delta string into a diff. deltaDiffs, err := dmp.DiffFromDelta(text1, delta) - assert.Equal(t, diffs, deltaDiffs) + assertEqual(t, diffs, deltaDiffs) + assertEqual(t, nil, err) // Test deltas with special characters. diffs = []Diff{ - Diff{DiffEqual, "\u0680 \x00 \t %"}, - Diff{DiffDelete, "\u0681 \x01 \n ^"}, - Diff{DiffInsert, "\u0682 \x02 \\ |"}, + {DiffEqual, "\u0680 \x00 \t %"}, + {DiffDelete, "\u0681 \x01 \n ^"}, + {DiffInsert, "\u0682 \x02 \\ |"}, } text1 = dmp.DiffText1(diffs) - assert.Equal(t, "\u0680 \x00 \t %\u0681 \x01 \n ^", text1) + assertEqual(t, "\u0680 \x00 \t %\u0681 \x01 \n ^", text1) // Lowercase, due to UrlEncode uses lower. delta = dmp.DiffToDelta(diffs) - assert.Equal(t, "=7\t-7\t+%DA%82 %02 %5C %7C", delta) + assertEqual(t, "=7\t-7\t+%DA%82 %02 %5C %7C", delta) deltaDiffs, err = dmp.DiffFromDelta(text1, delta) - assert.Equal(t, diffs, deltaDiffs) - assert.Nil(t, err) + assertEqual(t, diffs, deltaDiffs) + assertEqual(t, nil, err) // Verify pool of unchanged characters. diffs = []Diff{ - Diff{DiffInsert, "A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # "}, + {DiffInsert, "A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # "}, } delta = dmp.DiffToDelta(diffs) - assert.Equal(t, "+A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # ", delta, "Unchanged characters.") + assertEqual( + t, + "+A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # ", + delta, + "Unchanged characters.", + ) // Convert delta string into a diff. deltaDiffs, err = dmp.DiffFromDelta("", delta) - assert.Equal(t, diffs, deltaDiffs) - assert.Nil(t, err) + assertEqual(t, diffs, deltaDiffs) + assertEqual(t, nil, err) } func TestDiffXIndex(t *testing.T) { @@ -1177,7 +1208,7 @@ func TestDiffXIndex(t *testing.T) { {"Translation on deletion", []Diff{{DiffEqual, "a"}, {DiffDelete, "1234"}, {DiffEqual, "xyz"}}, 3, 1}, } { actual := dmp.DiffXIndex(tc.Diffs, tc.Location) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } } @@ -1198,7 +1229,7 @@ func TestDiffLevenshtein(t *testing.T) { {"Levenshtein with middle equality", []Diff{{DiffDelete, "абв"}, {DiffEqual, "эюя"}, {DiffInsert, "1234"}}, 7}, } { actual := dmp.DiffLevenshtein(tc.Diffs) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } } @@ -1228,7 +1259,7 @@ func TestDiffBisect(t *testing.T) { }, { Name: "Negative deadlines count as having infinite time", - Time: time.Date(0001, time.January, 01, 00, 00, 00, 00, time.UTC), + Time: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Expected: []Diff{ {DiffDelete, "c"}, @@ -1249,12 +1280,12 @@ func TestDiffBisect(t *testing.T) { }, } { actual := dmp.DiffBisect("cat", "map", tc.Time) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } // Test for invalid UTF-8 sequences - assert.Equal(t, []Diff{ - Diff{DiffEqual, "��"}, + assertEqual(t, []Diff{ + {DiffEqual, "��"}, }, dmp.DiffBisect("\xe0\xe5", "\xe0\xe5", time.Now().Add(time.Minute))) } @@ -1278,31 +1309,31 @@ func TestDiffMain(t *testing.T) { { "abc", "abc", - []Diff{Diff{DiffEqual, "abc"}}, + []Diff{{DiffEqual, "abc"}}, }, { "abc", "ab123c", - []Diff{Diff{DiffEqual, "ab"}, Diff{DiffInsert, "123"}, Diff{DiffEqual, "c"}}, + []Diff{{DiffEqual, "ab"}, {DiffInsert, "123"}, {DiffEqual, "c"}}, }, { "a123bc", "abc", - []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "123"}, Diff{DiffEqual, "bc"}}, + []Diff{{DiffEqual, "a"}, {DiffDelete, "123"}, {DiffEqual, "bc"}}, }, { "abc", "a123b456c", - []Diff{Diff{DiffEqual, "a"}, Diff{DiffInsert, "123"}, Diff{DiffEqual, "b"}, Diff{DiffInsert, "456"}, Diff{DiffEqual, "c"}}, + []Diff{{DiffEqual, "a"}, {DiffInsert, "123"}, {DiffEqual, "b"}, {DiffInsert, "456"}, {DiffEqual, "c"}}, }, { "a123b456c", "abc", - []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "123"}, Diff{DiffEqual, "b"}, Diff{DiffDelete, "456"}, Diff{DiffEqual, "c"}}, + []Diff{{DiffEqual, "a"}, {DiffDelete, "123"}, {DiffEqual, "b"}, {DiffDelete, "456"}, {DiffEqual, "c"}}, }, } { actual := dmp.DiffMain(tc.Text1, tc.Text2, false) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } // Perform a real diff and switch off the timeout. @@ -1312,84 +1343,85 @@ func TestDiffMain(t *testing.T) { { "a", "b", - []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "b"}}, + []Diff{{DiffDelete, "a"}, {DiffInsert, "b"}}, }, { "Apples are a fruit.", "Bananas are also fruit.", []Diff{ - Diff{DiffDelete, "Apple"}, - Diff{DiffInsert, "Banana"}, - Diff{DiffEqual, "s are a"}, - Diff{DiffInsert, "lso"}, - Diff{DiffEqual, " fruit."}, + {DiffDelete, "Apple"}, + {DiffInsert, "Banana"}, + {DiffEqual, "s are a"}, + {DiffInsert, "lso"}, + {DiffEqual, " fruit."}, }, }, { "ax\t", "\u0680x\u0000", []Diff{ - Diff{DiffDelete, "a"}, - Diff{DiffInsert, "\u0680"}, - Diff{DiffEqual, "x"}, - Diff{DiffDelete, "\t"}, - Diff{DiffInsert, "\u0000"}, + {DiffDelete, "a"}, + {DiffInsert, "\u0680"}, + {DiffEqual, "x"}, + {DiffDelete, "\t"}, + {DiffInsert, "\u0000"}, }, }, { "1ayb2", "abxab", []Diff{ - Diff{DiffDelete, "1"}, - Diff{DiffEqual, "a"}, - Diff{DiffDelete, "y"}, - Diff{DiffEqual, "b"}, - Diff{DiffDelete, "2"}, - Diff{DiffInsert, "xab"}, + {DiffDelete, "1"}, + {DiffEqual, "a"}, + {DiffDelete, "y"}, + {DiffEqual, "b"}, + {DiffDelete, "2"}, + {DiffInsert, "xab"}, }, }, { "abcy", "xaxcxabc", []Diff{ - Diff{DiffInsert, "xaxcx"}, - Diff{DiffEqual, "abc"}, Diff{DiffDelete, "y"}, + {DiffInsert, "xaxcx"}, + {DiffEqual, "abc"}, + {DiffDelete, "y"}, }, }, { "ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", "a-bcd-efghijklmnopqrs", []Diff{ - Diff{DiffDelete, "ABCD"}, - Diff{DiffEqual, "a"}, - Diff{DiffDelete, "="}, - Diff{DiffInsert, "-"}, - Diff{DiffEqual, "bcd"}, - Diff{DiffDelete, "="}, - Diff{DiffInsert, "-"}, - Diff{DiffEqual, "efghijklmnopqrs"}, - Diff{DiffDelete, "EFGHIJKLMNOefg"}, + {DiffDelete, "ABCD"}, + {DiffEqual, "a"}, + {DiffDelete, "="}, + {DiffInsert, "-"}, + {DiffEqual, "bcd"}, + {DiffDelete, "="}, + {DiffInsert, "-"}, + {DiffEqual, "efghijklmnopqrs"}, + {DiffDelete, "EFGHIJKLMNOefg"}, }, }, { "a [[Pennsylvania]] and [[New", " and [[Pennsylvania]]", []Diff{ - Diff{DiffInsert, " "}, - Diff{DiffEqual, "a"}, - Diff{DiffInsert, "nd"}, - Diff{DiffEqual, " [[Pennsylvania]]"}, - Diff{DiffDelete, " and [[New"}, + {DiffInsert, " "}, + {DiffEqual, "a"}, + {DiffInsert, "nd"}, + {DiffEqual, " [[Pennsylvania]]"}, + {DiffDelete, " and [[New"}, }, }, } { actual := dmp.DiffMain(tc.Text1, tc.Text2, false) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } // Test for invalid UTF-8 sequences - assert.Equal(t, []Diff{ - Diff{DiffDelete, "��"}, + assertEqual(t, []Diff{ + {DiffDelete, "��"}, }, dmp.DiffMain("\xe0\xe5", "", false)) } @@ -1397,8 +1429,11 @@ func TestDiffMainWithTimeout(t *testing.T) { dmp := New() dmp.DiffTimeout = 200 * time.Millisecond - 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" + a := "`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\n" + + "All mimsy were the borogoves,\nAnd the mome raths outgrabe.\n" + b := "I am the very model of a modern major general,\n" + + "I'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 < 13; x++ { a = a + a @@ -1412,10 +1447,13 @@ func TestDiffMainWithTimeout(t *testing.T) { delta := endTime.Sub(startTime) // Test that we took at least the timeout period. - assert.True(t, delta >= dmp.DiffTimeout, fmt.Sprintf("%v !>= %v", delta, dmp.DiffTimeout)) + assertEqual(t, true, delta >= dmp.DiffTimeout, fmt.Sprintf("%v !>= %v", delta, dmp.DiffTimeout)) - // Test that we didn't take forever (be very forgiving). Theoretically this test could fail very occasionally if the OS task swaps or locks up for a second at the wrong moment. - assert.True(t, delta < (dmp.DiffTimeout*100), fmt.Sprintf("%v !< %v", delta, dmp.DiffTimeout*100)) + // Test that we didn't take forever (be very forgiving). + // Theoretically this test could fail very occasionally if the OS task swaps or locks up + // for a second at the wrong moment. + assertEqual(t, true, delta < (dmp.DiffTimeout*100), + fmt.Sprintf("%v !< %v", delta, dmp.DiffTimeout*100)) } func TestDiffMainWithCheckLines(t *testing.T) { @@ -1430,16 +1468,22 @@ func TestDiffMainWithCheckLines(t *testing.T) { // Test cases must be at least 100 chars long to pass the cutoff. for i, tc := range []TestCase{ { - "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n", - "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n", + Text1: "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n" + + "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n", + Text2: "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n" + + "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n", }, { - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", - "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij", + Text1: "1234567890123456789012345678901234567890123456789012345678901234567890123" + + "456789012345678901234567890123456789012345678901234567890", + Text2: "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcde" + + "fghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij", }, { - "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n", - "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n", + Text1: "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n" + + "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n", + Text2: "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n" + + "1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n", }, } { resultWithoutCheckLines := dmp.DiffMain(tc.Text1, tc.Text2, false) @@ -1447,14 +1491,24 @@ func TestDiffMainWithCheckLines(t *testing.T) { // TODO this fails for the third test case, why? if i != 2 { - assert.Equal(t, resultWithoutCheckLines, resultWithCheckLines, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual( + t, + resultWithoutCheckLines, + resultWithCheckLines, + fmt.Sprintf("Test case #%d, %#v", i, tc), + ) } - assert.Equal(t, diffRebuildTexts(resultWithoutCheckLines), diffRebuildTexts(resultWithCheckLines), fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual( + t, + diffRebuildTexts(resultWithoutCheckLines), + diffRebuildTexts(resultWithCheckLines), + fmt.Sprintf("Test case #%d, %#v", i, tc), + ) } } func TestMassiveRuneDiffConversion(t *testing.T) { - sNew, err := ioutil.ReadFile("../testdata/fixture.go") + sNew, err := os.ReadFile("../testdata/fixture.go") if err != nil { panic(err) } @@ -1463,12 +1517,17 @@ func TestMassiveRuneDiffConversion(t *testing.T) { t1, t2, tt := dmp.DiffLinesToChars("", string(sNew)) diffs := dmp.DiffMain(t1, t2, false) diffs = dmp.DiffCharsToLines(diffs, tt) - assert.NotEmpty(t, diffs) + assertEqual(t, true, len(diffs) > 0) } func BenchmarkDiffMain(bench *testing.B) { - s1 := "`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.\n" - s2 := "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" + var r []Diff + + s1 := "`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\n" + + "All mimsy were the borogoves,\nAnd the mome raths outgrabe.\n" + s2 := "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,\n" + + "From Marathon to Waterloo, in order categorical.\n" // Increase the text lengths by 1024 times to ensure a timeout. for x := 0; x < 10; x++ { @@ -1482,25 +1541,31 @@ func BenchmarkDiffMain(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { - dmp.DiffMain(s1, s2, true) + r = dmp.DiffMain(s1, s2, true) } + + SinkSliceDiff = r } func BenchmarkDiffMainLarge(b *testing.B) { - s1, s2 := speedtestTexts() + var r []Diff + s1, s2 := speedtestTexts() dmp := New() b.ResetTimer() for i := 0; i < b.N; i++ { - dmp.DiffMain(s1, s2, true) + r = dmp.DiffMain(s1, s2, true) } + + SinkSliceDiff = r } func BenchmarkDiffMainRunesLargeLines(b *testing.B) { - s1, s2 := speedtestTexts() + var r []Diff + s1, s2 := speedtestTexts() dmp := New() b.ResetTimer() @@ -1508,24 +1573,28 @@ func BenchmarkDiffMainRunesLargeLines(b *testing.B) { for i := 0; i < b.N; i++ { text1, text2, linearray := dmp.DiffLinesToRunes(s1, s2) - diffs := dmp.DiffMainRunes(text1, text2, false) - diffs = dmp.DiffCharsToLines(diffs, linearray) + r = dmp.DiffMainRunes(text1, text2, false) + r = dmp.DiffCharsToLines(r, linearray) } + + SinkSliceDiff = r } func BenchmarkDiffMainRunesLargeDiffLines(b *testing.B) { + var r []Diff + fp, _ := os.Open("../testdata/diff10klinestest.txt") defer fp.Close() - data, _ := ioutil.ReadAll(fp) - + data, _ := io.ReadAll(fp) dmp := New() b.ResetTimer() for i := 0; i < b.N; i++ { text1, text2, linearray := dmp.DiffLinesToRunes(string(data), "") - - diffs := dmp.DiffMainRunes(text1, text2, false) - diffs = dmp.DiffCharsToLines(diffs, linearray) + r = dmp.DiffMainRunes(text1, text2, false) + r = dmp.DiffCharsToLines(r, linearray) } + + SinkSliceDiff = r } diff --git a/diffmatchpatch/diffmatchpatch.go b/diffmatchpatch/diffmatchpatch.go index d3acc32..8d5a034 100644 --- a/diffmatchpatch/diffmatchpatch.go +++ b/diffmatchpatch/diffmatchpatch.go @@ -19,9 +19,12 @@ type DiffMatchPatch struct { DiffTimeout time.Duration // 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). + // 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 MatchThreshold controls how closely the end points of a delete need to match. + // 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 MatchThreshold controls how closely the end points of a delete need to match. PatchDeleteThreshold float64 // Chunk size for context length. PatchMargin int diff --git a/diffmatchpatch/match.go b/diffmatchpatch/match.go index 17374e1..a8f5f6b 100644 --- a/diffmatchpatch/match.go +++ b/diffmatchpatch/match.go @@ -61,7 +61,8 @@ func (dmp *DiffMatchPatch) MatchBitap(text, pattern string, loc int) int { binMax := len(pattern) + len(text) lastRd := []int{} for d := 0; d < len(pattern); d++ { - // Scan for the best match; each iteration allows for one more error. Run a binary search to determine how far from 'loc' we can stray at this error level. + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from 'loc' we can stray at this error level. binMin = 0 binMid = binMax for binMin < binMid { diff --git a/diffmatchpatch/match_test.go b/diffmatchpatch/match_test.go index f9abe60..1d2d22d 100644 --- a/diffmatchpatch/match_test.go +++ b/diffmatchpatch/match_test.go @@ -11,8 +11,6 @@ package diffmatchpatch import ( "fmt" "testing" - - "github.com/stretchr/testify/assert" ) func TestMatchAlphabet(t *testing.T) { @@ -45,7 +43,7 @@ func TestMatchAlphabet(t *testing.T) { }, } { actual := dmp.MatchAlphabet(tc.Pattern) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } @@ -76,7 +74,7 @@ func TestMatchBitap(t *testing.T) { {"Oversized pattern", "abcdef", "xabcdefy", 0, 0}, } { actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } dmp.MatchThreshold = 0.4 @@ -85,7 +83,7 @@ func TestMatchBitap(t *testing.T) { {"Threshold #1", "abcdefghijk", "efxyhi", 1, 4}, } { actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } dmp.MatchThreshold = 0.3 @@ -94,7 +92,7 @@ func TestMatchBitap(t *testing.T) { {"Threshold #2", "abcdefghijk", "efxyhi", 1, -1}, } { actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } dmp.MatchThreshold = 0.0 @@ -103,7 +101,7 @@ func TestMatchBitap(t *testing.T) { {"Threshold #3", "abcdefghijk", "bcdef", 1, 1}, } { actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } dmp.MatchThreshold = 0.5 @@ -113,7 +111,7 @@ func TestMatchBitap(t *testing.T) { {"Multiple select #2", "abcdexyzabcde", "abccde", 5, 8}, } { actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } // Strict location. @@ -124,7 +122,7 @@ func TestMatchBitap(t *testing.T) { {"Distance test #2", "abcdefghijklmnopqrstuvwxyz", "abcdxxefg", 1, 0}, } { actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } // Loose location. @@ -134,7 +132,7 @@ func TestMatchBitap(t *testing.T) { {"Distance test #3", "abcdefghijklmnopqrstuvwxyz", "abcdefg", 24, 0}, } { actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } } @@ -160,7 +158,7 @@ func TestMatchMain(t *testing.T) { {"Oversized pattern", "abcdef", "abcdefy", 0, 0}, } { actual := dmp.MatchMain(tc.Text1, tc.Text2, tc.Location) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } dmp.MatchThreshold = 0.7 @@ -169,6 +167,6 @@ func TestMatchMain(t *testing.T) { {"Complex match", "I am the very model of a modern major general.", " that berry ", 5, 4}, } { actual := dmp.MatchMain(tc.Text1, tc.Text2, tc.Location) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } diff --git a/diffmatchpatch/patch.go b/diffmatchpatch/patch.go index 0dbe3bd..ec003c7 100644 --- a/diffmatchpatch/patch.go +++ b/diffmatchpatch/patch.go @@ -79,7 +79,8 @@ func (dmp *DiffMatchPatch) PatchAddContext(patch Patch, text string) Patch { pattern := text[patch.Start2 : patch.Start2+patch.Length1] padding := 0 - // Look for the first and last matches of pattern in text. If two different matches are found, increase the pattern length. + // 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-2*dmp.PatchMargin { padding += dmp.PatchMargin @@ -93,7 +94,7 @@ func (dmp *DiffMatchPatch) PatchAddContext(patch Patch, text string) Patch { // Add the prefix. prefix := text[max(0, patch.Start2-padding):patch.Start2] if len(prefix) != 0 { - patch.diffs = append([]Diff{Diff{DiffEqual, prefix}}, patch.diffs...) + patch.diffs = append([]Diff{{DiffEqual, prefix}}, patch.diffs...) } // Add the suffix. suffix := text[patch.Start2+patch.Length1 : min(len(text), patch.Start2+patch.Length1+padding)] @@ -148,7 +149,8 @@ func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch { patch := Patch{} charCount1 := 0 // Number of characters into the text1 string. charCount2 := 0 // Number of characters into the text2 string. - // Start with text1 (prepatchText) and apply the diffs until we arrive at text2 (postpatchText). We recreate the patches one by one to determine context info. + // Start with text1 (prepatchText) and apply the diffs until we arrive at text2 (postpatchText). + // We recreate the patches one by one to determine context info. prepatchText := text1 postpatchText := text1 @@ -183,7 +185,9 @@ func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch { patch = dmp.PatchAddContext(patch, prepatchText) patches = append(patches, patch) patch = Patch{} - // Unlike Unidiff, our patch lists have a rolling context. http://code.google.com/p/google-diff-match-patch/wiki/Unidiff Update prepatch text & pos to reflect the application of the just completed patch. + // Unlike Unidiff, our patch lists have a rolling context. + // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff Update prepatch text & pos to + // reflect the application of the just completed patch. prepatchText = postpatchText charCount1 = charCount2 } @@ -228,7 +232,9 @@ func (dmp *DiffMatchPatch) PatchDeepCopy(patches []Patch) []Patch { return patchesCopy } -// PatchApply merges a set of patches onto the text. Returns a patched text, as well as an array of true/false values indicating which patches were applied. +// PatchApply merges a set of patches onto the text. +// +// Returns a patched text, as well as an array of true/false values indicating which patches were applied. func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []bool) { if len(patches) == 0 { return text, []bool{} @@ -242,7 +248,9 @@ func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []b patches = dmp.PatchSplitMax(patches) x := 0 - // delta keeps track of the offset between the expected and actual location of the previous patch. If there are patches expected at 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 keeps track of the offset between the expected and actual location of the previous patch. + // If there are patches expected at 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 := make([]bool, len(patches)) for _, aPatch := range patches { @@ -285,7 +293,8 @@ func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []b } else { // 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))/float64(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 { @@ -336,7 +345,7 @@ func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string { // Add some padding on start of first diff. if len(patches[0].diffs) == 0 || patches[0].diffs[0].Type != DiffEqual { // Add nullPadding equality. - patches[0].diffs = append([]Diff{Diff{DiffEqual, nullPadding}}, patches[0].diffs...) + patches[0].diffs = append([]Diff{{DiffEqual, nullPadding}}, patches[0].diffs...) patches[0].Start1 -= paddingLength // Should be 0. patches[0].Start2 -= paddingLength // Should be 0. patches[0].Length1 += paddingLength @@ -353,7 +362,8 @@ func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string { // Add some padding on end of last diff. last := len(patches) - 1 - if len(patches[last].diffs) == 0 || patches[last].diffs[len(patches[last].diffs)-1].Type != DiffEqual { + if len(patches[last].diffs) == 0 || + patches[last].diffs[len(patches[last].diffs)-1].Type != DiffEqual { // Add nullPadding equality. patches[last].diffs = append(patches[last].diffs, Diff{DiffEqual, nullPadding}) patches[last].Length1 += paddingLength @@ -430,8 +440,7 @@ func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) []Patch { if diffText == bigpatch.diffs[0].Text { bigpatch.diffs = bigpatch.diffs[1:] } else { - bigpatch.diffs[0].Text = - bigpatch.diffs[0].Text[len(diffText):] + bigpatch.diffs[0].Text = bigpatch.diffs[0].Text[len(diffText):] } } } @@ -482,7 +491,7 @@ func (dmp *DiffMatchPatch) PatchFromText(textline string) ([]Patch, error) { } text := strings.Split(textline, "\n") textPointer := 0 - patchHeader := regexp.MustCompile("^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$") + patchHeader := regexp.MustCompile(`^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$`) var patch Patch var sign uint8 @@ -545,7 +554,7 @@ func (dmp *DiffMatchPatch) PatchFromText(textline string) ([]Patch, error) { break } else { // WTF? - return patches, errors.New("Invalid patch mode '" + string(sign) + "' in: " + string(line)) + return patches, errors.New("Invalid patch mode '" + string(sign) + "' in: " + line) } textPointer++ } diff --git a/diffmatchpatch/patch_test.go b/diffmatchpatch/patch_test.go index b019f88..d3e10cb 100644 --- a/diffmatchpatch/patch_test.go +++ b/diffmatchpatch/patch_test.go @@ -12,8 +12,6 @@ import ( "fmt" "strings" "testing" - - "github.com/stretchr/testify/assert" ) func TestPatchString(t *testing.T) { @@ -46,7 +44,7 @@ func TestPatchString(t *testing.T) { }, } { actual := tc.Patch.String() - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } @@ -60,29 +58,29 @@ func TestPatchFromText(t *testing.T) { dmp := New() for i, tc := range []TestCase{ - {"", ""}, - {"@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n", ""}, - {"@@ -1 +1 @@\n-a\n+b\n", ""}, - {"@@ -1,3 +0,0 @@\n-abc\n", ""}, - {"@@ -0,0 +1,3 @@\n+abc\n", ""}, - {"@@ _0,0 +0,0 @@\n+abc\n", "Invalid patch string: @@ _0,0 +0,0 @@"}, - {"Bad\nPatch\n", "Invalid patch string"}, + {Patch: "", ErrorMessagePrefix: ""}, + {Patch: "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n", ErrorMessagePrefix: ""}, + {Patch: "@@ -1 +1 @@\n-a\n+b\n", ErrorMessagePrefix: ""}, + {Patch: "@@ -1,3 +0,0 @@\n-abc\n", ErrorMessagePrefix: ""}, + {Patch: "@@ -0,0 +1,3 @@\n+abc\n", ErrorMessagePrefix: ""}, + {Patch: "@@ _0,0 +0,0 @@\n+abc\n", ErrorMessagePrefix: "Invalid patch string: @@ _0,0 +0,0 @@"}, + {Patch: "Bad\nPatch\n", ErrorMessagePrefix: "Invalid patch string"}, } { patches, err := dmp.PatchFromText(tc.Patch) if tc.ErrorMessagePrefix == "" { - assert.Nil(t, err) + assertEqual(t, nil, err) if tc.Patch == "" { - assert.Equal(t, []Patch{}, patches, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, []Patch{}, patches, fmt.Sprintf("Test case #%d, %#v", i, tc)) } else { - assert.Equal(t, tc.Patch, patches[0].String(), fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Patch, patches[0].String(), fmt.Sprintf("Test case #%d, %#v", i, tc)) } } else { e := err.Error() if strings.HasPrefix(e, tc.ErrorMessagePrefix) { e = tc.ErrorMessagePrefix } - assert.Equal(t, tc.ErrorMessagePrefix, e) + assertEqual(t, tc.ErrorMessagePrefix, e) } } @@ -91,12 +89,14 @@ func TestPatchFromText(t *testing.T) { {DiffInsert, "~!@#$%^&*()_+{}|:\"<>?"}, } - patches, err := dmp.PatchFromText("@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n") - assert.Len(t, patches, 1) - assert.Equal(t, diffs, + patches, err := dmp.PatchFromText( + "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n", + ) + assertEqual(t, 1, len(patches)) + assertEqual(t, diffs, patches[0].diffs, ) - assert.Nil(t, err) + assertEqual(t, nil, err) } func TestPatchToText(t *testing.T) { @@ -111,10 +111,10 @@ func TestPatchToText(t *testing.T) { {"@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n tes\n"}, } { patches, err := dmp.PatchFromText(tc.Patch) - assert.Nil(t, err) + assertEqual(t, nil, err) actual := dmp.PatchToText(patches) - assert.Equal(t, tc.Patch, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Patch, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } @@ -132,16 +132,36 @@ func TestPatchAddContext(t *testing.T) { dmp.PatchMargin = 4 for i, tc := range []TestCase{ - {"Simple case", "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", "The quick brown fox jumps over the lazy dog.", "@@ -17,12 +17,18 @@\n fox \n-jump\n+somersault\n s ov\n"}, - {"Not enough trailing context", "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", "The quick brown fox jumps.", "@@ -17,10 +17,16 @@\n fox \n-jump\n+somersault\n s.\n"}, - {"Not enough leading context", "@@ -3 +3,2 @@\n-e\n+at\n", "The quick brown fox jumps.", "@@ -1,7 +1,8 @@\n Th\n-e\n+at\n qui\n"}, - {"Ambiguity", "@@ -3 +3,2 @@\n-e\n+at\n", "The quick brown fox jumps. The quick brown fox crashes.", "@@ -1,27 +1,28 @@\n Th\n-e\n+at\n quick brown fox jumps. \n"}, + { + Name: "Simple case", + Patch: "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", + Text: "The quick brown fox jumps over the lazy dog.", + Expected: "@@ -17,12 +17,18 @@\n fox \n-jump\n+somersault\n s ov\n", + }, + { + Name: "Not enough trailing context", + Patch: "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", + Text: "The quick brown fox jumps.", + Expected: "@@ -17,10 +17,16 @@\n fox \n-jump\n+somersault\n s.\n", + }, + { + Name: "Not enough leading context", + Patch: "@@ -3 +3,2 @@\n-e\n+at\n", + Text: "The quick brown fox jumps.", + Expected: "@@ -1,7 +1,8 @@\n Th\n-e\n+at\n qui\n", + }, + { + Name: "Ambiguity", + Patch: "@@ -3 +3,2 @@\n-e\n+at\n", + Text: "The quick brown fox jumps. The quick brown fox crashes.", + Expected: "@@ -1,27 +1,28 @@\n Th\n-e\n+at\n quick brown fox jumps. \n", + }, } { patches, err := dmp.PatchFromText(tc.Patch) - assert.Nil(t, err) + assertEqual(t, nil, err) actual := dmp.PatchAddContext(patches[0], tc.Text) - assert.Equal(t, tc.Expected, actual.String(), fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual.String(), fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } } @@ -162,15 +182,69 @@ func TestPatchMakeAndPatchToText(t *testing.T) { text2 := "That quick brown fox jumped over a lazy dog." for i, tc := range []TestCase{ - {"Null case", "", "", nil, ""}, - {"Text2+Text1 inputs", text2, text1, nil, "@@ -1,8 +1,7 @@\n Th\n-at\n+e\n qui\n@@ -21,17 +21,18 @@\n jump\n-ed\n+s\n over \n-a\n+the\n laz\n"}, - {"Text1+Text2 inputs", text1, text2, nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"}, - {"Diff input", dmp.DiffMain(text1, text2, false), nil, nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"}, - {"Text1+Diff inputs", text1, dmp.DiffMain(text1, text2, false), nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"}, - {"Text1+Text2+Diff inputs (deprecated)", text1, text2, dmp.DiffMain(text1, text2, false), "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"}, - {"Character encoding", "`1234567890-=[]\\;',./", "~!@#$%^&*()_+{}|:\"<>?", nil, "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n"}, - {"Long string with repeats", strings.Repeat("abcdef", 100), strings.Repeat("abcdef", 100) + "123", nil, "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n"}, - {"Corner case of #31 fixed by #32", "2016-09-01T03:07:14.807830741Z", "2016-09-01T03:07:15.154800781Z", nil, "@@ -15,16 +15,16 @@\n 07:1\n+5.15\n 4\n-.\n 80\n+0\n 78\n-3074\n 1Z\n"}, + { + Name: "Null case", + Input1: "", + Input2: "", + Input3: nil, + Expected: "", + }, + { + Name: "Text2+Text1 inputs", + Input1: text2, + Input2: text1, + Input3: nil, + Expected: "@@ -1,8 +1,7 @@\n Th\n-at\n+e\n qui\n@@ -21,17 +21,18 @@\n jump\n-ed\n+s\n over \n-a\n+the\n laz\n", + }, + { + Name: "Text1+Text2 inputs", + Input1: text1, + Input2: text2, + Input3: nil, + Expected: "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", + }, + { + Name: "Diff input", + Input1: dmp.DiffMain(text1, text2, false), + Input2: nil, + Input3: nil, + Expected: "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", + }, + { + Name: "Text1+Diff inputs", + Input1: text1, + Input2: dmp.DiffMain(text1, text2, false), + Input3: nil, + Expected: "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", + }, + { + Name: "Text1+Text2+Diff inputs (deprecated)", + Input1: text1, + Input2: text2, + Input3: dmp.DiffMain(text1, text2, false), + Expected: "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", + }, + { + Name: "Character encoding", + Input1: "`1234567890-=[]\\;',./", + Input2: "~!@#$%^&*()_+{}|:\"<>?", + Input3: nil, + Expected: "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n", + }, + { + Name: "Long string with repeats", + Input1: strings.Repeat("abcdef", 100), + Input2: strings.Repeat("abcdef", 100) + "123", + Input3: nil, + Expected: "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n", + }, + { + Name: "Corner case of #31 fixed by #32", + Input1: "2016-09-01T03:07:14.807830741Z", + Input2: "2016-09-01T03:07:15.154800781Z", + Input3: nil, + Expected: "@@ -15,16 +15,16 @@\n 07:1\n+5.15\n 4\n-.\n 80\n+0\n 78\n-3074\n 1Z\n", + }, } { var patches []Patch if tc.Input3 != nil { @@ -184,28 +258,34 @@ func TestPatchMakeAndPatchToText(t *testing.T) { } actual := dmp.PatchToText(patches) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) } // Corner case of #28 wrong patch with timeout of 0 dmp.DiffTimeout = 0 - text1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum libero vel." - text2 = "Lorem a ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum liberovel." + text1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et " + + "enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum libero vel." + text2 = "Lorem a ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et " + + "enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum liberovel." diffs := dmp.DiffMain(text1, text2, true) // Additional check that the diff texts are equal to the originals even if we are using DiffMain with checklines=true #29 - assert.Equal(t, text1, dmp.DiffText1(diffs)) - assert.Equal(t, text2, dmp.DiffText2(diffs)) + assertEqual(t, text1, dmp.DiffText1(diffs)) + assertEqual(t, text2, dmp.DiffText2(diffs)) patches := dmp.PatchMake(text1, diffs) actual := dmp.PatchToText(patches) - assert.Equal(t, "@@ -1,14 +1,16 @@\n Lorem \n+a \n ipsum do\n@@ -148,13 +148,12 @@\n m libero\n- \n vel.\n", actual) + assertEqual( + t, + "@@ -1,14 +1,16 @@\n Lorem \n+a \n ipsum do\n@@ -148,13 +148,12 @@\n m libero\n- \n vel.\n", + actual, + ) // Check that empty Patch array is returned for no parameter call patches = dmp.PatchMake() - assert.Equal(t, []Patch{}, patches) + assertEqual(t, []Patch{}, patches) } func TestPatchSplitMax(t *testing.T) { @@ -219,16 +299,35 @@ func TestPatchSplitMax(t *testing.T) { dmp := New() for i, tc := range []TestCase{ - {"abcdefghijklmnopqrstuvwxyz01234567890", "XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0", "@@ -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"}, - {"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz", "abcdefuvwxyz", "@@ -3,78 +3,8 @@\n cdef\n-1234567890123456789012345678901234567890123456789012345678901234567890\n uvwx\n"}, - {"1234567890123456789012345678901234567890123456789012345678901234567890", "abc", "@@ -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"}, - {"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", "@@ -2,32 +2,32 @@\n bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n@@ -29,32 +29,32 @@\n bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n"}, + { + Text1: "abcdefghijklmnopqrstuvwxyz01234567890", + Text2: "XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0", + Expected: "@@ -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", + }, + { + Text1: "abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz", + Text2: "abcdefuvwxyz", + Expected: "@@ -3,78 +3,8 @@\n cdef\n-1234567890123456789012345678901234567890123456789012345678901234567890\n uvwx\n", + }, + { + Text1: "1234567890123456789012345678901234567890123456789012345678901234567890", + Text2: "abc", + Expected: "@@ -1,32 +1,4 @@\n-1234567890123456789012345678\n 9012\n@@ -29,32 +1,4 @@\n-9012345678901234" + + "567890123456\n 7890\n@@ -57,14 +1,3 @@\n-78901234567890\n+abc\n", + }, + { + Text1: "abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1", + Text2: "abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1", + Expected: "@@ -2,32 +2,32 @@\n bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n@@ -29,32 +29,32 @@\n " + + "bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n", + }, } { patches := dmp.PatchMake(tc.Text1, tc.Text2) patches = dmp.PatchSplitMax(patches) actual := dmp.PatchToText(patches) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } @@ -246,19 +345,42 @@ func TestPatchAddPadding(t *testing.T) { dmp := New() for i, tc := range []TestCase{ - {"Both edges full", "", "test", "@@ -0,0 +1,4 @@\n+test\n", "@@ -1,8 +1,12 @@\n %01%02%03%04\n+test\n %01%02%03%04\n"}, - {"Both edges partial", "XY", "XtestY", "@@ -1,2 +1,6 @@\n X\n+test\n Y\n", "@@ -2,8 +2,12 @@\n %02%03%04X\n+test\n Y%01%02%03\n"}, - {"Both edges none", "XXXXYYYY", "XXXXtestYYYY", "@@ -1,8 +1,12 @@\n XXXX\n+test\n YYYY\n", "@@ -5,8 +5,12 @@\n XXXX\n+test\n YYYY\n"}, + { + Name: "Both edges full", + Text1: "", + Text2: "test", + Expected: "@@ -0,0 +1,4 @@\n+test\n", + ExpectedWithPadding: "@@ -1,8 +1,12 @@\n %01%02%03%04\n+test\n %01%02%03%04\n", + }, + { + Name: "Both edges partial", + Text1: "XY", + Text2: "XtestY", + Expected: "@@ -1,2 +1,6 @@\n X\n+test\n Y\n", + ExpectedWithPadding: "@@ -2,8 +2,12 @@\n %02%03%04X\n+test\n Y%01%02%03\n", + }, + { + Name: "Both edges none", + Text1: "XXXXYYYY", + Text2: "XXXXtestYYYY", + Expected: "@@ -1,8 +1,12 @@\n XXXX\n+test\n YYYY\n", + ExpectedWithPadding: "@@ -5,8 +5,12 @@\n XXXX\n+test\n YYYY\n", + }, } { patches := dmp.PatchMake(tc.Text1, tc.Text2) actual := dmp.PatchToText(patches) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) dmp.PatchAddPadding(patches) actualWithPadding := dmp.PatchToText(patches) - assert.Equal(t, tc.ExpectedWithPadding, actualWithPadding, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual( + t, + tc.ExpectedWithPadding, + actualWithPadding, + fmt.Sprintf("Test case #%d, %s", i, tc.Name), + ) } } @@ -280,30 +402,87 @@ func TestPatchApply(t *testing.T) { dmp.PatchDeleteThreshold = 0.5 for i, tc := range []TestCase{ - {"Null case", "", "", "Hello world.", "Hello world.", []bool{}}, - {"Exact match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", []bool{true, true}}, - {"Partial match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "The quick red rabbit jumps over the tired tiger.", "That quick red rabbit jumped over a tired tiger.", []bool{true, true}}, - {"Failed match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "I am the very model of a modern major general.", "I am the very model of a modern major general.", []bool{false, false}}, - {"Big delete, small Diff", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y", "xabcy", []bool{true, true}}, - {"Big delete, big Diff 1", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x12345678901234567890---------------++++++++++---------------12345678901234567890y", "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y", []bool{false, true}}, + { + Name: "Null case", + Text1: "", + Text2: "", TextBase: "Hello world.", + Expected: "Hello world.", + ExpectedApplies: []bool{}, + }, + { + Name: "Exact match", + Text1: "The quick brown fox jumps over the lazy dog.", + Text2: "That quick brown fox jumped over a lazy dog.", + TextBase: "The quick brown fox jumps over the lazy dog.", + Expected: "That quick brown fox jumped over a lazy dog.", + ExpectedApplies: []bool{true, true}, + }, + { + Name: "Partial match", + Text1: "The quick brown fox jumps over the lazy dog.", + Text2: "That quick brown fox jumped over a lazy dog.", + TextBase: "The quick red rabbit jumps over the tired tiger.", + Expected: "That quick red rabbit jumped over a tired tiger.", + ExpectedApplies: []bool{true, true}, + }, + { + Name: "Failed match", + Text1: "The quick brown fox jumps over the lazy dog.", + Text2: "That quick brown fox jumped over a lazy dog.", + TextBase: "I am the very model of a modern major general.", + Expected: "I am the very model of a modern major general.", + ExpectedApplies: []bool{false, false}, + }, + { + Name: "Big delete, small Diff", + Text1: "x1234567890123456789012345678901234567890123456789012345678901234567890y", + Text2: "xabcy", + TextBase: "x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y", + Expected: "xabcy", + ExpectedApplies: []bool{true, true}, + }, + { + Name: "Big delete, big Diff 1", + Text1: "x1234567890123456789012345678901234567890123456789012345678901234567890y", + Text2: "xabcy", + TextBase: "x12345678901234567890---------------++++++++++---------------12345678901234567890y", + Expected: "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y", + ExpectedApplies: []bool{false, true}, + }, } { patches := dmp.PatchMake(tc.Text1, tc.Text2) actual, actualApplies := dmp.PatchApply(patches, tc.TextBase) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) - assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual( + t, + tc.ExpectedApplies, + actualApplies, + fmt.Sprintf("Test case #%d, %s", i, tc.Name), + ) } dmp.PatchDeleteThreshold = 0.6 for i, tc := range []TestCase{ - {"Big delete, big Diff 2", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x12345678901234567890---------------++++++++++---------------12345678901234567890y", "xabcy", []bool{true, true}}, + { + Name: "Big delete, big Diff 2", + Text1: "x1234567890123456789012345678901234567890123456789012345678901234567890y", + Text2: "xabcy", + TextBase: "x12345678901234567890---------------++++++++++---------------12345678901234567890y", + Expected: "xabcy", ExpectedApplies: []bool{true, true}, + }, } { patches := dmp.PatchMake(tc.Text1, tc.Text2) actual, actualApplies := dmp.PatchApply(patches, tc.TextBase) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) - assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual( + t, + tc.ExpectedApplies, + actualApplies, + fmt.Sprintf("Test case #%d, %s", i, tc.Name), + ) } dmp.MatchDistance = 0 @@ -311,29 +490,80 @@ func TestPatchApply(t *testing.T) { dmp.PatchDeleteThreshold = 0.5 for i, tc := range []TestCase{ - {"Compensate for failed patch", "abcdefghijklmnopqrstuvwxyz--------------------1234567890", "abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890", []bool{false, true}}, + { + Name: "Compensate for failed patch", + Text1: "abcdefghijklmnopqrstuvwxyz--------------------1234567890", + Text2: "abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890", + TextBase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890", + Expected: "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890", + ExpectedApplies: []bool{false, true}, + }, } { patches := dmp.PatchMake(tc.Text1, tc.Text2) actual, actualApplies := dmp.PatchApply(patches, tc.TextBase) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) - assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual( + t, + tc.ExpectedApplies, + actualApplies, + fmt.Sprintf("Test case #%d, %s", i, tc.Name), + ) } dmp.MatchThreshold = 0.5 dmp.MatchDistance = 1000 for i, tc := range []TestCase{ - {"No side effects", "", "test", "", "test", []bool{true}}, - {"No side effects with major delete", "The quick brown fox jumps over the lazy dog.", "Woof", "The quick brown fox jumps over the lazy dog.", "Woof", []bool{true, true}}, - {"Edge exact match", "", "test", "", "test", []bool{true}}, - {"Near edge exact match", "XY", "XtestY", "XY", "XtestY", []bool{true}}, - {"Edge partial match", "y", "y123", "x", "x123", []bool{true}}, + { + Name: "No side effects", + Text1: "", + Text2: "test", + TextBase: "", + Expected: "test", + ExpectedApplies: []bool{true}, + }, + { + Name: "No side effects with major delete", + Text1: "The quick brown fox jumps over the lazy dog.", + Text2: "Woof", + TextBase: "The quick brown fox jumps over the lazy dog.", + Expected: "Woof", + ExpectedApplies: []bool{true, true}, + }, + { + Name: "Edge exact match", + Text1: "", Text2: "test", + TextBase: "", + Expected: "test", + ExpectedApplies: []bool{true}, + }, + { + Name: "Near edge exact match", + Text1: "XY", + Text2: "XtestY", + TextBase: "XY", + Expected: "XtestY", + ExpectedApplies: []bool{true}, + }, + { + Name: "Edge partial match", + Text1: "y", + Text2: "y123", + TextBase: "x", + Expected: "x123", + ExpectedApplies: []bool{true}, + }, } { patches := dmp.PatchMake(tc.Text1, tc.Text2) actual, actualApplies := dmp.PatchApply(patches, tc.TextBase) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) - assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) + assertEqual( + t, + tc.ExpectedApplies, + actualApplies, + fmt.Sprintf("Test case #%d, %s", i, tc.Name), + ) } } diff --git a/diffmatchpatch/stringutil.go b/diffmatchpatch/stringutil.go index 44c4359..bb8fbc7 100644 --- a/diffmatchpatch/stringutil.go +++ b/diffmatchpatch/stringutil.go @@ -15,7 +15,12 @@ import ( ) // unescaper unescapes selected chars for compatibility with JavaScript's encodeURI. -// In speed critical applications this could be dropped since the receiving application will certainly decode these fine. Note that this function is case-sensitive. Thus "%3F" would not be unescaped. But this is ok because it is only called with the output of HttpUtility.UrlEncode which returns lowercase hex. Example: "%3f" -> "?", "%24" -> "$", etc. +// In speed critical applications this could be dropped since the receiving application +// will certainly decode these fine. Note that this function is case-sensitive. +// Thus "%3F" would not be unescaped. +// But this is ok because it is only called with the output of HttpUtility.UrlEncode which returns lowercase hex. +// +// Example: "%3f" -> "?", "%24" -> "$", etc. var unescaper = strings.NewReplacer( "%21", "!", "%7E", "~", "%27", "'", "%28", "(", "%29", ")", "%3B", ";", @@ -24,7 +29,7 @@ var unescaper = strings.NewReplacer( "%2B", "+", "%24", "$", "%2C", ",", "%23", "#", "%2A", "*") // indexOf returns the first index of pattern in str, starting at str[i]. -func indexOf(str string, pattern string, i int) int { +func indexOf(str, pattern string, i int) int { if i > len(str)-1 { return -1 } @@ -39,7 +44,7 @@ func indexOf(str string, pattern string, i int) int { } // lastIndexOf returns the last index of pattern in str, starting at str[i]. -func lastIndexOf(str string, pattern string, i int) int { +func lastIndexOf(str, pattern string, i int) int { if i < 0 { return -1 } diff --git a/diffmatchpatch/stringutil_test.go b/diffmatchpatch/stringutil_test.go index ab2bc10..c1ce48f 100644 --- a/diffmatchpatch/stringutil_test.go +++ b/diffmatchpatch/stringutil_test.go @@ -11,8 +11,6 @@ package diffmatchpatch import ( "fmt" "testing" - - "github.com/stretchr/testify/assert" ) func TestRunesIndexOf(t *testing.T) { @@ -37,7 +35,7 @@ func TestRunesIndexOf(t *testing.T) { {"e", 6, -1}, } { actual := runesIndexOf([]rune("abcde"), []rune(tc.Pattern), tc.Start) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } @@ -72,7 +70,7 @@ func TestIndexOf(t *testing.T) { {"a\u03b2\u03b2c", "\u03b2", 6, -1}, } { actual := indexOf(tc.String, tc.Pattern, tc.Position) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } @@ -111,6 +109,6 @@ func TestLastIndexOf(t *testing.T) { {"a\u03b2\u03b2c", "\u03b2", 6, 3}, } { actual := lastIndexOf(tc.String, tc.Pattern, tc.Position) - assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) + assertEqual(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) } } diff --git a/go.mod b/go.mod index d46c77d..d8926aa 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,3 @@ -module github.com/sergi/go-diff +module github.com/gkampitakis/go-diff -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/pretty v0.1.0 // indirect - github.com/stretchr/testify v1.4.0 - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/yaml.v2 v2.2.4 // indirect -) - -go 1.12 +go 1.16 diff --git a/go.sum b/go.sum index 4b80e08..e69de29 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/golangci.yml b/golangci.yml new file mode 100644 index 0000000..abc5776 --- /dev/null +++ b/golangci.yml @@ -0,0 +1,24 @@ +linters: + # enable-all: true + enable: + - unconvert + - lll + - unparam + - goimports + - unparam + - gofumpt + - revive + - unconvert + - gocognit + - errcheck + +linters-settings: + gocognit: + # This too high + min-complexity: 90 + lll: + line-length: 130 + gofumpt: + extra-rules: true + revive: + severity: warning diff --git a/scripts/lint.sh b/scripts/lint.sh deleted file mode 100755 index 3dad05f..0000000 --- a/scripts/lint.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh - -if [ -z ${PKG+x} ]; then echo "PKG is not set"; exit 1; fi -if [ -z ${ROOT_DIR+x} ]; then echo "ROOT_DIR is not set"; exit 1; fi - -echo "gofmt:" -OUT=$(gofmt -l $ROOT_DIR) -if [ $(echo "$OUT\c" | wc -l) -ne 0 ]; then echo "$OUT"; PROBLEM=1; fi - -echo "errcheck:" -OUT=$(errcheck $PKG/...) -if [ $(echo "$OUT\c" | wc -l) -ne 0 ]; then echo "$OUT"; PROBLEM=1; fi - -echo "go vet:" -OUT=$(go tool vet -all=true -v=true $ROOT_DIR 2>&1 | grep --invert-match -E "(Checking file|\%p of wrong type|can't check non-constant format)") -if [ $(echo "$OUT\c" | wc -l) -ne 0 ]; then echo "$OUT"; PROBLEM=1; fi - -echo "golint:" -OUT=$(golint $PKG/... | grep --invert-match -E "(method DiffPrettyHtml should be DiffPrettyHTML)") -if [ $(echo "$OUT\c" | wc -l) -ne 0 ]; then echo "$OUT"; PROBLEM=1; fi - -if [ -n "$PROBLEM" ]; then exit 1; fi