pax_global_header00006660000000000000000000000064141316076750014524gustar00rootroot0000000000000052 comment=3026925edefd8a1125321a9b7d4bce573496b35d deep-1.0.8/000077500000000000000000000000001413160767500124475ustar00rootroot00000000000000deep-1.0.8/.github/000077500000000000000000000000001413160767500140075ustar00rootroot00000000000000deep-1.0.8/.github/workflows/000077500000000000000000000000001413160767500160445ustar00rootroot00000000000000deep-1.0.8/.github/workflows/codeql-analysis.yml000066400000000000000000000046141413160767500216640ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '35 10 * * 2' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'go' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 deep-1.0.8/.gitignore000066400000000000000000000000141413160767500144320ustar00rootroot00000000000000*.swp *.out deep-1.0.8/.travis.yml000066400000000000000000000003531413160767500145610ustar00rootroot00000000000000language: go go: - "1.15" - "1.16" - "1.17" before_install: - go get github.com/mattn/goveralls - go get golang.org/x/tools/cover script: - $HOME/gopath/bin/goveralls -service=travis-pro -package github.com/go-test/deep deep-1.0.8/CHANGES.md000066400000000000000000000031641413160767500140450ustar00rootroot00000000000000# go-test/deep Changelog ## v1.0.8 released 2021-10-13 * Updated matrix to go1.15, go1.16, and go1.17 * Added SECURITY.md and GitHub code analysis ## v1.0.7 released 2020-07-11 * Fixed issue #39: Confusing diff when comparing distinct types with the same name (PR #44) ## v1.0.6 released 2020-04-21 * Added `NilMapsAreEmpty` variable which causes a nil map to equal an empty map (PR #43) (@yalegko) ## v1.0.5 released 2020-01-16 * Added `NilSlicesAreEmpty` variable which causes a nil slice to be equal to an empty slice (PR #27) (@Anaminus) ## v1.0.4 released 2019-09-15 * Added \`deep:"-"\` structure field tag to ignore field (PR #38) (@flga) ## v1.0.3 released 2019-08-18 * Fixed issue #31: panic on typed primitives that implement error interface ## v1.0.2 released 2019-07-14 * Enabled Go module (@radeksimko) * Changed supported and tested Go versions: 1.10, 1.11, and 1.12 (dropped 1.9) * Changed Error equality: additional struct fields are compared too (PR #29) (@andrewmostello) * Fixed typos and ineffassign issues (PR #25) (@tariq1890) * Fixed diff order for nil comparison (PR #16) (@gmarik) * Fixed slice equality when slices are extracted from the same array (PR #11) (@risteli) * Fixed test spelling and messages (PR #19) (@sofuture) * Fixed issue #15: panic on comparing struct with anonymous time.Time * Fixed issue #18: Panic when comparing structs with time.Time value and CompareUnexportedFields is true * Fixed issue #21: Set default MaxDepth = 0 (disabled) (PR #23) ## v1.0.1 released 2018-01-28 * Fixed issue #12: Arrays are not properly compared (@samlitowitz) ## v1.0.0 releaesd 2017-10-27 * First release deep-1.0.8/LICENSE000066400000000000000000000020601413160767500134520ustar00rootroot00000000000000MIT License Copyright 2015-2017 Daniel Nichter Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. deep-1.0.8/README.md000066400000000000000000000033701413160767500137310ustar00rootroot00000000000000# Deep Variable Equality for Humans [![Go Report Card](https://goreportcard.com/badge/github.com/go-test/deep)](https://goreportcard.com/report/github.com/go-test/deep) [![Build Status](https://app.travis-ci.com/go-test/deep.svg?branch=master)](https://app.travis-ci.com/go-test/deep) [![Coverage Status](https://coveralls.io/repos/github/go-test/deep/badge.svg?branch=master)](https://coveralls.io/github/go-test/deep?branch=master) [![GoDoc](https://godoc.org/github.com/go-test/deep?status.svg)](https://pkg.go.dev/github.com/go-test/deep) This package provides a single function: `deep.Equal`. It's like [reflect.DeepEqual](http://golang.org/pkg/reflect/#DeepEqual) but much friendlier to humans (or any sentient being) for two reason: * `deep.Equal` returns a list of differences * `deep.Equal` does not compare unexported fields (by default) `reflect.DeepEqual` is good (like all things Golang!), but it's a game of [Hunt the Wumpus](https://en.wikipedia.org/wiki/Hunt_the_Wumpus). For large maps, slices, and structs, finding the difference is difficult. `deep.Equal` doesn't play games with you, it lists the differences: ```go package main_test import ( "testing" "github.com/go-test/deep" ) type T struct { Name string Numbers []float64 } func TestDeepEqual(t *testing.T) { // Can you spot the difference? t1 := T{ Name: "Isabella", Numbers: []float64{1.13459, 2.29343, 3.010100010}, } t2 := T{ Name: "Isabella", Numbers: []float64{1.13459, 2.29843, 3.010100010}, } if diff := deep.Equal(t1, t2); diff != nil { t.Error(diff) } } ``` ``` $ go test --- FAIL: TestDeepEqual (0.00s) main_test.go:25: [Numbers.slice[1]: 2.29343 != 2.29843] ``` The difference is in `Numbers.slice[1]`: the two values aren't equal using Go `==`. deep-1.0.8/SECURITY.md000066400000000000000000000010321413160767500142340ustar00rootroot00000000000000# Security Policy ## Supported Versions For security patches, the latest release is supported: | Version | Supported | | ------- | ------------------ | | 1.0.x | :white_check_mark: | ## Reporting a Vulnerability To report a vulnerability, [create an issue](https://github.com/go-test/deep/issues) with the _security_ label. This project is developed and maintained by volunteers during their free time, so there is no SLA or ETA for fixing vulnerabilities (or any issues). Please help by submitting a PR to fix an issue. deep-1.0.8/deep.go000066400000000000000000000254501413160767500137210ustar00rootroot00000000000000// Package deep provides function deep.Equal which is like reflect.DeepEqual but // returns a list of differences. This is helpful when comparing complex types // like structures and maps. package deep import ( "errors" "fmt" "log" "reflect" "strings" ) var ( // FloatPrecision is the number of decimal places to round float values // to when comparing. FloatPrecision = 10 // MaxDiff specifies the maximum number of differences to return. MaxDiff = 10 // MaxDepth specifies the maximum levels of a struct to recurse into, // if greater than zero. If zero, there is no limit. MaxDepth = 0 // LogErrors causes errors to be logged to STDERR when true. LogErrors = false // CompareUnexportedFields causes unexported struct fields, like s in // T{s int}, to be compared when true. CompareUnexportedFields = false // NilSlicesAreEmpty causes a nil slice to be equal to an empty slice. NilSlicesAreEmpty = false // NilMapsAreEmpty causes a nil map to be equal to an empty map. NilMapsAreEmpty = false ) var ( // ErrMaxRecursion is logged when MaxDepth is reached. ErrMaxRecursion = errors.New("recursed to MaxDepth") // ErrTypeMismatch is logged when Equal passed two different types of values. ErrTypeMismatch = errors.New("variables are different reflect.Type") // ErrNotHandled is logged when a primitive Go kind is not handled. ErrNotHandled = errors.New("cannot compare the reflect.Kind") ) type cmp struct { diff []string buff []string floatFormat string } var errorType = reflect.TypeOf((*error)(nil)).Elem() // Equal compares variables a and b, recursing into their structure up to // MaxDepth levels deep (if greater than zero), and returns a list of differences, // or nil if there are none. Some differences may not be found if an error is // also returned. // // If a type has an Equal method, like time.Equal, it is called to check for // equality. // // When comparing a struct, if a field has the tag `deep:"-"` then it will be // ignored. func Equal(a, b interface{}) []string { aVal := reflect.ValueOf(a) bVal := reflect.ValueOf(b) c := &cmp{ diff: []string{}, buff: []string{}, floatFormat: fmt.Sprintf("%%.%df", FloatPrecision), } if a == nil && b == nil { return nil } else if a == nil && b != nil { c.saveDiff("", b) } else if a != nil && b == nil { c.saveDiff(a, "") } if len(c.diff) > 0 { return c.diff } c.equals(aVal, bVal, 0) if len(c.diff) > 0 { return c.diff // diffs } return nil // no diffs } func (c *cmp) equals(a, b reflect.Value, level int) { if MaxDepth > 0 && level > MaxDepth { logError(ErrMaxRecursion) return } // Check if one value is nil, e.g. T{x: *X} and T.x is nil if !a.IsValid() || !b.IsValid() { if a.IsValid() && !b.IsValid() { c.saveDiff(a.Type(), "") } else if !a.IsValid() && b.IsValid() { c.saveDiff("", b.Type()) } return } // If different types, they can't be equal aType := a.Type() bType := b.Type() if aType != bType { // Built-in types don't have a name, so don't report [3]int != [2]int as " != " if aType.Name() == "" || aType.Name() != bType.Name() { c.saveDiff(aType, bType) } else { // Type names can be the same, e.g. pkg/v1.Error and pkg/v2.Error // are both exported as pkg, so unless we include the full pkg path // the diff will be "pkg.Error != pkg.Error" // https://github.com/go-test/deep/issues/39 aFullType := aType.PkgPath() + "." + aType.Name() bFullType := bType.PkgPath() + "." + bType.Name() c.saveDiff(aFullType, bFullType) } logError(ErrTypeMismatch) return } // Primitive https://golang.org/pkg/reflect/#Kind aKind := a.Kind() bKind := b.Kind() // Do a and b have underlying elements? Yes if they're ptr or interface. aElem := aKind == reflect.Ptr || aKind == reflect.Interface bElem := bKind == reflect.Ptr || bKind == reflect.Interface // If both types implement the error interface, compare the error strings. // This must be done before dereferencing because the interface is on a // pointer receiver. Re https://github.com/go-test/deep/issues/31, a/b might // be primitive kinds; see TestErrorPrimitiveKind. if aType.Implements(errorType) && bType.Implements(errorType) { if (!aElem || !a.IsNil()) && (!bElem || !b.IsNil()) { aString := a.MethodByName("Error").Call(nil)[0].String() bString := b.MethodByName("Error").Call(nil)[0].String() if aString != bString { c.saveDiff(aString, bString) return } } } // Dereference pointers and interface{} if aElem || bElem { if aElem { a = a.Elem() } if bElem { b = b.Elem() } c.equals(a, b, level+1) return } switch aKind { ///////////////////////////////////////////////////////////////////// // Iterable kinds ///////////////////////////////////////////////////////////////////// case reflect.Struct: /* The variables are structs like: type T struct { FirstName string LastName string } Type = .T, Kind = reflect.Struct Iterate through the fields (FirstName, LastName), recurse into their values. */ // Types with an Equal() method, like time.Time, only if struct field // is exported (CanInterface) if eqFunc := a.MethodByName("Equal"); eqFunc.IsValid() && eqFunc.CanInterface() { // Handle https://github.com/go-test/deep/issues/15: // Don't call T.Equal if the method is from an embedded struct, like: // type Foo struct { time.Time } // First, we'll encounter Equal(Ttime, time.Time) but if we pass b // as the 2nd arg we'll panic: "Call using pkg.Foo as type time.Time" // As far as I can tell, there's no way to see that the method is from // time.Time not Foo. So we check the type of the 1st (0) arg and skip // unless it's b type. Later, we'll encounter the time.Time anonymous/ // embedded field and then we'll have Equal(time.Time, time.Time). funcType := eqFunc.Type() if funcType.NumIn() == 1 && funcType.In(0) == bType { retVals := eqFunc.Call([]reflect.Value{b}) if !retVals[0].Bool() { c.saveDiff(a, b) } return } } for i := 0; i < a.NumField(); i++ { if aType.Field(i).PkgPath != "" && !CompareUnexportedFields { continue // skip unexported field, e.g. s in type T struct {s string} } if aType.Field(i).Tag.Get("deep") == "-" { continue // field wants to be ignored } c.push(aType.Field(i).Name) // push field name to buff // Get the Value for each field, e.g. FirstName has Type = string, // Kind = reflect.String. af := a.Field(i) bf := b.Field(i) // Recurse to compare the field values c.equals(af, bf, level+1) c.pop() // pop field name from buff if len(c.diff) >= MaxDiff { break } } case reflect.Map: /* The variables are maps like: map[string]int{ "foo": 1, "bar": 2, } Type = map[string]int, Kind = reflect.Map Or: type T map[string]int{} Type = .T, Kind = reflect.Map Iterate through the map keys (foo, bar), recurse into their values. */ if a.IsNil() || b.IsNil() { if NilMapsAreEmpty { if a.IsNil() && b.Len() != 0 { c.saveDiff("", b) return } else if a.Len() != 0 && b.IsNil() { c.saveDiff(a, "") return } } else { if a.IsNil() && !b.IsNil() { c.saveDiff("", b) } else if !a.IsNil() && b.IsNil() { c.saveDiff(a, "") } } return } if a.Pointer() == b.Pointer() { return } for _, key := range a.MapKeys() { c.push(fmt.Sprintf("map[%v]", key)) aVal := a.MapIndex(key) bVal := b.MapIndex(key) if bVal.IsValid() { c.equals(aVal, bVal, level+1) } else { c.saveDiff(aVal, "") } c.pop() if len(c.diff) >= MaxDiff { return } } for _, key := range b.MapKeys() { if aVal := a.MapIndex(key); aVal.IsValid() { continue } c.push(fmt.Sprintf("map[%v]", key)) c.saveDiff("", b.MapIndex(key)) c.pop() if len(c.diff) >= MaxDiff { return } } case reflect.Array: n := a.Len() for i := 0; i < n; i++ { c.push(fmt.Sprintf("array[%d]", i)) c.equals(a.Index(i), b.Index(i), level+1) c.pop() if len(c.diff) >= MaxDiff { break } } case reflect.Slice: if NilSlicesAreEmpty { if a.IsNil() && b.Len() != 0 { c.saveDiff("", b) return } else if a.Len() != 0 && b.IsNil() { c.saveDiff(a, "") return } } else { if a.IsNil() && !b.IsNil() { c.saveDiff("", b) return } else if !a.IsNil() && b.IsNil() { c.saveDiff(a, "") return } } aLen := a.Len() bLen := b.Len() if a.Pointer() == b.Pointer() && aLen == bLen { return } n := aLen if bLen > aLen { n = bLen } for i := 0; i < n; i++ { c.push(fmt.Sprintf("slice[%d]", i)) if i < aLen && i < bLen { c.equals(a.Index(i), b.Index(i), level+1) } else if i < aLen { c.saveDiff(a.Index(i), "") } else { c.saveDiff("", b.Index(i)) } c.pop() if len(c.diff) >= MaxDiff { break } } ///////////////////////////////////////////////////////////////////// // Primitive kinds ///////////////////////////////////////////////////////////////////// case reflect.Float32, reflect.Float64: // Round floats to FloatPrecision decimal places to compare with // user-defined precision. As is commonly know, floats have "imprecision" // such that 0.1 becomes 0.100000001490116119384765625. This cannot // be avoided; it can only be handled. Issue 30 suggested that floats // be compared using an epsilon: equal = |a-b| < epsilon. // In many cases the result is the same, but I think epsilon is a little // less clear for users to reason about. See issue 30 for details. aval := fmt.Sprintf(c.floatFormat, a.Float()) bval := fmt.Sprintf(c.floatFormat, b.Float()) if aval != bval { c.saveDiff(a.Float(), b.Float()) } case reflect.Bool: if a.Bool() != b.Bool() { c.saveDiff(a.Bool(), b.Bool()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if a.Int() != b.Int() { c.saveDiff(a.Int(), b.Int()) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if a.Uint() != b.Uint() { c.saveDiff(a.Uint(), b.Uint()) } case reflect.String: if a.String() != b.String() { c.saveDiff(a.String(), b.String()) } default: logError(ErrNotHandled) } } func (c *cmp) push(name string) { c.buff = append(c.buff, name) } func (c *cmp) pop() { if len(c.buff) > 0 { c.buff = c.buff[0 : len(c.buff)-1] } } func (c *cmp) saveDiff(aval, bval interface{}) { if len(c.buff) > 0 { varName := strings.Join(c.buff, ".") c.diff = append(c.diff, fmt.Sprintf("%s: %v != %v", varName, aval, bval)) } else { c.diff = append(c.diff, fmt.Sprintf("%v != %v", aval, bval)) } } func logError(err error) { if LogErrors { log.Println(err) } } deep-1.0.8/deep_test.go000066400000000000000000000707171413160767500147660ustar00rootroot00000000000000package deep_test import ( "errors" "fmt" "reflect" "testing" "time" "github.com/go-test/deep" v1 "github.com/go-test/deep/test/v1" v2 "github.com/go-test/deep/test/v2" ) func TestString(t *testing.T) { diff := deep.Equal("foo", "foo") if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal("foo", "bar") if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "foo != bar" { t.Error("wrong diff:", diff[0]) } } func TestFloat(t *testing.T) { diff := deep.Equal(1.1, 1.1) if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal(1.1234561, 1.1234562) if diff == nil { t.Error("no diff") } defaultFloatPrecision := deep.FloatPrecision deep.FloatPrecision = 6 defer func() { deep.FloatPrecision = defaultFloatPrecision }() diff = deep.Equal(1.1234561, 1.1234562) if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal(1.123456, 1.123457) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "1.123456 != 1.123457" { t.Error("wrong diff:", diff[0]) } } func TestInt(t *testing.T) { diff := deep.Equal(1, 1) if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal(1, 2) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "1 != 2" { t.Error("wrong diff:", diff[0]) } } func TestUint(t *testing.T) { diff := deep.Equal(uint(2), uint(2)) if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal(uint(2), uint(3)) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "2 != 3" { t.Error("wrong diff:", diff[0]) } } func TestBool(t *testing.T) { diff := deep.Equal(true, true) if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal(false, false) if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal(true, false) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "true != false" { // unless you're fipar t.Error("wrong diff:", diff[0]) } } func TestTypeMismatch(t *testing.T) { type T1 int // same type kind (int) type T2 int // but different type var t1 T1 = 1 var t2 T2 = 1 diff := deep.Equal(t1, t2) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "deep_test.T1 != deep_test.T2" { t.Error("wrong diff:", diff[0]) } // Same pkg name but differnet full paths // https://github.com/go-test/deep/issues/39 err1 := v1.Error{} err2 := v2.Error{} diff = deep.Equal(err1, err2) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "github.com/go-test/deep/test/v1.Error != github.com/go-test/deep/test/v2.Error" { t.Error("wrong diff:", diff[0]) } } func TestKindMismatch(t *testing.T) { deep.LogErrors = true var x int = 100 var y float64 = 100 diff := deep.Equal(x, y) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "int != float64" { t.Error("wrong diff:", diff[0]) } deep.LogErrors = false } func TestDeepRecursion(t *testing.T) { deep.MaxDepth = 2 defer func() { deep.MaxDepth = 10 }() type s3 struct { S int } type s2 struct { S s3 } type s1 struct { S s2 } foo := map[string]s1{ "foo": { // 1 S: s2{ // 2 S: s3{ // 3 S: 42, // 4 }, }, }, } bar := map[string]s1{ "foo": { S: s2{ S: s3{ S: 100, }, }, }, } // No diffs because MaxDepth=2 prevents seeing the diff at 3rd level down diff := deep.Equal(foo, bar) if diff != nil { t.Errorf("got %d diffs, expected none: %v", len(diff), diff) } defaultMaxDepth := deep.MaxDepth deep.MaxDepth = 4 defer func() { deep.MaxDepth = defaultMaxDepth }() diff = deep.Equal(foo, bar) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "map[foo].S.S.S: 42 != 100" { t.Error("wrong diff:", diff[0]) } } func TestMaxDiff(t *testing.T) { a := []int{1, 2, 3, 4, 5, 6, 7} b := []int{0, 0, 0, 0, 0, 0, 0} defaultMaxDiff := deep.MaxDiff deep.MaxDiff = 3 defer func() { deep.MaxDiff = defaultMaxDiff }() diff := deep.Equal(a, b) if diff == nil { t.Fatal("no diffs") } if len(diff) != deep.MaxDiff { t.Errorf("got %d diffs, expected %d", len(diff), deep.MaxDiff) } defaultCompareUnexportedFields := deep.CompareUnexportedFields deep.CompareUnexportedFields = true defer func() { deep.CompareUnexportedFields = defaultCompareUnexportedFields }() type fiveFields struct { a int // unexported fields require ^ b int c int d int e int } t1 := fiveFields{1, 2, 3, 4, 5} t2 := fiveFields{0, 0, 0, 0, 0} diff = deep.Equal(t1, t2) if diff == nil { t.Fatal("no diffs") } if len(diff) != deep.MaxDiff { t.Errorf("got %d diffs, expected %d", len(diff), deep.MaxDiff) } // Same keys, too many diffs m1 := map[int]int{ 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, } m2 := map[int]int{ 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, } diff = deep.Equal(m1, m2) if diff == nil { t.Fatal("no diffs") } if len(diff) != deep.MaxDiff { t.Log(diff) t.Errorf("got %d diffs, expected %d", len(diff), deep.MaxDiff) } // Too many missing keys m1 = map[int]int{ 1: 1, 2: 2, } m2 = map[int]int{ 1: 1, 2: 2, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, } diff = deep.Equal(m1, m2) if diff == nil { t.Fatal("no diffs") } if len(diff) != deep.MaxDiff { t.Log(diff) t.Errorf("got %d diffs, expected %d", len(diff), deep.MaxDiff) } } func TestNotHandled(t *testing.T) { a := func(int) {} b := func(int) {} diff := deep.Equal(a, b) if len(diff) > 0 { t.Error("got diffs:", diff) } } func TestStruct(t *testing.T) { type s1 struct { id int Name string Number int } sa := s1{ id: 1, Name: "foo", Number: 2, } sb := sa diff := deep.Equal(sa, sb) if len(diff) > 0 { t.Error("should be equal:", diff) } sb.Name = "bar" diff = deep.Equal(sa, sb) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "Name: foo != bar" { t.Error("wrong diff:", diff[0]) } sb.Number = 22 diff = deep.Equal(sa, sb) if diff == nil { t.Fatal("no diff") } if len(diff) != 2 { t.Error("too many diff:", diff) } if diff[0] != "Name: foo != bar" { t.Error("wrong diff:", diff[0]) } if diff[1] != "Number: 2 != 22" { t.Error("wrong diff:", diff[1]) } sb.id = 11 diff = deep.Equal(sa, sb) if diff == nil { t.Fatal("no diff") } if len(diff) != 2 { t.Error("too many diff:", diff) } if diff[0] != "Name: foo != bar" { t.Error("wrong diff:", diff[0]) } if diff[1] != "Number: 2 != 22" { t.Error("wrong diff:", diff[1]) } } func TestStructWithTags(t *testing.T) { type s1 struct { same int modified int sameIgnored int `deep:"-"` modifiedIgnored int `deep:"-"` ExportedSame int ExportedModified int ExportedSameIgnored int `deep:"-"` ExportedModifiedIgnored int `deep:"-"` } type s2 struct { s1 same int modified int sameIgnored int `deep:"-"` modifiedIgnored int `deep:"-"` ExportedSame int ExportedModified int ExportedSameIgnored int `deep:"-"` ExportedModifiedIgnored int `deep:"-"` recurseInline s1 recursePtr *s2 } sa := s2{ s1: s1{ same: 0, modified: 1, sameIgnored: 2, modifiedIgnored: 3, ExportedSame: 4, ExportedModified: 5, ExportedSameIgnored: 6, ExportedModifiedIgnored: 7, }, same: 0, modified: 1, sameIgnored: 2, modifiedIgnored: 3, ExportedSame: 4, ExportedModified: 5, ExportedSameIgnored: 6, ExportedModifiedIgnored: 7, recurseInline: s1{ same: 0, modified: 1, sameIgnored: 2, modifiedIgnored: 3, ExportedSame: 4, ExportedModified: 5, ExportedSameIgnored: 6, ExportedModifiedIgnored: 7, }, recursePtr: &s2{ same: 0, modified: 1, sameIgnored: 2, modifiedIgnored: 3, ExportedSame: 4, ExportedModified: 5, ExportedSameIgnored: 6, ExportedModifiedIgnored: 7, }, } sb := s2{ s1: s1{ same: 0, modified: 10, sameIgnored: 2, modifiedIgnored: 30, ExportedSame: 4, ExportedModified: 50, ExportedSameIgnored: 6, ExportedModifiedIgnored: 70, }, same: 0, modified: 10, sameIgnored: 2, modifiedIgnored: 30, ExportedSame: 4, ExportedModified: 50, ExportedSameIgnored: 6, ExportedModifiedIgnored: 70, recurseInline: s1{ same: 0, modified: 10, sameIgnored: 2, modifiedIgnored: 30, ExportedSame: 4, ExportedModified: 50, ExportedSameIgnored: 6, ExportedModifiedIgnored: 70, }, recursePtr: &s2{ same: 0, modified: 10, sameIgnored: 2, modifiedIgnored: 30, ExportedSame: 4, ExportedModified: 50, ExportedSameIgnored: 6, ExportedModifiedIgnored: 70, }, } orig := deep.CompareUnexportedFields deep.CompareUnexportedFields = true diff := deep.Equal(sa, sb) deep.CompareUnexportedFields = orig want := []string{ "s1.modified: 1 != 10", "s1.ExportedModified: 5 != 50", "modified: 1 != 10", "ExportedModified: 5 != 50", "recurseInline.modified: 1 != 10", "recurseInline.ExportedModified: 5 != 50", "recursePtr.modified: 1 != 10", "recursePtr.ExportedModified: 5 != 50", } if !reflect.DeepEqual(want, diff) { t.Errorf("got %v, want %v", diff, want) } } func TestNestedStruct(t *testing.T) { type s2 struct { Nickname string } type s1 struct { Name string Alias s2 } sa := s1{ Name: "Robert", Alias: s2{Nickname: "Bob"}, } sb := sa diff := deep.Equal(sa, sb) if len(diff) > 0 { t.Error("should be equal:", diff) } sb.Alias.Nickname = "Bobby" diff = deep.Equal(sa, sb) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "Alias.Nickname: Bob != Bobby" { t.Error("wrong diff:", diff[0]) } } func TestMap(t *testing.T) { ma := map[string]int{ "foo": 1, "bar": 2, } mb := map[string]int{ "foo": 1, "bar": 2, } diff := deep.Equal(ma, mb) if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal(ma, ma) if len(diff) > 0 { t.Error("should be equal:", diff) } mb["foo"] = 111 diff = deep.Equal(ma, mb) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "map[foo]: 1 != 111" { t.Error("wrong diff:", diff[0]) } delete(mb, "foo") diff = deep.Equal(ma, mb) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "map[foo]: 1 != " { t.Error("wrong diff:", diff[0]) } diff = deep.Equal(mb, ma) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "map[foo]: != 1" { t.Error("wrong diff:", diff[0]) } var mc map[string]int diff = deep.Equal(ma, mc) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } // handle hash order randomness if diff[0] != "map[foo:1 bar:2] != " && diff[0] != "map[bar:2 foo:1] != " { t.Error("wrong diff:", diff[0]) } diff = deep.Equal(mc, ma) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != " != map[foo:1 bar:2]" && diff[0] != " != map[bar:2 foo:1]" { t.Error("wrong diff:", diff[0]) } } func TestArray(t *testing.T) { a := [3]int{1, 2, 3} b := [3]int{1, 2, 3} diff := deep.Equal(a, b) if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal(a, a) if len(diff) > 0 { t.Error("should be equal:", diff) } b[2] = 333 diff = deep.Equal(a, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "array[2]: 3 != 333" { t.Error("wrong diff:", diff[0]) } c := [3]int{1, 2, 2} diff = deep.Equal(a, c) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "array[2]: 3 != 2" { t.Error("wrong diff:", diff[0]) } var d [2]int diff = deep.Equal(a, d) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "[3]int != [2]int" { t.Error("wrong diff:", diff[0]) } e := [12]int{} f := [12]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} diff = deep.Equal(e, f) if diff == nil { t.Fatal("no diff") } if len(diff) != deep.MaxDiff { t.Error("not enough diffs:", diff) } for i := 0; i < deep.MaxDiff; i++ { if diff[i] != fmt.Sprintf("array[%d]: 0 != %d", i+1, i+1) { t.Error("wrong diff:", diff[i]) } } } func TestSlice(t *testing.T) { a := []int{1, 2, 3} b := []int{1, 2, 3} diff := deep.Equal(a, b) if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal(a, a) if len(diff) > 0 { t.Error("should be equal:", diff) } b[2] = 333 diff = deep.Equal(a, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "slice[2]: 3 != 333" { t.Error("wrong diff:", diff[0]) } b = b[0:2] diff = deep.Equal(a, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "slice[2]: 3 != " { t.Error("wrong diff:", diff[0]) } diff = deep.Equal(b, a) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "slice[2]: != 3" { t.Error("wrong diff:", diff[0]) } var c []int diff = deep.Equal(a, c) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "[1 2 3] != " { t.Error("wrong diff:", diff[0]) } diff = deep.Equal(c, a) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != " != [1 2 3]" { t.Error("wrong diff:", diff[0]) } } func TestSiblingSlices(t *testing.T) { father := []int{1, 2, 3, 4} a := father[0:3] b := father[0:3] diff := deep.Equal(a, b) if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal(b, a) if len(diff) > 0 { t.Error("should be equal:", diff) } a = father[0:3] b = father[0:2] diff = deep.Equal(a, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "slice[2]: 3 != " { t.Error("wrong diff:", diff[0]) } a = father[0:2] b = father[0:3] diff = deep.Equal(a, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "slice[2]: != 3" { t.Error("wrong diff:", diff[0]) } a = father[0:2] b = father[2:4] diff = deep.Equal(a, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 2 { t.Error("too many diff:", diff) } if diff[0] != "slice[0]: 1 != 3" { t.Error("wrong diff:", diff[0]) } if diff[1] != "slice[1]: 2 != 4" { t.Error("wrong diff:", diff[1]) } a = father[0:0] b = father[1:1] diff = deep.Equal(a, b) if len(diff) > 0 { t.Error("should be equal:", diff) } diff = deep.Equal(b, a) if len(diff) > 0 { t.Error("should be equal:", diff) } } func TestEmptySlice(t *testing.T) { a := []int{1} b := []int{} var c []int // Non-empty is not equal to empty. diff := deep.Equal(a, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "slice[0]: 1 != " { t.Error("wrong diff:", diff[0]) } // Empty is not equal to non-empty. diff = deep.Equal(b, a) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "slice[0]: != 1" { t.Error("wrong diff:", diff[0]) } // Empty is not equal to nil. diff = deep.Equal(b, c) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "[] != " { t.Error("wrong diff:", diff[0]) } // Nil is not equal to empty. diff = deep.Equal(c, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != " != []" { t.Error("wrong diff:", diff[0]) } } func TestNilSlicesAreEmpty(t *testing.T) { defaultNilSlicesAreEmpty := deep.NilSlicesAreEmpty deep.NilSlicesAreEmpty = true defer func() { deep.NilSlicesAreEmpty = defaultNilSlicesAreEmpty }() a := []int{1} b := []int{} var c []int // Empty is equal to nil. diff := deep.Equal(b, c) if len(diff) > 0 { t.Error("should be equal:", diff) } // Nil is equal to empty. diff = deep.Equal(c, b) if len(diff) > 0 { t.Error("should be equal:", diff) } // Non-empty is not equal to nil. diff = deep.Equal(a, c) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "[1] != " { t.Error("wrong diff:", diff[0]) } // Nil is not equal to non-empty. diff = deep.Equal(c, a) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != " != [1]" { t.Error("wrong diff:", diff[0]) } // Non-empty is not equal to empty. diff = deep.Equal(a, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "slice[0]: 1 != " { t.Error("wrong diff:", diff[0]) } // Empty is not equal to non-empty. diff = deep.Equal(b, a) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "slice[0]: != 1" { t.Error("wrong diff:", diff[0]) } } func TestNilMapsAreEmpty(t *testing.T) { defaultNilMapsAreEmpty := deep.NilSlicesAreEmpty deep.NilMapsAreEmpty = true defer func() { deep.NilSlicesAreEmpty = defaultNilMapsAreEmpty }() a := map[int]int{1: 1} b := map[int]int{} var c map[int]int // Empty is equal to nil. diff := deep.Equal(b, c) if len(diff) > 0 { t.Error("should be equal:", diff) } // Nil is equal to empty. diff = deep.Equal(c, b) if len(diff) > 0 { t.Error("should be equal:", diff) } // Non-empty is not equal to nil. diff = deep.Equal(a, c) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "map[1:1] != " { t.Error("wrong diff:", diff[0]) } // Nil is not equal to non-empty. diff = deep.Equal(c, a) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != " != map[1:1]" { t.Error("wrong diff:", diff[0]) } // Non-empty is not equal to empty. diff = deep.Equal(a, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "map[1]: 1 != " { t.Error("wrong diff:", diff[0]) } // Empty is not equal to non-empty. diff = deep.Equal(b, a) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "map[1]: != 1" { t.Error("wrong diff:", diff[0]) } } func TestNilInterface(t *testing.T) { type T struct{ i int } a := &T{i: 1} diff := deep.Equal(nil, a) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != " != &{1}" { t.Error("wrong diff:", diff[0]) } diff = deep.Equal(a, nil) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "&{1} != " { t.Error("wrong diff:", diff[0]) } diff = deep.Equal(nil, nil) if len(diff) > 0 { t.Error("should be equal:", diff) } } func TestPointer(t *testing.T) { type T struct{ i int } a, b := &T{i: 1}, &T{i: 1} diff := deep.Equal(a, b) if len(diff) > 0 { t.Error("should be equal:", diff) } a, b = nil, &T{} diff = deep.Equal(a, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != " != deep_test.T" { t.Error("wrong diff:", diff[0]) } a, b = &T{}, nil diff = deep.Equal(a, b) if diff == nil { t.Fatal("no diff") } if len(diff) != 1 { t.Error("too many diff:", diff) } if diff[0] != "deep_test.T != " { t.Error("wrong diff:", diff[0]) } a, b = nil, nil diff = deep.Equal(a, b) if len(diff) > 0 { t.Error("should be equal:", diff) } } func TestTime(t *testing.T) { // In an interable kind (i.e. a struct) type sTime struct { T time.Time } now := time.Now() got := sTime{T: now} expect := sTime{T: now.Add(1 * time.Second)} diff := deep.Equal(got, expect) if len(diff) != 1 { t.Error("expected 1 diff:", diff) } // Directly a := now b := now diff = deep.Equal(a, b) if len(diff) > 0 { t.Error("should be equal:", diff) } // https://github.com/go-test/deep/issues/15 type Time15 struct { time.Time } a15 := Time15{now} b15 := Time15{now} diff = deep.Equal(a15, b15) if len(diff) > 0 { t.Error("should be equal:", diff) } later := now.Add(1 * time.Second) b15 = Time15{later} diff = deep.Equal(a15, b15) if len(diff) != 1 { t.Errorf("got %d diffs, expected 1: %s", len(diff), diff) } // No diff in Equal should not affect diff of other fields (Foo) type Time17 struct { time.Time Foo int } a17 := Time17{Time: now, Foo: 1} b17 := Time17{Time: now, Foo: 2} diff = deep.Equal(a17, b17) if len(diff) != 1 { t.Errorf("got %d diffs, expected 1: %s", len(diff), diff) } } func TestTimeUnexported(t *testing.T) { // https://github.com/go-test/deep/issues/18 // Can't call Call() on exported Value func defaultCompareUnexportedFields := deep.CompareUnexportedFields deep.CompareUnexportedFields = true defer func() { deep.CompareUnexportedFields = defaultCompareUnexportedFields }() now := time.Now() type hiddenTime struct { t time.Time } htA := &hiddenTime{t: now} htB := &hiddenTime{t: now} diff := deep.Equal(htA, htB) if len(diff) > 0 { t.Error("should be equal:", diff) } // This doesn't call time.Time.Equal(), it compares the unexported fields // in time.Time, causing a diff like: // [t.wall: 13740788835924462040 != 13740788836998203864 t.ext: 1447549 != 1001447549] later := now.Add(1 * time.Second) htC := &hiddenTime{t: later} diff = deep.Equal(htA, htC) expected := 1 if _, ok := reflect.TypeOf(htA.t).FieldByName("ext"); ok { expected = 2 } if len(diff) != expected { t.Errorf("got %d diffs, expected %d: %s", len(diff), expected, diff) } } func TestInterface(t *testing.T) { a := map[string]interface{}{ "foo": map[string]string{ "bar": "a", }, } b := map[string]interface{}{ "foo": map[string]string{ "bar": "b", }, } diff := deep.Equal(a, b) if len(diff) == 0 { t.Fatalf("expected 1 diff, got zero") } if len(diff) != 1 { t.Errorf("expected 1 diff, got %d: %s", len(diff), diff) } } func TestInterface2(t *testing.T) { defer func() { if val := recover(); val != nil { t.Fatalf("panic: %v", val) } }() a := map[string]interface{}{ "bar": 1, } b := map[string]interface{}{ "bar": 1.23, } diff := deep.Equal(a, b) if len(diff) == 0 { t.Fatalf("expected 1 diff, got zero") } if len(diff) != 1 { t.Errorf("expected 1 diff, got %d: %s", len(diff), diff) } } func TestInterface3(t *testing.T) { type Value struct{ int } a := map[string]interface{}{ "foo": &Value{}, } b := map[string]interface{}{ "foo": 1.23, } diff := deep.Equal(a, b) if len(diff) == 0 { t.Fatalf("expected 1 diff, got zero") } if len(diff) != 1 { t.Errorf("expected 1 diff, got: %s", diff) } } func TestError(t *testing.T) { a := errors.New("it broke") b := errors.New("it broke") diff := deep.Equal(a, b) if len(diff) != 0 { t.Fatalf("expected zero diffs, got %d: %s", len(diff), diff) } b = errors.New("it fell apart") diff = deep.Equal(a, b) if len(diff) != 1 { t.Fatalf("expected 1 diff, got %d: %s", len(diff), diff) } if diff[0] != "it broke != it fell apart" { t.Errorf("got '%s', expected 'it broke != it fell apart'", diff[0]) } // Both errors set type tWithError struct { Error error } t1 := tWithError{ Error: a, } t2 := tWithError{ Error: b, } diff = deep.Equal(t1, t2) if len(diff) != 1 { t.Fatalf("expected 1 diff, got %d: %s", len(diff), diff) } if diff[0] != "Error: it broke != it fell apart" { t.Errorf("got '%s', expected 'Error: it broke != it fell apart'", diff[0]) } // Both errors nil t1 = tWithError{ Error: nil, } t2 = tWithError{ Error: nil, } diff = deep.Equal(t1, t2) if len(diff) != 0 { t.Log(diff) t.Fatalf("expected 0 diff, got %d: %s", len(diff), diff) } // One error is nil t1 = tWithError{ Error: errors.New("foo"), } t2 = tWithError{ Error: nil, } diff = deep.Equal(t1, t2) if len(diff) != 1 { t.Log(diff) t.Fatalf("expected 1 diff, got %d: %s", len(diff), diff) } if diff[0] != "Error: *errors.errorString != " { t.Errorf("got '%s', expected 'Error: *errors.errorString != '", diff[0]) } } func TestErrorWithOtherFields(t *testing.T) { a := errors.New("it broke") b := errors.New("it broke") diff := deep.Equal(a, b) if len(diff) != 0 { t.Fatalf("expected zero diffs, got %d: %s", len(diff), diff) } b = errors.New("it fell apart") diff = deep.Equal(a, b) if len(diff) != 1 { t.Fatalf("expected 1 diff, got %d: %s", len(diff), diff) } if diff[0] != "it broke != it fell apart" { t.Errorf("got '%s', expected 'it broke != it fell apart'", diff[0]) } // Both errors set type tWithError struct { Error error Other string } t1 := tWithError{ Error: a, Other: "ok", } t2 := tWithError{ Error: b, Other: "ok", } diff = deep.Equal(t1, t2) if len(diff) != 1 { t.Fatalf("expected 1 diff, got %d: %s", len(diff), diff) } if diff[0] != "Error: it broke != it fell apart" { t.Errorf("got '%s', expected 'Error: it broke != it fell apart'", diff[0]) } // Both errors nil t1 = tWithError{ Error: nil, Other: "ok", } t2 = tWithError{ Error: nil, Other: "ok", } diff = deep.Equal(t1, t2) if len(diff) != 0 { t.Log(diff) t.Fatalf("expected 0 diff, got %d: %s", len(diff), diff) } // Different Other value t1 = tWithError{ Error: nil, Other: "ok", } t2 = tWithError{ Error: nil, Other: "nope", } diff = deep.Equal(t1, t2) if len(diff) != 1 { t.Fatalf("expected 1 diff, got %d: %s", len(diff), diff) } if diff[0] != "Other: ok != nope" { t.Errorf("got '%s', expected 'Other: ok != nope'", diff[0]) } // Different Other value, same error t1 = tWithError{ Error: a, Other: "ok", } t2 = tWithError{ Error: a, Other: "nope", } diff = deep.Equal(t1, t2) if len(diff) != 1 { t.Fatalf("expected 1 diff, got %d: %s", len(diff), diff) } if diff[0] != "Other: ok != nope" { t.Errorf("got '%s', expected 'Other: ok != nope'", diff[0]) } } type primKindError string func (e primKindError) Error() string { return string(e) } func TestErrorPrimitiveKind(t *testing.T) { // The primKindError type above is valid and used by Go, e.g. // url.EscapeError and url.InvalidHostError. Before fixing this bug // (https://github.com/go-test/deep/issues/31), we presumed a and b // were ptr or interface (and not nil), so a.Elem() worked. But when // a/b are primitive kinds, Elem() causes a panic. var err1 primKindError = "abc" var err2 primKindError = "abc" diff := deep.Equal(err1, err2) if len(diff) != 0 { t.Fatalf("expected zero diffs, got %d: %s", len(diff), diff) } } func TestNil(t *testing.T) { type student struct { name string age int } mark := student{"mark", 10} var someNilThing interface{} = nil diff := deep.Equal(someNilThing, mark) if diff == nil { t.Error("Nil value to comparison should not be equal") } diff = deep.Equal(mark, someNilThing) if diff == nil { t.Error("Nil value to comparison should not be equal") } diff = deep.Equal(someNilThing, someNilThing) if diff != nil { t.Error("Nil value to comparison should not be equal") } } deep-1.0.8/go.mod000066400000000000000000000000501413160767500135500ustar00rootroot00000000000000module github.com/go-test/deep go 1.16 deep-1.0.8/go.sum000066400000000000000000000000001413160767500135700ustar00rootroot00000000000000deep-1.0.8/test/000077500000000000000000000000001413160767500134265ustar00rootroot00000000000000deep-1.0.8/test/v1/000077500000000000000000000000001413160767500137545ustar00rootroot00000000000000deep-1.0.8/test/v1/errors.go000066400000000000000000000001241413160767500156140ustar00rootroot00000000000000package deeptest type Error struct{} func (e Error) Error() string { return "" } deep-1.0.8/test/v2/000077500000000000000000000000001413160767500137555ustar00rootroot00000000000000deep-1.0.8/test/v2/errors.go000066400000000000000000000001241413160767500156150ustar00rootroot00000000000000package deeptest type Error struct{} func (e Error) Error() string { return "" }