Signed-off-by: Daniel Nephin <dnephin@docker.com>
| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 0 |
+Copyright (c) 2013, Patrick Mezard |
|
| 1 |
+All rights reserved. |
|
| 2 |
+ |
|
| 3 |
+Redistribution and use in source and binary forms, with or without |
|
| 4 |
+modification, are permitted provided that the following conditions are |
|
| 5 |
+met: |
|
| 6 |
+ |
|
| 7 |
+ Redistributions of source code must retain the above copyright |
|
| 8 |
+notice, this list of conditions and the following disclaimer. |
|
| 9 |
+ Redistributions in binary form must reproduce the above copyright |
|
| 10 |
+notice, this list of conditions and the following disclaimer in the |
|
| 11 |
+documentation and/or other materials provided with the distribution. |
|
| 12 |
+ The names of its contributors may not be used to endorse or promote |
|
| 13 |
+products derived from this software without specific prior written |
|
| 14 |
+permission. |
|
| 15 |
+ |
|
| 16 |
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
|
| 17 |
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
|
| 18 |
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
|
| 19 |
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 20 |
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 21 |
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
|
| 22 |
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
| 23 |
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
| 24 |
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
| 25 |
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
| 26 |
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 0 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,772 @@ |
| 0 |
+// Package difflib is a partial port of Python difflib module. |
|
| 1 |
+// |
|
| 2 |
+// It provides tools to compare sequences of strings and generate textual diffs. |
|
| 3 |
+// |
|
| 4 |
+// The following class and functions have been ported: |
|
| 5 |
+// |
|
| 6 |
+// - SequenceMatcher |
|
| 7 |
+// |
|
| 8 |
+// - unified_diff |
|
| 9 |
+// |
|
| 10 |
+// - context_diff |
|
| 11 |
+// |
|
| 12 |
+// Getting unified diffs was the main goal of the port. Keep in mind this code |
|
| 13 |
+// is mostly suitable to output text differences in a human friendly way, there |
|
| 14 |
+// are no guarantees generated diffs are consumable by patch(1). |
|
| 15 |
+package difflib |
|
| 16 |
+ |
|
| 17 |
+import ( |
|
| 18 |
+ "bufio" |
|
| 19 |
+ "bytes" |
|
| 20 |
+ "fmt" |
|
| 21 |
+ "io" |
|
| 22 |
+ "strings" |
|
| 23 |
+) |
|
| 24 |
+ |
|
| 25 |
+func min(a, b int) int {
|
|
| 26 |
+ if a < b {
|
|
| 27 |
+ return a |
|
| 28 |
+ } |
|
| 29 |
+ return b |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+func max(a, b int) int {
|
|
| 33 |
+ if a > b {
|
|
| 34 |
+ return a |
|
| 35 |
+ } |
|
| 36 |
+ return b |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func calculateRatio(matches, length int) float64 {
|
|
| 40 |
+ if length > 0 {
|
|
| 41 |
+ return 2.0 * float64(matches) / float64(length) |
|
| 42 |
+ } |
|
| 43 |
+ return 1.0 |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+type Match struct {
|
|
| 47 |
+ A int |
|
| 48 |
+ B int |
|
| 49 |
+ Size int |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+type OpCode struct {
|
|
| 53 |
+ Tag byte |
|
| 54 |
+ I1 int |
|
| 55 |
+ I2 int |
|
| 56 |
+ J1 int |
|
| 57 |
+ J2 int |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+// SequenceMatcher compares sequence of strings. The basic |
|
| 61 |
+// algorithm predates, and is a little fancier than, an algorithm |
|
| 62 |
+// published in the late 1980's by Ratcliff and Obershelp under the |
|
| 63 |
+// hyperbolic name "gestalt pattern matching". The basic idea is to find |
|
| 64 |
+// the longest contiguous matching subsequence that contains no "junk" |
|
| 65 |
+// elements (R-O doesn't address junk). The same idea is then applied |
|
| 66 |
+// recursively to the pieces of the sequences to the left and to the right |
|
| 67 |
+// of the matching subsequence. This does not yield minimal edit |
|
| 68 |
+// sequences, but does tend to yield matches that "look right" to people. |
|
| 69 |
+// |
|
| 70 |
+// SequenceMatcher tries to compute a "human-friendly diff" between two |
|
| 71 |
+// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the |
|
| 72 |
+// longest *contiguous* & junk-free matching subsequence. That's what |
|
| 73 |
+// catches peoples' eyes. The Windows(tm) windiff has another interesting |
|
| 74 |
+// notion, pairing up elements that appear uniquely in each sequence. |
|
| 75 |
+// That, and the method here, appear to yield more intuitive difference |
|
| 76 |
+// reports than does diff. This method appears to be the least vulnerable |
|
| 77 |
+// to synching up on blocks of "junk lines", though (like blank lines in |
|
| 78 |
+// ordinary text files, or maybe "<P>" lines in HTML files). That may be |
|
| 79 |
+// because this is the only method of the 3 that has a *concept* of |
|
| 80 |
+// "junk" <wink>. |
|
| 81 |
+// |
|
| 82 |
+// Timing: Basic R-O is cubic time worst case and quadratic time expected |
|
| 83 |
+// case. SequenceMatcher is quadratic time for the worst case and has |
|
| 84 |
+// expected-case behavior dependent in a complicated way on how many |
|
| 85 |
+// elements the sequences have in common; best case time is linear. |
|
| 86 |
+type SequenceMatcher struct {
|
|
| 87 |
+ a []string |
|
| 88 |
+ b []string |
|
| 89 |
+ b2j map[string][]int |
|
| 90 |
+ IsJunk func(string) bool |
|
| 91 |
+ autoJunk bool |
|
| 92 |
+ bJunk map[string]struct{}
|
|
| 93 |
+ matchingBlocks []Match |
|
| 94 |
+ fullBCount map[string]int |
|
| 95 |
+ bPopular map[string]struct{}
|
|
| 96 |
+ opCodes []OpCode |
|
| 97 |
+} |
|
| 98 |
+ |
|
| 99 |
+func NewMatcher(a, b []string) *SequenceMatcher {
|
|
| 100 |
+ m := SequenceMatcher{autoJunk: true}
|
|
| 101 |
+ m.SetSeqs(a, b) |
|
| 102 |
+ return &m |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+func NewMatcherWithJunk(a, b []string, autoJunk bool, |
|
| 106 |
+ isJunk func(string) bool) *SequenceMatcher {
|
|
| 107 |
+ |
|
| 108 |
+ m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
|
|
| 109 |
+ m.SetSeqs(a, b) |
|
| 110 |
+ return &m |
|
| 111 |
+} |
|
| 112 |
+ |
|
| 113 |
+// Set two sequences to be compared. |
|
| 114 |
+func (m *SequenceMatcher) SetSeqs(a, b []string) {
|
|
| 115 |
+ m.SetSeq1(a) |
|
| 116 |
+ m.SetSeq2(b) |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+// Set the first sequence to be compared. The second sequence to be compared is |
|
| 120 |
+// not changed. |
|
| 121 |
+// |
|
| 122 |
+// SequenceMatcher computes and caches detailed information about the second |
|
| 123 |
+// sequence, so if you want to compare one sequence S against many sequences, |
|
| 124 |
+// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other |
|
| 125 |
+// sequences. |
|
| 126 |
+// |
|
| 127 |
+// See also SetSeqs() and SetSeq2(). |
|
| 128 |
+func (m *SequenceMatcher) SetSeq1(a []string) {
|
|
| 129 |
+ if &a == &m.a {
|
|
| 130 |
+ return |
|
| 131 |
+ } |
|
| 132 |
+ m.a = a |
|
| 133 |
+ m.matchingBlocks = nil |
|
| 134 |
+ m.opCodes = nil |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+// Set the second sequence to be compared. The first sequence to be compared is |
|
| 138 |
+// not changed. |
|
| 139 |
+func (m *SequenceMatcher) SetSeq2(b []string) {
|
|
| 140 |
+ if &b == &m.b {
|
|
| 141 |
+ return |
|
| 142 |
+ } |
|
| 143 |
+ m.b = b |
|
| 144 |
+ m.matchingBlocks = nil |
|
| 145 |
+ m.opCodes = nil |
|
| 146 |
+ m.fullBCount = nil |
|
| 147 |
+ m.chainB() |
|
| 148 |
+} |
|
| 149 |
+ |
|
| 150 |
+func (m *SequenceMatcher) chainB() {
|
|
| 151 |
+ // Populate line -> index mapping |
|
| 152 |
+ b2j := map[string][]int{}
|
|
| 153 |
+ for i, s := range m.b {
|
|
| 154 |
+ indices := b2j[s] |
|
| 155 |
+ indices = append(indices, i) |
|
| 156 |
+ b2j[s] = indices |
|
| 157 |
+ } |
|
| 158 |
+ |
|
| 159 |
+ // Purge junk elements |
|
| 160 |
+ m.bJunk = map[string]struct{}{}
|
|
| 161 |
+ if m.IsJunk != nil {
|
|
| 162 |
+ junk := m.bJunk |
|
| 163 |
+ for s, _ := range b2j {
|
|
| 164 |
+ if m.IsJunk(s) {
|
|
| 165 |
+ junk[s] = struct{}{}
|
|
| 166 |
+ } |
|
| 167 |
+ } |
|
| 168 |
+ for s, _ := range junk {
|
|
| 169 |
+ delete(b2j, s) |
|
| 170 |
+ } |
|
| 171 |
+ } |
|
| 172 |
+ |
|
| 173 |
+ // Purge remaining popular elements |
|
| 174 |
+ popular := map[string]struct{}{}
|
|
| 175 |
+ n := len(m.b) |
|
| 176 |
+ if m.autoJunk && n >= 200 {
|
|
| 177 |
+ ntest := n/100 + 1 |
|
| 178 |
+ for s, indices := range b2j {
|
|
| 179 |
+ if len(indices) > ntest {
|
|
| 180 |
+ popular[s] = struct{}{}
|
|
| 181 |
+ } |
|
| 182 |
+ } |
|
| 183 |
+ for s, _ := range popular {
|
|
| 184 |
+ delete(b2j, s) |
|
| 185 |
+ } |
|
| 186 |
+ } |
|
| 187 |
+ m.bPopular = popular |
|
| 188 |
+ m.b2j = b2j |
|
| 189 |
+} |
|
| 190 |
+ |
|
| 191 |
+func (m *SequenceMatcher) isBJunk(s string) bool {
|
|
| 192 |
+ _, ok := m.bJunk[s] |
|
| 193 |
+ return ok |
|
| 194 |
+} |
|
| 195 |
+ |
|
| 196 |
+// Find longest matching block in a[alo:ahi] and b[blo:bhi]. |
|
| 197 |
+// |
|
| 198 |
+// If IsJunk is not defined: |
|
| 199 |
+// |
|
| 200 |
+// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where |
|
| 201 |
+// alo <= i <= i+k <= ahi |
|
| 202 |
+// blo <= j <= j+k <= bhi |
|
| 203 |
+// and for all (i',j',k') meeting those conditions, |
|
| 204 |
+// k >= k' |
|
| 205 |
+// i <= i' |
|
| 206 |
+// and if i == i', j <= j' |
|
| 207 |
+// |
|
| 208 |
+// In other words, of all maximal matching blocks, return one that |
|
| 209 |
+// starts earliest in a, and of all those maximal matching blocks that |
|
| 210 |
+// start earliest in a, return the one that starts earliest in b. |
|
| 211 |
+// |
|
| 212 |
+// If IsJunk is defined, first the longest matching block is |
|
| 213 |
+// determined as above, but with the additional restriction that no |
|
| 214 |
+// junk element appears in the block. Then that block is extended as |
|
| 215 |
+// far as possible by matching (only) junk elements on both sides. So |
|
| 216 |
+// the resulting block never matches on junk except as identical junk |
|
| 217 |
+// happens to be adjacent to an "interesting" match. |
|
| 218 |
+// |
|
| 219 |
+// If no blocks match, return (alo, blo, 0). |
|
| 220 |
+func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
|
|
| 221 |
+ // CAUTION: stripping common prefix or suffix would be incorrect. |
|
| 222 |
+ // E.g., |
|
| 223 |
+ // ab |
|
| 224 |
+ // acab |
|
| 225 |
+ // Longest matching block is "ab", but if common prefix is |
|
| 226 |
+ // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so |
|
| 227 |
+ // strip, so ends up claiming that ab is changed to acab by |
|
| 228 |
+ // inserting "ca" in the middle. That's minimal but unintuitive: |
|
| 229 |
+ // "it's obvious" that someone inserted "ac" at the front. |
|
| 230 |
+ // Windiff ends up at the same place as diff, but by pairing up |
|
| 231 |
+ // the unique 'b's and then matching the first two 'a's. |
|
| 232 |
+ besti, bestj, bestsize := alo, blo, 0 |
|
| 233 |
+ |
|
| 234 |
+ // find longest junk-free match |
|
| 235 |
+ // during an iteration of the loop, j2len[j] = length of longest |
|
| 236 |
+ // junk-free match ending with a[i-1] and b[j] |
|
| 237 |
+ j2len := map[int]int{}
|
|
| 238 |
+ for i := alo; i != ahi; i++ {
|
|
| 239 |
+ // look at all instances of a[i] in b; note that because |
|
| 240 |
+ // b2j has no junk keys, the loop is skipped if a[i] is junk |
|
| 241 |
+ newj2len := map[int]int{}
|
|
| 242 |
+ for _, j := range m.b2j[m.a[i]] {
|
|
| 243 |
+ // a[i] matches b[j] |
|
| 244 |
+ if j < blo {
|
|
| 245 |
+ continue |
|
| 246 |
+ } |
|
| 247 |
+ if j >= bhi {
|
|
| 248 |
+ break |
|
| 249 |
+ } |
|
| 250 |
+ k := j2len[j-1] + 1 |
|
| 251 |
+ newj2len[j] = k |
|
| 252 |
+ if k > bestsize {
|
|
| 253 |
+ besti, bestj, bestsize = i-k+1, j-k+1, k |
|
| 254 |
+ } |
|
| 255 |
+ } |
|
| 256 |
+ j2len = newj2len |
|
| 257 |
+ } |
|
| 258 |
+ |
|
| 259 |
+ // Extend the best by non-junk elements on each end. In particular, |
|
| 260 |
+ // "popular" non-junk elements aren't in b2j, which greatly speeds |
|
| 261 |
+ // the inner loop above, but also means "the best" match so far |
|
| 262 |
+ // doesn't contain any junk *or* popular non-junk elements. |
|
| 263 |
+ for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) && |
|
| 264 |
+ m.a[besti-1] == m.b[bestj-1] {
|
|
| 265 |
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 |
|
| 266 |
+ } |
|
| 267 |
+ for besti+bestsize < ahi && bestj+bestsize < bhi && |
|
| 268 |
+ !m.isBJunk(m.b[bestj+bestsize]) && |
|
| 269 |
+ m.a[besti+bestsize] == m.b[bestj+bestsize] {
|
|
| 270 |
+ bestsize += 1 |
|
| 271 |
+ } |
|
| 272 |
+ |
|
| 273 |
+ // Now that we have a wholly interesting match (albeit possibly |
|
| 274 |
+ // empty!), we may as well suck up the matching junk on each |
|
| 275 |
+ // side of it too. Can't think of a good reason not to, and it |
|
| 276 |
+ // saves post-processing the (possibly considerable) expense of |
|
| 277 |
+ // figuring out what to do with it. In the case of an empty |
|
| 278 |
+ // interesting match, this is clearly the right thing to do, |
|
| 279 |
+ // because no other kind of match is possible in the regions. |
|
| 280 |
+ for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) && |
|
| 281 |
+ m.a[besti-1] == m.b[bestj-1] {
|
|
| 282 |
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 |
|
| 283 |
+ } |
|
| 284 |
+ for besti+bestsize < ahi && bestj+bestsize < bhi && |
|
| 285 |
+ m.isBJunk(m.b[bestj+bestsize]) && |
|
| 286 |
+ m.a[besti+bestsize] == m.b[bestj+bestsize] {
|
|
| 287 |
+ bestsize += 1 |
|
| 288 |
+ } |
|
| 289 |
+ |
|
| 290 |
+ return Match{A: besti, B: bestj, Size: bestsize}
|
|
| 291 |
+} |
|
| 292 |
+ |
|
| 293 |
+// Return list of triples describing matching subsequences. |
|
| 294 |
+// |
|
| 295 |
+// Each triple is of the form (i, j, n), and means that |
|
| 296 |
+// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in |
|
| 297 |
+// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are |
|
| 298 |
+// adjacent triples in the list, and the second is not the last triple in the |
|
| 299 |
+// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe |
|
| 300 |
+// adjacent equal blocks. |
|
| 301 |
+// |
|
| 302 |
+// The last triple is a dummy, (len(a), len(b), 0), and is the only |
|
| 303 |
+// triple with n==0. |
|
| 304 |
+func (m *SequenceMatcher) GetMatchingBlocks() []Match {
|
|
| 305 |
+ if m.matchingBlocks != nil {
|
|
| 306 |
+ return m.matchingBlocks |
|
| 307 |
+ } |
|
| 308 |
+ |
|
| 309 |
+ var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match |
|
| 310 |
+ matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
|
|
| 311 |
+ match := m.findLongestMatch(alo, ahi, blo, bhi) |
|
| 312 |
+ i, j, k := match.A, match.B, match.Size |
|
| 313 |
+ if match.Size > 0 {
|
|
| 314 |
+ if alo < i && blo < j {
|
|
| 315 |
+ matched = matchBlocks(alo, i, blo, j, matched) |
|
| 316 |
+ } |
|
| 317 |
+ matched = append(matched, match) |
|
| 318 |
+ if i+k < ahi && j+k < bhi {
|
|
| 319 |
+ matched = matchBlocks(i+k, ahi, j+k, bhi, matched) |
|
| 320 |
+ } |
|
| 321 |
+ } |
|
| 322 |
+ return matched |
|
| 323 |
+ } |
|
| 324 |
+ matched := matchBlocks(0, len(m.a), 0, len(m.b), nil) |
|
| 325 |
+ |
|
| 326 |
+ // It's possible that we have adjacent equal blocks in the |
|
| 327 |
+ // matching_blocks list now. |
|
| 328 |
+ nonAdjacent := []Match{}
|
|
| 329 |
+ i1, j1, k1 := 0, 0, 0 |
|
| 330 |
+ for _, b := range matched {
|
|
| 331 |
+ // Is this block adjacent to i1, j1, k1? |
|
| 332 |
+ i2, j2, k2 := b.A, b.B, b.Size |
|
| 333 |
+ if i1+k1 == i2 && j1+k1 == j2 {
|
|
| 334 |
+ // Yes, so collapse them -- this just increases the length of |
|
| 335 |
+ // the first block by the length of the second, and the first |
|
| 336 |
+ // block so lengthened remains the block to compare against. |
|
| 337 |
+ k1 += k2 |
|
| 338 |
+ } else {
|
|
| 339 |
+ // Not adjacent. Remember the first block (k1==0 means it's |
|
| 340 |
+ // the dummy we started with), and make the second block the |
|
| 341 |
+ // new block to compare against. |
|
| 342 |
+ if k1 > 0 {
|
|
| 343 |
+ nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
|
|
| 344 |
+ } |
|
| 345 |
+ i1, j1, k1 = i2, j2, k2 |
|
| 346 |
+ } |
|
| 347 |
+ } |
|
| 348 |
+ if k1 > 0 {
|
|
| 349 |
+ nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
|
|
| 350 |
+ } |
|
| 351 |
+ |
|
| 352 |
+ nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
|
|
| 353 |
+ m.matchingBlocks = nonAdjacent |
|
| 354 |
+ return m.matchingBlocks |
|
| 355 |
+} |
|
| 356 |
+ |
|
| 357 |
+// Return list of 5-tuples describing how to turn a into b. |
|
| 358 |
+// |
|
| 359 |
+// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple |
|
| 360 |
+// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the |
|
| 361 |
+// tuple preceding it, and likewise for j1 == the previous j2. |
|
| 362 |
+// |
|
| 363 |
+// The tags are characters, with these meanings: |
|
| 364 |
+// |
|
| 365 |
+// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2] |
|
| 366 |
+// |
|
| 367 |
+// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case. |
|
| 368 |
+// |
|
| 369 |
+// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case. |
|
| 370 |
+// |
|
| 371 |
+// 'e' (equal): a[i1:i2] == b[j1:j2] |
|
| 372 |
+func (m *SequenceMatcher) GetOpCodes() []OpCode {
|
|
| 373 |
+ if m.opCodes != nil {
|
|
| 374 |
+ return m.opCodes |
|
| 375 |
+ } |
|
| 376 |
+ i, j := 0, 0 |
|
| 377 |
+ matching := m.GetMatchingBlocks() |
|
| 378 |
+ opCodes := make([]OpCode, 0, len(matching)) |
|
| 379 |
+ for _, m := range matching {
|
|
| 380 |
+ // invariant: we've pumped out correct diffs to change |
|
| 381 |
+ // a[:i] into b[:j], and the next matching block is |
|
| 382 |
+ // a[ai:ai+size] == b[bj:bj+size]. So we need to pump |
|
| 383 |
+ // out a diff to change a[i:ai] into b[j:bj], pump out |
|
| 384 |
+ // the matching block, and move (i,j) beyond the match |
|
| 385 |
+ ai, bj, size := m.A, m.B, m.Size |
|
| 386 |
+ tag := byte(0) |
|
| 387 |
+ if i < ai && j < bj {
|
|
| 388 |
+ tag = 'r' |
|
| 389 |
+ } else if i < ai {
|
|
| 390 |
+ tag = 'd' |
|
| 391 |
+ } else if j < bj {
|
|
| 392 |
+ tag = 'i' |
|
| 393 |
+ } |
|
| 394 |
+ if tag > 0 {
|
|
| 395 |
+ opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
|
|
| 396 |
+ } |
|
| 397 |
+ i, j = ai+size, bj+size |
|
| 398 |
+ // the list of matching blocks is terminated by a |
|
| 399 |
+ // sentinel with size 0 |
|
| 400 |
+ if size > 0 {
|
|
| 401 |
+ opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
|
|
| 402 |
+ } |
|
| 403 |
+ } |
|
| 404 |
+ m.opCodes = opCodes |
|
| 405 |
+ return m.opCodes |
|
| 406 |
+} |
|
| 407 |
+ |
|
| 408 |
+// Isolate change clusters by eliminating ranges with no changes. |
|
| 409 |
+// |
|
| 410 |
+// Return a generator of groups with up to n lines of context. |
|
| 411 |
+// Each group is in the same format as returned by GetOpCodes(). |
|
| 412 |
+func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
|
|
| 413 |
+ if n < 0 {
|
|
| 414 |
+ n = 3 |
|
| 415 |
+ } |
|
| 416 |
+ codes := m.GetOpCodes() |
|
| 417 |
+ if len(codes) == 0 {
|
|
| 418 |
+ codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
|
|
| 419 |
+ } |
|
| 420 |
+ // Fixup leading and trailing groups if they show no changes. |
|
| 421 |
+ if codes[0].Tag == 'e' {
|
|
| 422 |
+ c := codes[0] |
|
| 423 |
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 |
|
| 424 |
+ codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
|
|
| 425 |
+ } |
|
| 426 |
+ if codes[len(codes)-1].Tag == 'e' {
|
|
| 427 |
+ c := codes[len(codes)-1] |
|
| 428 |
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 |
|
| 429 |
+ codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
|
|
| 430 |
+ } |
|
| 431 |
+ nn := n + n |
|
| 432 |
+ groups := [][]OpCode{}
|
|
| 433 |
+ group := []OpCode{}
|
|
| 434 |
+ for _, c := range codes {
|
|
| 435 |
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 |
|
| 436 |
+ // End the current group and start a new one whenever |
|
| 437 |
+ // there is a large range with no changes. |
|
| 438 |
+ if c.Tag == 'e' && i2-i1 > nn {
|
|
| 439 |
+ group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
|
|
| 440 |
+ j1, min(j2, j1+n)}) |
|
| 441 |
+ groups = append(groups, group) |
|
| 442 |
+ group = []OpCode{}
|
|
| 443 |
+ i1, j1 = max(i1, i2-n), max(j1, j2-n) |
|
| 444 |
+ } |
|
| 445 |
+ group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
|
|
| 446 |
+ } |
|
| 447 |
+ if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
|
|
| 448 |
+ groups = append(groups, group) |
|
| 449 |
+ } |
|
| 450 |
+ return groups |
|
| 451 |
+} |
|
| 452 |
+ |
|
| 453 |
+// Return a measure of the sequences' similarity (float in [0,1]). |
|
| 454 |
+// |
|
| 455 |
+// Where T is the total number of elements in both sequences, and |
|
| 456 |
+// M is the number of matches, this is 2.0*M / T. |
|
| 457 |
+// Note that this is 1 if the sequences are identical, and 0 if |
|
| 458 |
+// they have nothing in common. |
|
| 459 |
+// |
|
| 460 |
+// .Ratio() is expensive to compute if you haven't already computed |
|
| 461 |
+// .GetMatchingBlocks() or .GetOpCodes(), in which case you may |
|
| 462 |
+// want to try .QuickRatio() or .RealQuickRation() first to get an |
|
| 463 |
+// upper bound. |
|
| 464 |
+func (m *SequenceMatcher) Ratio() float64 {
|
|
| 465 |
+ matches := 0 |
|
| 466 |
+ for _, m := range m.GetMatchingBlocks() {
|
|
| 467 |
+ matches += m.Size |
|
| 468 |
+ } |
|
| 469 |
+ return calculateRatio(matches, len(m.a)+len(m.b)) |
|
| 470 |
+} |
|
| 471 |
+ |
|
| 472 |
+// Return an upper bound on ratio() relatively quickly. |
|
| 473 |
+// |
|
| 474 |
+// This isn't defined beyond that it is an upper bound on .Ratio(), and |
|
| 475 |
+// is faster to compute. |
|
| 476 |
+func (m *SequenceMatcher) QuickRatio() float64 {
|
|
| 477 |
+ // viewing a and b as multisets, set matches to the cardinality |
|
| 478 |
+ // of their intersection; this counts the number of matches |
|
| 479 |
+ // without regard to order, so is clearly an upper bound |
|
| 480 |
+ if m.fullBCount == nil {
|
|
| 481 |
+ m.fullBCount = map[string]int{}
|
|
| 482 |
+ for _, s := range m.b {
|
|
| 483 |
+ m.fullBCount[s] = m.fullBCount[s] + 1 |
|
| 484 |
+ } |
|
| 485 |
+ } |
|
| 486 |
+ |
|
| 487 |
+ // avail[x] is the number of times x appears in 'b' less the |
|
| 488 |
+ // number of times we've seen it in 'a' so far ... kinda |
|
| 489 |
+ avail := map[string]int{}
|
|
| 490 |
+ matches := 0 |
|
| 491 |
+ for _, s := range m.a {
|
|
| 492 |
+ n, ok := avail[s] |
|
| 493 |
+ if !ok {
|
|
| 494 |
+ n = m.fullBCount[s] |
|
| 495 |
+ } |
|
| 496 |
+ avail[s] = n - 1 |
|
| 497 |
+ if n > 0 {
|
|
| 498 |
+ matches += 1 |
|
| 499 |
+ } |
|
| 500 |
+ } |
|
| 501 |
+ return calculateRatio(matches, len(m.a)+len(m.b)) |
|
| 502 |
+} |
|
| 503 |
+ |
|
| 504 |
+// Return an upper bound on ratio() very quickly. |
|
| 505 |
+// |
|
| 506 |
+// This isn't defined beyond that it is an upper bound on .Ratio(), and |
|
| 507 |
+// is faster to compute than either .Ratio() or .QuickRatio(). |
|
| 508 |
+func (m *SequenceMatcher) RealQuickRatio() float64 {
|
|
| 509 |
+ la, lb := len(m.a), len(m.b) |
|
| 510 |
+ return calculateRatio(min(la, lb), la+lb) |
|
| 511 |
+} |
|
| 512 |
+ |
|
| 513 |
+// Convert range to the "ed" format |
|
| 514 |
+func formatRangeUnified(start, stop int) string {
|
|
| 515 |
+ // Per the diff spec at http://www.unix.org/single_unix_specification/ |
|
| 516 |
+ beginning := start + 1 // lines start numbering with one |
|
| 517 |
+ length := stop - start |
|
| 518 |
+ if length == 1 {
|
|
| 519 |
+ return fmt.Sprintf("%d", beginning)
|
|
| 520 |
+ } |
|
| 521 |
+ if length == 0 {
|
|
| 522 |
+ beginning -= 1 // empty ranges begin at line just before the range |
|
| 523 |
+ } |
|
| 524 |
+ return fmt.Sprintf("%d,%d", beginning, length)
|
|
| 525 |
+} |
|
| 526 |
+ |
|
| 527 |
+// Unified diff parameters |
|
| 528 |
+type UnifiedDiff struct {
|
|
| 529 |
+ A []string // First sequence lines |
|
| 530 |
+ FromFile string // First file name |
|
| 531 |
+ FromDate string // First file time |
|
| 532 |
+ B []string // Second sequence lines |
|
| 533 |
+ ToFile string // Second file name |
|
| 534 |
+ ToDate string // Second file time |
|
| 535 |
+ Eol string // Headers end of line, defaults to LF |
|
| 536 |
+ Context int // Number of context lines |
|
| 537 |
+} |
|
| 538 |
+ |
|
| 539 |
+// Compare two sequences of lines; generate the delta as a unified diff. |
|
| 540 |
+// |
|
| 541 |
+// Unified diffs are a compact way of showing line changes and a few |
|
| 542 |
+// lines of context. The number of context lines is set by 'n' which |
|
| 543 |
+// defaults to three. |
|
| 544 |
+// |
|
| 545 |
+// By default, the diff control lines (those with ---, +++, or @@) are |
|
| 546 |
+// created with a trailing newline. This is helpful so that inputs |
|
| 547 |
+// created from file.readlines() result in diffs that are suitable for |
|
| 548 |
+// file.writelines() since both the inputs and outputs have trailing |
|
| 549 |
+// newlines. |
|
| 550 |
+// |
|
| 551 |
+// For inputs that do not have trailing newlines, set the lineterm |
|
| 552 |
+// argument to "" so that the output will be uniformly newline free. |
|
| 553 |
+// |
|
| 554 |
+// The unidiff format normally has a header for filenames and modification |
|
| 555 |
+// times. Any or all of these may be specified using strings for |
|
| 556 |
+// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. |
|
| 557 |
+// The modification times are normally expressed in the ISO 8601 format. |
|
| 558 |
+func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
|
|
| 559 |
+ buf := bufio.NewWriter(writer) |
|
| 560 |
+ defer buf.Flush() |
|
| 561 |
+ wf := func(format string, args ...interface{}) error {
|
|
| 562 |
+ _, err := buf.WriteString(fmt.Sprintf(format, args...)) |
|
| 563 |
+ return err |
|
| 564 |
+ } |
|
| 565 |
+ ws := func(s string) error {
|
|
| 566 |
+ _, err := buf.WriteString(s) |
|
| 567 |
+ return err |
|
| 568 |
+ } |
|
| 569 |
+ |
|
| 570 |
+ if len(diff.Eol) == 0 {
|
|
| 571 |
+ diff.Eol = "\n" |
|
| 572 |
+ } |
|
| 573 |
+ |
|
| 574 |
+ started := false |
|
| 575 |
+ m := NewMatcher(diff.A, diff.B) |
|
| 576 |
+ for _, g := range m.GetGroupedOpCodes(diff.Context) {
|
|
| 577 |
+ if !started {
|
|
| 578 |
+ started = true |
|
| 579 |
+ fromDate := "" |
|
| 580 |
+ if len(diff.FromDate) > 0 {
|
|
| 581 |
+ fromDate = "\t" + diff.FromDate |
|
| 582 |
+ } |
|
| 583 |
+ toDate := "" |
|
| 584 |
+ if len(diff.ToDate) > 0 {
|
|
| 585 |
+ toDate = "\t" + diff.ToDate |
|
| 586 |
+ } |
|
| 587 |
+ if diff.FromFile != "" || diff.ToFile != "" {
|
|
| 588 |
+ err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
|
| 589 |
+ if err != nil {
|
|
| 590 |
+ return err |
|
| 591 |
+ } |
|
| 592 |
+ err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
|
|
| 593 |
+ if err != nil {
|
|
| 594 |
+ return err |
|
| 595 |
+ } |
|
| 596 |
+ } |
|
| 597 |
+ } |
|
| 598 |
+ first, last := g[0], g[len(g)-1] |
|
| 599 |
+ range1 := formatRangeUnified(first.I1, last.I2) |
|
| 600 |
+ range2 := formatRangeUnified(first.J1, last.J2) |
|
| 601 |
+ if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
|
|
| 602 |
+ return err |
|
| 603 |
+ } |
|
| 604 |
+ for _, c := range g {
|
|
| 605 |
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 |
|
| 606 |
+ if c.Tag == 'e' {
|
|
| 607 |
+ for _, line := range diff.A[i1:i2] {
|
|
| 608 |
+ if err := ws(" " + line); err != nil {
|
|
| 609 |
+ return err |
|
| 610 |
+ } |
|
| 611 |
+ } |
|
| 612 |
+ continue |
|
| 613 |
+ } |
|
| 614 |
+ if c.Tag == 'r' || c.Tag == 'd' {
|
|
| 615 |
+ for _, line := range diff.A[i1:i2] {
|
|
| 616 |
+ if err := ws("-" + line); err != nil {
|
|
| 617 |
+ return err |
|
| 618 |
+ } |
|
| 619 |
+ } |
|
| 620 |
+ } |
|
| 621 |
+ if c.Tag == 'r' || c.Tag == 'i' {
|
|
| 622 |
+ for _, line := range diff.B[j1:j2] {
|
|
| 623 |
+ if err := ws("+" + line); err != nil {
|
|
| 624 |
+ return err |
|
| 625 |
+ } |
|
| 626 |
+ } |
|
| 627 |
+ } |
|
| 628 |
+ } |
|
| 629 |
+ } |
|
| 630 |
+ return nil |
|
| 631 |
+} |
|
| 632 |
+ |
|
| 633 |
+// Like WriteUnifiedDiff but returns the diff a string. |
|
| 634 |
+func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
|
|
| 635 |
+ w := &bytes.Buffer{}
|
|
| 636 |
+ err := WriteUnifiedDiff(w, diff) |
|
| 637 |
+ return string(w.Bytes()), err |
|
| 638 |
+} |
|
| 639 |
+ |
|
| 640 |
+// Convert range to the "ed" format. |
|
| 641 |
+func formatRangeContext(start, stop int) string {
|
|
| 642 |
+ // Per the diff spec at http://www.unix.org/single_unix_specification/ |
|
| 643 |
+ beginning := start + 1 // lines start numbering with one |
|
| 644 |
+ length := stop - start |
|
| 645 |
+ if length == 0 {
|
|
| 646 |
+ beginning -= 1 // empty ranges begin at line just before the range |
|
| 647 |
+ } |
|
| 648 |
+ if length <= 1 {
|
|
| 649 |
+ return fmt.Sprintf("%d", beginning)
|
|
| 650 |
+ } |
|
| 651 |
+ return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
|
|
| 652 |
+} |
|
| 653 |
+ |
|
| 654 |
+type ContextDiff UnifiedDiff |
|
| 655 |
+ |
|
| 656 |
+// Compare two sequences of lines; generate the delta as a context diff. |
|
| 657 |
+// |
|
| 658 |
+// Context diffs are a compact way of showing line changes and a few |
|
| 659 |
+// lines of context. The number of context lines is set by diff.Context |
|
| 660 |
+// which defaults to three. |
|
| 661 |
+// |
|
| 662 |
+// By default, the diff control lines (those with *** or ---) are |
|
| 663 |
+// created with a trailing newline. |
|
| 664 |
+// |
|
| 665 |
+// For inputs that do not have trailing newlines, set the diff.Eol |
|
| 666 |
+// argument to "" so that the output will be uniformly newline free. |
|
| 667 |
+// |
|
| 668 |
+// The context diff format normally has a header for filenames and |
|
| 669 |
+// modification times. Any or all of these may be specified using |
|
| 670 |
+// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate. |
|
| 671 |
+// The modification times are normally expressed in the ISO 8601 format. |
|
| 672 |
+// If not specified, the strings default to blanks. |
|
| 673 |
+func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
|
|
| 674 |
+ buf := bufio.NewWriter(writer) |
|
| 675 |
+ defer buf.Flush() |
|
| 676 |
+ var diffErr error |
|
| 677 |
+ wf := func(format string, args ...interface{}) {
|
|
| 678 |
+ _, err := buf.WriteString(fmt.Sprintf(format, args...)) |
|
| 679 |
+ if diffErr == nil && err != nil {
|
|
| 680 |
+ diffErr = err |
|
| 681 |
+ } |
|
| 682 |
+ } |
|
| 683 |
+ ws := func(s string) {
|
|
| 684 |
+ _, err := buf.WriteString(s) |
|
| 685 |
+ if diffErr == nil && err != nil {
|
|
| 686 |
+ diffErr = err |
|
| 687 |
+ } |
|
| 688 |
+ } |
|
| 689 |
+ |
|
| 690 |
+ if len(diff.Eol) == 0 {
|
|
| 691 |
+ diff.Eol = "\n" |
|
| 692 |
+ } |
|
| 693 |
+ |
|
| 694 |
+ prefix := map[byte]string{
|
|
| 695 |
+ 'i': "+ ", |
|
| 696 |
+ 'd': "- ", |
|
| 697 |
+ 'r': "! ", |
|
| 698 |
+ 'e': " ", |
|
| 699 |
+ } |
|
| 700 |
+ |
|
| 701 |
+ started := false |
|
| 702 |
+ m := NewMatcher(diff.A, diff.B) |
|
| 703 |
+ for _, g := range m.GetGroupedOpCodes(diff.Context) {
|
|
| 704 |
+ if !started {
|
|
| 705 |
+ started = true |
|
| 706 |
+ fromDate := "" |
|
| 707 |
+ if len(diff.FromDate) > 0 {
|
|
| 708 |
+ fromDate = "\t" + diff.FromDate |
|
| 709 |
+ } |
|
| 710 |
+ toDate := "" |
|
| 711 |
+ if len(diff.ToDate) > 0 {
|
|
| 712 |
+ toDate = "\t" + diff.ToDate |
|
| 713 |
+ } |
|
| 714 |
+ if diff.FromFile != "" || diff.ToFile != "" {
|
|
| 715 |
+ wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
|
| 716 |
+ wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
|
|
| 717 |
+ } |
|
| 718 |
+ } |
|
| 719 |
+ |
|
| 720 |
+ first, last := g[0], g[len(g)-1] |
|
| 721 |
+ ws("***************" + diff.Eol)
|
|
| 722 |
+ |
|
| 723 |
+ range1 := formatRangeContext(first.I1, last.I2) |
|
| 724 |
+ wf("*** %s ****%s", range1, diff.Eol)
|
|
| 725 |
+ for _, c := range g {
|
|
| 726 |
+ if c.Tag == 'r' || c.Tag == 'd' {
|
|
| 727 |
+ for _, cc := range g {
|
|
| 728 |
+ if cc.Tag == 'i' {
|
|
| 729 |
+ continue |
|
| 730 |
+ } |
|
| 731 |
+ for _, line := range diff.A[cc.I1:cc.I2] {
|
|
| 732 |
+ ws(prefix[cc.Tag] + line) |
|
| 733 |
+ } |
|
| 734 |
+ } |
|
| 735 |
+ break |
|
| 736 |
+ } |
|
| 737 |
+ } |
|
| 738 |
+ |
|
| 739 |
+ range2 := formatRangeContext(first.J1, last.J2) |
|
| 740 |
+ wf("--- %s ----%s", range2, diff.Eol)
|
|
| 741 |
+ for _, c := range g {
|
|
| 742 |
+ if c.Tag == 'r' || c.Tag == 'i' {
|
|
| 743 |
+ for _, cc := range g {
|
|
| 744 |
+ if cc.Tag == 'd' {
|
|
| 745 |
+ continue |
|
| 746 |
+ } |
|
| 747 |
+ for _, line := range diff.B[cc.J1:cc.J2] {
|
|
| 748 |
+ ws(prefix[cc.Tag] + line) |
|
| 749 |
+ } |
|
| 750 |
+ } |
|
| 751 |
+ break |
|
| 752 |
+ } |
|
| 753 |
+ } |
|
| 754 |
+ } |
|
| 755 |
+ return diffErr |
|
| 756 |
+} |
|
| 757 |
+ |
|
| 758 |
+// Like WriteContextDiff but returns the diff a string. |
|
| 759 |
+func GetContextDiffString(diff ContextDiff) (string, error) {
|
|
| 760 |
+ w := &bytes.Buffer{}
|
|
| 761 |
+ err := WriteContextDiff(w, diff) |
|
| 762 |
+ return string(w.Bytes()), err |
|
| 763 |
+} |
|
| 764 |
+ |
|
| 765 |
+// Split a string on "\n" while preserving them. The output can be used |
|
| 766 |
+// as input for UnifiedDiff and ContextDiff structures. |
|
| 767 |
+func SplitLines(s string) []string {
|
|
| 768 |
+ lines := strings.SplitAfter(s, "\n") |
|
| 769 |
+ lines[len(lines)-1] += "\n" |
|
| 770 |
+ return lines |
|
| 771 |
+} |
| 0 | 772 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,22 @@ |
| 0 |
+Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell |
|
| 1 |
+ |
|
| 2 |
+Please consider promoting this project if you find it useful. |
|
| 3 |
+ |
|
| 4 |
+Permission is hereby granted, free of charge, to any person |
|
| 5 |
+obtaining a copy of this software and associated documentation |
|
| 6 |
+files (the "Software"), to deal in the Software without restriction, |
|
| 7 |
+including without limitation the rights to use, copy, modify, merge, |
|
| 8 |
+publish, distribute, sublicense, and/or sell copies of the Software, |
|
| 9 |
+and to permit persons to whom the Software is furnished to do so, |
|
| 10 |
+subject to the following conditions: |
|
| 11 |
+ |
|
| 12 |
+The above copyright notice and this permission notice shall be included |
|
| 13 |
+in all copies or substantial portions of the Software. |
|
| 14 |
+ |
|
| 15 |
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
| 16 |
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
|
| 17 |
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
| 18 |
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
|
| 19 |
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT |
|
| 20 |
+OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE |
|
| 21 |
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 0 | 22 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,387 @@ |
| 0 |
+/* |
|
| 1 |
+* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen |
|
| 2 |
+* THIS FILE MUST NOT BE EDITED BY HAND |
|
| 3 |
+*/ |
|
| 4 |
+ |
|
| 5 |
+package assert |
|
| 6 |
+ |
|
| 7 |
+import ( |
|
| 8 |
+ |
|
| 9 |
+ http "net/http" |
|
| 10 |
+ url "net/url" |
|
| 11 |
+ time "time" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+ |
|
| 15 |
+// Condition uses a Comparison to assert a complex condition. |
|
| 16 |
+func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool {
|
|
| 17 |
+ return Condition(a.t, comp, msgAndArgs...) |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+ |
|
| 21 |
+// Contains asserts that the specified string, list(array, slice...) or map contains the |
|
| 22 |
+// specified substring or element. |
|
| 23 |
+// |
|
| 24 |
+// a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'")
|
|
| 25 |
+// a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") |
|
| 26 |
+// a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'")
|
|
| 27 |
+// |
|
| 28 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 29 |
+func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
|
|
| 30 |
+ return Contains(a.t, s, contains, msgAndArgs...) |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+ |
|
| 34 |
+// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either |
|
| 35 |
+// a slice or a channel with len == 0. |
|
| 36 |
+// |
|
| 37 |
+// a.Empty(obj) |
|
| 38 |
+// |
|
| 39 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 40 |
+func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 41 |
+ return Empty(a.t, object, msgAndArgs...) |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+ |
|
| 45 |
+// Equal asserts that two objects are equal. |
|
| 46 |
+// |
|
| 47 |
+// a.Equal(123, 123, "123 and 123 should be equal") |
|
| 48 |
+// |
|
| 49 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 50 |
+func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
| 51 |
+ return Equal(a.t, expected, actual, msgAndArgs...) |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+ |
|
| 55 |
+// EqualError asserts that a function returned an error (i.e. not `nil`) |
|
| 56 |
+// and that it is equal to the provided error. |
|
| 57 |
+// |
|
| 58 |
+// actualObj, err := SomeFunction() |
|
| 59 |
+// if assert.Error(t, err, "An error was expected") {
|
|
| 60 |
+// assert.Equal(t, err, expectedError) |
|
| 61 |
+// } |
|
| 62 |
+// |
|
| 63 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 64 |
+func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool {
|
|
| 65 |
+ return EqualError(a.t, theError, errString, msgAndArgs...) |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+ |
|
| 69 |
+// EqualValues asserts that two objects are equal or convertable to the same types |
|
| 70 |
+// and equal. |
|
| 71 |
+// |
|
| 72 |
+// a.EqualValues(uint32(123), int32(123), "123 and 123 should be equal") |
|
| 73 |
+// |
|
| 74 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 75 |
+func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
| 76 |
+ return EqualValues(a.t, expected, actual, msgAndArgs...) |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+ |
|
| 80 |
+// Error asserts that a function returned an error (i.e. not `nil`). |
|
| 81 |
+// |
|
| 82 |
+// actualObj, err := SomeFunction() |
|
| 83 |
+// if a.Error(err, "An error was expected") {
|
|
| 84 |
+// assert.Equal(t, err, expectedError) |
|
| 85 |
+// } |
|
| 86 |
+// |
|
| 87 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 88 |
+func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
|
|
| 89 |
+ return Error(a.t, err, msgAndArgs...) |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+ |
|
| 93 |
+// Exactly asserts that two objects are equal is value and type. |
|
| 94 |
+// |
|
| 95 |
+// a.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal") |
|
| 96 |
+// |
|
| 97 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 98 |
+func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
| 99 |
+ return Exactly(a.t, expected, actual, msgAndArgs...) |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+ |
|
| 103 |
+// Fail reports a failure through |
|
| 104 |
+func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool {
|
|
| 105 |
+ return Fail(a.t, failureMessage, msgAndArgs...) |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+ |
|
| 109 |
+// FailNow fails test |
|
| 110 |
+func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool {
|
|
| 111 |
+ return FailNow(a.t, failureMessage, msgAndArgs...) |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+ |
|
| 115 |
+// False asserts that the specified value is false. |
|
| 116 |
+// |
|
| 117 |
+// a.False(myBool, "myBool should be false") |
|
| 118 |
+// |
|
| 119 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 120 |
+func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
|
|
| 121 |
+ return False(a.t, value, msgAndArgs...) |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+ |
|
| 125 |
+// HTTPBodyContains asserts that a specified handler returns a |
|
| 126 |
+// body that contains a string. |
|
| 127 |
+// |
|
| 128 |
+// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") |
|
| 129 |
+// |
|
| 130 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 131 |
+func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
|
|
| 132 |
+ return HTTPBodyContains(a.t, handler, method, url, values, str) |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+ |
|
| 136 |
+// HTTPBodyNotContains asserts that a specified handler returns a |
|
| 137 |
+// body that does not contain a string. |
|
| 138 |
+// |
|
| 139 |
+// a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") |
|
| 140 |
+// |
|
| 141 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 142 |
+func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
|
|
| 143 |
+ return HTTPBodyNotContains(a.t, handler, method, url, values, str) |
|
| 144 |
+} |
|
| 145 |
+ |
|
| 146 |
+ |
|
| 147 |
+// HTTPError asserts that a specified handler returns an error status code. |
|
| 148 |
+// |
|
| 149 |
+// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
|
| 150 |
+// |
|
| 151 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 152 |
+func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) bool {
|
|
| 153 |
+ return HTTPError(a.t, handler, method, url, values) |
|
| 154 |
+} |
|
| 155 |
+ |
|
| 156 |
+ |
|
| 157 |
+// HTTPRedirect asserts that a specified handler returns a redirect status code. |
|
| 158 |
+// |
|
| 159 |
+// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
|
| 160 |
+// |
|
| 161 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 162 |
+func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) bool {
|
|
| 163 |
+ return HTTPRedirect(a.t, handler, method, url, values) |
|
| 164 |
+} |
|
| 165 |
+ |
|
| 166 |
+ |
|
| 167 |
+// HTTPSuccess asserts that a specified handler returns a success status code. |
|
| 168 |
+// |
|
| 169 |
+// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) |
|
| 170 |
+// |
|
| 171 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 172 |
+func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) bool {
|
|
| 173 |
+ return HTTPSuccess(a.t, handler, method, url, values) |
|
| 174 |
+} |
|
| 175 |
+ |
|
| 176 |
+ |
|
| 177 |
+// Implements asserts that an object is implemented by the specified interface. |
|
| 178 |
+// |
|
| 179 |
+// a.Implements((*MyInterface)(nil), new(MyObject), "MyObject") |
|
| 180 |
+func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 181 |
+ return Implements(a.t, interfaceObject, object, msgAndArgs...) |
|
| 182 |
+} |
|
| 183 |
+ |
|
| 184 |
+ |
|
| 185 |
+// InDelta asserts that the two numerals are within delta of each other. |
|
| 186 |
+// |
|
| 187 |
+// a.InDelta(math.Pi, (22 / 7.0), 0.01) |
|
| 188 |
+// |
|
| 189 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 190 |
+func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
|
| 191 |
+ return InDelta(a.t, expected, actual, delta, msgAndArgs...) |
|
| 192 |
+} |
|
| 193 |
+ |
|
| 194 |
+ |
|
| 195 |
+// InDeltaSlice is the same as InDelta, except it compares two slices. |
|
| 196 |
+func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
|
| 197 |
+ return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) |
|
| 198 |
+} |
|
| 199 |
+ |
|
| 200 |
+ |
|
| 201 |
+// InEpsilon asserts that expected and actual have a relative error less than epsilon |
|
| 202 |
+// |
|
| 203 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 204 |
+func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
|
| 205 |
+ return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) |
|
| 206 |
+} |
|
| 207 |
+ |
|
| 208 |
+ |
|
| 209 |
+// InEpsilonSlice is the same as InEpsilon, except it compares two slices. |
|
| 210 |
+func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
|
| 211 |
+ return InEpsilonSlice(a.t, expected, actual, delta, msgAndArgs...) |
|
| 212 |
+} |
|
| 213 |
+ |
|
| 214 |
+ |
|
| 215 |
+// IsType asserts that the specified objects are of the same type. |
|
| 216 |
+func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 217 |
+ return IsType(a.t, expectedType, object, msgAndArgs...) |
|
| 218 |
+} |
|
| 219 |
+ |
|
| 220 |
+ |
|
| 221 |
+// JSONEq asserts that two JSON strings are equivalent. |
|
| 222 |
+// |
|
| 223 |
+// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
|
| 224 |
+// |
|
| 225 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 226 |
+func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool {
|
|
| 227 |
+ return JSONEq(a.t, expected, actual, msgAndArgs...) |
|
| 228 |
+} |
|
| 229 |
+ |
|
| 230 |
+ |
|
| 231 |
+// Len asserts that the specified object has specific length. |
|
| 232 |
+// Len also fails if the object has a type that len() not accept. |
|
| 233 |
+// |
|
| 234 |
+// a.Len(mySlice, 3, "The size of slice is not 3") |
|
| 235 |
+// |
|
| 236 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 237 |
+func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool {
|
|
| 238 |
+ return Len(a.t, object, length, msgAndArgs...) |
|
| 239 |
+} |
|
| 240 |
+ |
|
| 241 |
+ |
|
| 242 |
+// Nil asserts that the specified object is nil. |
|
| 243 |
+// |
|
| 244 |
+// a.Nil(err, "err should be nothing") |
|
| 245 |
+// |
|
| 246 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 247 |
+func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 248 |
+ return Nil(a.t, object, msgAndArgs...) |
|
| 249 |
+} |
|
| 250 |
+ |
|
| 251 |
+ |
|
| 252 |
+// NoError asserts that a function returned no error (i.e. `nil`). |
|
| 253 |
+// |
|
| 254 |
+// actualObj, err := SomeFunction() |
|
| 255 |
+// if a.NoError(err) {
|
|
| 256 |
+// assert.Equal(t, actualObj, expectedObj) |
|
| 257 |
+// } |
|
| 258 |
+// |
|
| 259 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 260 |
+func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
|
|
| 261 |
+ return NoError(a.t, err, msgAndArgs...) |
|
| 262 |
+} |
|
| 263 |
+ |
|
| 264 |
+ |
|
| 265 |
+// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the |
|
| 266 |
+// specified substring or element. |
|
| 267 |
+// |
|
| 268 |
+// a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
|
|
| 269 |
+// a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") |
|
| 270 |
+// a.NotContains({"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'")
|
|
| 271 |
+// |
|
| 272 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 273 |
+func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
|
|
| 274 |
+ return NotContains(a.t, s, contains, msgAndArgs...) |
|
| 275 |
+} |
|
| 276 |
+ |
|
| 277 |
+ |
|
| 278 |
+// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either |
|
| 279 |
+// a slice or a channel with len == 0. |
|
| 280 |
+// |
|
| 281 |
+// if a.NotEmpty(obj) {
|
|
| 282 |
+// assert.Equal(t, "two", obj[1]) |
|
| 283 |
+// } |
|
| 284 |
+// |
|
| 285 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 286 |
+func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 287 |
+ return NotEmpty(a.t, object, msgAndArgs...) |
|
| 288 |
+} |
|
| 289 |
+ |
|
| 290 |
+ |
|
| 291 |
+// NotEqual asserts that the specified values are NOT equal. |
|
| 292 |
+// |
|
| 293 |
+// a.NotEqual(obj1, obj2, "two objects shouldn't be equal") |
|
| 294 |
+// |
|
| 295 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 296 |
+func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
| 297 |
+ return NotEqual(a.t, expected, actual, msgAndArgs...) |
|
| 298 |
+} |
|
| 299 |
+ |
|
| 300 |
+ |
|
| 301 |
+// NotNil asserts that the specified object is not nil. |
|
| 302 |
+// |
|
| 303 |
+// a.NotNil(err, "err should be something") |
|
| 304 |
+// |
|
| 305 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 306 |
+func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 307 |
+ return NotNil(a.t, object, msgAndArgs...) |
|
| 308 |
+} |
|
| 309 |
+ |
|
| 310 |
+ |
|
| 311 |
+// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. |
|
| 312 |
+// |
|
| 313 |
+// a.NotPanics(func(){
|
|
| 314 |
+// RemainCalm() |
|
| 315 |
+// }, "Calling RemainCalm() should NOT panic") |
|
| 316 |
+// |
|
| 317 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 318 |
+func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
| 319 |
+ return NotPanics(a.t, f, msgAndArgs...) |
|
| 320 |
+} |
|
| 321 |
+ |
|
| 322 |
+ |
|
| 323 |
+// NotRegexp asserts that a specified regexp does not match a string. |
|
| 324 |
+// |
|
| 325 |
+// a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
|
|
| 326 |
+// a.NotRegexp("^start", "it's not starting")
|
|
| 327 |
+// |
|
| 328 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 329 |
+func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
|
| 330 |
+ return NotRegexp(a.t, rx, str, msgAndArgs...) |
|
| 331 |
+} |
|
| 332 |
+ |
|
| 333 |
+ |
|
| 334 |
+// NotZero asserts that i is not the zero value for its type and returns the truth. |
|
| 335 |
+func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool {
|
|
| 336 |
+ return NotZero(a.t, i, msgAndArgs...) |
|
| 337 |
+} |
|
| 338 |
+ |
|
| 339 |
+ |
|
| 340 |
+// Panics asserts that the code inside the specified PanicTestFunc panics. |
|
| 341 |
+// |
|
| 342 |
+// a.Panics(func(){
|
|
| 343 |
+// GoCrazy() |
|
| 344 |
+// }, "Calling GoCrazy() should panic") |
|
| 345 |
+// |
|
| 346 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 347 |
+func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
| 348 |
+ return Panics(a.t, f, msgAndArgs...) |
|
| 349 |
+} |
|
| 350 |
+ |
|
| 351 |
+ |
|
| 352 |
+// Regexp asserts that a specified regexp matches a string. |
|
| 353 |
+// |
|
| 354 |
+// a.Regexp(regexp.MustCompile("start"), "it's starting")
|
|
| 355 |
+// a.Regexp("start...$", "it's not starting")
|
|
| 356 |
+// |
|
| 357 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 358 |
+func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
|
| 359 |
+ return Regexp(a.t, rx, str, msgAndArgs...) |
|
| 360 |
+} |
|
| 361 |
+ |
|
| 362 |
+ |
|
| 363 |
+// True asserts that the specified value is true. |
|
| 364 |
+// |
|
| 365 |
+// a.True(myBool, "myBool should be true") |
|
| 366 |
+// |
|
| 367 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 368 |
+func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
|
|
| 369 |
+ return True(a.t, value, msgAndArgs...) |
|
| 370 |
+} |
|
| 371 |
+ |
|
| 372 |
+ |
|
| 373 |
+// WithinDuration asserts that the two times are within duration delta of each other. |
|
| 374 |
+// |
|
| 375 |
+// a.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") |
|
| 376 |
+// |
|
| 377 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 378 |
+func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
|
| 379 |
+ return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) |
|
| 380 |
+} |
|
| 381 |
+ |
|
| 382 |
+ |
|
| 383 |
+// Zero asserts that i is the zero value for its type and returns the truth. |
|
| 384 |
+func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool {
|
|
| 385 |
+ return Zero(a.t, i, msgAndArgs...) |
|
| 386 |
+} |
| 0 | 387 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,1004 @@ |
| 0 |
+package assert |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bufio" |
|
| 4 |
+ "bytes" |
|
| 5 |
+ "encoding/json" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "math" |
|
| 8 |
+ "reflect" |
|
| 9 |
+ "regexp" |
|
| 10 |
+ "runtime" |
|
| 11 |
+ "strings" |
|
| 12 |
+ "time" |
|
| 13 |
+ "unicode" |
|
| 14 |
+ "unicode/utf8" |
|
| 15 |
+ |
|
| 16 |
+ "github.com/davecgh/go-spew/spew" |
|
| 17 |
+ "github.com/pmezard/go-difflib/difflib" |
|
| 18 |
+) |
|
| 19 |
+ |
|
| 20 |
+// TestingT is an interface wrapper around *testing.T |
|
| 21 |
+type TestingT interface {
|
|
| 22 |
+ Errorf(format string, args ...interface{})
|
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+// Comparison a custom function that returns true on success and false on failure |
|
| 26 |
+type Comparison func() (success bool) |
|
| 27 |
+ |
|
| 28 |
+/* |
|
| 29 |
+ Helper functions |
|
| 30 |
+*/ |
|
| 31 |
+ |
|
| 32 |
+// ObjectsAreEqual determines if two objects are considered equal. |
|
| 33 |
+// |
|
| 34 |
+// This function does no assertion of any kind. |
|
| 35 |
+func ObjectsAreEqual(expected, actual interface{}) bool {
|
|
| 36 |
+ |
|
| 37 |
+ if expected == nil || actual == nil {
|
|
| 38 |
+ return expected == actual |
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ return reflect.DeepEqual(expected, actual) |
|
| 42 |
+ |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+// ObjectsAreEqualValues gets whether two objects are equal, or if their |
|
| 46 |
+// values are equal. |
|
| 47 |
+func ObjectsAreEqualValues(expected, actual interface{}) bool {
|
|
| 48 |
+ if ObjectsAreEqual(expected, actual) {
|
|
| 49 |
+ return true |
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ actualType := reflect.TypeOf(actual) |
|
| 53 |
+ if actualType == nil {
|
|
| 54 |
+ return false |
|
| 55 |
+ } |
|
| 56 |
+ expectedValue := reflect.ValueOf(expected) |
|
| 57 |
+ if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
|
|
| 58 |
+ // Attempt comparison after type conversion |
|
| 59 |
+ return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) |
|
| 60 |
+ } |
|
| 61 |
+ |
|
| 62 |
+ return false |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+/* CallerInfo is necessary because the assert functions use the testing object |
|
| 66 |
+internally, causing it to print the file:line of the assert method, rather than where |
|
| 67 |
+the problem actually occured in calling code.*/ |
|
| 68 |
+ |
|
| 69 |
+// CallerInfo returns an array of strings containing the file and line number |
|
| 70 |
+// of each stack frame leading from the current test to the assert call that |
|
| 71 |
+// failed. |
|
| 72 |
+func CallerInfo() []string {
|
|
| 73 |
+ |
|
| 74 |
+ pc := uintptr(0) |
|
| 75 |
+ file := "" |
|
| 76 |
+ line := 0 |
|
| 77 |
+ ok := false |
|
| 78 |
+ name := "" |
|
| 79 |
+ |
|
| 80 |
+ callers := []string{}
|
|
| 81 |
+ for i := 0; ; i++ {
|
|
| 82 |
+ pc, file, line, ok = runtime.Caller(i) |
|
| 83 |
+ if !ok {
|
|
| 84 |
+ return nil |
|
| 85 |
+ } |
|
| 86 |
+ |
|
| 87 |
+ // This is a huge edge case, but it will panic if this is the case, see #180 |
|
| 88 |
+ if file == "<autogenerated>" {
|
|
| 89 |
+ break |
|
| 90 |
+ } |
|
| 91 |
+ |
|
| 92 |
+ parts := strings.Split(file, "/") |
|
| 93 |
+ dir := parts[len(parts)-2] |
|
| 94 |
+ file = parts[len(parts)-1] |
|
| 95 |
+ if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
|
|
| 96 |
+ callers = append(callers, fmt.Sprintf("%s:%d", file, line))
|
|
| 97 |
+ } |
|
| 98 |
+ |
|
| 99 |
+ f := runtime.FuncForPC(pc) |
|
| 100 |
+ if f == nil {
|
|
| 101 |
+ break |
|
| 102 |
+ } |
|
| 103 |
+ name = f.Name() |
|
| 104 |
+ // Drop the package |
|
| 105 |
+ segments := strings.Split(name, ".") |
|
| 106 |
+ name = segments[len(segments)-1] |
|
| 107 |
+ if isTest(name, "Test") || |
|
| 108 |
+ isTest(name, "Benchmark") || |
|
| 109 |
+ isTest(name, "Example") {
|
|
| 110 |
+ break |
|
| 111 |
+ } |
|
| 112 |
+ } |
|
| 113 |
+ |
|
| 114 |
+ return callers |
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+// Stolen from the `go test` tool. |
|
| 118 |
+// isTest tells whether name looks like a test (or benchmark, according to prefix). |
|
| 119 |
+// It is a Test (say) if there is a character after Test that is not a lower-case letter. |
|
| 120 |
+// We don't want TesticularCancer. |
|
| 121 |
+func isTest(name, prefix string) bool {
|
|
| 122 |
+ if !strings.HasPrefix(name, prefix) {
|
|
| 123 |
+ return false |
|
| 124 |
+ } |
|
| 125 |
+ if len(name) == len(prefix) { // "Test" is ok
|
|
| 126 |
+ return true |
|
| 127 |
+ } |
|
| 128 |
+ rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) |
|
| 129 |
+ return !unicode.IsLower(rune) |
|
| 130 |
+} |
|
| 131 |
+ |
|
| 132 |
+// getWhitespaceString returns a string that is long enough to overwrite the default |
|
| 133 |
+// output from the go testing framework. |
|
| 134 |
+func getWhitespaceString() string {
|
|
| 135 |
+ |
|
| 136 |
+ _, file, line, ok := runtime.Caller(1) |
|
| 137 |
+ if !ok {
|
|
| 138 |
+ return "" |
|
| 139 |
+ } |
|
| 140 |
+ parts := strings.Split(file, "/") |
|
| 141 |
+ file = parts[len(parts)-1] |
|
| 142 |
+ |
|
| 143 |
+ return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line)))
|
|
| 144 |
+ |
|
| 145 |
+} |
|
| 146 |
+ |
|
| 147 |
+func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
|
| 148 |
+ if len(msgAndArgs) == 0 || msgAndArgs == nil {
|
|
| 149 |
+ return "" |
|
| 150 |
+ } |
|
| 151 |
+ if len(msgAndArgs) == 1 {
|
|
| 152 |
+ return msgAndArgs[0].(string) |
|
| 153 |
+ } |
|
| 154 |
+ if len(msgAndArgs) > 1 {
|
|
| 155 |
+ return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) |
|
| 156 |
+ } |
|
| 157 |
+ return "" |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 160 |
+// Indents all lines of the message by appending a number of tabs to each line, in an output format compatible with Go's |
|
| 161 |
+// test printing (see inner comment for specifics) |
|
| 162 |
+func indentMessageLines(message string, tabs int) string {
|
|
| 163 |
+ outBuf := new(bytes.Buffer) |
|
| 164 |
+ |
|
| 165 |
+ for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ {
|
|
| 166 |
+ if i != 0 {
|
|
| 167 |
+ outBuf.WriteRune('\n')
|
|
| 168 |
+ } |
|
| 169 |
+ for ii := 0; ii < tabs; ii++ {
|
|
| 170 |
+ outBuf.WriteRune('\t')
|
|
| 171 |
+ // Bizarrely, all lines except the first need one fewer tabs prepended, so deliberately advance the counter |
|
| 172 |
+ // by 1 prematurely. |
|
| 173 |
+ if ii == 0 && i > 0 {
|
|
| 174 |
+ ii++ |
|
| 175 |
+ } |
|
| 176 |
+ } |
|
| 177 |
+ outBuf.WriteString(scanner.Text()) |
|
| 178 |
+ } |
|
| 179 |
+ |
|
| 180 |
+ return outBuf.String() |
|
| 181 |
+} |
|
| 182 |
+ |
|
| 183 |
+type failNower interface {
|
|
| 184 |
+ FailNow() |
|
| 185 |
+} |
|
| 186 |
+ |
|
| 187 |
+// FailNow fails test |
|
| 188 |
+func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
|
|
| 189 |
+ Fail(t, failureMessage, msgAndArgs...) |
|
| 190 |
+ |
|
| 191 |
+ // We cannot extend TestingT with FailNow() and |
|
| 192 |
+ // maintain backwards compatibility, so we fallback |
|
| 193 |
+ // to panicking when FailNow is not available in |
|
| 194 |
+ // TestingT. |
|
| 195 |
+ // See issue #263 |
|
| 196 |
+ |
|
| 197 |
+ if t, ok := t.(failNower); ok {
|
|
| 198 |
+ t.FailNow() |
|
| 199 |
+ } else {
|
|
| 200 |
+ panic("test failed and t is missing `FailNow()`")
|
|
| 201 |
+ } |
|
| 202 |
+ return false |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 205 |
+// Fail reports a failure through |
|
| 206 |
+func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
|
|
| 207 |
+ |
|
| 208 |
+ message := messageFromMsgAndArgs(msgAndArgs...) |
|
| 209 |
+ |
|
| 210 |
+ errorTrace := strings.Join(CallerInfo(), "\n\r\t\t\t") |
|
| 211 |
+ if len(message) > 0 {
|
|
| 212 |
+ t.Errorf("\r%s\r\tError Trace:\t%s\n"+
|
|
| 213 |
+ "\r\tError:%s\n"+ |
|
| 214 |
+ "\r\tMessages:\t%s\n\r", |
|
| 215 |
+ getWhitespaceString(), |
|
| 216 |
+ errorTrace, |
|
| 217 |
+ indentMessageLines(failureMessage, 2), |
|
| 218 |
+ message) |
|
| 219 |
+ } else {
|
|
| 220 |
+ t.Errorf("\r%s\r\tError Trace:\t%s\n"+
|
|
| 221 |
+ "\r\tError:%s\n\r", |
|
| 222 |
+ getWhitespaceString(), |
|
| 223 |
+ errorTrace, |
|
| 224 |
+ indentMessageLines(failureMessage, 2)) |
|
| 225 |
+ } |
|
| 226 |
+ |
|
| 227 |
+ return false |
|
| 228 |
+} |
|
| 229 |
+ |
|
| 230 |
+// Implements asserts that an object is implemented by the specified interface. |
|
| 231 |
+// |
|
| 232 |
+// assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject") |
|
| 233 |
+func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 234 |
+ |
|
| 235 |
+ interfaceType := reflect.TypeOf(interfaceObject).Elem() |
|
| 236 |
+ |
|
| 237 |
+ if !reflect.TypeOf(object).Implements(interfaceType) {
|
|
| 238 |
+ return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...)
|
|
| 239 |
+ } |
|
| 240 |
+ |
|
| 241 |
+ return true |
|
| 242 |
+ |
|
| 243 |
+} |
|
| 244 |
+ |
|
| 245 |
+// IsType asserts that the specified objects are of the same type. |
|
| 246 |
+func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 247 |
+ |
|
| 248 |
+ if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) {
|
|
| 249 |
+ return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...)
|
|
| 250 |
+ } |
|
| 251 |
+ |
|
| 252 |
+ return true |
|
| 253 |
+} |
|
| 254 |
+ |
|
| 255 |
+// Equal asserts that two objects are equal. |
|
| 256 |
+// |
|
| 257 |
+// assert.Equal(t, 123, 123, "123 and 123 should be equal") |
|
| 258 |
+// |
|
| 259 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 260 |
+func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
| 261 |
+ |
|
| 262 |
+ if !ObjectsAreEqual(expected, actual) {
|
|
| 263 |
+ diff := diff(expected, actual) |
|
| 264 |
+ return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+
|
|
| 265 |
+ " != %#v (actual)%s", expected, actual, diff), msgAndArgs...) |
|
| 266 |
+ } |
|
| 267 |
+ |
|
| 268 |
+ return true |
|
| 269 |
+ |
|
| 270 |
+} |
|
| 271 |
+ |
|
| 272 |
+// EqualValues asserts that two objects are equal or convertable to the same types |
|
| 273 |
+// and equal. |
|
| 274 |
+// |
|
| 275 |
+// assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal") |
|
| 276 |
+// |
|
| 277 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 278 |
+func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
| 279 |
+ |
|
| 280 |
+ if !ObjectsAreEqualValues(expected, actual) {
|
|
| 281 |
+ return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+
|
|
| 282 |
+ " != %#v (actual)", expected, actual), msgAndArgs...) |
|
| 283 |
+ } |
|
| 284 |
+ |
|
| 285 |
+ return true |
|
| 286 |
+ |
|
| 287 |
+} |
|
| 288 |
+ |
|
| 289 |
+// Exactly asserts that two objects are equal is value and type. |
|
| 290 |
+// |
|
| 291 |
+// assert.Exactly(t, int32(123), int64(123), "123 and 123 should NOT be equal") |
|
| 292 |
+// |
|
| 293 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 294 |
+func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
| 295 |
+ |
|
| 296 |
+ aType := reflect.TypeOf(expected) |
|
| 297 |
+ bType := reflect.TypeOf(actual) |
|
| 298 |
+ |
|
| 299 |
+ if aType != bType {
|
|
| 300 |
+ return Fail(t, fmt.Sprintf("Types expected to match exactly\n\r\t%v != %v", aType, bType), msgAndArgs...)
|
|
| 301 |
+ } |
|
| 302 |
+ |
|
| 303 |
+ return Equal(t, expected, actual, msgAndArgs...) |
|
| 304 |
+ |
|
| 305 |
+} |
|
| 306 |
+ |
|
| 307 |
+// NotNil asserts that the specified object is not nil. |
|
| 308 |
+// |
|
| 309 |
+// assert.NotNil(t, err, "err should be something") |
|
| 310 |
+// |
|
| 311 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 312 |
+func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 313 |
+ if !isNil(object) {
|
|
| 314 |
+ return true |
|
| 315 |
+ } |
|
| 316 |
+ return Fail(t, "Expected value not to be nil.", msgAndArgs...) |
|
| 317 |
+} |
|
| 318 |
+ |
|
| 319 |
+// isNil checks if a specified object is nil or not, without Failing. |
|
| 320 |
+func isNil(object interface{}) bool {
|
|
| 321 |
+ if object == nil {
|
|
| 322 |
+ return true |
|
| 323 |
+ } |
|
| 324 |
+ |
|
| 325 |
+ value := reflect.ValueOf(object) |
|
| 326 |
+ kind := value.Kind() |
|
| 327 |
+ if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
|
|
| 328 |
+ return true |
|
| 329 |
+ } |
|
| 330 |
+ |
|
| 331 |
+ return false |
|
| 332 |
+} |
|
| 333 |
+ |
|
| 334 |
+// Nil asserts that the specified object is nil. |
|
| 335 |
+// |
|
| 336 |
+// assert.Nil(t, err, "err should be nothing") |
|
| 337 |
+// |
|
| 338 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 339 |
+func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 340 |
+ if isNil(object) {
|
|
| 341 |
+ return true |
|
| 342 |
+ } |
|
| 343 |
+ return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
|
|
| 344 |
+} |
|
| 345 |
+ |
|
| 346 |
+var numericZeros = []interface{}{
|
|
| 347 |
+ int(0), |
|
| 348 |
+ int8(0), |
|
| 349 |
+ int16(0), |
|
| 350 |
+ int32(0), |
|
| 351 |
+ int64(0), |
|
| 352 |
+ uint(0), |
|
| 353 |
+ uint8(0), |
|
| 354 |
+ uint16(0), |
|
| 355 |
+ uint32(0), |
|
| 356 |
+ uint64(0), |
|
| 357 |
+ float32(0), |
|
| 358 |
+ float64(0), |
|
| 359 |
+} |
|
| 360 |
+ |
|
| 361 |
+// isEmpty gets whether the specified object is considered empty or not. |
|
| 362 |
+func isEmpty(object interface{}) bool {
|
|
| 363 |
+ |
|
| 364 |
+ if object == nil {
|
|
| 365 |
+ return true |
|
| 366 |
+ } else if object == "" {
|
|
| 367 |
+ return true |
|
| 368 |
+ } else if object == false {
|
|
| 369 |
+ return true |
|
| 370 |
+ } |
|
| 371 |
+ |
|
| 372 |
+ for _, v := range numericZeros {
|
|
| 373 |
+ if object == v {
|
|
| 374 |
+ return true |
|
| 375 |
+ } |
|
| 376 |
+ } |
|
| 377 |
+ |
|
| 378 |
+ objValue := reflect.ValueOf(object) |
|
| 379 |
+ |
|
| 380 |
+ switch objValue.Kind() {
|
|
| 381 |
+ case reflect.Map: |
|
| 382 |
+ fallthrough |
|
| 383 |
+ case reflect.Slice, reflect.Chan: |
|
| 384 |
+ {
|
|
| 385 |
+ return (objValue.Len() == 0) |
|
| 386 |
+ } |
|
| 387 |
+ case reflect.Struct: |
|
| 388 |
+ switch object.(type) {
|
|
| 389 |
+ case time.Time: |
|
| 390 |
+ return object.(time.Time).IsZero() |
|
| 391 |
+ } |
|
| 392 |
+ case reflect.Ptr: |
|
| 393 |
+ {
|
|
| 394 |
+ if objValue.IsNil() {
|
|
| 395 |
+ return true |
|
| 396 |
+ } |
|
| 397 |
+ switch object.(type) {
|
|
| 398 |
+ case *time.Time: |
|
| 399 |
+ return object.(*time.Time).IsZero() |
|
| 400 |
+ default: |
|
| 401 |
+ return false |
|
| 402 |
+ } |
|
| 403 |
+ } |
|
| 404 |
+ } |
|
| 405 |
+ return false |
|
| 406 |
+} |
|
| 407 |
+ |
|
| 408 |
+// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either |
|
| 409 |
+// a slice or a channel with len == 0. |
|
| 410 |
+// |
|
| 411 |
+// assert.Empty(t, obj) |
|
| 412 |
+// |
|
| 413 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 414 |
+func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 415 |
+ |
|
| 416 |
+ pass := isEmpty(object) |
|
| 417 |
+ if !pass {
|
|
| 418 |
+ Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...)
|
|
| 419 |
+ } |
|
| 420 |
+ |
|
| 421 |
+ return pass |
|
| 422 |
+ |
|
| 423 |
+} |
|
| 424 |
+ |
|
| 425 |
+// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either |
|
| 426 |
+// a slice or a channel with len == 0. |
|
| 427 |
+// |
|
| 428 |
+// if assert.NotEmpty(t, obj) {
|
|
| 429 |
+// assert.Equal(t, "two", obj[1]) |
|
| 430 |
+// } |
|
| 431 |
+// |
|
| 432 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 433 |
+func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|
| 434 |
+ |
|
| 435 |
+ pass := !isEmpty(object) |
|
| 436 |
+ if !pass {
|
|
| 437 |
+ Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...)
|
|
| 438 |
+ } |
|
| 439 |
+ |
|
| 440 |
+ return pass |
|
| 441 |
+ |
|
| 442 |
+} |
|
| 443 |
+ |
|
| 444 |
+// getLen try to get length of object. |
|
| 445 |
+// return (false, 0) if impossible. |
|
| 446 |
+func getLen(x interface{}) (ok bool, length int) {
|
|
| 447 |
+ v := reflect.ValueOf(x) |
|
| 448 |
+ defer func() {
|
|
| 449 |
+ if e := recover(); e != nil {
|
|
| 450 |
+ ok = false |
|
| 451 |
+ } |
|
| 452 |
+ }() |
|
| 453 |
+ return true, v.Len() |
|
| 454 |
+} |
|
| 455 |
+ |
|
| 456 |
+// Len asserts that the specified object has specific length. |
|
| 457 |
+// Len also fails if the object has a type that len() not accept. |
|
| 458 |
+// |
|
| 459 |
+// assert.Len(t, mySlice, 3, "The size of slice is not 3") |
|
| 460 |
+// |
|
| 461 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 462 |
+func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
|
|
| 463 |
+ ok, l := getLen(object) |
|
| 464 |
+ if !ok {
|
|
| 465 |
+ return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...)
|
|
| 466 |
+ } |
|
| 467 |
+ |
|
| 468 |
+ if l != length {
|
|
| 469 |
+ return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...)
|
|
| 470 |
+ } |
|
| 471 |
+ return true |
|
| 472 |
+} |
|
| 473 |
+ |
|
| 474 |
+// True asserts that the specified value is true. |
|
| 475 |
+// |
|
| 476 |
+// assert.True(t, myBool, "myBool should be true") |
|
| 477 |
+// |
|
| 478 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 479 |
+func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
|
| 480 |
+ |
|
| 481 |
+ if value != true {
|
|
| 482 |
+ return Fail(t, "Should be true", msgAndArgs...) |
|
| 483 |
+ } |
|
| 484 |
+ |
|
| 485 |
+ return true |
|
| 486 |
+ |
|
| 487 |
+} |
|
| 488 |
+ |
|
| 489 |
+// False asserts that the specified value is false. |
|
| 490 |
+// |
|
| 491 |
+// assert.False(t, myBool, "myBool should be false") |
|
| 492 |
+// |
|
| 493 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 494 |
+func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
|
| 495 |
+ |
|
| 496 |
+ if value != false {
|
|
| 497 |
+ return Fail(t, "Should be false", msgAndArgs...) |
|
| 498 |
+ } |
|
| 499 |
+ |
|
| 500 |
+ return true |
|
| 501 |
+ |
|
| 502 |
+} |
|
| 503 |
+ |
|
| 504 |
+// NotEqual asserts that the specified values are NOT equal. |
|
| 505 |
+// |
|
| 506 |
+// assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal") |
|
| 507 |
+// |
|
| 508 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 509 |
+func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
|
| 510 |
+ |
|
| 511 |
+ if ObjectsAreEqual(expected, actual) {
|
|
| 512 |
+ return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
|
|
| 513 |
+ } |
|
| 514 |
+ |
|
| 515 |
+ return true |
|
| 516 |
+ |
|
| 517 |
+} |
|
| 518 |
+ |
|
| 519 |
+// containsElement try loop over the list check if the list includes the element. |
|
| 520 |
+// return (false, false) if impossible. |
|
| 521 |
+// return (true, false) if element was not found. |
|
| 522 |
+// return (true, true) if element was found. |
|
| 523 |
+func includeElement(list interface{}, element interface{}) (ok, found bool) {
|
|
| 524 |
+ |
|
| 525 |
+ listValue := reflect.ValueOf(list) |
|
| 526 |
+ elementValue := reflect.ValueOf(element) |
|
| 527 |
+ defer func() {
|
|
| 528 |
+ if e := recover(); e != nil {
|
|
| 529 |
+ ok = false |
|
| 530 |
+ found = false |
|
| 531 |
+ } |
|
| 532 |
+ }() |
|
| 533 |
+ |
|
| 534 |
+ if reflect.TypeOf(list).Kind() == reflect.String {
|
|
| 535 |
+ return true, strings.Contains(listValue.String(), elementValue.String()) |
|
| 536 |
+ } |
|
| 537 |
+ |
|
| 538 |
+ if reflect.TypeOf(list).Kind() == reflect.Map {
|
|
| 539 |
+ mapKeys := listValue.MapKeys() |
|
| 540 |
+ for i := 0; i < len(mapKeys); i++ {
|
|
| 541 |
+ if ObjectsAreEqual(mapKeys[i].Interface(), element) {
|
|
| 542 |
+ return true, true |
|
| 543 |
+ } |
|
| 544 |
+ } |
|
| 545 |
+ return true, false |
|
| 546 |
+ } |
|
| 547 |
+ |
|
| 548 |
+ for i := 0; i < listValue.Len(); i++ {
|
|
| 549 |
+ if ObjectsAreEqual(listValue.Index(i).Interface(), element) {
|
|
| 550 |
+ return true, true |
|
| 551 |
+ } |
|
| 552 |
+ } |
|
| 553 |
+ return true, false |
|
| 554 |
+ |
|
| 555 |
+} |
|
| 556 |
+ |
|
| 557 |
+// Contains asserts that the specified string, list(array, slice...) or map contains the |
|
| 558 |
+// specified substring or element. |
|
| 559 |
+// |
|
| 560 |
+// assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'") |
|
| 561 |
+// assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") |
|
| 562 |
+// assert.Contains(t, {"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'")
|
|
| 563 |
+// |
|
| 564 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 565 |
+func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
|
| 566 |
+ |
|
| 567 |
+ ok, found := includeElement(s, contains) |
|
| 568 |
+ if !ok {
|
|
| 569 |
+ return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
|
|
| 570 |
+ } |
|
| 571 |
+ if !found {
|
|
| 572 |
+ return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...)
|
|
| 573 |
+ } |
|
| 574 |
+ |
|
| 575 |
+ return true |
|
| 576 |
+ |
|
| 577 |
+} |
|
| 578 |
+ |
|
| 579 |
+// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the |
|
| 580 |
+// specified substring or element. |
|
| 581 |
+// |
|
| 582 |
+// assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") |
|
| 583 |
+// assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") |
|
| 584 |
+// assert.NotContains(t, {"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'")
|
|
| 585 |
+// |
|
| 586 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 587 |
+func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
|
| 588 |
+ |
|
| 589 |
+ ok, found := includeElement(s, contains) |
|
| 590 |
+ if !ok {
|
|
| 591 |
+ return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
|
|
| 592 |
+ } |
|
| 593 |
+ if found {
|
|
| 594 |
+ return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...)
|
|
| 595 |
+ } |
|
| 596 |
+ |
|
| 597 |
+ return true |
|
| 598 |
+ |
|
| 599 |
+} |
|
| 600 |
+ |
|
| 601 |
+// Condition uses a Comparison to assert a complex condition. |
|
| 602 |
+func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
|
|
| 603 |
+ result := comp() |
|
| 604 |
+ if !result {
|
|
| 605 |
+ Fail(t, "Condition failed!", msgAndArgs...) |
|
| 606 |
+ } |
|
| 607 |
+ return result |
|
| 608 |
+} |
|
| 609 |
+ |
|
| 610 |
+// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics |
|
| 611 |
+// methods, and represents a simple func that takes no arguments, and returns nothing. |
|
| 612 |
+type PanicTestFunc func() |
|
| 613 |
+ |
|
| 614 |
+// didPanic returns true if the function passed to it panics. Otherwise, it returns false. |
|
| 615 |
+func didPanic(f PanicTestFunc) (bool, interface{}) {
|
|
| 616 |
+ |
|
| 617 |
+ didPanic := false |
|
| 618 |
+ var message interface{}
|
|
| 619 |
+ func() {
|
|
| 620 |
+ |
|
| 621 |
+ defer func() {
|
|
| 622 |
+ if message = recover(); message != nil {
|
|
| 623 |
+ didPanic = true |
|
| 624 |
+ } |
|
| 625 |
+ }() |
|
| 626 |
+ |
|
| 627 |
+ // call the target function |
|
| 628 |
+ f() |
|
| 629 |
+ |
|
| 630 |
+ }() |
|
| 631 |
+ |
|
| 632 |
+ return didPanic, message |
|
| 633 |
+ |
|
| 634 |
+} |
|
| 635 |
+ |
|
| 636 |
+// Panics asserts that the code inside the specified PanicTestFunc panics. |
|
| 637 |
+// |
|
| 638 |
+// assert.Panics(t, func(){
|
|
| 639 |
+// GoCrazy() |
|
| 640 |
+// }, "Calling GoCrazy() should panic") |
|
| 641 |
+// |
|
| 642 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 643 |
+func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
| 644 |
+ |
|
| 645 |
+ if funcDidPanic, panicValue := didPanic(f); !funcDidPanic {
|
|
| 646 |
+ return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...)
|
|
| 647 |
+ } |
|
| 648 |
+ |
|
| 649 |
+ return true |
|
| 650 |
+} |
|
| 651 |
+ |
|
| 652 |
+// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. |
|
| 653 |
+// |
|
| 654 |
+// assert.NotPanics(t, func(){
|
|
| 655 |
+// RemainCalm() |
|
| 656 |
+// }, "Calling RemainCalm() should NOT panic") |
|
| 657 |
+// |
|
| 658 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 659 |
+func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
|
| 660 |
+ |
|
| 661 |
+ if funcDidPanic, panicValue := didPanic(f); funcDidPanic {
|
|
| 662 |
+ return Fail(t, fmt.Sprintf("func %#v should not panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...)
|
|
| 663 |
+ } |
|
| 664 |
+ |
|
| 665 |
+ return true |
|
| 666 |
+} |
|
| 667 |
+ |
|
| 668 |
+// WithinDuration asserts that the two times are within duration delta of each other. |
|
| 669 |
+// |
|
| 670 |
+// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") |
|
| 671 |
+// |
|
| 672 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 673 |
+func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
|
| 674 |
+ |
|
| 675 |
+ dt := expected.Sub(actual) |
|
| 676 |
+ if dt < -delta || dt > delta {
|
|
| 677 |
+ return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...)
|
|
| 678 |
+ } |
|
| 679 |
+ |
|
| 680 |
+ return true |
|
| 681 |
+} |
|
| 682 |
+ |
|
| 683 |
+func toFloat(x interface{}) (float64, bool) {
|
|
| 684 |
+ var xf float64 |
|
| 685 |
+ xok := true |
|
| 686 |
+ |
|
| 687 |
+ switch xn := x.(type) {
|
|
| 688 |
+ case uint8: |
|
| 689 |
+ xf = float64(xn) |
|
| 690 |
+ case uint16: |
|
| 691 |
+ xf = float64(xn) |
|
| 692 |
+ case uint32: |
|
| 693 |
+ xf = float64(xn) |
|
| 694 |
+ case uint64: |
|
| 695 |
+ xf = float64(xn) |
|
| 696 |
+ case int: |
|
| 697 |
+ xf = float64(xn) |
|
| 698 |
+ case int8: |
|
| 699 |
+ xf = float64(xn) |
|
| 700 |
+ case int16: |
|
| 701 |
+ xf = float64(xn) |
|
| 702 |
+ case int32: |
|
| 703 |
+ xf = float64(xn) |
|
| 704 |
+ case int64: |
|
| 705 |
+ xf = float64(xn) |
|
| 706 |
+ case float32: |
|
| 707 |
+ xf = float64(xn) |
|
| 708 |
+ case float64: |
|
| 709 |
+ xf = float64(xn) |
|
| 710 |
+ default: |
|
| 711 |
+ xok = false |
|
| 712 |
+ } |
|
| 713 |
+ |
|
| 714 |
+ return xf, xok |
|
| 715 |
+} |
|
| 716 |
+ |
|
| 717 |
+// InDelta asserts that the two numerals are within delta of each other. |
|
| 718 |
+// |
|
| 719 |
+// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) |
|
| 720 |
+// |
|
| 721 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 722 |
+func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
|
| 723 |
+ |
|
| 724 |
+ af, aok := toFloat(expected) |
|
| 725 |
+ bf, bok := toFloat(actual) |
|
| 726 |
+ |
|
| 727 |
+ if !aok || !bok {
|
|
| 728 |
+ return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...)
|
|
| 729 |
+ } |
|
| 730 |
+ |
|
| 731 |
+ if math.IsNaN(af) {
|
|
| 732 |
+ return Fail(t, fmt.Sprintf("Actual must not be NaN"), msgAndArgs...)
|
|
| 733 |
+ } |
|
| 734 |
+ |
|
| 735 |
+ if math.IsNaN(bf) {
|
|
| 736 |
+ return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...)
|
|
| 737 |
+ } |
|
| 738 |
+ |
|
| 739 |
+ dt := af - bf |
|
| 740 |
+ if dt < -delta || dt > delta {
|
|
| 741 |
+ return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...)
|
|
| 742 |
+ } |
|
| 743 |
+ |
|
| 744 |
+ return true |
|
| 745 |
+} |
|
| 746 |
+ |
|
| 747 |
+// InDeltaSlice is the same as InDelta, except it compares two slices. |
|
| 748 |
+func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
|
| 749 |
+ if expected == nil || actual == nil || |
|
| 750 |
+ reflect.TypeOf(actual).Kind() != reflect.Slice || |
|
| 751 |
+ reflect.TypeOf(expected).Kind() != reflect.Slice {
|
|
| 752 |
+ return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...)
|
|
| 753 |
+ } |
|
| 754 |
+ |
|
| 755 |
+ actualSlice := reflect.ValueOf(actual) |
|
| 756 |
+ expectedSlice := reflect.ValueOf(expected) |
|
| 757 |
+ |
|
| 758 |
+ for i := 0; i < actualSlice.Len(); i++ {
|
|
| 759 |
+ result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta) |
|
| 760 |
+ if !result {
|
|
| 761 |
+ return result |
|
| 762 |
+ } |
|
| 763 |
+ } |
|
| 764 |
+ |
|
| 765 |
+ return true |
|
| 766 |
+} |
|
| 767 |
+ |
|
| 768 |
+func calcRelativeError(expected, actual interface{}) (float64, error) {
|
|
| 769 |
+ af, aok := toFloat(expected) |
|
| 770 |
+ if !aok {
|
|
| 771 |
+ return 0, fmt.Errorf("expected value %q cannot be converted to float", expected)
|
|
| 772 |
+ } |
|
| 773 |
+ if af == 0 {
|
|
| 774 |
+ return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error")
|
|
| 775 |
+ } |
|
| 776 |
+ bf, bok := toFloat(actual) |
|
| 777 |
+ if !bok {
|
|
| 778 |
+ return 0, fmt.Errorf("expected value %q cannot be converted to float", actual)
|
|
| 779 |
+ } |
|
| 780 |
+ |
|
| 781 |
+ return math.Abs(af-bf) / math.Abs(af), nil |
|
| 782 |
+} |
|
| 783 |
+ |
|
| 784 |
+// InEpsilon asserts that expected and actual have a relative error less than epsilon |
|
| 785 |
+// |
|
| 786 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 787 |
+func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
|
| 788 |
+ actualEpsilon, err := calcRelativeError(expected, actual) |
|
| 789 |
+ if err != nil {
|
|
| 790 |
+ return Fail(t, err.Error(), msgAndArgs...) |
|
| 791 |
+ } |
|
| 792 |
+ if actualEpsilon > epsilon {
|
|
| 793 |
+ return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+
|
|
| 794 |
+ " < %#v (actual)", actualEpsilon, epsilon), msgAndArgs...) |
|
| 795 |
+ } |
|
| 796 |
+ |
|
| 797 |
+ return true |
|
| 798 |
+} |
|
| 799 |
+ |
|
| 800 |
+// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. |
|
| 801 |
+func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
|
| 802 |
+ if expected == nil || actual == nil || |
|
| 803 |
+ reflect.TypeOf(actual).Kind() != reflect.Slice || |
|
| 804 |
+ reflect.TypeOf(expected).Kind() != reflect.Slice {
|
|
| 805 |
+ return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...)
|
|
| 806 |
+ } |
|
| 807 |
+ |
|
| 808 |
+ actualSlice := reflect.ValueOf(actual) |
|
| 809 |
+ expectedSlice := reflect.ValueOf(expected) |
|
| 810 |
+ |
|
| 811 |
+ for i := 0; i < actualSlice.Len(); i++ {
|
|
| 812 |
+ result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) |
|
| 813 |
+ if !result {
|
|
| 814 |
+ return result |
|
| 815 |
+ } |
|
| 816 |
+ } |
|
| 817 |
+ |
|
| 818 |
+ return true |
|
| 819 |
+} |
|
| 820 |
+ |
|
| 821 |
+/* |
|
| 822 |
+ Errors |
|
| 823 |
+*/ |
|
| 824 |
+ |
|
| 825 |
+// NoError asserts that a function returned no error (i.e. `nil`). |
|
| 826 |
+// |
|
| 827 |
+// actualObj, err := SomeFunction() |
|
| 828 |
+// if assert.NoError(t, err) {
|
|
| 829 |
+// assert.Equal(t, actualObj, expectedObj) |
|
| 830 |
+// } |
|
| 831 |
+// |
|
| 832 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 833 |
+func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
|
| 834 |
+ if isNil(err) {
|
|
| 835 |
+ return true |
|
| 836 |
+ } |
|
| 837 |
+ |
|
| 838 |
+ return Fail(t, fmt.Sprintf("Received unexpected error %q", err), msgAndArgs...)
|
|
| 839 |
+} |
|
| 840 |
+ |
|
| 841 |
+// Error asserts that a function returned an error (i.e. not `nil`). |
|
| 842 |
+// |
|
| 843 |
+// actualObj, err := SomeFunction() |
|
| 844 |
+// if assert.Error(t, err, "An error was expected") {
|
|
| 845 |
+// assert.Equal(t, err, expectedError) |
|
| 846 |
+// } |
|
| 847 |
+// |
|
| 848 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 849 |
+func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
|
| 850 |
+ |
|
| 851 |
+ message := messageFromMsgAndArgs(msgAndArgs...) |
|
| 852 |
+ return NotNil(t, err, "An error is expected but got nil. %s", message) |
|
| 853 |
+ |
|
| 854 |
+} |
|
| 855 |
+ |
|
| 856 |
+// EqualError asserts that a function returned an error (i.e. not `nil`) |
|
| 857 |
+// and that it is equal to the provided error. |
|
| 858 |
+// |
|
| 859 |
+// actualObj, err := SomeFunction() |
|
| 860 |
+// if assert.Error(t, err, "An error was expected") {
|
|
| 861 |
+// assert.Equal(t, err, expectedError) |
|
| 862 |
+// } |
|
| 863 |
+// |
|
| 864 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 865 |
+func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
|
|
| 866 |
+ |
|
| 867 |
+ message := messageFromMsgAndArgs(msgAndArgs...) |
|
| 868 |
+ if !NotNil(t, theError, "An error is expected but got nil. %s", message) {
|
|
| 869 |
+ return false |
|
| 870 |
+ } |
|
| 871 |
+ s := "An error with value \"%s\" is expected but got \"%s\". %s" |
|
| 872 |
+ return Equal(t, errString, theError.Error(), |
|
| 873 |
+ s, errString, theError.Error(), message) |
|
| 874 |
+} |
|
| 875 |
+ |
|
| 876 |
+// matchRegexp return true if a specified regexp matches a string. |
|
| 877 |
+func matchRegexp(rx interface{}, str interface{}) bool {
|
|
| 878 |
+ |
|
| 879 |
+ var r *regexp.Regexp |
|
| 880 |
+ if rr, ok := rx.(*regexp.Regexp); ok {
|
|
| 881 |
+ r = rr |
|
| 882 |
+ } else {
|
|
| 883 |
+ r = regexp.MustCompile(fmt.Sprint(rx)) |
|
| 884 |
+ } |
|
| 885 |
+ |
|
| 886 |
+ return (r.FindStringIndex(fmt.Sprint(str)) != nil) |
|
| 887 |
+ |
|
| 888 |
+} |
|
| 889 |
+ |
|
| 890 |
+// Regexp asserts that a specified regexp matches a string. |
|
| 891 |
+// |
|
| 892 |
+// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
|
|
| 893 |
+// assert.Regexp(t, "start...$", "it's not starting") |
|
| 894 |
+// |
|
| 895 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 896 |
+func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
|
| 897 |
+ |
|
| 898 |
+ match := matchRegexp(rx, str) |
|
| 899 |
+ |
|
| 900 |
+ if !match {
|
|
| 901 |
+ Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...)
|
|
| 902 |
+ } |
|
| 903 |
+ |
|
| 904 |
+ return match |
|
| 905 |
+} |
|
| 906 |
+ |
|
| 907 |
+// NotRegexp asserts that a specified regexp does not match a string. |
|
| 908 |
+// |
|
| 909 |
+// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
|
|
| 910 |
+// assert.NotRegexp(t, "^start", "it's not starting") |
|
| 911 |
+// |
|
| 912 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 913 |
+func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
|
| 914 |
+ match := matchRegexp(rx, str) |
|
| 915 |
+ |
|
| 916 |
+ if match {
|
|
| 917 |
+ Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...)
|
|
| 918 |
+ } |
|
| 919 |
+ |
|
| 920 |
+ return !match |
|
| 921 |
+ |
|
| 922 |
+} |
|
| 923 |
+ |
|
| 924 |
+// Zero asserts that i is the zero value for its type and returns the truth. |
|
| 925 |
+func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
|
|
| 926 |
+ if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
|
|
| 927 |
+ return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...)
|
|
| 928 |
+ } |
|
| 929 |
+ return true |
|
| 930 |
+} |
|
| 931 |
+ |
|
| 932 |
+// NotZero asserts that i is not the zero value for its type and returns the truth. |
|
| 933 |
+func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
|
|
| 934 |
+ if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
|
|
| 935 |
+ return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...)
|
|
| 936 |
+ } |
|
| 937 |
+ return true |
|
| 938 |
+} |
|
| 939 |
+ |
|
| 940 |
+// JSONEq asserts that two JSON strings are equivalent. |
|
| 941 |
+// |
|
| 942 |
+// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
|
| 943 |
+// |
|
| 944 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 945 |
+func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
|
|
| 946 |
+ var expectedJSONAsInterface, actualJSONAsInterface interface{}
|
|
| 947 |
+ |
|
| 948 |
+ if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil {
|
|
| 949 |
+ return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...)
|
|
| 950 |
+ } |
|
| 951 |
+ |
|
| 952 |
+ if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil {
|
|
| 953 |
+ return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...)
|
|
| 954 |
+ } |
|
| 955 |
+ |
|
| 956 |
+ return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) |
|
| 957 |
+} |
|
| 958 |
+ |
|
| 959 |
+func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {
|
|
| 960 |
+ t := reflect.TypeOf(v) |
|
| 961 |
+ k := t.Kind() |
|
| 962 |
+ |
|
| 963 |
+ if k == reflect.Ptr {
|
|
| 964 |
+ t = t.Elem() |
|
| 965 |
+ k = t.Kind() |
|
| 966 |
+ } |
|
| 967 |
+ return t, k |
|
| 968 |
+} |
|
| 969 |
+ |
|
| 970 |
+// diff returns a diff of both values as long as both are of the same type and |
|
| 971 |
+// are a struct, map, slice or array. Otherwise it returns an empty string. |
|
| 972 |
+func diff(expected interface{}, actual interface{}) string {
|
|
| 973 |
+ if expected == nil || actual == nil {
|
|
| 974 |
+ return "" |
|
| 975 |
+ } |
|
| 976 |
+ |
|
| 977 |
+ et, ek := typeAndKind(expected) |
|
| 978 |
+ at, _ := typeAndKind(actual) |
|
| 979 |
+ |
|
| 980 |
+ if et != at {
|
|
| 981 |
+ return "" |
|
| 982 |
+ } |
|
| 983 |
+ |
|
| 984 |
+ if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array {
|
|
| 985 |
+ return "" |
|
| 986 |
+ } |
|
| 987 |
+ |
|
| 988 |
+ spew.Config.SortKeys = true |
|
| 989 |
+ e := spew.Sdump(expected) |
|
| 990 |
+ a := spew.Sdump(actual) |
|
| 991 |
+ |
|
| 992 |
+ diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
|
|
| 993 |
+ A: difflib.SplitLines(e), |
|
| 994 |
+ B: difflib.SplitLines(a), |
|
| 995 |
+ FromFile: "Expected", |
|
| 996 |
+ FromDate: "", |
|
| 997 |
+ ToFile: "Actual", |
|
| 998 |
+ ToDate: "", |
|
| 999 |
+ Context: 1, |
|
| 1000 |
+ }) |
|
| 1001 |
+ |
|
| 1002 |
+ return "\n\nDiff:\n" + diff |
|
| 1003 |
+} |
| 0 | 1004 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,45 @@ |
| 0 |
+// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. |
|
| 1 |
+// |
|
| 2 |
+// Example Usage |
|
| 3 |
+// |
|
| 4 |
+// The following is a complete example using assert in a standard test function: |
|
| 5 |
+// import ( |
|
| 6 |
+// "testing" |
|
| 7 |
+// "github.com/stretchr/testify/assert" |
|
| 8 |
+// ) |
|
| 9 |
+// |
|
| 10 |
+// func TestSomething(t *testing.T) {
|
|
| 11 |
+// |
|
| 12 |
+// var a string = "Hello" |
|
| 13 |
+// var b string = "Hello" |
|
| 14 |
+// |
|
| 15 |
+// assert.Equal(t, a, b, "The two words should be the same.") |
|
| 16 |
+// |
|
| 17 |
+// } |
|
| 18 |
+// |
|
| 19 |
+// if you assert many times, use the format below: |
|
| 20 |
+// |
|
| 21 |
+// import ( |
|
| 22 |
+// "testing" |
|
| 23 |
+// "github.com/stretchr/testify/assert" |
|
| 24 |
+// ) |
|
| 25 |
+// |
|
| 26 |
+// func TestSomething(t *testing.T) {
|
|
| 27 |
+// assert := assert.New(t) |
|
| 28 |
+// |
|
| 29 |
+// var a string = "Hello" |
|
| 30 |
+// var b string = "Hello" |
|
| 31 |
+// |
|
| 32 |
+// assert.Equal(a, b, "The two words should be the same.") |
|
| 33 |
+// } |
|
| 34 |
+// |
|
| 35 |
+// Assertions |
|
| 36 |
+// |
|
| 37 |
+// Assertions allow you to easily write test code, and are global funcs in the `assert` package. |
|
| 38 |
+// All assertion functions take, as the first argument, the `*testing.T` object provided by the |
|
| 39 |
+// testing framework. This allows the assertion funcs to write the failings and other details to |
|
| 40 |
+// the correct place. |
|
| 41 |
+// |
|
| 42 |
+// Every assertion function also takes an optional string message as the final argument, |
|
| 43 |
+// allowing custom error messages to be appended to the message the assertion method outputs. |
|
| 44 |
+package assert |
| 0 | 45 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,10 @@ |
| 0 |
+package assert |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+// AnError is an error instance useful for testing. If the code does not care |
|
| 7 |
+// about error specifics, and only needs to return the error for example, this |
|
| 8 |
+// error should be used to make the test code more readable. |
|
| 9 |
+var AnError = errors.New("assert.AnError general error for testing")
|
| 0 | 10 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,16 @@ |
| 0 |
+package assert |
|
| 1 |
+ |
|
| 2 |
+// Assertions provides assertion methods around the |
|
| 3 |
+// TestingT interface. |
|
| 4 |
+type Assertions struct {
|
|
| 5 |
+ t TestingT |
|
| 6 |
+} |
|
| 7 |
+ |
|
| 8 |
+// New makes a new Assertions object for the specified TestingT. |
|
| 9 |
+func New(t TestingT) *Assertions {
|
|
| 10 |
+ return &Assertions{
|
|
| 11 |
+ t: t, |
|
| 12 |
+ } |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl |
| 0 | 16 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,106 @@ |
| 0 |
+package assert |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "net/http" |
|
| 5 |
+ "net/http/httptest" |
|
| 6 |
+ "net/url" |
|
| 7 |
+ "strings" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// httpCode is a helper that returns HTTP code of the response. It returns -1 |
|
| 11 |
+// if building a new request fails. |
|
| 12 |
+func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int {
|
|
| 13 |
+ w := httptest.NewRecorder() |
|
| 14 |
+ req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) |
|
| 15 |
+ if err != nil {
|
|
| 16 |
+ return -1 |
|
| 17 |
+ } |
|
| 18 |
+ handler(w, req) |
|
| 19 |
+ return w.Code |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+// HTTPSuccess asserts that a specified handler returns a success status code. |
|
| 23 |
+// |
|
| 24 |
+// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) |
|
| 25 |
+// |
|
| 26 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 27 |
+func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
|
|
| 28 |
+ code := httpCode(handler, method, url, values) |
|
| 29 |
+ if code == -1 {
|
|
| 30 |
+ return false |
|
| 31 |
+ } |
|
| 32 |
+ return code >= http.StatusOK && code <= http.StatusPartialContent |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+// HTTPRedirect asserts that a specified handler returns a redirect status code. |
|
| 36 |
+// |
|
| 37 |
+// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
|
| 38 |
+// |
|
| 39 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 40 |
+func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
|
|
| 41 |
+ code := httpCode(handler, method, url, values) |
|
| 42 |
+ if code == -1 {
|
|
| 43 |
+ return false |
|
| 44 |
+ } |
|
| 45 |
+ return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+// HTTPError asserts that a specified handler returns an error status code. |
|
| 49 |
+// |
|
| 50 |
+// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
|
| 51 |
+// |
|
| 52 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 53 |
+func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
|
|
| 54 |
+ code := httpCode(handler, method, url, values) |
|
| 55 |
+ if code == -1 {
|
|
| 56 |
+ return false |
|
| 57 |
+ } |
|
| 58 |
+ return code >= http.StatusBadRequest |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+// HTTPBody is a helper that returns HTTP body of the response. It returns |
|
| 62 |
+// empty string if building a new request fails. |
|
| 63 |
+func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {
|
|
| 64 |
+ w := httptest.NewRecorder() |
|
| 65 |
+ req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) |
|
| 66 |
+ if err != nil {
|
|
| 67 |
+ return "" |
|
| 68 |
+ } |
|
| 69 |
+ handler(w, req) |
|
| 70 |
+ return w.Body.String() |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+// HTTPBodyContains asserts that a specified handler returns a |
|
| 74 |
+// body that contains a string. |
|
| 75 |
+// |
|
| 76 |
+// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") |
|
| 77 |
+// |
|
| 78 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 79 |
+func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
|
|
| 80 |
+ body := HTTPBody(handler, method, url, values) |
|
| 81 |
+ |
|
| 82 |
+ contains := strings.Contains(body, fmt.Sprint(str)) |
|
| 83 |
+ if !contains {
|
|
| 84 |
+ Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
|
|
| 85 |
+ } |
|
| 86 |
+ |
|
| 87 |
+ return contains |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+// HTTPBodyNotContains asserts that a specified handler returns a |
|
| 91 |
+// body that does not contain a string. |
|
| 92 |
+// |
|
| 93 |
+// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") |
|
| 94 |
+// |
|
| 95 |
+// Returns whether the assertion was successful (true) or not (false). |
|
| 96 |
+func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
|
|
| 97 |
+ body := HTTPBody(handler, method, url, values) |
|
| 98 |
+ |
|
| 99 |
+ contains := strings.Contains(body, fmt.Sprint(str)) |
|
| 100 |
+ if contains {
|
|
| 101 |
+ Fail(t, "Expected response body for %s to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body) |
|
| 102 |
+ } |
|
| 103 |
+ |
|
| 104 |
+ return !contains |
|
| 105 |
+} |