Git Product home page Git Product logo

go-cmp's Introduction

Package for equality of Go values

GoDev Build Status

This package is intended to be a more powerful and safer alternative to reflect.DeepEqual for comparing whether two values are semantically equal.

The primary features of cmp are:

  • When the default behavior of equality does not suit the needs of the test, custom equality functions can override the equality operation. For example, an equality function may report floats as equal so long as they are within some tolerance of each other.

  • Types that have an Equal method may use that method to determine equality. This allows package authors to determine the equality operation for the types that they define.

  • If no custom equality functions are used and no Equal method is defined, equality is determined by recursively comparing the primitive kinds on both values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported fields are not compared by default; they result in panics unless suppressed by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared using the AllowUnexported option.

See the documentation for more information.

This is not an official Google product.

Install

go get -u github.com/google/go-cmp/cmp

License

BSD - See LICENSE file

go-cmp's People

Contributors

178inaba avatar aoang avatar at-ishikawa avatar bakhtiyar-garashov avatar bradfitz avatar catatsuy avatar cespare avatar colinnewell avatar crawshaw avatar dmitshur avatar dsnet avatar eltociear avatar ernest-galbrun avatar ferhatelmas avatar jbl428 avatar ko30005 avatar kylelemons avatar lmmilewski avatar mattdee123 avatar morrowc avatar muesli avatar neild avatar rogpeppe avatar rsc avatar tklauser avatar wgliang avatar zombiezen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-cmp's Issues

add EquateString to cmpopts

I found myself needing this simple function that just compares the string value of two Stringer-compatible objects:

cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })

I propose adding this to cmpopts as e.g. EquateString.

Output difference in a new struct

Would it be possible to have a function return a struct where the only properties containing values are the differences between 2 structs?
The human readable string that is returned for the Diff function is already useful, but the function comment indicates it's not reliable.

Example of how this could be useful:
You need a version timeline of a data struct, and you only want to save the differences for every "version" of a struct.

stringer not used for map keys

When rendering type T, the cmp package uses the String method for slice values, but not for map keys.

package main

import (
	"fmt"

	"github.com/google/go-cmp/cmp"
)

type T [5]byte

func (t T) String() string { return string(t[:]) }

func main() {
	var t T
	copy(t[:], "world")
	m := map[T]string{t: "val"}

	s := []T{t}

	fmt.Println(cmp.Diff(nil, m))
	fmt.Println(cmp.Diff(nil, s))
}

Outputs:

  interface{}(
+ 	map[main.T]string{{0x77, 0x6f, 0x72, 0x6c, 0x64}: "val"},
  )

  interface{}(
+ 	[]main.T{s"world"},
  )

Try it here: https://play.golang.org/p/GS8f7ABvd61

If you agree this is a bug, I can take a crack at fixing it.

Add AcyclicTransformer that does not recursively apply to itself in an infinite cycle

Despite the efforts from #29 and related, the below code will still cause an infinite recursion, if no Filter is provided.
Is this expected?
If yes, I think the removed comments referring to this issue (e.g. c2186a4#diff-80edd9c1cccda07a46cde07a99b4e236L261) should be re-added.

package main

import (
	"fmt"
	"github.com/google/go-cmp/cmp"
	"strings"
)

func main() {
	a := "foo bar"
	b := "baz qux"
	fmt.Printf("%v", cmp.Equal(a, b, cmp.Transformer("", func(in string) []string { return strings.Split(in, " ") })))
}
$ go run cmptest.go
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x4fc7d2, 0xe)
	/usr/lib/google-golang/src/runtime/panic.go:622 +0x8a
runtime.newstack()
	/usr/lib/google-golang/src/runtime/stack.go:1054 +0x71f
runtime.morestack()
	/usr/lib/google-golang/src/runtime/asm_amd64.s:480 +0x89

goroutine 1 [running]:
sync.(*Map).Load(0x5973c0, 0x4e30a0, 0xc440100428, 0x0, 0x0, 0x0)
	/usr/lib/google-golang/src/sync/map.go:102 +0x2f9 fp=0xc440100308 sp=0xc440100300 pc=0x460a99
reflect.funcLayout(0x4d9bc0, 0x0, 0x4d3400, 0x4d3401, 0x4d3400, 0x0, 0x0)
	/usr/lib/google-golang/src/reflect/type.go:3076 +0xa2 fp=0xc440100530 sp=0xc440100308 pc=0x4926f2
reflect.Value.call(0x4d9bc0, 0x502e28, 0x13, 0x4fb22a, 0x4, 0xc440100920, 0x1, 0x1, 0x0, 0x0, ...)
	/usr/lib/google-golang/src/reflect/value.go:405 +0x642 fp=0xc4401007f0 sp=0xc440100530 pc=0x4949a2
reflect.Value.Call(0x4d9bc0, 0x502e28, 0x13, 0xc440100920, 0x1, 0x1, 0x18, 0x4ee6a0, 0x0)
	/usr/lib/google-golang/src/reflect/value.go:308 +0xa4 fp=0xc440100858 sp=0xc4401007f0 pc=0x494244
github.com/google/go-cmp/cmp.(*state).callTRFunc(0xc42009a000, 0x4d9bc0, 0x502e28, 0x13, 0x4d3400, 0xc426dc72d0, 0x198, 0x0, 0x8ea5389315cdc701, 0x8e00000000000000)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:326 +0x3c5 fp=0xc440100948 sp=0xc440100858 pc=0x4b72f5
github.com/google/go-cmp/cmp.(*transformer).apply(0xc42006e040, 0xc42009a000, 0x4d3400, 0xc426dc72d0, 0x198, 0x4d3400, 0xc426dc72e0, 0x198, 0xc426dc7200)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/options.go:264 +0x1c2 fp=0xc4401009e0 sp=0xc440100948 pc=0x4ba902
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc42009a000, 0x4d3400, 0xc426dc72d0, 0x198, 0x4d3400, 0xc426dc72e0, 0x198, 0x511ea0, 0x4d3400, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:307 +0x14d fp=0xc440100a80 sp=0xc4401009e0 pc=0x4b6bed
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d3400, 0xc426dc72d0, 0x198, 0x4d3400, 0xc426dc72e0, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:202 +0x347 fp=0xc440100bd0 sp=0xc440100a80 pc=0x4b4f07
github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc42009a000, 0x4d3400, 0xc426dc72d0, 0x198, 0x4d3400, 0xc426dc72e0, 0x198, 0x48c903, 0xc440100cb0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:176 +0xcc fp=0xc440100c40 sp=0xc440100bd0 pc=0x4b4b3c
github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x0, 0x5b37d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:382 +0x108 fp=0xc440100cc0 sp=0xc440100c40 pc=0x4bd108
github.com/google/go-cmp/cmp/internal/diff.searchGraph(0x1, 0x1, 0xc440100ef0, 0x97, 0xc440100e40, 0x40f9f8)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:222 +0x251 fp=0xc440100df0 sp=0xc440100cc0 pc=0x4ae7b1
github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc440100ef0, 0x1, 0x0, 0x0, 0x0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:121 +0x43 fp=0xc440100e50 sp=0xc440100df0 pc=0x4ae493
github.com/google/go-cmp/cmp.(*state).compareArray(0xc42009a000, 0x4d1400, 0xc426dcf9f0, 0x97, 0x4d1400, 0xc426dcfa20, 0x97, 0x511ea0, 0x4d1400)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:380 +0x249 fp=0xc440100f48 sp=0xc440100e50 pc=0x4b7d59
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d1400, 0xc426dcf9f0, 0x97, 0x4d1400, 0xc426dcfa20, 0x97)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:266 +0x9ce fp=0xc440101098 sp=0xc440100f48 pc=0x4b558e
github.com/google/go-cmp/cmp.(*transformer).apply(0xc42006e040, 0xc42009a000, 0x4d3400, 0xc426dc72b0, 0x198, 0x4d3400, 0xc426dc72c0, 0x198, 0xc426dc7200)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/options.go:266 +0x288 fp=0xc440101130 sp=0xc440101098 pc=0x4ba9c8
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc42009a000, 0x4d3400, 0xc426dc72b0, 0x198, 0x4d3400, 0xc426dc72c0, 0x198, 0x511ea0, 0x4d3400, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:307 +0x14d fp=0xc4401011d0 sp=0xc440101130 pc=0x4b6bed
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d3400, 0xc426dc72b0, 0x198, 0x4d3400, 0xc426dc72c0, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:202 +0x347 fp=0xc440101320 sp=0xc4401011d0 pc=0x4b4f07
github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc42009a000, 0x4d3400, 0xc426dc72b0, 0x198, 0x4d3400, 0xc426dc72c0, 0x198, 0x48c903, 0xc440101400)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:176 +0xcc fp=0xc440101390 sp=0xc440101320 pc=0x4b4b3c
github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x0, 0x5b37d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:382 +0x108 fp=0xc440101410 sp=0xc440101390 pc=0x4bd108
github.com/google/go-cmp/cmp/internal/diff.searchGraph(0x1, 0x1, 0xc440101640, 0x97, 0xc440101590, 0x40f9f8)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:222 +0x251 fp=0xc440101540 sp=0xc440101410 pc=0x4ae7b1
github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc440101640, 0x1, 0x0, 0x0, 0x0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:121 +0x43 fp=0xc4401015a0 sp=0xc440101540 pc=0x4ae493
github.com/google/go-cmp/cmp.(*state).compareArray(0xc42009a000, 0x4d1400, 0xc426dcf990, 0x97, 0x4d1400, 0xc426dcf9c0, 0x97, 0x511ea0, 0x4d1400)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:380 +0x249 fp=0xc440101698 sp=0xc4401015a0 pc=0x4b7d59
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d1400, 0xc426dcf990, 0x97, 0x4d1400, 0xc426dcf9c0, 0x97)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:266 +0x9ce fp=0xc4401017e8 sp=0xc440101698 pc=0x4b558e
github.com/google/go-cmp/cmp.(*transformer).apply(0xc42006e040, 0xc42009a000, 0x4d3400, 0xc426dc7290, 0x198, 0x4d3400, 0xc426dc72a0, 0x198, 0xc426dc7200)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/options.go:266 +0x288 fp=0xc440101880 sp=0xc4401017e8 pc=0x4ba9c8
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc42009a000, 0x4d3400, 0xc426dc7290, 0x198, 0x4d3400, 0xc426dc72a0, 0x198, 0x511ea0, 0x4d3400, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:307 +0x14d fp=0xc440101920 sp=0xc440101880 pc=0x4b6bed
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d3400, 0xc426dc7290, 0x198, 0x4d3400, 0xc426dc72a0, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:202 +0x347 fp=0xc440101a70 sp=0xc440101920 pc=0x4b4f07
github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc42009a000, 0x4d3400, 0xc426dc7290, 0x198, 0x4d3400, 0xc426dc72a0, 0x198, 0x48c903, 0xc440101b50)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:176 +0xcc fp=0xc440101ae0 sp=0xc440101a70 pc=0x4b4b3c
github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x0, 0x5b37d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:382 +0x108 fp=0xc440101b60 sp=0xc440101ae0 pc=0x4bd108
github.com/google/go-cmp/cmp/internal/diff.searchGraph(0x1, 0x1, 0xc440101d90, 0x97, 0xc440101ce0, 0x40f9f8)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:222 +0x251 fp=0xc440101c90 sp=0xc440101b60 pc=0x4ae7b1
github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc440101d90, 0x1, 0x0, 0x0, 0x0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:121 +0x43 fp=0xc440101cf0 sp=0xc440101c90 pc=0x4ae493
github.com/google/go-cmp/cmp.(*state).compareArray(0xc42009a000, 0x4d1400, 0xc426dcf930, 0x97, 0x4d1400, 0xc426dcf960, 0x97, 0x511ea0, 0x4d1400)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:380 +0x249 fp=0xc440101de8 sp=0xc440101cf0 pc=0x4b7d59
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d1400, 0xc426dcf930, 0x97, 0x4d1400, 0xc426dcf960, 0x97)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:266 +0x9ce fp=0xc440101f38 sp=0xc440101de8 pc=0x4b558e
github.com/google/go-cmp/cmp.(*transformer).apply(0xc42006e040, 0xc42009a000, 0x4d3400, 0xc426dc7270, 0x198, 0x4d3400, 0xc426dc7280, 0x198, 0xc426dc7200)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/options.go:266 +0x288 fp=0xc440101fd0 sp=0xc440101f38 pc=0x4ba9c8
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc42009a000, 0x4d3400, 0xc426dc7270, 0x198, 0x4d3400, 0xc426dc7280, 0x198, 0x511ea0, 0x4d3400, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:307 +0x14d fp=0xc440102070 sp=0xc440101fd0 pc=0x4b6bed
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d3400, 0xc426dc7270, 0x198, 0x4d3400, 0xc426dc7280, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:202 +0x347 fp=0xc4401021c0 sp=0xc440102070 pc=0x4b4f07
github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc42009a000, 0x4d3400, 0xc426dc7270, 0x198, 0x4d3400, 0xc426dc7280, 0x198, 0x48c903, 0xc4401022a0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:176 +0xcc fp=0xc440102230 sp=0xc4401021c0 pc=0x4b4b3c
github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x0, 0x5b37d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:382 +0x108 fp=0xc4401022b0 sp=0xc440102230 pc=0x4bd108
github.com/google/go-cmp/cmp/internal/diff.searchGraph(0x1, 0x1, 0xc4401024e0, 0x97, 0xc440102430, 0x40f9f8)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:222 +0x251 fp=0xc4401023e0 sp=0xc4401022b0 pc=0x4ae7b1
github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc4401024e0, 0x1, 0x0, 0x0, 0x0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:121 +0x43 fp=0xc440102440 sp=0xc4401023e0 pc=0x4ae493
github.com/google/go-cmp/cmp.(*state).compareArray(0xc42009a000, 0x4d1400, 0xc426dcf8d0, 0x97, 0x4d1400, 0xc426dcf900, 0x97, 0x511ea0, 0x4d1400)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:380 +0x249 fp=0xc440102538 sp=0xc440102440 pc=0x4b7d59
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d1400, 0xc426dcf8d0, 0x97, 0x4d1400, 0xc426dcf900, 0x97)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:266 +0x9ce fp=0xc440102688 sp=0xc440102538 pc=0x4b558e
github.com/google/go-cmp/cmp.(*transformer).apply(0xc42006e040, 0xc42009a000, 0x4d3400, 0xc426dc7250, 0x198, 0x4d3400, 0xc426dc7260, 0x198, 0xc426dc7200)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/options.go:266 +0x288 fp=0xc440102720 sp=0xc440102688 pc=0x4ba9c8
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc42009a000, 0x4d3400, 0xc426dc7250, 0x198, 0x4d3400, 0xc426dc7260, 0x198, 0x511ea0, 0x4d3400, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:307 +0x14d fp=0xc4401027c0 sp=0xc440102720 pc=0x4b6bed
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d3400, 0xc426dc7250, 0x198, 0x4d3400, 0xc426dc7260, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:202 +0x347 fp=0xc440102910 sp=0xc4401027c0 pc=0x4b4f07
github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc42009a000, 0x4d3400, 0xc426dc7250, 0x198, 0x4d3400, 0xc426dc7260, 0x198, 0x48c903, 0xc4401029f0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:176 +0xcc fp=0xc440102980 sp=0xc440102910 pc=0x4b4b3c
github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x0, 0x5b37d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:382 +0x108 fp=0xc440102a00 sp=0xc440102980 pc=0x4bd108
github.com/google/go-cmp/cmp/internal/diff.searchGraph(0x1, 0x1, 0xc440102c30, 0x97, 0xc440102b80, 0x40f9f8)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:222 +0x251 fp=0xc440102b30 sp=0xc440102a00 pc=0x4ae7b1
github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc440102c30, 0x1, 0x0, 0x0, 0x0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:121 +0x43 fp=0xc440102b90 sp=0xc440102b30 pc=0x4ae493
github.com/google/go-cmp/cmp.(*state).compareArray(0xc42009a000, 0x4d1400, 0xc426dcf870, 0x97, 0x4d1400, 0xc426dcf8a0, 0x97, 0x511ea0, 0x4d1400)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:380 +0x249 fp=0xc440102c88 sp=0xc440102b90 pc=0x4b7d59
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d1400, 0xc426dcf870, 0x97, 0x4d1400, 0xc426dcf8a0, 0x97)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:266 +0x9ce fp=0xc440102dd8 sp=0xc440102c88 pc=0x4b558e
github.com/google/go-cmp/cmp.(*transformer).apply(0xc42006e040, 0xc42009a000, 0x4d3400, 0xc426dc7230, 0x198, 0x4d3400, 0xc426dc7240, 0x198, 0xc426dc7200)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/options.go:266 +0x288 fp=0xc440102e70 sp=0xc440102dd8 pc=0x4ba9c8
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc42009a000, 0x4d3400, 0xc426dc7230, 0x198, 0x4d3400, 0xc426dc7240, 0x198, 0x511ea0, 0x4d3400, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:307 +0x14d fp=0xc440102f10 sp=0xc440102e70 pc=0x4b6bed
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d3400, 0xc426dc7230, 0x198, 0x4d3400, 0xc426dc7240, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:202 +0x347 fp=0xc440103060 sp=0xc440102f10 pc=0x4b4f07
github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc42009a000, 0x4d3400, 0xc426dc7230, 0x198, 0x4d3400, 0xc426dc7240, 0x198, 0x48c903, 0xc440103140)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:176 +0xcc fp=0xc4401030d0 sp=0xc440103060 pc=0x4b4b3c
github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x0, 0x5b37d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:382 +0x108 fp=0xc440103150 sp=0xc4401030d0 pc=0x4bd108
github.com/google/go-cmp/cmp/internal/diff.searchGraph(0x1, 0x1, 0xc440103380, 0x97, 0xc4401032d0, 0x40f9f8)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:222 +0x251 fp=0xc440103280 sp=0xc440103150 pc=0x4ae7b1
github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc440103380, 0x1, 0x0, 0x0, 0x0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:121 +0x43 fp=0xc4401032e0 sp=0xc440103280 pc=0x4ae493
github.com/google/go-cmp/cmp.(*state).compareArray(0xc42009a000, 0x4d1400, 0xc426dcf810, 0x97, 0x4d1400, 0xc426dcf840, 0x97, 0x511ea0, 0x4d1400)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:380 +0x249 fp=0xc4401033d8 sp=0xc4401032e0 pc=0x4b7d59
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d1400, 0xc426dcf810, 0x97, 0x4d1400, 0xc426dcf840, 0x97)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:266 +0x9ce fp=0xc440103528 sp=0xc4401033d8 pc=0x4b558e
github.com/google/go-cmp/cmp.(*transformer).apply(0xc42006e040, 0xc42009a000, 0x4d3400, 0xc426dc7210, 0x198, 0x4d3400, 0xc426dc7220, 0x198, 0xc426dc7200)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/options.go:266 +0x288 fp=0xc4401035c0 sp=0xc440103528 pc=0x4ba9c8
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc42009a000, 0x4d3400, 0xc426dc7210, 0x198, 0x4d3400, 0xc426dc7220, 0x198, 0x511ea0, 0x4d3400, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:307 +0x14d fp=0xc440103660 sp=0xc4401035c0 pc=0x4b6bed
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d3400, 0xc426dc7210, 0x198, 0x4d3400, 0xc426dc7220, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:202 +0x347 fp=0xc4401037b0 sp=0xc440103660 pc=0x4b4f07
github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc42009a000, 0x4d3400, 0xc426dc7210, 0x198, 0x4d3400, 0xc426dc7220, 0x198, 0x48c903, 0xc440103890)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:176 +0xcc fp=0xc440103820 sp=0xc4401037b0 pc=0x4b4b3c
github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x0, 0x5b37d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:382 +0x108 fp=0xc4401038a0 sp=0xc440103820 pc=0x4bd108
github.com/google/go-cmp/cmp/internal/diff.searchGraph(0x1, 0x1, 0xc440103ad0, 0x97, 0xc440103a20, 0x40f9f8)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:222 +0x251 fp=0xc4401039d0 sp=0xc4401038a0 pc=0x4ae7b1
github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc440103ad0, 0x1, 0x0, 0x0, 0x0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:121 +0x43 fp=0xc440103a30 sp=0xc4401039d0 pc=0x4ae493
github.com/google/go-cmp/cmp.(*state).compareArray(0xc42009a000, 0x4d1400, 0xc426dcf7b0, 0x97, 0x4d1400, 0xc426dcf7e0, 0x97, 0x511ea0, 0x4d1400)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:380 +0x249 fp=0xc440103b28 sp=0xc440103a30 pc=0x4b7d59
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d1400, 0xc426dcf7b0, 0x97, 0x4d1400, 0xc426dcf7e0, 0x97)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:266 +0x9ce fp=0xc440103c78 sp=0xc440103b28 pc=0x4b558e
github.com/google/go-cmp/cmp.(*transformer).apply(0xc42006e040, 0xc42009a000, 0x4d3400, 0xc426dc71f0, 0x198, 0x4d3400, 0xc426dc7200, 0x198, 0xc426dc7200)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/options.go:266 +0x288 fp=0xc440103d10 sp=0xc440103c78 pc=0x4ba9c8
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc42009a000, 0x4d3400, 0xc426dc71f0, 0x198, 0x4d3400, 0xc426dc7200, 0x198, 0x511ea0, 0x4d3400, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:307 +0x14d fp=0xc440103db0 sp=0xc440103d10 pc=0x4b6bed
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d3400, 0xc426dc71f0, 0x198, 0x4d3400, 0xc426dc7200, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:202 +0x347 fp=0xc440103f00 sp=0xc440103db0 pc=0x4b4f07
github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc42009a000, 0x4d3400, 0xc426dc71f0, 0x198, 0x4d3400, 0xc426dc7200, 0x198, 0x48c903, 0xc440103fe0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:176 +0xcc fp=0xc440103f70 sp=0xc440103f00 pc=0x4b4b3c
github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x0, 0x5b37d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:382 +0x108 fp=0xc440103ff0 sp=0xc440103f70 pc=0x4bd108
github.com/google/go-cmp/cmp/internal/diff.searchGraph(0x1, 0x1, 0xc440104220, 0x97, 0xc440104170, 0x40f9f8)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:222 +0x251 fp=0xc440104120 sp=0xc440103ff0 pc=0x4ae7b1
github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc440104220, 0x1, 0x0, 0x0, 0x0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:121 +0x43 fp=0xc440104180 sp=0xc440104120 pc=0x4ae493
github.com/google/go-cmp/cmp.(*state).compareArray(0xc42009a000, 0x4d1400, 0xc426dcf750, 0x97, 0x4d1400, 0xc426dcf780, 0x97, 0x511ea0, 0x4d1400)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:380 +0x249 fp=0xc440104278 sp=0xc440104180 pc=0x4b7d59
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d1400, 0xc426dcf750, 0x97, 0x4d1400, 0xc426dcf780, 0x97)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:266 +0x9ce fp=0xc4401043c8 sp=0xc440104278 pc=0x4b558e
github.com/google/go-cmp/cmp.(*transformer).apply(0xc42006e040, 0xc42009a000, 0x4d3400, 0xc426dc71d0, 0x198, 0x4d3400, 0xc426dc71e0, 0x198, 0xc426dc7100)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/options.go:266 +0x288 fp=0xc440104460 sp=0xc4401043c8 pc=0x4ba9c8
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc42009a000, 0x4d3400, 0xc426dc71d0, 0x198, 0x4d3400, 0xc426dc71e0, 0x198, 0x511ea0, 0x4d3400, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:307 +0x14d fp=0xc440104500 sp=0xc440104460 pc=0x4b6bed
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d3400, 0xc426dc71d0, 0x198, 0x4d3400, 0xc426dc71e0, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:202 +0x347 fp=0xc440104650 sp=0xc440104500 pc=0x4b4f07
github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc42009a000, 0x4d3400, 0xc426dc71d0, 0x198, 0x4d3400, 0xc426dc71e0, 0x198, 0x48c903, 0xc440104730)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:176 +0xcc fp=0xc4401046c0 sp=0xc440104650 pc=0x4b4b3c
github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x0, 0x5b37d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:382 +0x108 fp=0xc440104740 sp=0xc4401046c0 pc=0x4bd108
github.com/google/go-cmp/cmp/internal/diff.searchGraph(0x1, 0x1, 0xc440104970, 0x97, 0xc4401048c0, 0x40f9f8)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:222 +0x251 fp=0xc440104870 sp=0xc440104740 pc=0x4ae7b1
github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc440104970, 0x1, 0x0, 0x0, 0x0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:121 +0x43 fp=0xc4401048d0 sp=0xc440104870 pc=0x4ae493
github.com/google/go-cmp/cmp.(*state).compareArray(0xc42009a000, 0x4d1400, 0xc426dcf6f0, 0x97, 0x4d1400, 0xc426dcf720, 0x97, 0x511ea0, 0x4d1400)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:380 +0x249 fp=0xc4401049c8 sp=0xc4401048d0 pc=0x4b7d59
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d1400, 0xc426dcf6f0, 0x97, 0x4d1400, 0xc426dcf720, 0x97)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:266 +0x9ce fp=0xc440104b18 sp=0xc4401049c8 pc=0x4b558e
github.com/google/go-cmp/cmp.(*transformer).apply(0xc42006e040, 0xc42009a000, 0x4d3400, 0xc426dc71b0, 0x198, 0x4d3400, 0xc426dc71c0, 0x198, 0xc426dc7100)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/options.go:266 +0x288 fp=0xc440104bb0 sp=0xc440104b18 pc=0x4ba9c8
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc42009a000, 0x4d3400, 0xc426dc71b0, 0x198, 0x4d3400, 0xc426dc71c0, 0x198, 0x511ea0, 0x4d3400, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:307 +0x14d fp=0xc440104c50 sp=0xc440104bb0 pc=0x4b6bed
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d3400, 0xc426dc71b0, 0x198, 0x4d3400, 0xc426dc71c0, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:202 +0x347 fp=0xc440104da0 sp=0xc440104c50 pc=0x4b4f07
github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc42009a000, 0x4d3400, 0xc426dc71b0, 0x198, 0x4d3400, 0xc426dc71c0, 0x198, 0x48c903, 0xc440104e80)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:176 +0xcc fp=0xc440104e10 sp=0xc440104da0 pc=0x4b4b3c
github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x0, 0x5b37d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:382 +0x108 fp=0xc440104e90 sp=0xc440104e10 pc=0x4bd108
github.com/google/go-cmp/cmp/internal/diff.searchGraph(0x1, 0x1, 0xc4401050c0, 0x97, 0xc440105010, 0x40f9f8)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:222 +0x251 fp=0xc440104fc0 sp=0xc440104e90 pc=0x4ae7b1
github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc4401050c0, 0x1, 0x0, 0x0, 0x0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/internal/diff/diff.go:121 +0x43 fp=0xc440105020 sp=0xc440104fc0 pc=0x4ae493
github.com/google/go-cmp/cmp.(*state).compareArray(0xc42009a000, 0x4d1400, 0xc426dcf690, 0x97, 0x4d1400, 0xc426dcf6c0, 0x97, 0x511ea0, 0x4d1400)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:380 +0x249 fp=0xc440105118 sp=0xc440105020 pc=0x4b7d59
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d1400, 0xc426dcf690, 0x97, 0x4d1400, 0xc426dcf6c0, 0x97)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:266 +0x9ce fp=0xc440105268 sp=0xc440105118 pc=0x4b558e
github.com/google/go-cmp/cmp.(*transformer).apply(0xc42006e040, 0xc42009a000, 0x4d3400, 0xc426dc7190, 0x198, 0x4d3400, 0xc426dc71a0, 0x198, 0xc426dc7100)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/options.go:266 +0x288 fp=0xc440105300 sp=0xc440105268 pc=0x4ba9c8
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc42009a000, 0x4d3400, 0xc426dc7190, 0x198, 0x4d3400, 0xc426dc71a0, 0x198, 0x511ea0, 0x4d3400, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:307 +0x14d fp=0xc4401053a0 sp=0xc440105300 pc=0x4b6bed
github.com/google/go-cmp/cmp.(*state).compareAny(0xc42009a000, 0x4d3400, 0xc426dc7190, 0x198, 0x4d3400, 0xc426dc71a0, 0x198)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:202 +0x347 fp=0xc4401054f0 sp=0xc4401053a0 pc=0x4b4f07
github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc42009a000, 0x4d3400, 0xc426dc7190, 0x198, 0x4d3400, 0xc426dc71a0, 0x198, 0x48c903, 0xc4401055d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:176 +0xcc fp=0xc440105560 sp=0xc4401054f0 pc=0x4b4b3c
github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x0, 0x5b37d0)
	/usr/share/gocode/src/github.com/google/go-cmp/cmp/compare.go:382 +0x108 fp=0xc4401055e0 sp=0xc440105560 pc=0x4bd108
...additional frames elided...
exit status 2

What's the deal with trying the Equal() method

It seems that go-cmp panics when encountering a nil struct that has an Equal method which doesn't handle nil values.

Here's a pull request that demonstrates the problem. #62

I think this is because there's no contract on what Equal method should do in the edge cases. Indeed,, what should Equal do?

question: can this example be done more cleanly?

In the data structure I wanted to compare, there was a field of type I, where I is an interface. Types A and B both implemented I and nothing else did. I wanted to convert As to Bs to facilitate comparison. (A is a deprecated version of B, and I wanted to treat them equivalently for comparisons.)

My first thought was a transformer from A to B. This failed, because cmp.Equal first checks that its two arguments have identical type before transforming.

I ended up writing a comparer func(x, y I) bool that had to convert each of x and y before comparing them. (Original code).

Is there a better way?

Reporter - embedded types

Hi, I'm attempting to document changed field values between a struct and it's modified copy, and I want to get the field paths to match their location on a marshalled document (json & protobuf). However within the reporter I'm unable to reliably identify embedded type fields.

I have a forked implementation that copies the value of the Anonymous from reflect.StructField to cmp.StructField in the compareStruct and that seems to do the job with minimal modification.

Would you consider making this (or a similar) change to support this usecase?

Thanks,

Ben

add examples for AllowUnexported and IgnoreUnexported

Reading the docs and attempting simple implementations, I'm still not sure how to use these, since I keep getting panics. I'm going to search Github to see people using these successfully to figure out what I am missing.

question: policy/schedule for tagging a git release

Related to #37 , apologies if this is documented somewhere and I missed it.

I'm looking forward to getting the changes from #45 into a tagged git release so that I can use a dep constraint other than branch = master, which is not great for consumers of my package.

Do you have a policy for when to tag a new release, based on time or the number/size of changes since the last tagged release?

It has been just over 6 months from 0.1 so I'm wondering if you'd consider a 0.2.

Thanks for this package!

Comparing reflect types behaves not as expected

When comparing two equal types, the response is false, for example, the following code print false:

t1 := reflect.TypeOf(1)
t2 := reflect.TypeOf(2)
fmt.Println(cmp.Equal(t1, t2, deepAllowUnexported(t1)))

See full code

Just to clarify, if the diff is being printed, this is the reason that those two types are not the same:

{*reflect.rtype}.alg.hash:
	-: (func(unsafe.Pointer, uintptr) uintptr)(0x451530)
	+: (func(unsafe.Pointer, uintptr) uintptr)(0x451530)
{*reflect.rtype}.alg.equal:
	-: (func(unsafe.Pointer, unsafe.Pointer) bool)(0x401a50)
	+: (func(unsafe.Pointer, unsafe.Pointer) bool)(0x401a50)

This is being solved in the standard refelect.DeepEqual when comparing two pointers.

If the compareAny will do the same trick, the above code will print true as expected. It also does not break any of the tests:

diff --git a/cmp/compare.go b/cmp/compare.go
index 1260603..8fbbaa9 100644
--- a/cmp/compare.go
+++ b/cmp/compare.go
@@ -239,6 +239,12 @@ func (s *state) compareAny(vx, vy reflect.Value) {
                        s.report(vx.IsNil() && vy.IsNil(), vx, vy)
                        return
                }
+
+               if vx.Pointer() == vy.Pointer() {
+                       s.report(true, vx, vy)
+                       return
+               }
+

I am not sure why this comparison is not done in the go-cmp implementation, I can't see any harm in doing it, it can also saves running time.

Implement an AllowAllUnexported Option

Hi, I was wondering if you would consider adding a new option which would apply the AllowUnexported Option to all types and would not require a slice of input like the current AllowUnexported function does?

My use case is that I have a lot of tests which use the github.com/stretchr/testify package, which ultimately uses reflect.DeepEqual for comparing two objects, that break in Go 1.9 because they try to compare structs which contain time.Time fields. Admittedly, I could define an Equal method for all such structs but that doesn't seem to address the root of the problem to me, which is that I would like reflect.DeepEqual functionality (which compares all fields, both private and public) with the caveat that I would like to check if the types being compared have an Equal method for testing equality. Furthermore, while for structs I could pass the structs being compared to AllowUnexported, I would like the same behavior for composite types as well (e.g. maps and slices) which is not supported by AllowUnexported currently since it specifically checks that the types of the objects passed to it are structs.

I would be more than happy to put up a PR for such an Option, a first look at the code leads me to believe that we could define an additional field on state indicating that we want to always check private fields. Then, when set to true, we would always extract a struct's fields in tryExporting. With that being said, I got the impression that you might be opposed to such an Option given the warnings for AllowUnexported already and so I thought it better to open an issue first where we could discuss.

Thanks!

Add generic less function for SortSlices

It's a bit of a pain writing a comparison function when we want slices to compare equal regardless of order. How about making SortSlices use a generic less function when it's argument is nil, so that in the common case we can just use SortSlices(nil) to check slice contents regardless of order and type?

The generic less function might be something like this, perhaps: https://play.golang.org/p/NQ1u36jKsi-

cmpopts to ignore fields within slices?

Hi all,

My struct looks like this:

type info struct {
  Num int
  Slice []dbEntry
}
type dbEntry struct {
  ID int
  CreatedAt time.Time
  OtherInfo int
}

Is there a way within cmpopts to compare info, ignoring the ID and CreatedAt fields within its Slice?

proposal: support cyclic data structures

Hi
The following program crashes on an infinite recursion:

package main

import "github.com/google/go-cmp/cmp"

type Node struct {
	Next *Node
}

func main() {
	a := &Node{}
	a.Next = a

	cmp.Equal(a, a)
}

Fixing this is needed to use this package in testify.

I saw that there is already a TODO comment.

Difference with debug enabled deadlocks on compareArray

Enabling the debug flag can cause lock contention as compareArray can loop on itself calling diff.Difference twice: https://github.com/google/go-cmp/blob/master/cmp/compare.go#L403

goroutine 5 [semacquire]:
sync.runtime_SemacquireMutex(0x4dffcc4, 0x900000000)
	GOROOT/src/runtime/sema.go:71 +0x3d
sync.(*Mutex).Lock(0x4dffcc0)
	GOROOT/src/sync/mutex.go:134 +0x153
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp/internal/diff.(*debugger).Lock(0x4dffcc0)
	vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go:78 +0x7b
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp/internal/diff.(*debugger).Begin(0x4dffcc0, 0x1, 0x1, 0xc000328550, 0xc0003466d8, 0xc000346708, 0xc00032e810)
	vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go:82 +0x54
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc000328550, 0x1, 0x3, 0xc000326540)
	vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go:200 +0x381
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp.(*state).compareArray(0xc000342850, 0x477cd20, 0xc00032ad40, 0x197, 0x477cd20, 0xc0001a6c40, 0x197, 0x4947940, 0x477cd20)
	vendor/github.com/google/go-cmp/cmp/compare.go:403 +0x3e3
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp.(*state).compareAny(0xc000342850, 0x477cd20, 0xc00032ad40, 0x197, 0x477cd20, 0xc0001a6c40, 0x197)
	vendor/github.com/google/go-cmp/cmp/compare.go:272 +0x1189
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp.(*state).statelessCompare(0xc000342850, 0x477cd20, 0xc00032ad40, 0x197, 0x477cd20, 0xc0001a6c40, 0x197, 0x4102374, 0xc00032cf00)
	vendor/github.com/google/go-cmp/cmp/compare.go:181 +0x149
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp.(*state).compareArray.func1(0x0, 0x0, 0x4933420, 0xc0000d4008)
	vendor/github.com/google/go-cmp/cmp/compare.go:405 +0x170
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp/internal/diff.(*debugger).Begin.func1(0x0, 0x0, 0x1, 0x10)
	vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go:97 +0x141
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp/internal/diff.Difference(0x1, 0x1, 0xc000326500, 0x1, 0x2, 0xc00032ade0)
	vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go:216 +0x693
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp.(*state).compareArray(0xc000342850, 0x477bda0, 0xc00032ad60, 0x97, 0x477bda0, 0xc00032ad80, 0x97, 0x4947940, 0x477bda0)
	vendor/github.com/google/go-cmp/cmp/compare.go:403 +0x3e3
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp.(*state).compareAny(0xc000342850, 0x477bda0, 0xc00032ad60, 0x97, 0x477bda0, 0xc00032ad80, 0x97)
	vendor/github.com/google/go-cmp/cmp/compare.go:272 +0x1189
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp.Equal(0x477bda0, 0xc00032ad60, 0x477bda0, 0xc00032ad80, 0xc00032ada0, 0x2, 0x2, 0x46c6b3a)
	vendor/github.com/google/go-cmp/cmp/compare.go:87 +0xda
repo.jazznetworks.com/jazz/main/vendor/github.com/google/go-cmp/cmp.Diff(0x477bda0, 0xc00032ad60, 0x477bda0, 0xc00032ad80, 0x0, 0x0, 0x0, 0x1, 0xc000040120)
	vendor/github.com/google/go-cmp/cmp/compare.go:101 +0x182
repo.jazznetworks.com/jazz/main/vendor/gocloud.dev/blob/drivertest.testList.func4(0xc000374000)
	vendor/gocloud.dev/blob/drivertest/drivertest.go:346 +0x619
testing.tRunner(0xc000374000, 0xc000348000)
	GOROOT/src/testing/testing.go:827 +0x163
created by testing.(*T).Run
	GOROOT/src/testing/testing.go:878 +0x65a

check that some struct fully contains in another

i have example struct
type X struct {
Name string
Enabled bool
Opts map[string]interface{}
}

and i have slice of such structs,
i need to get all structs that have for example Enabled: true in that slice. Does it possible with this package?
Now i have ugly reflect based variant.

Allow configure diff reporter to output all diffs

I'm trying to compare two huge protos from bazel query in a non-testing context, and Diff comes back with:

{map[string]*build_proto.Rule}["//external:com_github_pelletier_go_toml"]:
	-: s`name:"//external:com_github_pelletier_go_toml" rule_class:"git_repository" attribute:<name:"commit" explicitly_specified:true nodep:false type:STRING string_value:"16398bac157da96aa88f98a2df640c7f32af1da2" > attribute:<name:"remote" explicitly_specified:true nodep:false type:STRING string_value:"https://github.com/pelletier/go-toml" > `
	+: <non-existent>
{map[string]*build_proto.Rule}["//external:go_googleapis"]:
	-: <non-existent>
	+: s`name:"//external:go_googleapis" rule_class:"http_archive" attribute:<name:"patches" explicitly_specified:true nodep:false type:LABEL_LIST string_list_value:"@io_bazel_rules_go//third_party:go_googleapis-directives.patch" string_list_value:"@io_bazel_rules_go//third_party:go_googleapis-gazelle.patch" string_list_value:"@io_bazel_rules_go//third_party:go_googleapis-fix.patch" > attribute:<name:"urls" explicitly_specified:true nodep:false type:STRING_LIST string_list_value:"https://codeload.github.com/googleapis/googleapis/zip/b71d0c74de0b84f2f10a2c61cd66fbb48873709f" > `
... 8 more differences ...

For my case, I'd prefer to see a longer list of diffs, instead of a truncated one. Reading the code, the default reporter comes with hard limits on the number of diffs to return.

Do you open to the idea to make it configurable by passing a few more options to it? Or even make the reporter an interface that people may implement?

how to compare time.Time type?

Hi, I have a package defining a custom type for time which is used in various structs. I am trying to use a custom comparer to see if the times are equal but if I cast to the original type to use the time.Equals function, I'm getting an error cannot handle unexported field: {main.Foo}.Created.wall consider using a custom Comparer; if you control the implementation of type, you can also consider AllowUnexported or cmpopts.IgnoreUnexported - which is why I started writing a custom comparer in the first place.

How can I approach this?

My code: https://gist.github.com/falnyr/4643f4b4a8c20975d5ddb67055f0f675

[FR] Change cmpopts.SortSlices param from `interface{}` to `func(a,b interface{}) bool`

I just tried using cmpopts.SortSlices, and I know.. there are no generics in Go yet, but I think the current signature is not intuitive.

Current state of the world:

opt := cmpopts.SortSlices("yay") // can do whatever I want, it's an interface{}
cmp.Diff(someFoo, otherFoo, opt)

My desired state of the world:

// Can still mess things up, but it's more obvious.
opt := cmpopts.SortSlices(func (a, b interface{}){ return a.(Foo).Val < b.(Foo).Val })
cmp.Diff(someFoo, otherFoo, opt)

This proposal goes against Issue #67 but we could make it work like...

opt := cmpopts.SortSlices(cmpopts.DefaultSort) // renamed GenericLess to DefaultSort
cmp.Diff(someFoo, otherFoo, opt)

Convenient FilterPath functions

Hello, I've been using go-cmp for a bit now and I've found that the two most common cases for needing an Option are:

  1. AllowUnexported/IgnoreUnexported - which is already easy to use
  2. A FilterPath for fields that have values set from time.Now() (time.Time, or time.Duration)

It wasn't immediately obvious to me how to create a correct FilterPath filter function. After a quick scan of the godoc my first approach was something like this:

func(path gocmp.Path) bool {		
    return path.Last().String() == ".Elapsed"		
}

This worked, but it seems to me like it could potentially match fields that I don't want it to match if I used it with a large nested structure where multiple types had an Elapsed field.

Digging into the godoc further I noticed that a bunch of the types embedded PathStep, which made me realize that a more correct way of using Path would probably be to iterate over the steps, type assert to the expected type, and compare to Name() or Key().

My next iteration (gotestyourself/gotest.tools#62) looks something like this:

func fieldPathWithTypes(pathspec string) func(gocmp.Path) bool {
	return func(path gocmp.Path) bool {
		spec := strings.Split(pathspec, ".")
		index := 0

		for _, step := range path {
			fieldStep, ok := step.(gocmp.StructField)
			if !ok {
				continue
			}
			if index >= len(spec) || spec[index] != fieldStep.Name() {
				return false
			}
			index++
		}
		return index == len(spec)
	}
}

Which could be made a lot simpler if it's expected that PathStep.String() is stable and wont change between minor versions:

func fieldPathSimple(pathspec string) func(gocmp.Path) bool {
	return func(path gocmp.Path) bool {
		return path.String() == pathspec
	}
}

The same idea could be applied using GoString() if that is considered stable. The idea behind both of these functions is that it's much easier for a reader to understand a dotted path string, than it is to read a long function with a bunch of type assertions.

Questions:

  1. What is the expected/"best practice" way of using PathStep? Is it similar to fieldPathWithTypes ?
  2. Would you be interested in a PR that adds a convenience function for building the PathFilter filter function from a simple string? This could be something like fieldPathWithTypes or it could be more verbose (to match against something like GoString(). I believe the only type that can't be easily translated from a string representation would be Transform.
  3. Would you be interested in a PR that adds one or more examples for using FilterPath (based on feedback from these other questions) ?

Are error comparisons supported?

Does this library currently support comparisons of the error type?

I tried playing around with AllowUnexported and cmpopts.IgnoreUnexported but, can't seem to figure out how to make work.

not-working:

package main

import (
	"errors"

	"github.com/google/go-cmp/cmp"
)

func main() {
	err1 := errors.New("err1")
	err2 := errors.New("err2")
	Match(err1, err2)
}

func Match(expected, result interface{}) {
	test := cmp.Diff(expected, result)
	if test != "" {
		panic(test)
	}
}

cmp: top-level interfaces are not matched

Consider the following:

equalErrors := cmp.Comparer(func(_, _ error) bool { return true })

cmp.Equal(io.EOF, &os.PathError{}, equalErrors) // false

type E struct{ E error }
cmp.Equal(E{io.EOF}, E{&os.PathError{}}, equalErrors) // true

Note that the first comparison always reports false, while the later reports true. The reason is because reflect.ValueOf on an interface loses information about the fact that the value passed in is an interface. Thus, the first call to cmp.Equal reports false because the underlying types are not equal, before even giving the custom Comparer passed in a chance to apply.

We should probably box the top-level values into an empty interface to fix this.

\cc @neild

diff output with multiple differences is unhelpful

I ran across this recently when using cmp to test differences in unmarshal results.
One unmarshaler was unmarshaling numbers as floats, the other as ints, but the
Diff result didn't make it at all clear what the difference was.

Example code:

package main

import (
	"fmt"

	"github.com/google/go-cmp/cmp"
)

func main() {
	x := []interface{}{map[string]interface{}{"avg": 0.278, "hr": 65, "name": "Mark McGwire"}, map[string]interface{}{"avg": 0.288, "hr": 63, "name": "Sammy Sosa"}}
	y := []interface{}{map[string]interface{}{"avg": 0.278, "hr": 65.0, "name": "Mark McGwire"}, map[string]interface{}{"avg": 0.288, "hr": 63.0, "name": "Sammy Sosa"}}

	diff := cmp.Diff(x, y)
	fmt.Printf("diff: %s\n", diff)
}

This prints:

diff: root:
	-: []interface {}{map[string]interface {}{"avg": 0.278, "hr": 65, "name": "Mark McGwire"}, map[string]interface {}{"avg": 0.288, "hr": 63, "name": "Sammy Sosa"}}
	+: []interface {}{map[string]interface {}{"avg": 0.278, "hr": 65, "name": "Mark McGwire"}, map[string]interface {}{"avg": 0.288, "hr": 63, "name": "Sammy Sosa"}}

Note that both lines are identical.

Properly handle NaNs as map keys

http://golang.org/cl/33572 was just submitted, which adds a new reflect.Value.MapRange method, which can be used to retrieve the value for NaN keys. It is currently slated for release in Go 1.12.

go-cmp/cmp/compare.go

Lines 39 to 43 in 5411ab9

// BUG(dsnet): Maps with keys containing NaN values cannot be properly compared due to
// the reflection package's inability to retrieve such entries. Equal will panic
// anytime it comes across a NaN key, but this behavior may change.
//
// See https://golang.org/issue/11104 for more details.

invalid reflect.Value in compare reporter output

When we compare two structures using the compare method and custom reporter. In some cases, it is returning back nil, 0 etc and in some cases returning invalid reflect.Value.

Do we know what case it could return invalid reflect.Value?

Make it easier to do same-type transforms

I wanted to treat time.Times as rounded to the nearest microsecond. I first tried a transform, but it got into an infinite loop (as advertised). I had to add a FilterPath in front of it. This turned a one-line option into a ~10-line option.

So how about adding something like this to cmpopts:

// TransformOnce applies f, which should be of type func(T) T, to values of type T.                                                                                                            
// Unlike Transform, f is applied only once to each original value.                                                                                                                            
func TransformOnce(name string, f interface{}) cmp.Option {
    return cmp.FilterPath(func(p cmp.Path) bool {
        if len(p) == 0 {
            return true
        }
        xform, ok := p[len(p)-1].(cmp.Transform)
        if !ok {
            return true
        }
        return xform.Name() != name
    }, cmp.Transformer(name, f))
}

I know we discussed this earlier, but I still don't really understand why this isn't the default behavior of transformers.

/cc @bcmills

proposal: cmpopts: add convenience functions for errors

I've been frustrated lately with the number of tests that do error checking by performing string equality on the error message. As it stands, cmp is better than reflect.DeepEqual in that it usually fails when comparing errors since many errors have an unexported field somewhere in it forcing users to think about how to properly compare them. I believe cmp (or rather cmpopts) can go one further and assist users with comparing errors.

Consider the following:

// EquateErrors returns a Comparer option that determines errors to be equal
// if both are nil or both are non-nil. When both errors are non-nil,
// the Comparer checks whether either error is a CheckError and if so,
// calls it on the other error to determine equality.
// If neither error are a CheckError, then they are compared as if they
// are sentinel errors (i.e., using the == operator).
func EquateErrors() cmp.Option {
	return cmp.Comparer(func(e1, e2 error) bool {
		if e1 == nil || e2 == nil {
			return e1 == nil && e2 == nil
		}
		f1, _ := e1.(CheckError)
		f2, _ := e2.(CheckError)
		switch {
		case f1 == nil && f2 == nil:
			return e1 == e2
		case f1 != nil && f2 == nil:
			return f1(e2)
		case f1 == nil && f2 != nil:
			return f2(e1)
		default:
			return false
		}
	})
}

// CheckError is an error used by EquateErrors for finer comparisons on errors.
type CheckError func(error) bool

func (c CheckError) Error() string {
	return fmt.Sprintf("%#v", c)
}

It seem odd at first that CheckError is both a function and also an error, but it gives flexibility in being able to control how comparisons with errors works, by configuring the comparison as data, rather than through an option.

Consider the following example usage:

func Test(t *testing.T) {
	// nonNilError equates any non-nil error.
	// This is usual for just checking that the result failed, but not how it failed.
	nonNilError := CheckError(func(error) bool {
		return true
	})

	// notExistError equates any error based on a function predicate.
	notExistError := CheckError(os.IsNotExist)

	// timeoutError equates any error that is a timeout error.
	timeoutError := CheckError(func(e error) bool {
		ne, ok := e.(net.Error)
		return ok && ne.Timeout()
	})

	// containsEOF equates any error with EOF in the string.
	// NOTE: string matching on error messages is heavily frowned upon.
	containsEOF := CheckError(func(e error) bool {
		return strings.Contains(e.Error(), "EOF")
	})

	tests := []struct {
		err1 error
		err2 error
		want bool
	}{
		{io.EOF, io.EOF, true},
		{io.EOF, errors.New("EOF"), false},
		{io.EOF, io.ErrUnexpectedEOF, false},

		{io.EOF, nonNilError, true},
		{nonNilError, io.EOF, true},
		{nil, nonNilError, false},

		{io.EOF, notExistError, false},
		{os.ErrNotExist, notExistError, true},
		{os.ErrPermission, notExistError, false},

		{timeoutError, io.EOF, false},
		{timeoutError, &net.AddrError{}, false},
		{timeoutError, syscall.ETIMEDOUT, true},

		{io.EOF, containsEOF, true},
		{io.ErrUnexpectedEOF, containsEOF, true},
		{&net.ParseError{}, containsEOF, false},
	}

	for _, tt := range tests {
		got := cmp.Equal(&tt.err1, &tt.err2, EquateErrors())
		if got != tt.want {
			t.Errorf("Equal(%v, %v) = %v, want %v", tt.err1, tt.err2, got, tt.want)
		}
	}
}

Thus, EquateErrors has the following properties:

  • It is still symmetric, which is one of the required properties of cmp.Comparer. That is, the result the same regardless of whether CheckError is on the left or right side.
  • By default, it compares errors as if they are sentinel error.
  • By default, it does not perform string comparisons on error messages, but does not prevent it either.
  • It is extensible to handle all other error types through the use of a predicate function, which handles the other two common ways to distinguish errors (which are type assertions to an interface or type, or by using a predicate function like os.IsNotExist).

Related to #24

\cc @neild

Filtering struct field by name

Is it possible to filter a struct field by its name? Inside FilterValues we can only access to the variable itself, and reflect does not know about the variable name.

cmp.Equal doesn't work with big.Int out of the box

It would be nice if types like big.Int worked out of the box.
big.Int doesn't provide Equal method. It has Cmp though.

I don't know how common types with Cmp but no Equal are. If they're common then we could consider teaching cmp about Cmp methods. If they're not then perhaps we could make math/big provide Equal methods.

In general, it would be nice if cmp just worked with as many stdlib types as possible.

Having simple cmp.Equal work with more types would be nice but isn't a big deal either.

comparing different types

Hi, just a question about comparing different types as docs don't explicitly say it's not possible, but it's not working for me so far. Example on playground.

So it's a case where two structs have a lot of common fields, but also some specific to each one. I tried using cmpopts.IgnoreFields to ignore the differences, but still getting negative results from cmp.Equal.

Am I missing something, or is this not possible currently? Should it be possible (would be very handy for me 😉 )?

Should Transformers be recursive?

The current implementation of cmp.Equal recursively applies any Transformers that remain after applying all the filters. For a trivial Transformer of the type func(T) T, this will be recursively apply to the output since it will always match. For this reason, the cmpopts.SortSlices is wrapped with a FilterValues that acts as a "base-case" to prevent infinite recursion. One alteration to the algorithm is to say that a given Transformer cannot be recursively applied on a value that was transformed at some point by that very Transformer.

The major advantage of non-recursive Transformers is:

  • A naive use of func(T) T works as expected without any need for a "base-case" filter.

The disadvantages of non-recursive Transformer is:

  • The additional documentation needed to explain the special-case where Transformers are not executed. The implementation of this is essentially a set of "spent" Transformers, where transformers are added to the set when descending down the tree, and popped when ascending up the tree.
  • In some rare use-cases you may actually want Transformers to be recursive. Imagine you have a non-binary tree, where each node is essentially a set of children. When comparing this data-structure, you may want to sort each node using a transformer. You would need the property of recursive transformers to ensure each node may get transformed.
  • The need for "base-case" filters to prevent recursive cycles.

Thoughts? I do see the value in non-recursive Transformers. It simplifies use of them, but prevents some very rare use-cases.

\cc @bcmills @jba

Merge cmpopts and cmp package?

As a semi-frequent user of the cmp package, most of the time I pull up godoc.org reference is to figure out whether a particular useful option is in cmpopts or the cmp package. Genuinely curious: is there a meaningful end-user benefit to keeping these packages separate? I seldom use cmp without cmpopts, and I suspect this is common.

/cc @dsnet

Provide API to programmatically interpret differences

I have config objects in a DSL that are described via a series of structs. As an example:
https://play.golang.org/p/NQfRr0iJy8s

Given two different instances of the Quiz struct, I'd like to be able to find out starting at any particular level, if there are differences.

If I used the cmp.Diff() method, I think I could parse out the syntax to determine what the actual changes, adds, and deletes are, but not only does this feel fragile, it is explicitly warned against in the docs.

So, is there a better way?

Get tag struct field inside reporter

Hi everyone,

I'm actually trying to make a Diff between 2 objects. I want to create a custom reporter to get back the path of the fields which have been changed from obj1 to obj2, using the "json" tag of the field.

For example, if the calling code of the user has been changed, the path would be something like this : phone.callingCode and not Phone.CallingCode like the reporter I created is doing right now :s.

This is the structure for my 2 objects

type Phone struct {
	CallingCode int `json:"callingCode"`
}
type User struct {
	Name  string `json:"name"`
	Phone Phone  `json:"phone"`
}

And here is the code of my current reporter :

type DiffReporter struct {
	path  cmp.Path
	Diffs []string
}

func (r *DiffReporter) PushStep(ps cmp.PathStep) {
	r.path = append(r.path, ps)
}

func (r *DiffReporter) Report(rs cmp.Result) {
	if !rs.Equal() {
		r.Diffs = append(r.Diffs, r.path.String())
	}
}

func (r *DiffReporter) PopStep() {
	r.path = r.path[:len(r.path)-1]
}

func (r *DiffReporter) String() string {
	return strings.Join(r.Diffs, "\n")
}

Is there a way to have, in the Report or PushStep function, the json tag field rather than the name of the structure field ?

Thanks in advance :)

`go-cmp` should ignore generated proto fields

XXX_cachesize auto-generated proto fields are not currently ignored by go-cmp. Example from Grafeas:

storage_test.go:287: GetOccurrence returned diff (want -> got):
            {*grafeas_go_proto.Occurrence}.Resource.XXX_sizecache:
            	-: 0
            	+: 16
            {*grafeas_go_proto.Occurrence}.Details.Vulnerability.PackageIssue[0].AffectedLocation.Version.XXX_sizecache:
            	-: 0
            	+: 16
            {*grafeas_go_proto.Occurrence}.Details.Vulnerability.PackageIssue[0].AffectedLocation.XXX_sizecache:
            	-: 0
            	+: 53
            {*grafeas_go_proto.Occurrence}.Details.Vulnerability.PackageIssue[0].FixedLocation.Version.XXX_sizecache:
            	-: 0
            	+: 16
            {*grafeas_go_proto.Occurrence}.Details.Vulnerability.PackageIssue[0].FixedLocation.XXX_sizecache:
            	-: 0
            	+: 53
            {*grafeas_go_proto.Occurrence}.Details.Vulnerability.PackageIssue[0].XXX_sizecache:
            	-: 0
            	+: 116
            {*grafeas_go_proto.Occurrence}.Details.Vulnerability.XXX_sizecache:
            	-: 0
            	+: 125
            {*grafeas_go_proto.Occurrence}.XXX_sizecache:
            	-: 0
            	+: 307

Improve diffing of modified elements

The following snippet:

fs, _ := ioutil.ReadDir("/etc")
if len(fs) > 10 {
	fs = fs[:10]
}

var got, want []*tar.Header
for _, fi := range fs {
	hdr, _ := tar.FileInfoHeader(fi, "")
	want = append(want, hdr)
	hdr2 := *hdr
	hdr2.Typeflag = 0 // Make a small change
	got = append(got, &hdr2)
}

fmt.Println(cmp.Diff(got, want))

prints the following on my Linux machine:

{[]*tar.Header}:
	-: []*tar.Header{&{Name: ".java/", Mode: 493, ModTime: "2014-11-26 14:38:11 -0800 PST", AccessTime: "2017-09-22 12:45:06.64783386 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: ".pwd.lock", Mode: 384, ModTime: "2014-11-26 14:14:08 -0800 PST", AccessTime: "2015-05-04 12:54:24.4199913 -0700 PDT", ChangeTime: "2015-05-02 02:22:13.794329959 -0700 PDT"}, &{Name: "ConsoleKit/", Mode: 493, ModTime: "2014-11-26 14:28:50 -0800 PST", AccessTime: "2017-09-22 12:45:06.643833911 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "ImageMagick/", Mode: 493, ModTime: "2016-07-24 03:15:28.041037224 -0700 PDT", AccessTime: "2017-09-22 12:45:06.65583376 -0700 PDT", ChangeTime: "2016-07-24 03:15:28.041037224 -0700 PDT"}, &{Name: "NetworkManager/", Mode: 493, ModTime: "2014-11-26 14:44:19 -0800 PST", AccessTime: "2017-09-22 12:45:06.65183381 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "ODBCDataSources/", Mode: 493, ModTime: "2013-12-11 00:06:48 -0800 PST", AccessTime: "2017-09-22 12:45:06.64783386 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "UPower/", Mode: 493, ModTime: "2014-11-26 14:43:32 -0800 PST", AccessTime: "2017-09-22 12:45:06.65183381 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "X11/", Mode: 493, ModTime: "2014-11-26 14:46:10 -0800 PST", AccessTime: "2017-09-22 12:45:06.65183381 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "acpi/", Mode: 493, ModTime: "2014-11-26 14:48:03 -0800 PST", AccessTime: "2017-09-22 12:45:06.65183381 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "adduser.conf", Mode: 420, Size: 2981, ModTime: "2014-11-26 14:14:10 -0800 PST", AccessTime: "2017-09-22 12:45:31.547520829 -0700 PDT", ChangeTime: "2015-05-02 02:22:13.794329959 -0700 PDT"}}
	+: []*tar.Header{&{Name: ".java/", Mode: 493, ModTime: "2014-11-26 14:38:11 -0800 PST", Typeflag: 0x35, AccessTime: "2017-09-22 12:45:06.64783386 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: ".pwd.lock", Mode: 384, ModTime: "2014-11-26 14:14:08 -0800 PST", Typeflag: 0x30, AccessTime: "2015-05-04 12:54:24.4199913 -0700 PDT", ChangeTime: "2015-05-02 02:22:13.794329959 -0700 PDT"}, &{Name: "ConsoleKit/", Mode: 493, ModTime: "2014-11-26 14:28:50 -0800 PST", Typeflag: 0x35, AccessTime: "2017-09-22 12:45:06.643833911 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "ImageMagick/", Mode: 493, ModTime: "2016-07-24 03:15:28.041037224 -0700 PDT", Typeflag: 0x35, AccessTime: "2017-09-22 12:45:06.65583376 -0700 PDT", ChangeTime: "2016-07-24 03:15:28.041037224 -0700 PDT"}, &{Name: "NetworkManager/", Mode: 493, ModTime: "2014-11-26 14:44:19 -0800 PST", Typeflag: 0x35, AccessTime: "2017-09-22 12:45:06.65183381 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "ODBCDataSources/", Mode: 493, ModTime: "2013-12-11 00:06:48 -0800 PST", Typeflag: 0x35, AccessTime: "2017-09-22 12:45:06.64783386 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "UPower/", Mode: 493, ModTime: "2014-11-26 14:43:32 -0800 PST", Typeflag: 0x35, AccessTime: "2017-09-22 12:45:06.65183381 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "X11/", Mode: 493, ModTime: "2014-11-26 14:46:10 -0800 PST", Typeflag: 0x35, AccessTime: "2017-09-22 12:45:06.65183381 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "acpi/", Mode: 493, ModTime: "2014-11-26 14:48:03 -0800 PST", Typeflag: 0x35, AccessTime: "2017-09-22 12:45:06.65183381 -0700 PDT", ChangeTime: "2015-05-02 02:26:44.442187155 -0700 PDT"}, &{Name: "adduser.conf", Mode: 420, Size: 2981, ModTime: "2014-11-26 14:14:10 -0800 PST", Typeflag: 0x30, AccessTime: "2017-09-22 12:45:31.547520829 -0700 PDT", ChangeTime: "2015-05-02 02:22:13.794329959 -0700 PDT"}}

This is completely unreadable. The issue is that the diffing algorithm detects every element to be different (modified from each other) and decides that these two slices are completely unrelated and tries to just print the whole thing. We should adjust the heuristics for this.

Unexported map value causes go-cmp to panic

Input:

func TestBreakCmp(t *testing.T) {

type unexported int
type Foo struct {
	m map[string]unexported
}

f1 := &Foo{m: map[string]unexported{"foo": 1, "bar": 2}} // One key will work. Multiple causes panic.
f2 := &Foo{m: map[string]unexported{"bar": 3, "baz": 4}}

// Passes!
cmp.Diff(f1, f2, cmp.Comparer(func(_, _ Foo) bool {
	return true
}))

// Panics!
cmp.Diff(f1, f2, cmp.Comparer(func(_, _ Foo) bool {
	return false
}))

}

Output:

panic: reflect.Value.Interface: cannot return value obtained from unexported field or method [recovered]
panic: ,reflect.Value.Interface: cannot return value obtained from unexported field or method

goroutine 6 [running]:
testing.tRunner.func1(0xc0420a40f0)
C:/Go/src/testing/testing.go:711 +0x2d9
panic,(0x578e40, 0x5d5660)
C:/Go/src/runtime/panic.go:491 +0x291
,reflect.valueInterface(0x578e40, 0xc0421065b0, 0xb8, ,0x1, 0x4, 0x2)
C:/Go/src/reflect/value.go:936, +0x1e4
reflect.Value.Interface(0x578e40, 0xc0421065b0,, 0xb8, 0xc0420fe3e0, 0xc042136000)
C:/Go/src/reflect/value.go,:925 +0x4b
github.com/google/go-cmp/cmp/internal/value.SortKeys(0xc042102060,, 0x2, 0x2, 0xc042102060, 0x2, ,0x2)
D:/work/go/src/github.com/google/go-cmp/cmp/internal/value/sort.go:27 +0x16d
,github.com/google/go-cmp/cmp/internal/value.formatAny(0x587e20, 0xc0420026d8, 0x1b5,, 0x1010100, 0x0, 0x25, 0x688ce0)
, D:/work/go/src/github.com/google/go-cmp/cmp/internal/value/format.go:132 +0x1a38
github.com/google/go-cmp/cmp/internal/value.formatAny,(0x590c40, 0xc0420026c0, 0x199, 0xc001010101, ,0x0, 0xc042100012, 0x9)
D:/work/go/src/github.com/google/go-cmp/cmp/internal/value/format.go:,153 +0xea0
github.com/google/go-cmp/cmp/internal/value.Format(0x590c40, 0xc0420026c0, 0x199, 0x4bf901, 0xc042100012, 0x9)
D:/work/go/src/github.com/google/go-cmp/cmp/internal/value/format.go:30 +0x6d
github.com/google/go-cmp/cmp.(*defaultReporter).Report(0xc042038c40, 0x590c40,, 0xc042002680, 0x199, 0x590c40, 0xc0420026c0, 0x199, ,0x590c00, 0xc0420028c0, 0x2, ...)
D:/work/go/src/github.com/google/go-cmp/cmp/reporter.go:,34 +0xf7
github.com/google/go-cmp/cmp.(*state).report(0xc042082310, 0x583f00,, 0x590c40, 0xc042002680, 0x199, 0x590c40, ,0xc0420026c0, 0x199)
D:/work/go/src/github.com/google/go-cmp/cmp/compare.go:492, +0xce
github.com/google/go-cmp/cmp.(*comparer).apply(0xc042070630, 0xc042082310,, 0x590c40, 0xc042002680, 0x199, 0x590c40, 0xc0420026c0,, 0x199, 0xc0420026c0)
D:/work/go/src/github.com/google/go-cmp/cmp/options.go:315, +0x103
github.com/google/go-cmp/cmp.(*state).tryOptions(0xc042082310, 0x590c40, ,0xc042002680, 0x199, 0x590c40, 0xc0420026c0, 0x199,, 0x688ce0, 0x590c40, 0x199)
D:/work/go/src/github.com/google/go-cmp/cmp/compare.go:307 +0x154
github.com/google/go-cmp/cmp.(*state).compareAny,(0xc042082310, 0x590c40, 0xc042002680, 0x199, 0x590c40,, 0xc0420026c0, 0x199)
D:/work/go/src/github.com/google/go-cmp/cmp/compare.go:202, +0x343
github.com/google/go-cmp/cmp.(*state).compareAny(0xc042082310, 0x5857e0, ,0xc042002680, 0x16, 0x5857e0, 0xc0420026c0, 0x16)
D:/work/go/src/github.com/google/go-cmp/cmp/compare.go:244 +0x1540
github.com/google/go-cmp/cmp.Equal(0x5857e0, 0xc042002680, 0x5857e0, 0xc0420026c0, 0xc042002880, 0x2, 0x2, 0x590c40)
D:/work/go/src/github.com/google/go-cmp/cmp/compare.go:86 +0xf8
github.com/google/go-cmp/cmp.Diff(0x5857e0, 0xc042002680, 0x5857e0, 0xc0420026c0, 0xc042002860, 0x2, 0x2, 0xc042093f98, 0x4f6b55)
D:/work/go/src/github.com/google/go-cmp/cmp/compare.go,:100 +0x13a

nil slice/map should compare equal to empty slice/map

The documentation states:

Slices and arrays are equal if they have the same length and the elements
at each index are equal. Maps are equal if their keys are exactly equal
(according to the == operator) and the corresponding elements for each
key are equal.

I read this as implying that this program would print true true, but
instead it prints false false.

package main

import (
	"github.com/google/go-cmp/cmp"
	"log"
)

func main() {
	log.Println(
		cmp.Equal(map[int]string{}, map[int]string(nil)),
		cmp.Equal([]int{}, []int(nil)),
	)
}

Ignore doesn't always work for map keys

If there's an Ignore rule that matches a key in a map, I'd expect that key
to be ignored even if it doesn't exist in the map being compared to.

For example, the following code says that the two values aren't equal
even though I've specified that the "x" key should be ignored:

package main

import (
	"fmt"

	"github.com/google/go-cmp/cmp"
)

func main() {
	a := map[string]int{
		"x": 1,
		"y": 2,
	}
	b := map[string]int{
		"y": 2,
	}
	fmt.Println(cmp.Diff(a, b, cmp.FilterPath(isKeyX, cmp.Ignore())))
}

func ignorePath(pat string) cmp.Option {
	return cmp.FilterPath(isKeyX, cmp.Ignore())
}

func isKeyX(p cmp.Path) bool {
	step, ok := p[len(p)-1].(cmp.MapIndex)
	return ok && step.Key().String() == "x"
}

It prints:

{map[string]int}["x"]:
	-: 1
	+: <non-existent>

If I add an "x" key (with any value) to b, then they compare equal as expected.

Perform additional checks in Travis CI.

@dsnet, in #9 (comment), I asked if you wanted a PR that adds a gofmt check to Travis CI, and you said "that would be great". So I sent #12 for that.

However, I'd like to propose performing additional checks in CI. After years of iterating on it, I've arrived at the following .travis.yml script, which I generate for all my Go repositories:

sudo: false
language: go
go:
  - 1.x
  - master
matrix:
  allow_failures:
    - go: master
  fast_finish: true
install:
  - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
script:
  - go get -t -v ./...
  - diff -u <(echo -n) <(gofmt -d -s .)
  - go tool vet .
  - go test -v -race ./...

Here's an explanation of its features over the minimal .travis.yml change I sent in #12 that adds the gofmt only:

  • Use -s flag with gofmt to ensure code simplifications are made.
  • Use -race flag with go test to enable race detector.
  • Run go vet on the directory to catch vet issues.
  • Run a fast-finish allowed failure build on Go tip version. Allowed failure means a temporary issue in Go tip won't fail the build, but one can still manually see if there are some real failures in that version by manually checking Travis builds occasionally. Fast-finish just means the overall build status is computed faster, without waiting for allowed failure steps (I'm not really sure why it's an option instead of just being a default). See more info about it at https://blog.travis-ci.com/2013-11-27-fast-finishing-builds/.
  • Move go get -t -v ./... step from install step to script. This one is very minor and more of a personal preference, but it makes it so that if there's a build failure in any of the dependencies, the build appears as a red "failure", rather than a gray "error".

@dsnet Let me know if you'd like any or all of the above enhancements, I'm happy to send a followup PR.

Question: How do I apply a transformer to a child field of a struct depending on the value of another field in the struct?

I have a data structure that looks like this:

type Mystruct struct {
  DataType int
  Data []byte
}

type Container struct {
  SomeValue string
  Children []Mystruct
}

The bytes contained in Data must be unmarshalled and transformed depending on the value DataType holds.

I tried using a FilterValue:

func mytransformer(in []bytes) AnotherConcreteType{
   ...
}

cmp.FilterValue(func(x,y Mystruct) bool {
  return x.DataType == somevalue && y.DataType == somevalue
}, cmpopts. AcyclicTransformer("mytransformer", mytransformer)),

I think (according to my understanding, it will attempt to apply the transfer from the Mystruct struct and will not attempt to apply it to the fields within Mystruct.

Is there any way to achieve this?

IgnoreFields doesn't seem to work?

Let me know if am doing something wrong here.

I tried ignorefields but it doesn't work.
struct is defined as below and trying to ignore the As field. xxx is the package in which the struct is defined.
type Neighbor struct {
Address string
LocalAddress string
As uint32
}
cmpopts.IgnoreFields(xxx.Neighbor{}, "AS")

gives panic error.

github.com/google/go-cmp/cmp/cmpopts.newStructFilter(0x126daa0, 0xc0003fdb30, 0xc000641c38, 0x1, 0x1, 0xc000641a68, 0xc000641a68, 0x40b01f, 0x126daa0)
go/src/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go:51 +0x2ff
github.com/google/go-cmp/cmp/cmpopts.IgnoreFields(0x126daa0, 0xc0003fdb30, 0xc000641c38, 0x1, 0x1, 0x98, 0x12043c0)
go/src/github.com/google/go-cmp/cmp/cmpopts/ignore.go:25 +0x63

Improve documentation for Transformer name

I tried writing something like:

// StatusCode is a type I declared with a String method.
diff := cmp.Diff(test.want, got, cmp.Transformer("StatusCode.String", StatusCode.String))

When I do this, I get a runtime panic of:

panic: invalid name: "StatusCode.String"

In the documentation for cmp.Transformer, the only mention of the name I see is at the very end:

The name is a user provided label that is used as the Transform.Name in the transformation PathStep. If empty, an arbitrary name is used.

Here are the problem I see:

  1. This doesn't help me understand what kind of name to use. As an example of the unanswered questions I'm left with: Is it human readable or used for programmatic keying later? Do I need to care for the above? Should I leave it blank? When does this show up?
  2. I see no mention of what characters are allowed or not or why. Without digging into source code, I have no way of knowing why I got this panic.

@dsnet

Add IgnoreFieldsExcept

For larger structs and when the API is potentially evolving it would be handy to have the inverse of IgnoreFields, i.e. cmpopts.FilterFields(typ interface{}, names ...string) cmp.Option.

And for symmetry I guess there should also be FilterInterfaces and FilterTypes.

An alternative naming could be Include… if this clashes to much with the root packages concept of filters.

proposal: add Debugger option

For sufficiently complex values that are deeply nested and some combination of filters, it may be worth adding functionality to help with debugging. Perhaps we could add:

func Debugger(io.Writer) Option

When this option is passed to Equal or Diff, it will log all of the steps going on as Equal traverses through the value tree. It can also report which options were applicable.

Example output:

{teststructs.Cartel}.Headquarter
{teststructs.Cartel}.Headquarter.id
{teststructs.Cartel}.Headquarter.modified
	applicable method: time.Time.Equal
	result: returned true
{teststructs.Cartel}.Headquarter.metaData
	applicable options:
		FilterPath(cmpopts.(structFilter).filter, Ignore())
		Comparer(testprotos.Equal)
	result: ignored
{teststructs.Cartel}.Headquarter.subDivisions
{teststructs.Cartel}.Headquarter.subDivisions[0]
	applicable option: Comparer(testprotos.Equal)
	result: returned true
{teststructs.Cartel}.Headquarter.subDivisions[1]
	applicable option: Comparer(testprotos.Equal)
	result: returned true

It would be nice if cmpopts.(structFilter).filter could be more descriptive (e.g., which struct and field), but I don't know any way for Go to have custom String methods on function pointers.

Compile under Go 1.6

For now, we're only worrying about current Go version, but since we'd like to use this for App Engine-targeted packages as well, it would be good to support Go 1.6.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.