1 // Copyright 2017, The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE.md file.
27 "github.com/google/go-cmp/cmp"
28 "github.com/google/go-cmp/cmp/cmpopts"
29 "github.com/google/go-cmp/cmp/internal/flags"
31 pb "github.com/google/go-cmp/cmp/internal/testprotos"
32 ts "github.com/google/go-cmp/cmp/internal/teststructs"
33 foo1 "github.com/google/go-cmp/cmp/internal/teststructs/foo1"
34 foo2 "github.com/google/go-cmp/cmp/internal/teststructs/foo2"
38 flags.Deterministic = true
41 var update = flag.Bool("update", false, "update golden test files")
43 const goldenHeaderPrefix = "<<< "
44 const goldenFooterPrefix = ">>> "
46 /// mustParseGolden parses a file as a set of key-value pairs.
48 // The syntax is simple and looks something like:
58 // It is the user's responsibility to choose a sufficiently unique key name
59 // such that it never appears in the body of the value itself.
60 func mustParseGolden(path string) map[string]string {
61 b, err := ioutil.ReadFile(path)
67 out := map[string]string{}
69 // Identify the next header.
70 i := strings.Index(s, "\n") + len("\n")
72 if !strings.HasPrefix(header, goldenHeaderPrefix) {
73 panic(fmt.Sprintf("invalid header: %q", header))
76 // Locate the next footer.
77 footer := goldenFooterPrefix + header[len(goldenHeaderPrefix):]
78 j := strings.Index(s, footer)
80 panic(fmt.Sprintf("missing footer: %q", footer))
83 // Store the name and data.
84 name := header[len(goldenHeaderPrefix) : len(header)-len("\n")]
85 if _, ok := out[name]; ok {
86 panic(fmt.Sprintf("duplicate name: %q", name))
88 out[name] = s[len(header):j]
93 func mustFormatGolden(path string, in []struct{ Name, Data string }) {
95 for _, v := range in {
96 b = append(b, goldenHeaderPrefix+v.Name+"\n"...)
97 b = append(b, v.Data...)
98 b = append(b, goldenFooterPrefix+v.Name+"\n"...)
100 if err := ioutil.WriteFile(path, b, 0664); err != nil {
105 var now = time.Date(2009, time.November, 10, 23, 00, 00, 00, time.UTC)
107 func newInt(n int) *int { return &n }
111 func newStringer(s string) fmt.Stringer { return (*Stringer)(&s) }
112 func (s Stringer) String() string { return string(s) }
115 label string // Test name
116 x, y interface{} // Input values to compare
117 opts []cmp.Option // Input options
118 wantEqual bool // Whether any difference is expected
119 wantPanic string // Sub-string of an expected panic message
120 reason string // The reason for the expected outcome
123 func TestDiff(t *testing.T) {
125 tests = append(tests, comparerTests()...)
126 tests = append(tests, transformerTests()...)
127 tests = append(tests, reporterTests()...)
128 tests = append(tests, embeddedTests()...)
129 tests = append(tests, methodTests()...)
130 tests = append(tests, cycleTests()...)
131 tests = append(tests, project1Tests()...)
132 tests = append(tests, project2Tests()...)
133 tests = append(tests, project3Tests()...)
134 tests = append(tests, project4Tests()...)
136 const goldenFile = "testdata/diffs"
137 gotDiffs := []struct{ Name, Data string }{}
138 wantDiffs := mustParseGolden(goldenFile)
139 for _, tt := range tests {
141 t.Run(tt.label, func(t *testing.T) {
145 var gotDiff, gotPanic string
148 if ex := recover(); ex != nil {
149 if s, ok := ex.(string); ok {
156 gotDiff = cmp.Diff(tt.x, tt.y, tt.opts...)
160 case strings.Contains(t.Name(), "#"):
161 panic("unique test name must be provided")
162 case tt.reason == "":
163 panic("reason must be provided")
164 case tt.wantPanic == "":
166 t.Fatalf("unexpected panic message: %s\nreason: %v", gotPanic, tt.reason)
170 gotDiffs = append(gotDiffs, struct{ Name, Data string }{t.Name(), gotDiff})
173 wantDiff := wantDiffs[t.Name()]
174 if diff := cmp.Diff(wantDiff, gotDiff); diff != "" {
175 t.Fatalf("Diff:\ngot:\n%s\nwant:\n%s\ndiff: (-want +got)\n%s\nreason: %v", gotDiff, wantDiff, diff, tt.reason)
178 gotEqual := gotDiff == ""
179 if gotEqual != tt.wantEqual {
180 t.Fatalf("Equal = %v, want %v\nreason: %v", gotEqual, tt.wantEqual, tt.reason)
183 if !strings.Contains(gotPanic, tt.wantPanic) {
184 t.Fatalf("panic message:\ngot: %s\nwant: %s\nreason: %v", gotPanic, tt.wantPanic, tt.reason)
191 mustFormatGolden(goldenFile, gotDiffs)
195 func comparerTests() []test {
196 const label = "Comparer"
198 type Iface1 interface {
201 type Iface2 interface {
205 type tarHeader struct {
220 Xattrs map[string]string
223 type namedWithUnexported struct {
227 makeTarHeaders := func(tf byte) (hs []tarHeader) {
228 for i := 0; i < 5; i++ {
229 hs = append(hs, tarHeader{
230 Name: fmt.Sprintf("some/dummy/test/file%d", i),
231 Mode: 0664, Uid: i * 1000, Gid: i * 1000, Size: 1 << uint(i),
232 ModTime: now.Add(time.Duration(i) * time.Hour),
233 Uname: "user", Gname: "group",
241 label: label + "/Nil",
245 reason: "nils are equal",
247 label: label + "/Integer",
251 reason: "identical integers are equal",
253 label: label + "/UnfilteredIgnore",
256 opts: []cmp.Option{cmp.Ignore()},
257 wantPanic: "cannot use an unfiltered option",
258 reason: "unfiltered options are functionally useless",
260 label: label + "/UnfilteredCompare",
263 opts: []cmp.Option{cmp.Comparer(func(_, _ interface{}) bool { return true })},
264 wantPanic: "cannot use an unfiltered option",
265 reason: "unfiltered options are functionally useless",
267 label: label + "/UnfilteredTransform",
270 opts: []cmp.Option{cmp.Transformer("λ", func(x interface{}) interface{} { return x })},
271 wantPanic: "cannot use an unfiltered option",
272 reason: "unfiltered options are functionally useless",
274 label: label + "/AmbiguousOptions",
278 cmp.Comparer(func(x, y int) bool { return true }),
279 cmp.Transformer("λ", func(x int) float64 { return float64(x) }),
281 wantPanic: "ambiguous set of applicable options",
282 reason: "both options apply on int, leading to ambiguity",
284 label: label + "/IgnorePrecedence",
288 cmp.FilterPath(func(p cmp.Path) bool {
289 return len(p) > 0 && p[len(p)-1].Type().Kind() == reflect.Int
290 }, cmp.Options{cmp.Ignore(), cmp.Ignore(), cmp.Ignore()}),
291 cmp.Comparer(func(x, y int) bool { return true }),
292 cmp.Transformer("λ", func(x int) float64 { return float64(x) }),
295 reason: "ignore takes precedence over other options",
297 label: label + "/UnknownOption",
298 opts: []cmp.Option{struct{ cmp.Option }{}},
299 wantPanic: "unknown option",
300 reason: "use of unknown option should panic",
302 label: label + "/StructEqual",
303 x: struct{ A, B, C int }{1, 2, 3},
304 y: struct{ A, B, C int }{1, 2, 3},
306 reason: "struct comparison with all equal fields",
308 label: label + "/StructInequal",
309 x: struct{ A, B, C int }{1, 2, 3},
310 y: struct{ A, B, C int }{1, 2, 4},
312 reason: "struct comparison with inequal C field",
314 label: label + "/StructUnexported",
315 x: struct{ a, b, c int }{1, 2, 3},
316 y: struct{ a, b, c int }{1, 2, 4},
317 wantPanic: "cannot handle unexported field",
318 reason: "unexported fields result in a panic by default",
320 label: label + "/PointerStructEqual",
321 x: &struct{ A *int }{newInt(4)},
322 y: &struct{ A *int }{newInt(4)},
324 reason: "comparison of pointer to struct with equal A field",
326 label: label + "/PointerStructInequal",
327 x: &struct{ A *int }{newInt(4)},
328 y: &struct{ A *int }{newInt(5)},
330 reason: "comparison of pointer to struct with inequal A field",
332 label: label + "/PointerStructTrueComparer",
333 x: &struct{ A *int }{newInt(4)},
334 y: &struct{ A *int }{newInt(5)},
336 cmp.Comparer(func(x, y int) bool { return true }),
339 reason: "comparison of pointer to struct with inequal A field, but treated as equal with always equal comparer",
341 label: label + "/PointerStructNonNilComparer",
342 x: &struct{ A *int }{newInt(4)},
343 y: &struct{ A *int }{newInt(5)},
345 cmp.Comparer(func(x, y *int) bool { return x != nil && y != nil }),
348 reason: "comparison of pointer to struct with inequal A field, but treated as equal with comparer checking pointers for nilness",
350 label: label + "/StructNestedPointerEqual",
351 x: &struct{ R *bytes.Buffer }{},
352 y: &struct{ R *bytes.Buffer }{},
354 reason: "equal since both pointers in R field are nil",
356 label: label + "/StructNestedPointerInequal",
357 x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)},
358 y: &struct{ R *bytes.Buffer }{},
360 reason: "inequal since R field is inequal",
362 label: label + "/StructNestedPointerTrueComparer",
363 x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)},
364 y: &struct{ R *bytes.Buffer }{},
366 cmp.Comparer(func(x, y io.Reader) bool { return true }),
369 reason: "equal despite inequal R field values since the comparer always reports true",
371 label: label + "/StructNestedValueUnexportedPanic1",
372 x: &struct{ R bytes.Buffer }{},
373 y: &struct{ R bytes.Buffer }{},
374 wantPanic: "cannot handle unexported field",
375 reason: "bytes.Buffer contains unexported fields",
377 label: label + "/StructNestedValueUnexportedPanic2",
378 x: &struct{ R bytes.Buffer }{},
379 y: &struct{ R bytes.Buffer }{},
381 cmp.Comparer(func(x, y io.Reader) bool { return true }),
383 wantPanic: "cannot handle unexported field",
384 reason: "bytes.Buffer value does not implement io.Reader",
386 label: label + "/StructNestedValueEqual",
387 x: &struct{ R bytes.Buffer }{},
388 y: &struct{ R bytes.Buffer }{},
390 cmp.Transformer("Ref", func(x bytes.Buffer) *bytes.Buffer { return &x }),
391 cmp.Comparer(func(x, y io.Reader) bool { return true }),
394 reason: "bytes.Buffer pointer due to shallow copy does implement io.Reader",
396 label: label + "/RegexpUnexportedPanic",
397 x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
398 y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
399 wantPanic: "cannot handle unexported field",
400 reason: "regexp.Regexp contains unexported fields",
402 label: label + "/RegexpEqual",
403 x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
404 y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
405 opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool {
406 if x == nil || y == nil {
407 return x == nil && y == nil
409 return x.String() == y.String()
412 reason: "comparer for *regexp.Regexp applied with equal regexp strings",
414 label: label + "/RegexpInequal",
415 x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
416 y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*d*")},
417 opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool {
418 if x == nil || y == nil {
419 return x == nil && y == nil
421 return x.String() == y.String()
424 reason: "comparer for *regexp.Regexp applied with inequal regexp strings",
426 label: label + "/TriplePointerEqual",
440 reason: "three layers of pointers to the same value",
442 label: label + "/TriplePointerInequal",
456 reason: "three layers of pointers to different values",
458 label: label + "/SliceWithDifferingCapacity",
459 x: []int{1, 2, 3, 4, 5}[:3],
462 reason: "elements past the slice length are not compared",
464 label: label + "/StringerEqual",
465 x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")},
466 y: struct{ fmt.Stringer }{regexp.MustCompile("hello")},
467 opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })},
469 reason: "comparer for fmt.Stringer used to compare differing types with same string",
471 label: label + "/StringerInequal",
472 x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")},
473 y: struct{ fmt.Stringer }{regexp.MustCompile("hello2")},
474 opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })},
476 reason: "comparer for fmt.Stringer used to compare differing types with different strings",
478 label: label + "/DifferingHash",
479 x: md5.Sum([]byte{'a'}),
480 y: md5.Sum([]byte{'b'}),
482 reason: "hash differs",
484 label: label + "/NilStringer",
485 x: new(fmt.Stringer),
488 reason: "by default differing types are always inequal",
490 label: label + "/TarHeaders",
491 x: makeTarHeaders('0'),
492 y: makeTarHeaders('\x00'),
494 reason: "type flag differs between the headers",
496 label: label + "/NonDeterministicComparer",
497 x: make([]int, 1000),
498 y: make([]int, 1000),
500 cmp.Comparer(func(_, _ int) bool {
501 return rand.Intn(2) == 0
504 wantPanic: "non-deterministic or non-symmetric function detected",
505 reason: "non-deterministic comparer",
507 label: label + "/NonDeterministicFilter",
508 x: make([]int, 1000),
509 y: make([]int, 1000),
511 cmp.FilterValues(func(_, _ int) bool {
512 return rand.Intn(2) == 0
515 wantPanic: "non-deterministic or non-symmetric function detected",
516 reason: "non-deterministic filter",
518 label: label + "/AssymetricComparer",
519 x: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
520 y: []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
522 cmp.Comparer(func(x, y int) bool {
526 wantPanic: "non-deterministic or non-symmetric function detected",
527 reason: "asymmetric comparer",
529 label: label + "/NonDeterministicTransformer",
530 x: make([]string, 1000),
531 y: make([]string, 1000),
533 cmp.Transformer("λ", func(x string) int {
537 wantPanic: "non-deterministic function detected",
538 reason: "non-deterministic transformer",
540 label: label + "/IrreflexiveComparison",
544 cmp.Transformer("λ", func(x int) float64 {
549 reason: "dynamic checks should not panic for non-reflexive comparisons",
551 label: label + "/StringerMapKey",
552 x: map[*pb.Stringer]*pb.Stringer{{"hello"}: {"world"}},
553 y: map[*pb.Stringer]*pb.Stringer(nil),
555 reason: "stringer should be used to format the map key",
557 label: label + "/StringerBacktick",
558 x: []*pb.Stringer{{`multi\nline\nline\nline`}},
560 reason: "stringer should use backtick quoting if more readable",
562 label: label + "/AvoidPanicAssignableConverter",
563 x: struct{ I Iface2 }{},
564 y: struct{ I Iface2 }{},
566 cmp.Comparer(func(x, y Iface1) bool {
567 return x == nil && y == nil
571 reason: "function call using Go reflection should automatically convert assignable interfaces; see https://golang.org/issues/22143",
573 label: label + "/AvoidPanicAssignableTransformer",
574 x: struct{ I Iface2 }{},
575 y: struct{ I Iface2 }{},
577 cmp.Transformer("λ", func(v Iface1) bool {
582 reason: "function call using Go reflection should automatically convert assignable interfaces; see https://golang.org/issues/22143",
584 label: label + "/AvoidPanicAssignableFilter",
585 x: struct{ I Iface2 }{},
586 y: struct{ I Iface2 }{},
588 cmp.FilterValues(func(x, y Iface1) bool {
589 return x == nil && y == nil
593 reason: "function call using Go reflection should automatically convert assignable interfaces; see https://golang.org/issues/22143",
595 label: label + "/DynamicMap",
596 x: []interface{}{map[string]interface{}{"avg": 0.278, "hr": 65, "name": "Mark McGwire"}, map[string]interface{}{"avg": 0.288, "hr": 63, "name": "Sammy Sosa"}},
597 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"}},
599 reason: "dynamic map with differing types (but semantically equivalent values) should be inequal",
601 label: label + "/MapKeyPointer",
609 reason: "map keys should use shallow (rather than deep) pointer comparison",
611 label: label + "/IgnoreSliceElements",
613 {0, 0, 0, 1, 2, 3, 0, 0, 4, 5, 6, 7, 8, 0, 9, 0, 0},
617 {1, 2, 3, 0, 4, 5, 6, 7, 0, 8, 9, 0, 0, 0},
618 {0, 0, 1, 2, 0, 0, 0},
621 cmp.FilterPath(func(p cmp.Path) bool {
622 vx, vy := p.Last().Values()
623 if vx.IsValid() && vx.Kind() == reflect.Int && vx.Int() == 0 {
626 if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 {
633 reason: "all zero slice elements are ignored (even if missing)",
635 label: label + "/IgnoreMapEntries",
636 x: [2]map[string]int{
637 {"ignore1": 0, "ignore2": 0, "keep1": 1, "keep2": 2, "KEEP3": 3, "IGNORE3": 0},
638 {"keep1": 1, "ignore1": 0},
640 y: [2]map[string]int{
641 {"ignore1": 0, "ignore3": 0, "ignore4": 0, "keep1": 1, "keep2": 2, "KEEP3": 3},
642 {"keep1": 1, "keep2": 2, "ignore2": 0},
645 cmp.FilterPath(func(p cmp.Path) bool {
646 vx, vy := p.Last().Values()
647 if vx.IsValid() && vx.Kind() == reflect.Int && vx.Int() == 0 {
650 if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 {
657 reason: "all zero map entries are ignored (even if missing)",
659 label: label + "/PanicUnexportedNamed",
660 x: namedWithUnexported{},
661 y: namedWithUnexported{},
662 wantPanic: strconv.Quote(reflect.TypeOf(namedWithUnexported{}).PkgPath()) + ".namedWithUnexported",
663 reason: "panic on named struct type with unexported field",
665 label: label + "/PanicUnexportedUnnamed",
666 x: struct{ a int }{},
667 y: struct{ a int }{},
668 wantPanic: strconv.Quote(reflect.TypeOf(namedWithUnexported{}).PkgPath()) + ".(struct { a int })",
669 reason: "panic on unnamed struct type with unexported field",
671 label: label + "/UnaddressableStruct",
672 x: struct{ s fmt.Stringer }{new(bytes.Buffer)},
673 y: struct{ s fmt.Stringer }{nil},
675 cmp.AllowUnexported(struct{ s fmt.Stringer }{}),
676 cmp.FilterPath(func(p cmp.Path) bool {
677 if _, ok := p.Last().(cmp.StructField); !ok {
681 t := p.Index(-1).Type()
682 vx, vy := p.Index(-1).Values()
683 pvx, pvy := p.Index(-2).Values()
686 panic(fmt.Sprintf("inconsistent type: %v != %v", vx.Type(), t))
688 panic(fmt.Sprintf("inconsistent type: %v != %v", vy.Type(), t))
689 case vx.CanAddr() != pvx.CanAddr():
690 panic(fmt.Sprintf("inconsistent addressability: %v != %v", vx.CanAddr(), pvx.CanAddr()))
691 case vy.CanAddr() != pvy.CanAddr():
692 panic(fmt.Sprintf("inconsistent addressability: %v != %v", vy.CanAddr(), pvy.CanAddr()))
698 reason: "verify that exporter does not leak implementation details",
702 func transformerTests() []test {
703 type StringBytes struct {
708 const label = "Transformer"
710 transformOnce := func(name string, f interface{}) cmp.Option {
711 xform := cmp.Transformer(name, f)
712 return cmp.FilterPath(func(p cmp.Path) bool {
713 for _, ps := range p {
714 if tr, ok := ps.(cmp.Transform); ok && tr.Option() == xform {
723 label: label + "/Uints",
727 cmp.Transformer("λ", func(in uint8) uint16 { return uint16(in) }),
728 cmp.Transformer("λ", func(in uint16) uint32 { return uint32(in) }),
729 cmp.Transformer("λ", func(in uint32) uint64 { return uint64(in) }),
732 reason: "transform uint8 -> uint16 -> uint32 -> uint64",
734 label: label + "/Ambiguous",
738 cmp.Transformer("λ", func(in int) int { return in / 2 }),
739 cmp.Transformer("λ", func(in int) int { return in }),
741 wantPanic: "ambiguous set of applicable options",
742 reason: "both transformers apply on int",
744 label: label + "/Filtered",
745 x: []int{0, -5, 0, -1},
746 y: []int{1, 3, 0, -5},
749 func(x, y int) bool { return x+y >= 0 },
750 cmp.Transformer("λ", func(in int) int64 { return int64(in / 2) }),
753 func(x, y int) bool { return x+y < 0 },
754 cmp.Transformer("λ", func(in int) int64 { return int64(in) }),
758 reason: "disjoint transformers filtered based on the values",
760 label: label + "/DisjointOutput",
764 cmp.Transformer("λ", func(in int) interface{} {
772 reason: "output type differs based on input value",
774 label: label + "/JSON",
781 "city": "Los Angeles",
782 "postalCode": "10021-3100",
784 "streetAddress": "21 2nd Street"
788 "number": "212 555-4321"
791 "number": "646 555-4567"
793 "number": "123 456-7890",
798 y: `{"firstName":"John","lastName":"Smith","isAlive":true,"age":25,
799 "address":{"streetAddress":"21 2nd Street","city":"New York",
800 "state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home",
801 "number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{
802 "type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`,
804 transformOnce("ParseJSON", func(s string) (m map[string]interface{}) {
805 if err := json.Unmarshal([]byte(s), &m); err != nil {
812 reason: "transformer used to parse JSON input",
814 label: label + "/AcyclicString",
815 x: StringBytes{String: "some\nmulti\nLine\nstring", Bytes: []byte("some\nmulti\nline\nbytes")},
816 y: StringBytes{String: "some\nmulti\nline\nstring", Bytes: []byte("some\nmulti\nline\nBytes")},
818 transformOnce("SplitString", func(s string) []string { return strings.Split(s, "\n") }),
819 transformOnce("SplitBytes", func(b []byte) [][]byte { return bytes.Split(b, []byte("\n")) }),
822 reason: "string -> []string and []byte -> [][]byte transformer only applied once",
824 label: label + "/CyclicString",
828 cmp.Transformer("SplitLines", func(s string) []string { return strings.Split(s, "\n") }),
830 wantPanic: "recursive set of Transformers detected",
831 reason: "cyclic transformation from string -> []string -> string",
833 label: label + "/CyclicComplex",
837 cmp.Transformer("T1", func(x complex64) complex128 { return complex128(x) }),
838 cmp.Transformer("T2", func(x complex128) [2]float64 { return [2]float64{real(x), imag(x)} }),
839 cmp.Transformer("T3", func(x float64) complex64 { return complex64(complex(x, 0)) }),
841 wantPanic: "recursive set of Transformers detected",
842 reason: "cyclic transformation from complex64 -> complex128 -> [2]float64 -> complex64",
846 func reporterTests() []test {
847 const label = "Reporter"
878 label: label + "/PanicStringer",
879 x: struct{ X fmt.Stringer }{struct{ fmt.Stringer }{nil}},
880 y: struct{ X fmt.Stringer }{bytes.NewBuffer(nil)},
882 reason: "panic from fmt.Stringer should not crash the reporter",
884 label: label + "/PanicError",
885 x: struct{ X error }{struct{ error }{nil}},
886 y: struct{ X error }{errors.New("")},
888 reason: "panic from error should not crash the reporter",
890 label: label + "/AmbiguousType",
894 reason: "reporter should display the qualified type name to disambiguate between the two values",
896 label: label + "/AmbiguousPointer",
900 cmp.Comparer(func(x, y *int) bool { return x == y }),
903 reason: "reporter should display the address to disambiguate between the two values",
905 label: label + "/AmbiguousPointerStruct",
906 x: struct{ I *int }{newInt(0)},
907 y: struct{ I *int }{newInt(0)},
909 cmp.Comparer(func(x, y *int) bool { return x == y }),
912 reason: "reporter should display the address to disambiguate between the two struct fields",
914 label: label + "/AmbiguousPointerSlice",
915 x: []*int{newInt(0)},
916 y: []*int{newInt(0)},
918 cmp.Comparer(func(x, y *int) bool { return x == y }),
921 reason: "reporter should display the address to disambiguate between the two slice elements",
923 label: label + "/AmbiguousPointerMap",
924 x: map[string]*int{"zero": newInt(0)},
925 y: map[string]*int{"zero": newInt(0)},
927 cmp.Comparer(func(x, y *int) bool { return x == y }),
930 reason: "reporter should display the address to disambiguate between the two map values",
932 label: label + "/AmbiguousStringer",
933 x: Stringer("hello"),
934 y: newStringer("hello"),
936 reason: "reporter should avoid calling String to disambiguate between the two values",
938 label: label + "/AmbiguousStringerStruct",
939 x: struct{ S fmt.Stringer }{Stringer("hello")},
940 y: struct{ S fmt.Stringer }{newStringer("hello")},
942 reason: "reporter should avoid calling String to disambiguate between the two struct fields",
944 label: label + "/AmbiguousStringerSlice",
945 x: []fmt.Stringer{Stringer("hello")},
946 y: []fmt.Stringer{newStringer("hello")},
948 reason: "reporter should avoid calling String to disambiguate between the two slice elements",
950 label: label + "/AmbiguousStringerMap",
951 x: map[string]fmt.Stringer{"zero": Stringer("hello")},
952 y: map[string]fmt.Stringer{"zero": newStringer("hello")},
954 reason: "reporter should avoid calling String to disambiguate between the two map values",
956 label: label + "/AmbiguousSliceHeader",
957 x: make([]int, 0, 5),
958 y: make([]int, 0, 1000),
960 cmp.Comparer(func(x, y []int) bool { return cap(x) == cap(y) }),
963 reason: "reporter should display the slice header to disambiguate between the two slice values",
965 label: label + "/AmbiguousStringerMapKey",
966 x: map[interface{}]string{
968 Stringer("hello"): "goodbye",
969 foo1.Bar{"fizz"}: "buzz",
971 y: map[interface{}]string{
972 newStringer("hello"): "goodbye",
973 foo2.Bar{"fizz"}: "buzz",
976 reason: "reporter should avoid calling String to disambiguate between the two map keys",
978 label: label + "/NonAmbiguousStringerMapKey",
979 x: map[interface{}]string{Stringer("hello"): "goodbye"},
980 y: map[interface{}]string{newStringer("fizz"): "buzz"},
982 reason: "reporter should call String as there is no ambiguity between the two map keys",
984 label: label + "/InvalidUTF8",
985 x: MyString("\xed\xa0\x80"),
987 reason: "invalid UTF-8 should format as quoted string",
989 label: label + "/UnbatchedSlice",
990 x: MyComposite{IntsA: []int8{11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
991 y: MyComposite{IntsA: []int8{10, 11, 21, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
993 reason: "unbatched diffing desired since few elements differ",
995 label: label + "/BatchedSlice",
996 x: MyComposite{IntsA: []int8{10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
997 y: MyComposite{IntsA: []int8{12, 29, 13, 27, 22, 23, 17, 18, 19, 20, 21, 10, 26, 16, 25, 28, 11, 15, 24, 14}},
999 reason: "batched diffing desired since many elements differ",
1001 label: label + "/BatchedWithComparer",
1002 x: MyComposite{BytesA: []byte{10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
1003 y: MyComposite{BytesA: []byte{12, 29, 13, 27, 22, 23, 17, 18, 19, 20, 21, 10, 26, 16, 25, 28, 11, 15, 24, 14}},
1006 cmp.Comparer(bytes.Equal),
1008 reason: "batched diffing desired since many elements differ",
1010 label: label + "/BatchedLong",
1011 x: MyComposite{IntsA: []int8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}},
1013 reason: "batched output desired for a single slice of primitives unique to one of the inputs",
1015 label: label + "/BatchedNamedAndUnnamed",
1017 BytesA: []byte{1, 2, 3},
1018 BytesB: []MyByte{4, 5, 6},
1019 BytesC: MyBytes{7, 8, 9},
1020 IntsA: []int8{-1, -2, -3},
1021 IntsB: []MyInt{-4, -5, -6},
1022 IntsC: MyInts{-7, -8, -9},
1023 UintsA: []uint16{1000, 2000, 3000},
1024 UintsB: []MyUint{4000, 5000, 6000},
1025 UintsC: MyUints{7000, 8000, 9000},
1026 FloatsA: []float32{1.5, 2.5, 3.5},
1027 FloatsB: []MyFloat{4.5, 5.5, 6.5},
1028 FloatsC: MyFloats{7.5, 8.5, 9.5},
1031 BytesA: []byte{3, 2, 1},
1032 BytesB: []MyByte{6, 5, 4},
1033 BytesC: MyBytes{9, 8, 7},
1034 IntsA: []int8{-3, -2, -1},
1035 IntsB: []MyInt{-6, -5, -4},
1036 IntsC: MyInts{-9, -8, -7},
1037 UintsA: []uint16{3000, 2000, 1000},
1038 UintsB: []MyUint{6000, 5000, 4000},
1039 UintsC: MyUints{9000, 8000, 7000},
1040 FloatsA: []float32{3.5, 2.5, 1.5},
1041 FloatsB: []MyFloat{6.5, 5.5, 4.5},
1042 FloatsC: MyFloats{9.5, 8.5, 7.5},
1045 reason: "batched diffing available for both named and unnamed slices",
1047 label: label + "/BinaryHexdump",
1048 x: MyComposite{BytesA: []byte("\xf3\x0f\x8a\xa4\xd3\x12R\t$\xbeX\x95A\xfd$fX\x8byT\xac\r\xd8qwp\x20j\\s\u007f\x8c\x17U\xc04\xcen\xf7\xaaG\xee2\x9d\xc5\xca\x1eX\xaf\x8f'\xf3\x02J\x90\xedi.p2\xb4\xab0 \xb6\xbd\\b4\x17\xb0\x00\xbbO~'G\x06\xf4.f\xfdc\xd7\x04ݷ0\xb7\xd1U~{\xf6\xb3~\x1dWi \x9e\xbc\xdf\xe1M\xa9\xef\xa2\xd2\xed\xb4Gx\xc9\xc9'\xa4\xc6\xce\xecDp]")},
1049 y: MyComposite{BytesA: []byte("\xf3\x0f\x8a\xa4\xd3\x12R\t$\xbeT\xac\r\xd8qwp\x20j\\s\u007f\x8c\x17U\xc04\xcen\xf7\xaaG\xee2\x9d\xc5\xca\x1eX\xaf\x8f'\xf3\x02J\x90\xedi.p2\xb4\xab0 \xb6\xbd\\b4\x17\xb0\x00\xbbO~'G\x06\xf4.f\xfdc\xd7\x04ݷ0\xb7\xd1u-[]]\xf6\xb3haha~\x1dWI \x9e\xbc\xdf\xe1M\xa9\xef\xa2\xd2\xed\xb4Gx\xc9\xc9'\xa4\xc6\xce\xecDp]")},
1051 reason: "binary diff in hexdump form since data is binary data",
1053 label: label + "/StringHexdump",
1054 x: MyComposite{StringB: MyString("readme.txt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000600\x000000000\x000000000\x0000000000046\x0000000000000\x00011173\x00 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ustar\x0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000\x000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")},
1055 y: MyComposite{StringB: MyString("gopher.txt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000600\x000000000\x000000000\x0000000000043\x0000000000000\x00011217\x00 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ustar\x0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000\x000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")},
1057 reason: "binary diff desired since string looks like binary data",
1059 label: label + "/BinaryString",
1060 x: MyComposite{BytesA: []byte(`{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"address":{"streetAddress":"314 54th Avenue","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`)},
1061 y: MyComposite{BytesA: []byte(`{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"address":{"streetAddress":"21 2nd Street","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`)},
1063 reason: "batched textual diff desired since bytes looks like textual data",
1065 label: label + "/TripleQuote",
1066 x: MyComposite{StringA: "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n"},
1067 y: MyComposite{StringA: "aaa\nbbb\nCCC\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nSSS\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n"},
1069 reason: "use triple-quote syntax",
1071 label: label + "/TripleQuoteSlice",
1073 "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1074 "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1077 "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\n",
1078 "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1081 reason: "use triple-quote syntax for slices of strings",
1083 label: label + "/TripleQuoteNamedTypes",
1085 StringB: MyString("aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz"),
1086 BytesC: MyBytes("aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz"),
1089 StringB: MyString("aaa\nbbb\nCCC\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nSSS\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz"),
1090 BytesC: MyBytes("aaa\nbbb\nCCC\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nSSS\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz"),
1093 reason: "use triple-quote syntax for named types",
1095 label: label + "/TripleQuoteSliceNamedTypes",
1097 "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1098 "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1101 "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\n",
1102 "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1105 reason: "use triple-quote syntax for slices of named strings",
1107 label: label + "/TripleQuoteEndlines",
1108 x: "aaa\nbbb\nccc\nddd\neee\nfff\nggg\r\nhhh\n\riii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n\r",
1109 y: "aaa\nbbb\nCCC\nddd\neee\nfff\nggg\r\nhhh\n\riii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz",
1111 reason: "use triple-quote syntax",
1113 label: label + "/AvoidTripleQuoteAmbiguousQuotes",
1114 x: "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1115 y: "aaa\nbbb\nCCC\nddd\neee\n\"\"\"\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1117 reason: "avoid triple-quote syntax due to presence of ambiguous triple quotes",
1119 label: label + "/AvoidTripleQuoteAmbiguousEllipsis",
1120 x: "aaa\nbbb\nccc\n...\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1121 y: "aaa\nbbb\nCCC\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1123 reason: "avoid triple-quote syntax due to presence of ambiguous ellipsis",
1125 label: label + "/AvoidTripleQuoteNonPrintable",
1126 x: "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1127 y: "aaa\nbbb\nCCC\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\no\roo\nppp\nqqq\nrrr\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1129 reason: "use triple-quote syntax",
1131 label: label + "/AvoidTripleQuoteIdenticalWhitespace",
1132 x: "aaa\nbbb\nccc\n ddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1133 y: "aaa\nbbb\nccc \nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n",
1135 reason: "avoid triple-quote syntax due to visual equivalence of differences",
1137 label: label + "/TripleQuoteStringer",
1139 bytes.NewBuffer([]byte("package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hello, playground\")\n}\n")),
1140 bytes.NewBuffer([]byte("package main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n)\n\nfunc main() {\n\tfmt.Println(\"My favorite number is\", rand.Intn(10))\n}\n")),
1143 bytes.NewBuffer([]byte("package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hello, playground\")\n}\n")),
1144 bytes.NewBuffer([]byte("package main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\nfunc main() {\n\tfmt.Printf(\"Now you have %g problems.\\n\", math.Sqrt(7))\n}\n")),
1146 opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })},
1148 reason: "multi-line String output should be formatted with triple quote",
1150 label: label + "/LimitMaximumBytesDiffs",
1151 x: []byte("\xcd====\x06\x1f\xc2\xcc\xc2-S=====\x1d\xdfa\xae\x98\x9fH======ǰ\xb7=======\xef====:\\\x94\xe6J\xc7=====\xb4======\n\n\xf7\x94===========\xf2\x9c\xc0f=====4\xf6\xf1\xc3\x17\x82======n\x16`\x91D\xc6\x06=======\x1cE====.===========\xc4\x18=======\x8a\x8d\x0e====\x87\xb1\xa5\x8e\xc3=====z\x0f1\xaeU======G,=======5\xe75\xee\x82\xf4\xce====\x11r===========\xaf]=======z\x05\xb3\x91\x88%\xd2====\n1\x89=====i\xb7\x055\xe6\x81\xd2=============\x883=@̾====\x14\x05\x96%^t\x04=====\xe7Ȉ\x90\x1d============="),
1152 y: []byte("\\====|\x96\xe7SB\xa0\xab=====\xf0\xbd\xa5q\xab\x17;======\xabP\x00=======\xeb====\xa5\x14\xe6O(\xe4=====(======/c@?===========\xd9x\xed\x13=====J\xfc\x918B\x8d======a8A\xebs\x04\xae=======\aC====\x1c===========\x91\"=======uؾ====s\xec\x845\a=====;\xabS9t======\x1f\x1b=======\x80\xab/\xed+:;====\xeaI===========\xabl=======\xb9\xe9\xfdH\x93\x8e\u007f====ח\xe5=====Ig\x88m\xf5\x01V=============\xf7+4\xb0\x92E====\x9fj\xf8&\xd0h\xf9=====\xeeΨ\r\xbf============="),
1154 reason: "total bytes difference output is truncated due to excessive number of differences",
1156 label: label + "/LimitMaximumStringDiffs",
1157 x: "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np\nq\nr\ns\nt\nu\nv\nw\nx\ny\nz\nA\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ\n",
1158 y: "aa\nb\ncc\nd\nee\nf\ngg\nh\nii\nj\nkk\nl\nmm\nn\noo\np\nqq\nr\nss\nt\nuu\nv\nww\nx\nyy\nz\nAA\nB\nCC\nD\nEE\nF\nGG\nH\nII\nJ\nKK\nL\nMM\nN\nOO\nP\nQQ\nR\nSS\nT\nUU\nV\nWW\nX\nYY\nZ\n",
1160 reason: "total string difference output is truncated due to excessive number of differences",
1162 label: label + "/LimitMaximumSliceDiffs",
1163 x: func() (out []struct{ S string }) {
1164 for _, s := range strings.Split("a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np\nq\nr\ns\nt\nu\nv\nw\nx\ny\nz\nA\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ\n", "\n") {
1165 out = append(out, struct{ S string }{s})
1169 y: func() (out []struct{ S string }) {
1170 for _, s := range strings.Split("aa\nb\ncc\nd\nee\nf\ngg\nh\nii\nj\nkk\nl\nmm\nn\noo\np\nqq\nr\nss\nt\nuu\nv\nww\nx\nyy\nz\nAA\nB\nCC\nD\nEE\nF\nGG\nH\nII\nJ\nKK\nL\nMM\nN\nOO\nP\nQQ\nR\nSS\nT\nUU\nV\nWW\nX\nYY\nZ\n", "\n") {
1171 out = append(out, struct{ S string }{s})
1176 reason: "total slice difference output is truncated due to excessive number of differences",
1178 label: label + "/MultilineString",
1180 StringA: strings.TrimPrefix(`
1181 Package cmp determines equality of values.
1183 This package is intended to be a more powerful and safer alternative to
1184 reflect.DeepEqual for comparing whether two values are semantically equal.
1186 The primary features of cmp are:
1188 • When the default behavior of equality does not suit the needs of the test,
1189 custom equality functions can override the equality operation.
1190 For example, an equality function may report floats as equal so long as they
1191 are within some tolerance of each other.
1193 • Types that have an Equal method may use that method to determine equality.
1194 This allows package authors to determine the equality operation for the types
1197 • If no custom equality functions are used and no Equal method is defined,
1198 equality is determined by recursively comparing the primitive kinds on both
1199 values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported
1200 fields are not compared by default; they result in panics unless suppressed
1201 by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared
1202 using the AllowUnexported option.
1206 StringA: strings.TrimPrefix(`
1207 Package cmp determines equality of value.
1209 This package is intended to be a more powerful and safer alternative to
1210 reflect.DeepEqual for comparing whether two values are semantically equal.
1212 The primary features of cmp are:
1214 • When the default behavior of equality does not suit the needs of the test,
1215 custom equality functions can override the equality operation.
1216 For example, an equality function may report floats as equal so long as they
1217 are within some tolerance of each other.
1219 • If no custom equality functions are used and no Equal method is defined,
1220 equality is determined by recursively comparing the primitive kinds on both
1221 values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported
1222 fields are not compared by default; they result in panics unless suppressed
1223 by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared
1224 using the AllowUnexported option.`, "\n"),
1227 reason: "batched per-line diff desired since string looks like multi-line textual data",
1229 label: label + "/Slices",
1231 BytesA: []byte{1, 2, 3},
1232 BytesB: []MyByte{4, 5, 6},
1233 BytesC: MyBytes{7, 8, 9},
1234 IntsA: []int8{-1, -2, -3},
1235 IntsB: []MyInt{-4, -5, -6},
1236 IntsC: MyInts{-7, -8, -9},
1237 UintsA: []uint16{1000, 2000, 3000},
1238 UintsB: []MyUint{4000, 5000, 6000},
1239 UintsC: MyUints{7000, 8000, 9000},
1240 FloatsA: []float32{1.5, 2.5, 3.5},
1241 FloatsB: []MyFloat{4.5, 5.5, 6.5},
1242 FloatsC: MyFloats{7.5, 8.5, 9.5},
1246 reason: "batched diffing for non-nil slices and nil slices",
1248 label: label + "/EmptySlices",
1259 FloatsA: []float32{},
1260 FloatsB: []MyFloat{},
1261 FloatsC: MyFloats{},
1265 reason: "batched diffing for empty slices and nil slices",
1269 func embeddedTests() []test {
1270 const label = "EmbeddedStruct"
1272 privateStruct := *new(ts.ParentStructA).PrivateStruct()
1274 createStructA := func(i int) ts.ParentStructA {
1275 s := ts.ParentStructA{}
1276 s.PrivateStruct().Public = 1 + i
1277 s.PrivateStruct().SetPrivate(2 + i)
1281 createStructB := func(i int) ts.ParentStructB {
1282 s := ts.ParentStructB{}
1283 s.PublicStruct.Public = 1 + i
1284 s.PublicStruct.SetPrivate(2 + i)
1288 createStructC := func(i int) ts.ParentStructC {
1289 s := ts.ParentStructC{}
1290 s.PrivateStruct().Public = 1 + i
1291 s.PrivateStruct().SetPrivate(2 + i)
1297 createStructD := func(i int) ts.ParentStructD {
1298 s := ts.ParentStructD{}
1299 s.PublicStruct.Public = 1 + i
1300 s.PublicStruct.SetPrivate(2 + i)
1306 createStructE := func(i int) ts.ParentStructE {
1307 s := ts.ParentStructE{}
1308 s.PrivateStruct().Public = 1 + i
1309 s.PrivateStruct().SetPrivate(2 + i)
1310 s.PublicStruct.Public = 3 + i
1311 s.PublicStruct.SetPrivate(4 + i)
1315 createStructF := func(i int) ts.ParentStructF {
1316 s := ts.ParentStructF{}
1317 s.PrivateStruct().Public = 1 + i
1318 s.PrivateStruct().SetPrivate(2 + i)
1319 s.PublicStruct.Public = 3 + i
1320 s.PublicStruct.SetPrivate(4 + i)
1326 createStructG := func(i int) *ts.ParentStructG {
1327 s := ts.NewParentStructG()
1328 s.PrivateStruct().Public = 1 + i
1329 s.PrivateStruct().SetPrivate(2 + i)
1333 createStructH := func(i int) *ts.ParentStructH {
1334 s := ts.NewParentStructH()
1335 s.PublicStruct.Public = 1 + i
1336 s.PublicStruct.SetPrivate(2 + i)
1340 createStructI := func(i int) *ts.ParentStructI {
1341 s := ts.NewParentStructI()
1342 s.PrivateStruct().Public = 1 + i
1343 s.PrivateStruct().SetPrivate(2 + i)
1344 s.PublicStruct.Public = 3 + i
1345 s.PublicStruct.SetPrivate(4 + i)
1349 createStructJ := func(i int) *ts.ParentStructJ {
1350 s := ts.NewParentStructJ()
1351 s.PrivateStruct().Public = 1 + i
1352 s.PrivateStruct().SetPrivate(2 + i)
1353 s.PublicStruct.Public = 3 + i
1354 s.PublicStruct.SetPrivate(4 + i)
1355 s.Private().Public = 5 + i
1356 s.Private().SetPrivate(6 + i)
1357 s.Public.Public = 7 + i
1358 s.Public.SetPrivate(8 + i)
1362 // TODO(≥go1.10): Workaround for reflect bug (https://golang.org/issue/21122).
1363 wantPanicNotGo110 := func(s string) string {
1364 if !flags.AtLeastGo110 {
1371 label: label + "/ParentStructA/PanicUnexported1",
1372 x: ts.ParentStructA{},
1373 y: ts.ParentStructA{},
1374 wantPanic: "cannot handle unexported field",
1375 reason: "ParentStructA has an unexported field",
1377 label: label + "/ParentStructA/Ignored",
1378 x: ts.ParentStructA{},
1379 y: ts.ParentStructA{},
1381 cmpopts.IgnoreUnexported(ts.ParentStructA{}),
1384 reason: "the only field (which is unexported) of ParentStructA is ignored",
1386 label: label + "/ParentStructA/PanicUnexported2",
1387 x: createStructA(0),
1388 y: createStructA(0),
1390 cmp.AllowUnexported(ts.ParentStructA{}),
1392 wantPanic: "cannot handle unexported field",
1393 reason: "privateStruct also has unexported fields",
1395 label: label + "/ParentStructA/Equal",
1396 x: createStructA(0),
1397 y: createStructA(0),
1399 cmp.AllowUnexported(ts.ParentStructA{}, privateStruct),
1402 reason: "unexported fields of both ParentStructA and privateStruct are allowed",
1404 label: label + "/ParentStructA/Inequal",
1405 x: createStructA(0),
1406 y: createStructA(1),
1408 cmp.AllowUnexported(ts.ParentStructA{}, privateStruct),
1411 reason: "the two values differ on some fields",
1413 label: label + "/ParentStructB/PanicUnexported1",
1414 x: ts.ParentStructB{},
1415 y: ts.ParentStructB{},
1417 cmpopts.IgnoreUnexported(ts.ParentStructB{}),
1419 wantPanic: "cannot handle unexported field",
1420 reason: "PublicStruct has an unexported field",
1422 label: label + "/ParentStructB/Ignored",
1423 x: ts.ParentStructB{},
1424 y: ts.ParentStructB{},
1426 cmpopts.IgnoreUnexported(ts.ParentStructB{}),
1427 cmpopts.IgnoreUnexported(ts.PublicStruct{}),
1430 reason: "unexported fields of both ParentStructB and PublicStruct are ignored",
1432 label: label + "/ParentStructB/PanicUnexported2",
1433 x: createStructB(0),
1434 y: createStructB(0),
1436 cmp.AllowUnexported(ts.ParentStructB{}),
1438 wantPanic: "cannot handle unexported field",
1439 reason: "PublicStruct also has unexported fields",
1441 label: label + "/ParentStructB/Equal",
1442 x: createStructB(0),
1443 y: createStructB(0),
1445 cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}),
1448 reason: "unexported fields of both ParentStructB and PublicStruct are allowed",
1450 label: label + "/ParentStructB/Inequal",
1451 x: createStructB(0),
1452 y: createStructB(1),
1454 cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}),
1457 reason: "the two values differ on some fields",
1459 label: label + "/ParentStructC/PanicUnexported1",
1460 x: ts.ParentStructC{},
1461 y: ts.ParentStructC{},
1462 wantPanic: "cannot handle unexported field",
1463 reason: "ParentStructC has unexported fields",
1465 label: label + "/ParentStructC/Ignored",
1466 x: ts.ParentStructC{},
1467 y: ts.ParentStructC{},
1469 cmpopts.IgnoreUnexported(ts.ParentStructC{}),
1472 reason: "unexported fields of ParentStructC are ignored",
1474 label: label + "/ParentStructC/PanicUnexported2",
1475 x: createStructC(0),
1476 y: createStructC(0),
1478 cmp.AllowUnexported(ts.ParentStructC{}),
1480 wantPanic: "cannot handle unexported field",
1481 reason: "privateStruct also has unexported fields",
1483 label: label + "/ParentStructC/Equal",
1484 x: createStructC(0),
1485 y: createStructC(0),
1487 cmp.AllowUnexported(ts.ParentStructC{}, privateStruct),
1490 reason: "unexported fields of both ParentStructC and privateStruct are allowed",
1492 label: label + "/ParentStructC/Inequal",
1493 x: createStructC(0),
1494 y: createStructC(1),
1496 cmp.AllowUnexported(ts.ParentStructC{}, privateStruct),
1499 reason: "the two values differ on some fields",
1501 label: label + "/ParentStructD/PanicUnexported1",
1502 x: ts.ParentStructD{},
1503 y: ts.ParentStructD{},
1505 cmpopts.IgnoreUnexported(ts.ParentStructD{}),
1507 wantPanic: "cannot handle unexported field",
1508 reason: "ParentStructD has unexported fields",
1510 label: label + "/ParentStructD/Ignored",
1511 x: ts.ParentStructD{},
1512 y: ts.ParentStructD{},
1514 cmpopts.IgnoreUnexported(ts.ParentStructD{}),
1515 cmpopts.IgnoreUnexported(ts.PublicStruct{}),
1518 reason: "unexported fields of ParentStructD and PublicStruct are ignored",
1520 label: label + "/ParentStructD/PanicUnexported2",
1521 x: createStructD(0),
1522 y: createStructD(0),
1524 cmp.AllowUnexported(ts.ParentStructD{}),
1526 wantPanic: "cannot handle unexported field",
1527 reason: "PublicStruct also has unexported fields",
1529 label: label + "/ParentStructD/Equal",
1530 x: createStructD(0),
1531 y: createStructD(0),
1533 cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}),
1536 reason: "unexported fields of both ParentStructD and PublicStruct are allowed",
1538 label: label + "/ParentStructD/Inequal",
1539 x: createStructD(0),
1540 y: createStructD(1),
1542 cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}),
1545 reason: "the two values differ on some fields",
1547 label: label + "/ParentStructE/PanicUnexported1",
1548 x: ts.ParentStructE{},
1549 y: ts.ParentStructE{},
1551 cmpopts.IgnoreUnexported(ts.ParentStructE{}),
1553 wantPanic: "cannot handle unexported field",
1554 reason: "ParentStructE has unexported fields",
1556 label: label + "/ParentStructE/Ignored",
1557 x: ts.ParentStructE{},
1558 y: ts.ParentStructE{},
1560 cmpopts.IgnoreUnexported(ts.ParentStructE{}),
1561 cmpopts.IgnoreUnexported(ts.PublicStruct{}),
1564 reason: "unexported fields of ParentStructE and PublicStruct are ignored",
1566 label: label + "/ParentStructE/PanicUnexported2",
1567 x: createStructE(0),
1568 y: createStructE(0),
1570 cmp.AllowUnexported(ts.ParentStructE{}),
1572 wantPanic: "cannot handle unexported field",
1573 reason: "PublicStruct and privateStruct also has unexported fields",
1575 label: label + "/ParentStructE/PanicUnexported3",
1576 x: createStructE(0),
1577 y: createStructE(0),
1579 cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}),
1581 wantPanic: "cannot handle unexported field",
1582 reason: "privateStruct also has unexported fields",
1584 label: label + "/ParentStructE/Equal",
1585 x: createStructE(0),
1586 y: createStructE(0),
1588 cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct),
1591 reason: "unexported fields of both ParentStructE, PublicStruct, and privateStruct are allowed",
1593 label: label + "/ParentStructE/Inequal",
1594 x: createStructE(0),
1595 y: createStructE(1),
1597 cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct),
1600 reason: "the two values differ on some fields",
1602 label: label + "/ParentStructF/PanicUnexported1",
1603 x: ts.ParentStructF{},
1604 y: ts.ParentStructF{},
1606 cmpopts.IgnoreUnexported(ts.ParentStructF{}),
1608 wantPanic: "cannot handle unexported field",
1609 reason: "ParentStructF has unexported fields",
1611 label: label + "/ParentStructF/Ignored",
1612 x: ts.ParentStructF{},
1613 y: ts.ParentStructF{},
1615 cmpopts.IgnoreUnexported(ts.ParentStructF{}),
1616 cmpopts.IgnoreUnexported(ts.PublicStruct{}),
1619 reason: "unexported fields of ParentStructF and PublicStruct are ignored",
1621 label: label + "/ParentStructF/PanicUnexported2",
1622 x: createStructF(0),
1623 y: createStructF(0),
1625 cmp.AllowUnexported(ts.ParentStructF{}),
1627 wantPanic: "cannot handle unexported field",
1628 reason: "PublicStruct and privateStruct also has unexported fields",
1630 label: label + "/ParentStructF/PanicUnexported3",
1631 x: createStructF(0),
1632 y: createStructF(0),
1634 cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}),
1636 wantPanic: "cannot handle unexported field",
1637 reason: "privateStruct also has unexported fields",
1639 label: label + "/ParentStructF/Equal",
1640 x: createStructF(0),
1641 y: createStructF(0),
1643 cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct),
1646 reason: "unexported fields of both ParentStructF, PublicStruct, and privateStruct are allowed",
1648 label: label + "/ParentStructF/Inequal",
1649 x: createStructF(0),
1650 y: createStructF(1),
1652 cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct),
1655 reason: "the two values differ on some fields",
1657 label: label + "/ParentStructG/PanicUnexported1",
1658 x: ts.ParentStructG{},
1659 y: ts.ParentStructG{},
1660 wantPanic: wantPanicNotGo110("cannot handle unexported field"),
1661 wantEqual: !flags.AtLeastGo110,
1662 reason: "ParentStructG has unexported fields",
1664 label: label + "/ParentStructG/Ignored",
1665 x: ts.ParentStructG{},
1666 y: ts.ParentStructG{},
1668 cmpopts.IgnoreUnexported(ts.ParentStructG{}),
1671 reason: "unexported fields of ParentStructG are ignored",
1673 label: label + "/ParentStructG/PanicUnexported2",
1674 x: createStructG(0),
1675 y: createStructG(0),
1677 cmp.AllowUnexported(ts.ParentStructG{}),
1679 wantPanic: "cannot handle unexported field",
1680 reason: "privateStruct also has unexported fields",
1682 label: label + "/ParentStructG/Equal",
1683 x: createStructG(0),
1684 y: createStructG(0),
1686 cmp.AllowUnexported(ts.ParentStructG{}, privateStruct),
1689 reason: "unexported fields of both ParentStructG and privateStruct are allowed",
1691 label: label + "/ParentStructG/Inequal",
1692 x: createStructG(0),
1693 y: createStructG(1),
1695 cmp.AllowUnexported(ts.ParentStructG{}, privateStruct),
1698 reason: "the two values differ on some fields",
1700 label: label + "/ParentStructH/EqualNil",
1701 x: ts.ParentStructH{},
1702 y: ts.ParentStructH{},
1704 reason: "PublicStruct is not compared because the pointer is nil",
1706 label: label + "/ParentStructH/PanicUnexported1",
1707 x: createStructH(0),
1708 y: createStructH(0),
1709 wantPanic: "cannot handle unexported field",
1710 reason: "PublicStruct has unexported fields",
1712 label: label + "/ParentStructH/Ignored",
1713 x: ts.ParentStructH{},
1714 y: ts.ParentStructH{},
1716 cmpopts.IgnoreUnexported(ts.ParentStructH{}),
1719 reason: "unexported fields of ParentStructH are ignored (it has none)",
1721 label: label + "/ParentStructH/PanicUnexported2",
1722 x: createStructH(0),
1723 y: createStructH(0),
1725 cmp.AllowUnexported(ts.ParentStructH{}),
1727 wantPanic: "cannot handle unexported field",
1728 reason: "PublicStruct also has unexported fields",
1730 label: label + "/ParentStructH/Equal",
1731 x: createStructH(0),
1732 y: createStructH(0),
1734 cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}),
1737 reason: "unexported fields of both ParentStructH and PublicStruct are allowed",
1739 label: label + "/ParentStructH/Inequal",
1740 x: createStructH(0),
1741 y: createStructH(1),
1743 cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}),
1746 reason: "the two values differ on some fields",
1748 label: label + "/ParentStructI/PanicUnexported1",
1749 x: ts.ParentStructI{},
1750 y: ts.ParentStructI{},
1751 wantPanic: wantPanicNotGo110("cannot handle unexported field"),
1752 wantEqual: !flags.AtLeastGo110,
1753 reason: "ParentStructI has unexported fields",
1755 label: label + "/ParentStructI/Ignored1",
1756 x: ts.ParentStructI{},
1757 y: ts.ParentStructI{},
1759 cmpopts.IgnoreUnexported(ts.ParentStructI{}),
1762 reason: "unexported fields of ParentStructI are ignored",
1764 label: label + "/ParentStructI/PanicUnexported2",
1765 x: createStructI(0),
1766 y: createStructI(0),
1768 cmpopts.IgnoreUnexported(ts.ParentStructI{}),
1770 wantPanic: "cannot handle unexported field",
1771 reason: "PublicStruct and privateStruct also has unexported fields",
1773 label: label + "/ParentStructI/Ignored2",
1774 x: createStructI(0),
1775 y: createStructI(0),
1777 cmpopts.IgnoreUnexported(ts.ParentStructI{}, ts.PublicStruct{}),
1780 reason: "unexported fields of ParentStructI and PublicStruct are ignored",
1782 label: label + "/ParentStructI/PanicUnexported3",
1783 x: createStructI(0),
1784 y: createStructI(0),
1786 cmp.AllowUnexported(ts.ParentStructI{}),
1788 wantPanic: "cannot handle unexported field",
1789 reason: "PublicStruct and privateStruct also has unexported fields",
1791 label: label + "/ParentStructI/Equal",
1792 x: createStructI(0),
1793 y: createStructI(0),
1795 cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct),
1798 reason: "unexported fields of both ParentStructI, PublicStruct, and privateStruct are allowed",
1800 label: label + "/ParentStructI/Inequal",
1801 x: createStructI(0),
1802 y: createStructI(1),
1804 cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct),
1807 reason: "the two values differ on some fields",
1809 label: label + "/ParentStructJ/PanicUnexported1",
1810 x: ts.ParentStructJ{},
1811 y: ts.ParentStructJ{},
1812 wantPanic: "cannot handle unexported field",
1813 reason: "ParentStructJ has unexported fields",
1815 label: label + "/ParentStructJ/PanicUnexported2",
1816 x: ts.ParentStructJ{},
1817 y: ts.ParentStructJ{},
1819 cmpopts.IgnoreUnexported(ts.ParentStructJ{}),
1821 wantPanic: "cannot handle unexported field",
1822 reason: "PublicStruct and privateStruct also has unexported fields",
1824 label: label + "/ParentStructJ/Ignored",
1825 x: ts.ParentStructJ{},
1826 y: ts.ParentStructJ{},
1828 cmpopts.IgnoreUnexported(ts.ParentStructJ{}, ts.PublicStruct{}),
1831 reason: "unexported fields of ParentStructJ and PublicStruct are ignored",
1833 label: label + "/ParentStructJ/PanicUnexported3",
1834 x: createStructJ(0),
1835 y: createStructJ(0),
1837 cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}),
1839 wantPanic: "cannot handle unexported field",
1840 reason: "privateStruct also has unexported fields",
1842 label: label + "/ParentStructJ/Equal",
1843 x: createStructJ(0),
1844 y: createStructJ(0),
1846 cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct),
1849 reason: "unexported fields of both ParentStructJ, PublicStruct, and privateStruct are allowed",
1851 label: label + "/ParentStructJ/Inequal",
1852 x: createStructJ(0),
1853 y: createStructJ(1),
1855 cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct),
1858 reason: "the two values differ on some fields",
1862 func methodTests() []test {
1863 const label = "EqualMethod"
1865 // A common mistake that the Equal method is on a pointer receiver,
1866 // but only a non-pointer value is present in the struct.
1867 // A transform can be used to forcibly reference the value.
1868 addrTransform := cmp.FilterPath(func(p cmp.Path) bool {
1872 t := p[len(p)-1].Type()
1873 if _, ok := t.MethodByName("Equal"); ok || t.Kind() == reflect.Ptr {
1876 if m, ok := reflect.PtrTo(t).MethodByName("Equal"); ok {
1878 return !tf.IsVariadic() && tf.NumIn() == 2 && tf.NumOut() == 1 &&
1879 tf.In(0).AssignableTo(tf.In(1)) && tf.Out(0) == reflect.TypeOf(true)
1882 }, cmp.Transformer("Addr", func(x interface{}) interface{} {
1883 v := reflect.ValueOf(x)
1884 vp := reflect.New(v.Type())
1886 return vp.Interface()
1889 // For each of these types, there is an Equal method defined, which always
1890 // returns true, while the underlying data are fundamentally different.
1891 // Since the method should be called, these are expected to be equal.
1893 label: label + "/StructA/ValueEqual",
1894 x: ts.StructA{X: "NotEqual"},
1895 y: ts.StructA{X: "not_equal"},
1897 reason: "Equal method on StructA value called",
1899 label: label + "/StructA/PointerEqual",
1900 x: &ts.StructA{X: "NotEqual"},
1901 y: &ts.StructA{X: "not_equal"},
1903 reason: "Equal method on StructA pointer called",
1905 label: label + "/StructB/ValueInequal",
1906 x: ts.StructB{X: "NotEqual"},
1907 y: ts.StructB{X: "not_equal"},
1909 reason: "Equal method on StructB value not called",
1911 label: label + "/StructB/ValueAddrEqual",
1912 x: ts.StructB{X: "NotEqual"},
1913 y: ts.StructB{X: "not_equal"},
1914 opts: []cmp.Option{addrTransform},
1916 reason: "Equal method on StructB pointer called due to shallow copy transform",
1918 label: label + "/StructB/PointerEqual",
1919 x: &ts.StructB{X: "NotEqual"},
1920 y: &ts.StructB{X: "not_equal"},
1922 reason: "Equal method on StructB pointer called",
1924 label: label + "/StructC/ValueEqual",
1925 x: ts.StructC{X: "NotEqual"},
1926 y: ts.StructC{X: "not_equal"},
1928 reason: "Equal method on StructC value called",
1930 label: label + "/StructC/PointerEqual",
1931 x: &ts.StructC{X: "NotEqual"},
1932 y: &ts.StructC{X: "not_equal"},
1934 reason: "Equal method on StructC pointer called",
1936 label: label + "/StructD/ValueInequal",
1937 x: ts.StructD{X: "NotEqual"},
1938 y: ts.StructD{X: "not_equal"},
1940 reason: "Equal method on StructD value not called",
1942 label: label + "/StructD/ValueAddrEqual",
1943 x: ts.StructD{X: "NotEqual"},
1944 y: ts.StructD{X: "not_equal"},
1945 opts: []cmp.Option{addrTransform},
1947 reason: "Equal method on StructD pointer called due to shallow copy transform",
1949 label: label + "/StructD/PointerEqual",
1950 x: &ts.StructD{X: "NotEqual"},
1951 y: &ts.StructD{X: "not_equal"},
1953 reason: "Equal method on StructD pointer called",
1955 label: label + "/StructE/ValueInequal",
1956 x: ts.StructE{X: "NotEqual"},
1957 y: ts.StructE{X: "not_equal"},
1959 reason: "Equal method on StructE value not called",
1961 label: label + "/StructE/ValueAddrEqual",
1962 x: ts.StructE{X: "NotEqual"},
1963 y: ts.StructE{X: "not_equal"},
1964 opts: []cmp.Option{addrTransform},
1966 reason: "Equal method on StructE pointer called due to shallow copy transform",
1968 label: label + "/StructE/PointerEqual",
1969 x: &ts.StructE{X: "NotEqual"},
1970 y: &ts.StructE{X: "not_equal"},
1972 reason: "Equal method on StructE pointer called",
1974 label: label + "/StructF/ValueInequal",
1975 x: ts.StructF{X: "NotEqual"},
1976 y: ts.StructF{X: "not_equal"},
1978 reason: "Equal method on StructF value not called",
1980 label: label + "/StructF/PointerEqual",
1981 x: &ts.StructF{X: "NotEqual"},
1982 y: &ts.StructF{X: "not_equal"},
1984 reason: "Equal method on StructF pointer called",
1986 label: label + "/StructA1/ValueEqual",
1987 x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"},
1988 y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"},
1990 reason: "Equal method on StructA value called with equal X field",
1992 label: label + "/StructA1/ValueInequal",
1993 x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"},
1994 y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"},
1996 reason: "Equal method on StructA value called, but inequal X field",
1998 label: label + "/StructA1/PointerEqual",
1999 x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"},
2000 y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"},
2002 reason: "Equal method on StructA value called with equal X field",
2004 label: label + "/StructA1/PointerInequal",
2005 x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"},
2006 y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"},
2008 reason: "Equal method on StructA value called, but inequal X field",
2010 label: label + "/StructB1/ValueEqual",
2011 x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"},
2012 y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"},
2013 opts: []cmp.Option{addrTransform},
2015 reason: "Equal method on StructB pointer called due to shallow copy transform with equal X field",
2017 label: label + "/StructB1/ValueInequal",
2018 x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"},
2019 y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"},
2020 opts: []cmp.Option{addrTransform},
2022 reason: "Equal method on StructB pointer called due to shallow copy transform, but inequal X field",
2024 label: label + "/StructB1/PointerEqual",
2025 x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"},
2026 y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"},
2027 opts: []cmp.Option{addrTransform},
2029 reason: "Equal method on StructB pointer called due to shallow copy transform with equal X field",
2031 label: label + "/StructB1/PointerInequal",
2032 x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"},
2033 y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"},
2034 opts: []cmp.Option{addrTransform},
2036 reason: "Equal method on StructB pointer called due to shallow copy transform, but inequal X field",
2038 label: label + "/StructC1/ValueEqual",
2039 x: ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"},
2040 y: ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"},
2042 reason: "Equal method on StructC1 value called",
2044 label: label + "/StructC1/PointerEqual",
2045 x: &ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"},
2046 y: &ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"},
2048 reason: "Equal method on StructC1 pointer called",
2050 label: label + "/StructD1/ValueInequal",
2051 x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
2052 y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
2054 reason: "Equal method on StructD1 value not called",
2056 label: label + "/StructD1/PointerAddrEqual",
2057 x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
2058 y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
2059 opts: []cmp.Option{addrTransform},
2061 reason: "Equal method on StructD1 pointer called due to shallow copy transform",
2063 label: label + "/StructD1/PointerEqual",
2064 x: &ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
2065 y: &ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
2067 reason: "Equal method on StructD1 pointer called",
2069 label: label + "/StructE1/ValueInequal",
2070 x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
2071 y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
2073 reason: "Equal method on StructE1 value not called",
2075 label: label + "/StructE1/ValueAddrEqual",
2076 x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
2077 y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
2078 opts: []cmp.Option{addrTransform},
2080 reason: "Equal method on StructE1 pointer called due to shallow copy transform",
2082 label: label + "/StructE1/PointerEqual",
2083 x: &ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
2084 y: &ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
2086 reason: "Equal method on StructE1 pointer called",
2088 label: label + "/StructF1/ValueInequal",
2089 x: ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"},
2090 y: ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"},
2092 reason: "Equal method on StructF1 value not called",
2094 label: label + "/StructF1/PointerEqual",
2095 x: &ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"},
2096 y: &ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"},
2098 reason: "Equal method on StructF1 pointer called",
2100 label: label + "/StructA2/ValueEqual",
2101 x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"},
2102 y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"},
2104 reason: "Equal method on StructA pointer called with equal X field",
2106 label: label + "/StructA2/ValueInequal",
2107 x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"},
2108 y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"},
2110 reason: "Equal method on StructA pointer called, but inequal X field",
2112 label: label + "/StructA2/PointerEqual",
2113 x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"},
2114 y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"},
2116 reason: "Equal method on StructA pointer called with equal X field",
2118 label: label + "/StructA2/PointerInequal",
2119 x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"},
2120 y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"},
2122 reason: "Equal method on StructA pointer called, but inequal X field",
2124 label: label + "/StructB2/ValueEqual",
2125 x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"},
2126 y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"},
2128 reason: "Equal method on StructB pointer called with equal X field",
2130 label: label + "/StructB2/ValueInequal",
2131 x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"},
2132 y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"},
2134 reason: "Equal method on StructB pointer called, but inequal X field",
2136 label: label + "/StructB2/PointerEqual",
2137 x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"},
2138 y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"},
2140 reason: "Equal method on StructB pointer called with equal X field",
2142 label: label + "/StructB2/PointerInequal",
2143 x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"},
2144 y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"},
2146 reason: "Equal method on StructB pointer called, but inequal X field",
2148 label: label + "/StructC2/ValueEqual",
2149 x: ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"},
2150 y: ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"},
2152 reason: "Equal method called on StructC2 value due to forwarded StructC pointer",
2154 label: label + "/StructC2/PointerEqual",
2155 x: &ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"},
2156 y: &ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"},
2158 reason: "Equal method called on StructC2 pointer due to forwarded StructC pointer",
2160 label: label + "/StructD2/ValueEqual",
2161 x: ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"},
2162 y: ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"},
2164 reason: "Equal method called on StructD2 value due to forwarded StructD pointer",
2166 label: label + "/StructD2/PointerEqual",
2167 x: &ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"},
2168 y: &ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"},
2170 reason: "Equal method called on StructD2 pointer due to forwarded StructD pointer",
2172 label: label + "/StructE2/ValueEqual",
2173 x: ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"},
2174 y: ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"},
2176 reason: "Equal method called on StructE2 value due to forwarded StructE pointer",
2178 label: label + "/StructE2/PointerEqual",
2179 x: &ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"},
2180 y: &ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"},
2182 reason: "Equal method called on StructE2 pointer due to forwarded StructE pointer",
2184 label: label + "/StructF2/ValueEqual",
2185 x: ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"},
2186 y: ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"},
2188 reason: "Equal method called on StructF2 value due to forwarded StructF pointer",
2190 label: label + "/StructF2/PointerEqual",
2191 x: &ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"},
2192 y: &ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"},
2194 reason: "Equal method called on StructF2 pointer due to forwarded StructF pointer",
2196 label: label + "/StructNo/Inequal",
2197 x: ts.StructNo{X: "NotEqual"},
2198 y: ts.StructNo{X: "not_equal"},
2200 reason: "Equal method not called since StructNo is not assignable to InterfaceA",
2202 label: label + "/AssignA/Equal",
2203 x: ts.AssignA(func() int { return 0 }),
2204 y: ts.AssignA(func() int { return 1 }),
2206 reason: "Equal method called since named func is assignable to unnamed func",
2208 label: label + "/AssignB/Equal",
2209 x: ts.AssignB(struct{ A int }{0}),
2210 y: ts.AssignB(struct{ A int }{1}),
2212 reason: "Equal method called since named struct is assignable to unnamed struct",
2214 label: label + "/AssignC/Equal",
2215 x: ts.AssignC(make(chan bool)),
2216 y: ts.AssignC(make(chan bool)),
2218 reason: "Equal method called since named channel is assignable to unnamed channel",
2220 label: label + "/AssignD/Equal",
2221 x: ts.AssignD(make(chan bool)),
2222 y: ts.AssignD(make(chan bool)),
2224 reason: "Equal method called since named channel is assignable to unnamed channel",
2231 Bravos map[string]*CycleBravo
2237 Alphas map[string]*CycleAlpha
2241 func cycleTests() []test {
2242 const label = "Cycle"
2250 makeGraph := func() map[string]*CycleAlpha {
2251 v := map[string]*CycleAlpha{
2254 Bravos: map[string]*CycleBravo{
2255 "FooBravo": &CycleBravo{
2259 Alphas: map[string]*CycleAlpha{
2260 "Foo": nil, // cyclic reference
2267 Bravos: map[string]*CycleBravo{
2268 "BarBuzzBravo": &CycleBravo{
2269 Name: "BarBuzzBravo",
2272 Alphas: map[string]*CycleAlpha{
2273 "Bar": nil, // cyclic reference
2274 "Buzz": nil, // cyclic reference
2277 "BuzzBarBravo": &CycleBravo{
2278 Name: "BuzzBarBravo",
2281 Alphas: map[string]*CycleAlpha{
2282 "Bar": nil, // cyclic reference
2283 "Buzz": nil, // cyclic reference
2288 "Buzz": &CycleAlpha{
2290 Bravos: map[string]*CycleBravo{
2291 "BarBuzzBravo": nil, // cyclic reference
2292 "BuzzBarBravo": nil, // cyclic reference
2296 v["Foo"].Bravos["FooBravo"].Alphas["Foo"] = v["Foo"]
2297 v["Bar"].Bravos["BarBuzzBravo"].Alphas["Bar"] = v["Bar"]
2298 v["Bar"].Bravos["BarBuzzBravo"].Alphas["Buzz"] = v["Buzz"]
2299 v["Bar"].Bravos["BuzzBarBravo"].Alphas["Bar"] = v["Bar"]
2300 v["Bar"].Bravos["BuzzBarBravo"].Alphas["Buzz"] = v["Buzz"]
2301 v["Buzz"].Bravos["BarBuzzBravo"] = v["Bar"].Bravos["BarBuzzBravo"]
2302 v["Buzz"].Bravos["BuzzBarBravo"] = v["Bar"].Bravos["BuzzBarBravo"]
2307 type XY struct{ x, y interface{} }
2308 for _, tt := range []struct {
2314 label: "PointersEqual",
2323 reason: "equal pair of single-node pointers",
2325 label: "PointersInequal",
2329 y1, y2 := new(P), new(P)
2335 reason: "inequal pair of single-node and double-node pointers",
2337 label: "SlicesEqual",
2346 reason: "equal pair of single-node slices",
2348 label: "SlicesInequal",
2352 y1, y2 := S{nil}, S{nil}
2358 reason: "inequal pair of single-node and double node slices",
2369 reason: "equal pair of single-node maps",
2371 label: "MapsInequal",
2375 y1, y2 := M{0: nil}, M{0: nil}
2381 reason: "inequal pair of single-node and double-node maps",
2383 label: "GraphEqual",
2384 in: XY{makeGraph(), makeGraph()},
2386 reason: "graphs are equal since they have identical forms",
2388 label: "GraphInequalZeroed",
2392 y["Foo"].Bravos["FooBravo"].ID = 0
2393 y["Bar"].Bravos["BarBuzzBravo"].ID = 0
2394 y["Bar"].Bravos["BuzzBarBravo"].ID = 0
2398 reason: "graphs are inequal because the ID fields are different",
2400 label: "GraphInequalStruct",
2404 x["Buzz"].Bravos["BuzzBarBravo"] = &CycleBravo{
2405 Name: "BuzzBarBravo",
2411 reason: "graphs are inequal because they differ on a map element",
2413 tests = append(tests, test{
2414 label: label + "/" + tt.label,
2417 wantEqual: tt.wantEqual,
2424 func project1Tests() []test {
2425 const label = "Project1"
2427 ignoreUnexported := cmpopts.IgnoreUnexported(
2428 ts.EagleImmutable{},
2429 ts.DreamerImmutable{},
2432 ts.DonkeyImmutable{},
2435 ts.SummerLoveSummary{},
2438 createEagle := func() ts.Eagle {
2441 Hounds: []string{"buford", "tannen"},
2442 Desc: "some description",
2443 Dreamers: []ts.Dreamer{{}, {
2445 Animal: []interface{}{
2447 Target: "corporation",
2448 Immutable: &ts.GoatImmutable{
2450 State: (*pb.Goat_States)(newInt(5)),
2460 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2461 Immutable: &ts.SlapImmutable{
2462 ID: "immutableSlap",
2465 LoveRadius: &ts.LoveRadius{
2466 Summer: &ts.SummerLove{
2467 Summary: &ts.SummerLoveSummary{
2468 Devices: []string{"foo", "bar", "baz"},
2469 ChangeType: []pb.SummerType{1, 2, 3},
2475 Immutable: &ts.EagleImmutable{
2478 MissingCall: (*pb.Eagle_MissingCalls)(newInt(55)),
2484 label: label + "/PanicUnexported",
2485 x: ts.Eagle{Slaps: []ts.Slap{{
2486 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2488 y: ts.Eagle{Slaps: []ts.Slap{{
2489 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2491 wantPanic: "cannot handle unexported field",
2492 reason: "struct contains unexported fields",
2494 label: label + "/ProtoEqual",
2495 x: ts.Eagle{Slaps: []ts.Slap{{
2496 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2498 y: ts.Eagle{Slaps: []ts.Slap{{
2499 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2501 opts: []cmp.Option{cmp.Comparer(pb.Equal)},
2503 reason: "simulated protobuf messages contain the same values",
2505 label: label + "/ProtoInequal",
2506 x: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, {
2507 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2509 y: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, {
2510 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata2"}},
2512 opts: []cmp.Option{cmp.Comparer(pb.Equal)},
2514 reason: "simulated protobuf messages contain different values",
2516 label: label + "/Equal",
2519 opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)},
2521 reason: "equal because values are the same",
2523 label: label + "/Inequal",
2524 x: func() ts.Eagle {
2526 eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.ID = "southbay2"
2527 eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.State = (*pb.Goat_States)(newInt(6))
2528 eg.Slaps[0].Immutable.MildSlap = false
2531 y: func() ts.Eagle {
2533 devs := eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices
2534 eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices = devs[:1]
2537 opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)},
2539 reason: "inequal because some values are different",
2543 type germSorter []*pb.Germ
2545 func (gs germSorter) Len() int { return len(gs) }
2546 func (gs germSorter) Less(i, j int) bool { return gs[i].String() < gs[j].String() }
2547 func (gs germSorter) Swap(i, j int) { gs[i], gs[j] = gs[j], gs[i] }
2549 func project2Tests() []test {
2550 const label = "Project2"
2552 sortGerms := cmp.Transformer("Sort", func(in []*pb.Germ) []*pb.Germ {
2553 out := append([]*pb.Germ(nil), in...) // Make copy
2554 sort.Sort(germSorter(out))
2558 equalDish := cmp.Comparer(func(x, y *ts.Dish) bool {
2559 if x == nil || y == nil {
2560 return x == nil && y == nil
2562 px, err1 := x.Proto()
2563 py, err2 := y.Proto()
2564 if err1 != nil || err2 != nil {
2567 return pb.Equal(px, py)
2570 createBatch := func() ts.GermBatch {
2571 return ts.GermBatch{
2572 DirtyGerms: map[int32][]*pb.Germ{
2574 {Stringer: pb.Stringer{X: "germ1"}},
2577 {Stringer: pb.Stringer{X: "germ2"}},
2578 {Stringer: pb.Stringer{X: "germ3"}},
2579 {Stringer: pb.Stringer{X: "germ4"}},
2582 GermMap: map[int32]*pb.Germ{
2583 13: {Stringer: pb.Stringer{X: "germ13"}},
2584 21: {Stringer: pb.Stringer{X: "germ21"}},
2586 DishMap: map[int32]*ts.Dish{
2587 0: ts.CreateDish(nil, io.EOF),
2588 1: ts.CreateDish(nil, io.ErrUnexpectedEOF),
2589 2: ts.CreateDish(&pb.Dish{Stringer: pb.Stringer{X: "dish"}}, nil),
2591 HasPreviousResult: true,
2599 label: label + "/PanicUnexported",
2602 wantPanic: "cannot handle unexported field",
2603 reason: "struct contains unexported fields",
2605 label: label + "/Equal",
2608 opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
2610 reason: "equal because identical values are compared",
2612 label: label + "/InequalOrder",
2614 y: func() ts.GermBatch {
2616 s := gb.DirtyGerms[18]
2617 s[0], s[1], s[2] = s[1], s[2], s[0]
2620 opts: []cmp.Option{cmp.Comparer(pb.Equal), equalDish},
2622 reason: "inequal because slice contains elements in differing order",
2624 label: label + "/EqualOrder",
2626 y: func() ts.GermBatch {
2628 s := gb.DirtyGerms[18]
2629 s[0], s[1], s[2] = s[1], s[2], s[0]
2632 opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
2634 reason: "equal because unordered slice is sorted using transformer",
2636 label: label + "/Inequal",
2637 x: func() ts.GermBatch {
2639 delete(gb.DirtyGerms, 17)
2643 y: func() ts.GermBatch {
2645 gb.DirtyGerms[18] = gb.DirtyGerms[18][:2]
2649 opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
2651 reason: "inequal because some values are different",
2655 func project3Tests() []test {
2656 const label = "Project3"
2658 allowVisibility := cmp.AllowUnexported(ts.Dirt{})
2660 ignoreLocker := cmpopts.IgnoreInterfaces(struct{ sync.Locker }{})
2662 transformProtos := cmp.Transformer("λ", func(x pb.Dirt) *pb.Dirt {
2666 equalTable := cmp.Comparer(func(x, y ts.Table) bool {
2667 tx, ok1 := x.(*ts.MockTable)
2668 ty, ok2 := y.(*ts.MockTable)
2670 panic("table type must be MockTable")
2672 return cmp.Equal(tx.State(), ty.State())
2675 createDirt := func() (d ts.Dirt) {
2676 d.SetTable(ts.CreateMockTable([]string{"a", "b", "c"}))
2677 d.SetTimestamp(12345)
2679 d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "proto"}}
2680 d.SetWizard(map[string]*pb.Wizard{
2681 "harry": {Stringer: pb.Stringer{X: "potter"}},
2682 "albus": {Stringer: pb.Stringer{X: "dumbledore"}},
2684 d.SetLastTime(54321)
2689 label: label + "/PanicUnexported1",
2692 wantPanic: "cannot handle unexported field",
2693 reason: "struct contains unexported fields",
2695 label: label + "/PanicUnexported2",
2698 opts: []cmp.Option{allowVisibility, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
2699 wantPanic: "cannot handle unexported field",
2700 reason: "struct contains references to simulated protobuf types with unexported fields",
2702 label: label + "/Equal",
2705 opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
2707 reason: "transformer used to create reference to protobuf message so it works with pb.Equal",
2709 label: label + "/Inequal",
2712 d.SetTable(ts.CreateMockTable([]string{"a", "c"}))
2713 d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "blah"}}
2719 d.SetWizard(map[string]*pb.Wizard{
2720 "harry": {Stringer: pb.Stringer{X: "otter"}},
2724 opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
2726 reason: "inequal because some values are different",
2730 func project4Tests() []test {
2731 const label = "Project4"
2733 allowVisibility := cmp.AllowUnexported(
2739 transformProtos := cmp.Transformer("λ", func(x pb.Restrictions) *pb.Restrictions {
2743 createCartel := func() ts.Cartel {
2746 p.SetExpiration(now)
2747 p.SetManufacturer("acme")
2749 var hq ts.Headquarter
2751 hq.SetLocation("moon")
2752 hq.SetSubDivisions([]string{"alpha", "bravo", "charlie"})
2753 hq.SetMetaData(&pb.MetaData{Stringer: pb.Stringer{X: "metadata"}})
2754 hq.SetPublicMessage([]byte{1, 2, 3, 4, 5})
2755 hq.SetHorseBack("abcdef")
2761 c.SetCreationTime(now)
2762 c.SetBoss("al capone")
2763 c.SetPoisons([]*ts.Poison{&p})
2769 label: label + "/PanicUnexported1",
2772 wantPanic: "cannot handle unexported field",
2773 reason: "struct contains unexported fields",
2775 label: label + "/PanicUnexported2",
2778 opts: []cmp.Option{allowVisibility, cmp.Comparer(pb.Equal)},
2779 wantPanic: "cannot handle unexported field",
2780 reason: "struct contains references to simulated protobuf types with unexported fields",
2782 label: label + "/Equal",
2785 opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)},
2787 reason: "transformer used to create reference to protobuf message so it works with pb.Equal",
2789 label: label + "/Inequal",
2790 x: func() ts.Cartel {
2792 var p1, p2 ts.Poison
2794 p1.SetExpiration(now)
2795 p1.SetManufacturer("acme")
2797 p2.SetManufacturer("acme2")
2798 d.SetPoisons([]*ts.Poison{&p1, &p2})
2801 y: func() ts.Cartel {
2803 d.SetSubDivisions([]string{"bravo", "charlie"})
2804 d.SetPublicMessage([]byte{1, 2, 4, 3, 5})
2807 opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)},
2809 reason: "inequal because some values are different",
2813 // BenchmarkBytes benchmarks the performance of performing Equal or Diff on
2814 // large slices of bytes.
2815 func BenchmarkBytes(b *testing.B) {
2816 // Create a list of PathFilters that never apply, but are evaluated.
2817 const maxFilters = 5
2818 var filters cmp.Options
2819 errorIface := reflect.TypeOf((*error)(nil)).Elem()
2820 for i := 0; i <= maxFilters; i++ {
2821 filters = append(filters, cmp.FilterPath(func(p cmp.Path) bool {
2822 return p.Last().Type().AssignableTo(errorIface) // Never true
2826 type benchSize struct {
2830 for _, ts := range []benchSize{
2836 bx := append(append(make([]byte, ts.size/2), 'x'), make([]byte, ts.size/2)...)
2837 by := append(append(make([]byte, ts.size/2), 'y'), make([]byte, ts.size/2)...)
2838 b.Run(ts.label, func(b *testing.B) {
2839 // Iteratively add more filters that never apply, but are evaluated
2840 // to measure the cost of simply evaluating each filter.
2841 for i := 0; i <= maxFilters; i++ {
2842 b.Run(fmt.Sprintf("EqualFilter%d", i), func(b *testing.B) {
2844 b.SetBytes(2 * ts.size)
2845 for j := 0; j < b.N; j++ {
2846 cmp.Equal(bx, by, filters[:i]...)
2850 for i := 0; i <= maxFilters; i++ {
2851 b.Run(fmt.Sprintf("DiffFilter%d", i), func(b *testing.B) {
2853 b.SetBytes(2 * ts.size)
2854 for j := 0; j < b.N; j++ {
2855 cmp.Diff(bx, by, filters[:i]...)