pax_global_header00006660000000000000000000000064134601465260014521gustar00rootroot0000000000000052 comment=1e6e6c47725efd027d7d0f89a247b611f9c7f47d golang-github-gcla-deep-1.0.2/000077500000000000000000000000001346014652600160675ustar00rootroot00000000000000golang-github-gcla-deep-1.0.2/.gitignore000066400000000000000000000000141346014652600200520ustar00rootroot00000000000000*.swp *.out golang-github-gcla-deep-1.0.2/.travis.yml000066400000000000000000000003211346014652600201740ustar00rootroot00000000000000language: go go: - "1.7" - "1.8" - "1.9" - "1.10" before_install: - go get github.com/mattn/goveralls - go get golang.org/x/tools/cover script: - $HOME/gopath/bin/goveralls -service=travis-ci golang-github-gcla-deep-1.0.2/CHANGES.md000066400000000000000000000002461346014652600174630ustar00rootroot00000000000000# go-test/deep Changelog ## v1.0.1 released 2018-01-28 * Fixed #12: Arrays are not properly compared (samlitowitz) ## v1.0.0 releaesd 2017-10-27 * First release golang-github-gcla-deep-1.0.2/LICENSE000066400000000000000000000020601346014652600170720ustar00rootroot00000000000000MIT 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. golang-github-gcla-deep-1.0.2/README.md000066400000000000000000000033571346014652600173560ustar00rootroot00000000000000# 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://travis-ci.org/go-test/deep.svg?branch=master)](https://travis-ci.org/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://godoc.org/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 `==`. golang-github-gcla-deep-1.0.2/deep.go000066400000000000000000000244671346014652600173500ustar00rootroot00000000000000// 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. MaxDepth = 10 // 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 comparsed when true. CompareUnexportedFields = 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") ) // Comparer is a struct capturing the configuration used for Equals(). The package // Equal() function uses a default Comparer struct which references the global // variables that control the execution of the equality algorithm. type Comparer struct { FloatPrecision *int MaxDiff *int MaxDepth *int LogErrors *bool CompareUnexportedFields *bool ErrMaxRecursion *error ErrTypeMismatch *error ErrNotHandled *error } // MakeComparer returns a Comparer struct with nil fields initialized to point // to the global settings. func MakeComparer(c Comparer) Comparer { if c.FloatPrecision == nil { c.FloatPrecision = &FloatPrecision } if c.MaxDiff == nil { c.MaxDiff = &MaxDiff } if c.MaxDepth == nil { c.MaxDepth = &MaxDepth } if c.LogErrors == nil { c.LogErrors = &LogErrors } if c.CompareUnexportedFields == nil { c.CompareUnexportedFields = &CompareUnexportedFields } if c.ErrMaxRecursion == nil { c.ErrMaxRecursion = &ErrMaxRecursion } if c.ErrTypeMismatch == nil { c.ErrTypeMismatch = &ErrTypeMismatch } if c.ErrNotHandled == nil { c.ErrNotHandled = &ErrNotHandled } return c } func makeDefaultComparer() Comparer { return MakeComparer(Comparer{}) } var DefaultComparer = makeDefaultComparer() // Equal compares variables a and b, recursing into their structure up to // MaxDepth levels deep, 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. func Equal(a, b interface{}) []string { return DefaultComparer.Equal(a, b) } type cmp struct { diff []string buff []string floatFormat string *Comparer } var errorType = reflect.TypeOf((*error)(nil)).Elem() func (cp *Comparer) Equal(a, b interface{}) []string { aVal := reflect.ValueOf(a) bVal := reflect.ValueOf(b) c := &cmp{ diff: []string{}, buff: []string{}, floatFormat: fmt.Sprintf("%%.%df", *cp.FloatPrecision), Comparer: cp, } 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 level > *c.MaxDepth { c.logError(*c.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 differenet types, they can't be equal aType := a.Type() bType := b.Type() if aType != bType { c.saveDiff(aType, bType) c.logError(*c.ErrTypeMismatch) return } // Primitive https://golang.org/pkg/reflect/#Kind aKind := a.Kind() bKind := b.Kind() // 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. if aType.Implements(errorType) && bType.Implements(errorType) { if a.Elem().IsValid() && b.Elem().IsValid() { // both err != nil 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 := (aKind == reflect.Ptr || aKind == reflect.Interface), (bKind == reflect.Ptr || bKind == reflect.Interface); 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 != "" && !*c.CompareUnexportedFields { continue // skip unexported field, e.g. s in type T struct {s string} } 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) >= *c.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 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[%s]", 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) >= *c.MaxDiff { return } } for _, key := range b.MapKeys() { if aVal := a.MapIndex(key); aVal.IsValid() { continue } c.push(fmt.Sprintf("map[%s]", key)) c.saveDiff("", b.MapIndex(key)) c.pop() if len(c.diff) >= *c.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) >= *c.MaxDiff { break } } case reflect.Slice: if a.IsNil() || b.IsNil() { if a.IsNil() && !b.IsNil() { c.saveDiff("", b) } else if !a.IsNil() && b.IsNil() { c.saveDiff(a, "") } return } if a.Pointer() == b.Pointer() { return } aLen := a.Len() bLen := b.Len() 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) >= *c.MaxDiff { break } } ///////////////////////////////////////////////////////////////////// // Primitive kinds ///////////////////////////////////////////////////////////////////// case reflect.Float32, reflect.Float64: // Avoid 0.04147685731961082 != 0.041476857319611 // 6 decimal places is close enough 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: c.logError(*c.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 (c *cmp) logError(err error) { if *c.LogErrors { log.Println(err) } } golang-github-gcla-deep-1.0.2/deep_test.go000066400000000000000000000407421346014652600204010ustar00rootroot00000000000000package deep_test import ( "errors" "fmt" "reflect" "testing" "time" "github.com/go-test/deep" ) 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]) } } 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, }, }, }, } diff := deep.Equal(foo, bar) 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 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 TestPointer(t *testing.T) { type T struct { i int } a := &T{i: 1} b := &T{i: 1} diff := deep.Equal(a, b) if len(diff) > 0 { t.Error("should be equal:", diff) } a = 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 b = 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 = nil b = 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 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") } }