// 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.
// 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.
lineStart:=0
lineEnd:=-1
runes:=[]rune{}
forlineEnd<len(text)-1{
lineEnd=indexOf(text,"\n",lineStart)
iflineEnd==-1{
lineEnd=len(text)-1
}
line:=text[lineStart:lineEnd+1]
lineStart=lineEnd+1
lineValue,ok:=lineHash[line]
ifok{
runes=append(runes,rune(lineValue))
}else{
*lineArray=append(*lineArray,line)
lineHash[line]=len(*lineArray)-1
runes=append(runes,rune(len(*lineArray)-1))
}
}
returnrunes
}
// DiffCharsToLines rehydrates the text in a diff from a string of line hashes to real lines of text.
// 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/
// 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.
// A half-match was found, sort out the return data.
iflen(text1)>len(text2){
returnhm
}
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.
// diffCleanupSemanticScore computes a score representing whether the internal boundary falls on logical boundaries.
// Scores range from 6 (best) to 0 (worst). Closure, but does not reference any external variables.
funcdiffCleanupSemanticScore(one,twostring)int{
iflen(one)==0||len(two)==0{
// Edges are the best.
return6
}
// 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.
// 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 c<ins>at c</ins>ame. -> The <ins>cat </ins>came.
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: A<ins>BA</ins>C -> <ins>AB</ins>AC
changes:=false
pointer=1
// Intentionally ignore the first and last element (don't need checking).
forpointer<(len(diffs)-1){
ifdiffs[pointer-1].Type==DiffEqual&&
diffs[pointer+1].Type==DiffEqual{
// This is a single edit surrounded by equalities.
// DiffFromDelta given the original text1, and an encoded string which describes the operations required to transform text1 into text2, comAdde the full diff.
// Package diffmatchpatch offers robust algorithms to perform the operations required for synchronizing plain text.
packagediffmatchpatch
import(
"time"
)
// DiffMatchPatch holds the configuration for diff-match-patch operations.
typeDiffMatchPatchstruct{
// Number of seconds to map a diff before giving up (0 for infinity).
DiffTimeouttime.Duration
// Cost of an empty edit operation in terms of edit characters.
DiffEditCostint
// 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).
MatchDistanceint
// 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.
PatchDeleteThresholdfloat64
// Chunk size for context length.
PatchMarginint
// The number of bits in an int.
MatchMaxBitsint
// At what point is no match declared (0.0 = perfection, 1.0 = very loose).
MatchThresholdfloat64
}
// New creates a new DiffMatchPatch object with default parameters.
// 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.
// Check for null inputs not needed since null can't be passed in C#.
patches:=[]Patch{}
iflen(diffs)==0{
returnpatches// Get rid of the null case.
}
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.
// 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
}
}
}
// Update the current character count.
ifaDiff.Type!=DiffInsert{
charCount1+=len(aDiff.Text)
}
ifaDiff.Type!=DiffDelete{
charCount2+=len(aDiff.Text)
}
}
// Pick up the leftover patch if not empty.
iflen(patch.diffs)!=0{
patch=dmp.PatchAddContext(patch,prepatchText)
patches=append(patches,patch)
}
returnpatches
}
// PatchDeepCopy returns an array that is identical to a given an array of patches.
// 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.
// Deep copy the patches so that no changes are made to originals.
patches=dmp.PatchDeepCopy(patches)
nullPadding:=dmp.PatchAddPadding(patches)
text=nullPadding+text+nullPadding
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:=0
results:=make([]bool,len(patches))
for_,aPatch:=rangepatches{
expectedLoc:=aPatch.Start2+delta
text1:=dmp.DiffText1(aPatch.diffs)
varstartLocint
endLoc:=-1
iflen(text1)>dmp.MatchMaxBits{
// PatchSplitMax will only provide an oversized pattern in the case of a monster delete.
// 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.
shouldHaveBeenEqual="Expected: '%v'\nActual: '%v'\n(Should be equal)"
shouldHaveBeenEqualNoResemblance="Both the actual and expected values render equally ('%s') and their types are the same. Try using ShouldResemble instead."
shouldNotHaveBeenEqual="Expected '%v'\nto NOT equal '%v'\n(but it did)!"
shouldHaveBeenEqualTypeMismatch="Expected: '%v' (%T)\nActual: '%v' (%T)\n(Should be equal, type mismatch)"
shouldHaveBeenAlmostEqual="Expected '%v' to almost equal '%v' (but it didn't)!"
shouldHaveNotBeenAlmostEqual="Expected '%v' to NOT almost equal '%v' (but it did)!"
shouldNotHaveResembled="Expected '%#v'\nto NOT resemble '%#v'\n(but it did)!"
shouldBePointers="Both arguments should be pointers "
shouldHaveBeenNonNilPointer=shouldBePointers+"(the %s was %s)!"
shouldHavePointedTo="Expected '%+v' (address: '%v') and '%+v' (address: '%v') to be the same address (but their weren't)!"
shouldNotHavePointedTo="Expected '%+v' and '%+v' to be different references (but they matched: '%v')!"
shouldHaveBeenNil="Expected: nil\nActual: '%v'"
shouldNotHaveBeenNil="Expected '%+v' to NOT be nil (but it was)!"
shouldHaveBeenTrue="Expected: true\nActual: %v"
shouldHaveBeenFalse="Expected: false\nActual: %v"
shouldHaveBeenZeroValue="'%+v' should have been the zero value"//"Expected: (zero value)\nActual: %v"
)
shouldNotHaveBeenZeroValue="'%+v' should NOT have been the zero value"
const(// quantity comparisons
shouldHaveBeenGreater="Expected '%v' to be greater than '%v' (but it wasn't)!"
shouldHaveBeenGreaterOrEqual="Expected '%v' to be greater than or equal to '%v' (but it wasn't)!"
shouldHaveBeenLess="Expected '%v' to be less than '%v' (but it wasn't)!"
shouldHaveBeenLessOrEqual="Expected '%v' to be less than or equal to '%v' (but it wasn't)!"
shouldHaveBeenBetween="Expected '%v' to be between '%v' and '%v' (but it wasn't)!"
shouldNotHaveBeenBetween="Expected '%v' NOT to be between '%v' and '%v' (but it was)!"
shouldHaveDifferentUpperAndLower="The lower and upper bounds must be different values (they were both '%v')."
shouldHaveBeenBetweenOrEqual="Expected '%v' to be between '%v' and '%v' or equal to one of them (but it wasn't)!"
shouldNotHaveBeenBetweenOrEqual="Expected '%v' NOT to be between '%v' and '%v' or equal to one of them (but it was)!"
)
const(// collections
shouldHaveContained="Expected the container (%v) to contain: '%v' (but it didn't)!"
shouldNotHaveContained="Expected the container (%v) NOT to contain: '%v' (but it did)!"
shouldHaveBeenAValidCollection="You must provide a valid container (was %v)!"
shouldHaveContainedKey="Expected the %v to contain the key: %v (but it didn't)!"
shouldNotHaveContainedKey="Expected the %v NOT to contain the key: %v (but it did)!"
shouldHaveBeenAValidMap="You must provide a valid map type (was %v)!"
shouldHaveBeenIn="Expected '%v' to be in the container (%v), but it wasn't!"
shouldNotHaveBeenIn="Expected '%v' NOT to be in the container (%v), but it was!"
shouldHaveBeenAValidCollection="You must provide a valid container (was %v)!"
shouldHaveBeenAValidMap="You must provide a valid map type (was %v)!"
shouldHaveBeenEmpty="Expected %+v to be empty (but it wasn't)!"
shouldNotHaveBeenEmpty="Expected %+v to NOT be empty (but it was)!"
shouldHaveBeenAValidInteger="You must provide a valid integer (was %v)!"
shouldHaveBeenAValidLength="You must provide a valid positive integer (was %v)!"
shouldHaveHadLength="Expected %+v (length: %v) to have length equal to '%v', but it wasn't!"
)
shouldHaveHadLength="Expected collection to have length equal to [%v], but it's length was [%v] instead! contents: %+v"
const(// strings
shouldHaveStartedWith="Expected '%v'\nto start with '%v'\n(but it didn't)!"
shouldNotHaveStartedWith="Expected '%v'\nNOT to start with '%v'\n(but it did)!"
shouldHaveEndedWith="Expected '%v'\nto end with '%v'\n(but it didn't)!"
shouldNotHaveEndedWith="Expected '%v'\nNOT to end with '%v'\n(but it did)!"
shouldAllBeStrings="All arguments to this assertion must be strings (you provided: %v)."
shouldBothBeStrings="Both arguments to this assertion must be strings (you provided %v and %v)."
shouldBeString="The argument to this assertion must be a string (you provided %v)."
shouldHaveContainedSubstring="Expected '%s' to contain substring '%s' (but it didn't)!"
shouldNotHaveContainedSubstring="Expected '%s' NOT to contain substring '%s' (but it did)!"
shouldBeString="The argument to this assertion must be a string (you provided %v)."
shouldHaveBeenBlank="Expected '%s' to be blank (but it wasn't)!"
shouldNotHaveBeenBlank="Expected value to NOT be blank (but it was)!"
)
const(// panics
shouldUseVoidNiladicFunction="You must provide a void, niladic function as the first argument!"
shouldHavePanickedWith="Expected func() to panic with '%v' (but it panicked with '%v')!"
shouldHavePanicked="Expected func() to panic (but it didn't)!"
shouldNotHavePanicked="Expected func() NOT to panic (error: '%+v')!"
shouldHavePanickedWith="Expected func() to panic with '%v' (but it panicked with '%v')!"
shouldNotHavePanickedWith="Expected func() NOT to panic with '%v' (but it did)!"
)
const(// type checking
shouldHaveBeenA="Expected '%v' to be: '%v' (but was: '%v')!"
shouldNotHaveBeenA="Expected '%v' to NOT be: '%v' (but it was)!"
...
...
@@ -80,12 +90,11 @@ const ( // type checking
shouldBeError="Expected an error value (but was '%v' instead)!"
shouldBeErrorInvalidComparisonValue="The final argument to this assertion must be a string or an error value (you provided: '%v')."
)
const(// time comparisons
shouldUseTimes="You must provide time instances as arguments to this assertion."
shouldUseTimeSlice="You must provide a slice of time instances as the first argument to this assertion."
shouldUseDurationAndTime="You must provide a duration and a time as arguments to this assertion."
shouldHaveHappenedBefore="Expected '%v' to happen before '%v' (it happened '%v' after)!"
shouldHaveHappenedAfter="Expected '%v' to happen after '%v' (it happened '%v' before)!"
shouldHaveHappenedBetween="Expected '%v' to happen between '%v' and '%v' (it happened '%v' outside threshold)!"
...
...
@@ -93,4 +102,5 @@ const ( // time comparisons
// format params: incorrect-index, previous-index, previous-time, incorrect-index, incorrect-time
shouldHaveBeenChronological="The 'Time' at index [%d] should have happened after the previous one (but it didn't!):\n [%d]: %s\n [%d]: %s (see, it happened before!)"
shouldNotHaveBeenchronological="The provided times should NOT be chronological, but they were."
flag.BoolVar(&json,"convey-json",false,"When true, emits results in JSON blocks. Default: 'false'")
flag.BoolVar(&silent,"convey-silent",false,"When true, all output from GoConvey is suppressed.")
flag.BoolVar(&story,"convey-story",false,"When true, emits story output, otherwise emits dot output. When not provided, this flag mirros the value of the '-test.v' flag")
flag.BoolVar(&story,"convey-story",false,"When true, emits story output, otherwise emits dot output. When not provided, this flag mirrors the value of the '-test.v' flag")