From db95b7660305e395fd4966c69deed974ac6d0301 Mon Sep 17 00:00:00 2001 From: Mikhail Shabunin Date: Thu, 5 Oct 2023 17:54:48 +0400 Subject: [PATCH] Add the option to colorize diffs produced by assert.Equal --- assert/assertions.go | 110 ++++++++++++++++++---- assert/assertions_test.go | 190 +++++++++++++++++++++++++++++++------- go.mod | 1 + go.sum | 13 ++- 4 files changed, 264 insertions(+), 50 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 719164e7e..84a7dfe0f 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -6,19 +6,20 @@ import ( "encoding/json" "errors" "fmt" + "github.com/sergi/go-diff/diffmatchpatch" "math" "os" "reflect" "regexp" "runtime" "runtime/debug" + "strconv" "strings" "time" "unicode" "unicode/utf8" "github.com/davecgh/go-spew/spew" - "github.com/pmezard/go-difflib/difflib" "gopkg.in/yaml.v3" ) @@ -410,9 +411,9 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", expected, actual, err), msgAndArgs...) } - + coloredOutput, _ := strconv.Atoi(os.Getenv("TESTIFY_COLORED_DIFF")) if !ObjectsAreEqual(expected, actual) { - diff := diff(expected, actual) + diff := diff(expected, actual, diffOptions{ColoredOutput: coloredOutput == 1}) expected, actual = formatUnequalValues(expected, actual) return Fail(t, fmt.Sprintf("Not equal: \n"+ "expected: %s\n"+ @@ -533,7 +534,7 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa } if !ObjectsAreEqualValues(expected, actual) { - diff := diff(expected, actual) + diff := diff(expected, actual, diffOptions{}) expected, actual = formatUnequalValues(expected, actual) return Fail(t, fmt.Sprintf("Not equal: \n"+ "expected: %s\n"+ @@ -578,7 +579,7 @@ func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs .. actual = copyExportedFields(actual) if !ObjectsAreEqualValues(expected, actual) { - diff := diff(expected, actual) + diff := diff(expected, actual, diffOptions{}) expected, actual = formatUnequalValues(expected, actual) return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+ "expected: %s\n"+ @@ -1751,9 +1752,13 @@ func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { return t, k } +type diffOptions struct { + ColoredOutput bool +} + // diff returns a diff of both values as long as both are of the same type and // are a struct, map, slice, array or string. Otherwise it returns an empty string. -func diff(expected interface{}, actual interface{}) string { +func diff(expected interface{}, actual interface{}, options diffOptions) string { if expected == nil || actual == nil { return "" } @@ -1782,18 +1787,89 @@ func diff(expected interface{}, actual interface{}) string { e = spewConfig.Sdump(expected) a = spewConfig.Sdump(actual) } + structuredDiff := structuredDiff(e, a) + prettyDiff := prettyDiff(structuredDiff, options.ColoredOutput) + return "\n\nDiff:\n" + prettyDiff +} + +func structuredDiff(e string, a string) []diffmatchpatch.Diff { + dmp := diffmatchpatch.New() + fromRunes, toRunes, runesToLines := dmp.DiffLinesToRunes(e, a) + diffs := dmp.DiffMainRunes(fromRunes, toRunes, false) + hydrated := make([]diffmatchpatch.Diff, 0, len(diffs)) + for _, aDiff := range diffs { + chars := strings.FieldsFunc(aDiff.Text, func(r rune) bool { + return string(r) == diffmatchpatch.IndexSeparator + }) + text := make([]string, len(chars)) + + for i, char := range chars { + i1, err := strconv.Atoi(char) + if err == nil { + text[i] = runesToLines[i1] + } + } + for idx, line := range text { + if aDiff.Type == diffmatchpatch.DiffEqual && idx < len(text)-1 { + continue + } + hydrated = append(hydrated, diffmatchpatch.Diff{ + Type: aDiff.Type, + Text: line, + }) + } + } + return hydrated +} - diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ - A: difflib.SplitLines(e), - B: difflib.SplitLines(a), - FromFile: "Expected", - FromDate: "", - ToFile: "Actual", - ToDate: "", - Context: 1, - }) - - return "\n\nDiff:\n" + diff +func prettyDiff(diffs []diffmatchpatch.Diff, useColoredOutput bool) string { + var diff strings.Builder + if useColoredOutput { + diff.WriteString("\\033[31m--- Expected\\033[0m\n\\033[32m+++ Actual\\033[0m\n") + } else { + diff.WriteString("--- Expected\n+++ Actual\n") + } + for _, diffChunk := range diffs { + switch diffChunk.Type { + case diffmatchpatch.DiffInsert: + // Make sure the different parts are on separate lines for better readability + // i.e. it makes diffs like +got-expected to go as +got\n-expected\n + if !strings.HasSuffix(diffChunk.Text, "\n") { + diffChunk.Text = diffChunk.Text + "\n" + } + if useColoredOutput { + _, _ = fmt.Fprintf(&diff, "\\033[32m+%s\\033[0m", diffChunk.Text) + } else { + _, _ = fmt.Fprintf(&diff, "+%s", diffChunk.Text) + } + case diffmatchpatch.DiffDelete: + // Make sure the different parts are on separate lines for better readability + // i.e. it makes diffs like +got-expected to go as +got\n-expected\n + if !strings.HasSuffix(diffChunk.Text, "\n") { + diffChunk.Text = diffChunk.Text + "\n" + } + if useColoredOutput { + _, _ = fmt.Fprintf(&diff, "\\033[31m-%s\\033[0m", diffChunk.Text) + } else { + _, _ = fmt.Fprintf(&diff, "-%s", diffChunk.Text) + } + default: + if len(diffChunk.Text) == 0 { + continue + } + equalTextByLines := strings.SplitAfter(diffChunk.Text, "\n") + var linesTrimmed []string + for _, line := range equalTextByLines { + if len(line) == 0 { + continue + } + linesTrimmed = append(linesTrimmed, line) + } + // We're not interested in the equal parts, so only keep the last line for some context + _, _ = fmt.Fprintf(&diff, " %s", equalTextByLines[len(linesTrimmed)-1]) + } + } + return diff.String() } func isFunction(arg interface{}) bool { diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 40e372015..a8c90a388 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -362,11 +362,9 @@ func TestEqualExportedValues(t *testing.T) { Diff: --- Expected +++ Actual - @@ -3,3 +3,3 @@ Exported2: (assert.Nested) { - Exported: (int) 2, - + Exported: (int) 1, - notExported: (interface {}) `, + + Exported: (int) 1`, }, { value1: S3{&Nested{1, 2}, &Nested{3, 4}}, @@ -376,11 +374,9 @@ func TestEqualExportedValues(t *testing.T) { Diff: --- Expected +++ Actual - @@ -2,3 +2,3 @@ Exported1: (*assert.Nested)({ - Exported: (int) 1, - + Exported: (string) (len=1) "a", - notExported: (interface {}) `, + + Exported: (string) (len=1) "a"`, }, { value1: S4{[]*Nested{ @@ -396,11 +392,9 @@ func TestEqualExportedValues(t *testing.T) { Diff: --- Expected +++ Actual - @@ -7,3 +7,3 @@ (*assert.Nested)({ - Exported: (int) 3, - + Exported: (int) 2, - notExported: (interface {}) `, + + Exported: (int) 2`, }, } @@ -628,7 +622,7 @@ func TestStringEqual(t *testing.T) { msgAndArgs []interface{} want string }{ - {equalWant: "hi, \nmy name is", equalGot: "what,\nmy name is", want: "\tassertions.go:\\d+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"hi, \\\\nmy name is\"\n\\s+actual\\s+: \"what,\\\\nmy name is\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1,2 \\+1,2 @@\n\\s+-hi, \n\\s+\\+what,\n\\s+my name is"}, + {equalWant: "hi, \nmy name is", equalGot: "what,\nmy name is", want: "\tassertions.go:\\d+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"hi, \\\\nmy name is\"\n\\s+actual\\s+: \"what,\\\\nmy name is\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+-hi, \n\\s+\\+what,\n\\s+my name is"}, } { mockT := &bufferT{} Equal(mockT, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...) @@ -643,10 +637,10 @@ func TestEqualFormatting(t *testing.T) { msgAndArgs []interface{} want string }{ - {equalWant: "want", equalGot: "got", want: "\tassertions.go:\\d+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n"}, - {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{"hello, %v!", "world"}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+hello, world!\n"}, - {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{123}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+123\n"}, - {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{struct{ a string }{"hello"}}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+@@ -1 \\+1 @@\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+{a:hello}\n"}, + {equalWant: "want", equalGot: "got", want: "\tassertions.go:\\d+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+-want\n\\s+\\+got\n"}, + {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{"hello, %v!", "world"}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+hello, world!\n"}, + {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{123}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+123\n"}, + {equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{struct{ a string }{"hello"}}, want: "\tassertions.go:[0-9]+: \n\t+Error Trace:\t\n\t+Error:\\s+Not equal:\\s+\n\\s+expected: \"want\"\n\\s+actual\\s+: \"got\"\n\\s+Diff:\n\\s+-+ Expected\n\\s+\\++ Actual\n\\s+-want\n\\s+\\+got\n\\s+Messages:\\s+{a:hello}\n"}, } { mockT := &bufferT{} Equal(mockT, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...) @@ -1850,7 +1844,7 @@ func TestInDeltaMapValues(t *testing.T) { f: False, }, } { - tc.f(t, InDeltaMapValues(mockT, tc.expect, tc.actual, tc.delta), tc.title+"\n"+diff(tc.expect, tc.actual)) + tc.f(t, InDeltaMapValues(mockT, tc.expect, tc.actual, tc.delta), tc.title+"\n"+diff(tc.expect, tc.actual, diffOptions{})) } } @@ -2289,7 +2283,6 @@ func TestDiff(t *testing.T) { Diff: --- Expected +++ Actual -@@ -1,3 +1,3 @@ (struct { foo string }) { - foo: (string) (len=5) "hello" + foo: (string) (len=3) "bar" @@ -2298,6 +2291,7 @@ Diff: actual := diff( struct{ foo string }{"hello"}, struct{ foo string }{"bar"}, + diffOptions{}, ) Equal(t, expected, actual) @@ -2306,7 +2300,6 @@ Diff: Diff: --- Expected +++ Actual -@@ -2,5 +2,5 @@ (int) 1, - (int) 2, (int) 3, @@ -2318,6 +2311,7 @@ Diff: actual = diff( []int{1, 2, 3, 4}, []int{1, 3, 5, 7}, + diffOptions{}, ) Equal(t, expected, actual) @@ -2326,17 +2320,17 @@ Diff: Diff: --- Expected +++ Actual -@@ -2,4 +2,4 @@ (int) 1, - (int) 2, -- (int) 3 + (int) 3, +- (int) 3 + (int) 5 } ` actual = diff( []int{1, 2, 3, 4}[0:3], []int{1, 3, 5, 7}[0:3], + diffOptions{}, ) Equal(t, expected, actual) @@ -2345,14 +2339,13 @@ Diff: Diff: --- Expected +++ Actual -@@ -1,6 +1,6 @@ (map[string]int) (len=4) { - (string) (len=4) "four": (int) 4, + (string) (len=4) "five": (int) 5, (string) (len=3) "one": (int) 1, - (string) (len=5) "three": (int) 3, -- (string) (len=3) "two": (int) 2 + (string) (len=5) "seven": (int) 7, +- (string) (len=3) "two": (int) 2 + (string) (len=5) "three": (int) 3 } ` @@ -2360,6 +2353,7 @@ Diff: actual = diff( map[string]int{"one": 1, "two": 2, "three": 3, "four": 4}, map[string]int{"one": 1, "three": 3, "five": 5, "seven": 7}, + diffOptions{}, ) Equal(t, expected, actual) @@ -2368,7 +2362,6 @@ Diff: Diff: --- Expected +++ Actual -@@ -1,3 +1,3 @@ (*errors.errorString)({ - s: (string) (len=19) "some expected error" + s: (string) (len=12) "actual error" @@ -2378,6 +2371,7 @@ Diff: actual = diff( errors.New("some expected error"), errors.New("actual error"), + diffOptions{}, ) Equal(t, expected, actual) @@ -2386,7 +2380,6 @@ Diff: Diff: --- Expected +++ Actual -@@ -2,3 +2,3 @@ A: (string) (len=11) "some string", - B: (int) 10 + B: (int) 15 @@ -2396,6 +2389,7 @@ Diff: actual = diff( diffTestingStruct{A: "some string", B: 10}, diffTestingStruct{A: "some string", B: 15}, + diffOptions{}, ) Equal(t, expected, actual) @@ -2404,15 +2398,147 @@ Diff: Diff: --- Expected +++ Actual -@@ -1,2 +1,2 @@ -(time.Time) 2020-09-24 00:00:00 +0000 UTC +(time.Time) 2020-09-25 00:00:00 +0000 UTC - ` actual = diff( time.Date(2020, 9, 24, 0, 0, 0, 0, time.UTC), time.Date(2020, 9, 25, 0, 0, 0, 0, time.UTC), + diffOptions{}, + ) + Equal(t, expected, actual) +} + +func TestColoredDiff(t *testing.T) { + expected := ` + +Diff: +\033[31m--- Expected\033[0m +\033[32m+++ Actual\033[0m + (struct { foo string }) { +\033[31m- foo: (string) (len=5) "hello" +\033[0m\033[32m+ foo: (string) (len=3) "bar" +\033[0m } +` + actual := diff( + struct{ foo string }{"hello"}, + struct{ foo string }{"bar"}, + diffOptions{ColoredOutput: true}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +\033[31m--- Expected\033[0m +\033[32m+++ Actual\033[0m + (int) 1, +\033[31m- (int) 2, +\033[0m (int) 3, +\033[31m- (int) 4 +\033[0m\033[32m+ (int) 5, +\033[0m\033[32m+ (int) 7 +\033[0m } +` + actual = diff( + []int{1, 2, 3, 4}, + []int{1, 3, 5, 7}, + diffOptions{ColoredOutput: true}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +\033[31m--- Expected\033[0m +\033[32m+++ Actual\033[0m + (int) 1, +\033[31m- (int) 2, +\033[0m\033[32m+ (int) 3, +\033[0m\033[31m- (int) 3 +\033[0m\033[32m+ (int) 5 +\033[0m } +` + actual = diff( + []int{1, 2, 3, 4}[0:3], + []int{1, 3, 5, 7}[0:3], + diffOptions{ColoredOutput: true}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +\033[31m--- Expected\033[0m +\033[32m+++ Actual\033[0m + (map[string]int) (len=4) { +\033[31m- (string) (len=4) "four": (int) 4, +\033[0m\033[32m+ (string) (len=4) "five": (int) 5, +\033[0m (string) (len=3) "one": (int) 1, +\033[31m- (string) (len=5) "three": (int) 3, +\033[0m\033[32m+ (string) (len=5) "seven": (int) 7, +\033[0m\033[31m- (string) (len=3) "two": (int) 2 +\033[0m\033[32m+ (string) (len=5) "three": (int) 3 +\033[0m } +` + + actual = diff( + map[string]int{"one": 1, "two": 2, "three": 3, "four": 4}, + map[string]int{"one": 1, "three": 3, "five": 5, "seven": 7}, + diffOptions{ColoredOutput: true}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +\033[31m--- Expected\033[0m +\033[32m+++ Actual\033[0m + (*errors.errorString)({ +\033[31m- s: (string) (len=19) "some expected error" +\033[0m\033[32m+ s: (string) (len=12) "actual error" +\033[0m }) +` + + actual = diff( + errors.New("some expected error"), + errors.New("actual error"), + diffOptions{ColoredOutput: true}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +\033[31m--- Expected\033[0m +\033[32m+++ Actual\033[0m + A: (string) (len=11) "some string", +\033[31m- B: (int) 10 +\033[0m\033[32m+ B: (int) 15 +\033[0m } +` + + actual = diff( + diffTestingStruct{A: "some string", B: 10}, + diffTestingStruct{A: "some string", B: 15}, + diffOptions{ColoredOutput: true}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +\033[31m--- Expected\033[0m +\033[32m+++ Actual\033[0m +\033[31m-(time.Time) 2020-09-24 00:00:00 +0000 UTC +\033[0m\033[32m+(time.Time) 2020-09-25 00:00:00 +0000 UTC +\033[0m` + + actual = diff( + time.Date(2020, 9, 24, 0, 0, 0, 0, time.UTC), + time.Date(2020, 9, 25, 0, 0, 0, 0, time.UTC), + diffOptions{ColoredOutput: true}, ) Equal(t, expected, actual) } @@ -2427,12 +2553,12 @@ func TestTimeEqualityErrorFormatting(t *testing.T) { } func TestDiffEmptyCases(t *testing.T) { - Equal(t, "", diff(nil, nil)) - Equal(t, "", diff(struct{ foo string }{}, nil)) - Equal(t, "", diff(nil, struct{ foo string }{})) - Equal(t, "", diff(1, 2)) - Equal(t, "", diff(1, 2)) - Equal(t, "", diff([]int{1}, []bool{true})) + Equal(t, "", diff(nil, nil, diffOptions{})) + Equal(t, "", diff(struct{ foo string }{}, nil, diffOptions{})) + Equal(t, "", diff(nil, struct{ foo string }{}, diffOptions{})) + Equal(t, "", diff(1, 2, diffOptions{})) + Equal(t, "", diff(1, 2, diffOptions{})) + Equal(t, "", diff([]int{1}, []bool{true}, diffOptions{})) } // Ensure there are no data races @@ -2458,7 +2584,7 @@ func TestDiffRace(t *testing.T) { rChans[idx] = make(chan string) go func(ch chan string) { defer close(ch) - ch <- diff(expected, actual) + ch <- diff(expected, actual, diffOptions{}) }(rChans[idx]) } diff --git a/go.mod b/go.mod index d3c4d723b..f45ee1dcf 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ go 1.17 require ( github.com/davecgh/go-spew v1.1.1 github.com/pmezard/go-difflib v1.0.0 + github.com/sergi/go-diff v1.3.1 github.com/stretchr/objx v0.5.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 4f3ced6f3..9d6b203fd 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,27 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=