.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / github.com / google / go-cmp@v0.5.4 / cmp / compare_test.go
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 file.
4
5 package cmp_test
6
7 import (
8         "bytes"
9         "crypto/md5"
10         "encoding/json"
11         "errors"
12         "flag"
13         "fmt"
14         "io"
15         "io/ioutil"
16         "math"
17         "math/rand"
18         "reflect"
19         "regexp"
20         "sort"
21         "strconv"
22         "strings"
23         "sync"
24         "testing"
25         "time"
26
27         "github.com/google/go-cmp/cmp"
28         "github.com/google/go-cmp/cmp/cmpopts"
29         "github.com/google/go-cmp/cmp/internal/flags"
30
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"
35 )
36
37 func init() {
38         flags.Deterministic = true
39 }
40
41 var update = flag.Bool("update", false, "update golden test files")
42
43 const goldenHeaderPrefix = "<<< "
44 const goldenFooterPrefix = ">>> "
45
46 /// mustParseGolden parses a file as a set of key-value pairs.
47 //
48 // The syntax is simple and looks something like:
49 //
50 //      <<< Key1
51 //      value1a
52 //      value1b
53 //      >>> Key1
54 //      <<< Key2
55 //      value2
56 //      >>> Key2
57 //
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)
62         if err != nil {
63                 panic(err)
64         }
65         s := string(b)
66
67         out := map[string]string{}
68         for len(s) > 0 {
69                 // Identify the next header.
70                 i := strings.Index(s, "\n") + len("\n")
71                 header := s[:i]
72                 if !strings.HasPrefix(header, goldenHeaderPrefix) {
73                         panic(fmt.Sprintf("invalid header: %q", header))
74                 }
75
76                 // Locate the next footer.
77                 footer := goldenFooterPrefix + header[len(goldenHeaderPrefix):]
78                 j := strings.Index(s, footer)
79                 if j < 0 {
80                         panic(fmt.Sprintf("missing footer: %q", footer))
81                 }
82
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))
87                 }
88                 out[name] = s[len(header):j]
89                 s = s[j+len(footer):]
90         }
91         return out
92 }
93 func mustFormatGolden(path string, in []struct{ Name, Data string }) {
94         var b []byte
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"...)
99         }
100         if err := ioutil.WriteFile(path, b, 0664); err != nil {
101                 panic(err)
102         }
103 }
104
105 var now = time.Date(2009, time.November, 10, 23, 00, 00, 00, time.UTC)
106
107 func newInt(n int) *int { return &n }
108
109 type Stringer string
110
111 func newStringer(s string) fmt.Stringer { return (*Stringer)(&s) }
112 func (s Stringer) String() string       { return string(s) }
113
114 type test struct {
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
121 }
122
123 func TestDiff(t *testing.T) {
124         var tests []test
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()...)
135
136         const goldenFile = "testdata/diffs"
137         gotDiffs := []struct{ Name, Data string }{}
138         wantDiffs := mustParseGolden(goldenFile)
139         for _, tt := range tests {
140                 tt := tt
141                 t.Run(tt.label, func(t *testing.T) {
142                         if !*update {
143                                 t.Parallel()
144                         }
145                         var gotDiff, gotPanic string
146                         func() {
147                                 defer func() {
148                                         if ex := recover(); ex != nil {
149                                                 if s, ok := ex.(string); ok {
150                                                         gotPanic = s
151                                                 } else {
152                                                         panic(ex)
153                                                 }
154                                         }
155                                 }()
156                                 gotDiff = cmp.Diff(tt.x, tt.y, tt.opts...)
157                         }()
158
159                         switch {
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 == "":
165                                 if gotPanic != "" {
166                                         t.Fatalf("unexpected panic message: %s\nreason: %v", gotPanic, tt.reason)
167                                 }
168                                 if *update {
169                                         if gotDiff != "" {
170                                                 gotDiffs = append(gotDiffs, struct{ Name, Data string }{t.Name(), gotDiff})
171                                         }
172                                 } else {
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)
176                                         }
177                                 }
178                                 gotEqual := gotDiff == ""
179                                 if gotEqual != tt.wantEqual {
180                                         t.Fatalf("Equal = %v, want %v\nreason: %v", gotEqual, tt.wantEqual, tt.reason)
181                                 }
182                         default:
183                                 if !strings.Contains(gotPanic, tt.wantPanic) {
184                                         t.Fatalf("panic message:\ngot:  %s\nwant: %s\nreason: %v", gotPanic, tt.wantPanic, tt.reason)
185                                 }
186                         }
187                 })
188         }
189
190         if *update {
191                 mustFormatGolden(goldenFile, gotDiffs)
192         }
193 }
194
195 func comparerTests() []test {
196         const label = "Comparer"
197
198         type Iface1 interface {
199                 Method()
200         }
201         type Iface2 interface {
202                 Method()
203         }
204
205         type tarHeader struct {
206                 Name       string
207                 Mode       int64
208                 Uid        int
209                 Gid        int
210                 Size       int64
211                 ModTime    time.Time
212                 Typeflag   byte
213                 Linkname   string
214                 Uname      string
215                 Gname      string
216                 Devmajor   int64
217                 Devminor   int64
218                 AccessTime time.Time
219                 ChangeTime time.Time
220                 Xattrs     map[string]string
221         }
222
223         type namedWithUnexported struct {
224                 unexported string
225         }
226
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",
234                                 Typeflag: tf,
235                         })
236                 }
237                 return hs
238         }
239
240         return []test{{
241                 label:     label + "/Nil",
242                 x:         nil,
243                 y:         nil,
244                 wantEqual: true,
245                 reason:    "nils are equal",
246         }, {
247                 label:     label + "/Integer",
248                 x:         1,
249                 y:         1,
250                 wantEqual: true,
251                 reason:    "identical integers are equal",
252         }, {
253                 label:     label + "/UnfilteredIgnore",
254                 x:         1,
255                 y:         1,
256                 opts:      []cmp.Option{cmp.Ignore()},
257                 wantPanic: "cannot use an unfiltered option",
258                 reason:    "unfiltered options are functionally useless",
259         }, {
260                 label:     label + "/UnfilteredCompare",
261                 x:         1,
262                 y:         1,
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",
266         }, {
267                 label:     label + "/UnfilteredTransform",
268                 x:         1,
269                 y:         1,
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",
273         }, {
274                 label: label + "/AmbiguousOptions",
275                 x:     1,
276                 y:     1,
277                 opts: []cmp.Option{
278                         cmp.Comparer(func(x, y int) bool { return true }),
279                         cmp.Transformer("λ", func(x int) float64 { return float64(x) }),
280                 },
281                 wantPanic: "ambiguous set of applicable options",
282                 reason:    "both options apply on int, leading to ambiguity",
283         }, {
284                 label: label + "/IgnorePrecedence",
285                 x:     1,
286                 y:     1,
287                 opts: []cmp.Option{
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) }),
293                 },
294                 wantEqual: true,
295                 reason:    "ignore takes precedence over other options",
296         }, {
297                 label:     label + "/UnknownOption",
298                 opts:      []cmp.Option{struct{ cmp.Option }{}},
299                 wantPanic: "unknown option",
300                 reason:    "use of unknown option should panic",
301         }, {
302                 label:     label + "/StructEqual",
303                 x:         struct{ A, B, C int }{1, 2, 3},
304                 y:         struct{ A, B, C int }{1, 2, 3},
305                 wantEqual: true,
306                 reason:    "struct comparison with all equal fields",
307         }, {
308                 label:     label + "/StructInequal",
309                 x:         struct{ A, B, C int }{1, 2, 3},
310                 y:         struct{ A, B, C int }{1, 2, 4},
311                 wantEqual: false,
312                 reason:    "struct comparison with inequal C field",
313         }, {
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",
319         }, {
320                 label:     label + "/PointerStructEqual",
321                 x:         &struct{ A *int }{newInt(4)},
322                 y:         &struct{ A *int }{newInt(4)},
323                 wantEqual: true,
324                 reason:    "comparison of pointer to struct with equal A field",
325         }, {
326                 label:     label + "/PointerStructInequal",
327                 x:         &struct{ A *int }{newInt(4)},
328                 y:         &struct{ A *int }{newInt(5)},
329                 wantEqual: false,
330                 reason:    "comparison of pointer to struct with inequal A field",
331         }, {
332                 label: label + "/PointerStructTrueComparer",
333                 x:     &struct{ A *int }{newInt(4)},
334                 y:     &struct{ A *int }{newInt(5)},
335                 opts: []cmp.Option{
336                         cmp.Comparer(func(x, y int) bool { return true }),
337                 },
338                 wantEqual: true,
339                 reason:    "comparison of pointer to struct with inequal A field, but treated as equal with always equal comparer",
340         }, {
341                 label: label + "/PointerStructNonNilComparer",
342                 x:     &struct{ A *int }{newInt(4)},
343                 y:     &struct{ A *int }{newInt(5)},
344                 opts: []cmp.Option{
345                         cmp.Comparer(func(x, y *int) bool { return x != nil && y != nil }),
346                 },
347                 wantEqual: true,
348                 reason:    "comparison of pointer to struct with inequal A field, but treated as equal with comparer checking pointers for nilness",
349         }, {
350                 label:     label + "/StructNestedPointerEqual",
351                 x:         &struct{ R *bytes.Buffer }{},
352                 y:         &struct{ R *bytes.Buffer }{},
353                 wantEqual: true,
354                 reason:    "equal since both pointers in R field are nil",
355         }, {
356                 label:     label + "/StructNestedPointerInequal",
357                 x:         &struct{ R *bytes.Buffer }{new(bytes.Buffer)},
358                 y:         &struct{ R *bytes.Buffer }{},
359                 wantEqual: false,
360                 reason:    "inequal since R field is inequal",
361         }, {
362                 label: label + "/StructNestedPointerTrueComparer",
363                 x:     &struct{ R *bytes.Buffer }{new(bytes.Buffer)},
364                 y:     &struct{ R *bytes.Buffer }{},
365                 opts: []cmp.Option{
366                         cmp.Comparer(func(x, y io.Reader) bool { return true }),
367                 },
368                 wantEqual: true,
369                 reason:    "equal despite inequal R field values since the comparer always reports true",
370         }, {
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",
376         }, {
377                 label: label + "/StructNestedValueUnexportedPanic2",
378                 x:     &struct{ R bytes.Buffer }{},
379                 y:     &struct{ R bytes.Buffer }{},
380                 opts: []cmp.Option{
381                         cmp.Comparer(func(x, y io.Reader) bool { return true }),
382                 },
383                 wantPanic: "cannot handle unexported field",
384                 reason:    "bytes.Buffer value does not implement io.Reader",
385         }, {
386                 label: label + "/StructNestedValueEqual",
387                 x:     &struct{ R bytes.Buffer }{},
388                 y:     &struct{ R bytes.Buffer }{},
389                 opts: []cmp.Option{
390                         cmp.Transformer("Ref", func(x bytes.Buffer) *bytes.Buffer { return &x }),
391                         cmp.Comparer(func(x, y io.Reader) bool { return true }),
392                 },
393                 wantEqual: true,
394                 reason:    "bytes.Buffer pointer due to shallow copy does implement io.Reader",
395         }, {
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",
401         }, {
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
408                         }
409                         return x.String() == y.String()
410                 })},
411                 wantEqual: true,
412                 reason:    "comparer for *regexp.Regexp applied with equal regexp strings",
413         }, {
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
420                         }
421                         return x.String() == y.String()
422                 })},
423                 wantEqual: false,
424                 reason:    "comparer for *regexp.Regexp applied with inequal regexp strings",
425         }, {
426                 label: label + "/TriplePointerEqual",
427                 x: func() ***int {
428                         a := 0
429                         b := &a
430                         c := &b
431                         return &c
432                 }(),
433                 y: func() ***int {
434                         a := 0
435                         b := &a
436                         c := &b
437                         return &c
438                 }(),
439                 wantEqual: true,
440                 reason:    "three layers of pointers to the same value",
441         }, {
442                 label: label + "/TriplePointerInequal",
443                 x: func() ***int {
444                         a := 0
445                         b := &a
446                         c := &b
447                         return &c
448                 }(),
449                 y: func() ***int {
450                         a := 1
451                         b := &a
452                         c := &b
453                         return &c
454                 }(),
455                 wantEqual: false,
456                 reason:    "three layers of pointers to different values",
457         }, {
458                 label:     label + "/SliceWithDifferingCapacity",
459                 x:         []int{1, 2, 3, 4, 5}[:3],
460                 y:         []int{1, 2, 3},
461                 wantEqual: true,
462                 reason:    "elements past the slice length are not compared",
463         }, {
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() })},
468                 wantEqual: true,
469                 reason:    "comparer for fmt.Stringer used to compare differing types with same string",
470         }, {
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() })},
475                 wantEqual: false,
476                 reason:    "comparer for fmt.Stringer used to compare differing types with different strings",
477         }, {
478                 label:     label + "/DifferingHash",
479                 x:         md5.Sum([]byte{'a'}),
480                 y:         md5.Sum([]byte{'b'}),
481                 wantEqual: false,
482                 reason:    "hash differs",
483         }, {
484                 label:     label + "/NilStringer",
485                 x:         new(fmt.Stringer),
486                 y:         nil,
487                 wantEqual: false,
488                 reason:    "by default differing types are always inequal",
489         }, {
490                 label:     label + "/TarHeaders",
491                 x:         makeTarHeaders('0'),
492                 y:         makeTarHeaders('\x00'),
493                 wantEqual: false,
494                 reason:    "type flag differs between the headers",
495         }, {
496                 label: label + "/NonDeterministicComparer",
497                 x:     make([]int, 1000),
498                 y:     make([]int, 1000),
499                 opts: []cmp.Option{
500                         cmp.Comparer(func(_, _ int) bool {
501                                 return rand.Intn(2) == 0
502                         }),
503                 },
504                 wantPanic: "non-deterministic or non-symmetric function detected",
505                 reason:    "non-deterministic comparer",
506         }, {
507                 label: label + "/NonDeterministicFilter",
508                 x:     make([]int, 1000),
509                 y:     make([]int, 1000),
510                 opts: []cmp.Option{
511                         cmp.FilterValues(func(_, _ int) bool {
512                                 return rand.Intn(2) == 0
513                         }, cmp.Ignore()),
514                 },
515                 wantPanic: "non-deterministic or non-symmetric function detected",
516                 reason:    "non-deterministic filter",
517         }, {
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},
521                 opts: []cmp.Option{
522                         cmp.Comparer(func(x, y int) bool {
523                                 return x < y
524                         }),
525                 },
526                 wantPanic: "non-deterministic or non-symmetric function detected",
527                 reason:    "asymmetric comparer",
528         }, {
529                 label: label + "/NonDeterministicTransformer",
530                 x:     make([]string, 1000),
531                 y:     make([]string, 1000),
532                 opts: []cmp.Option{
533                         cmp.Transformer("λ", func(x string) int {
534                                 return rand.Int()
535                         }),
536                 },
537                 wantPanic: "non-deterministic function detected",
538                 reason:    "non-deterministic transformer",
539         }, {
540                 label: label + "/IrreflexiveComparison",
541                 x:     make([]int, 10),
542                 y:     make([]int, 10),
543                 opts: []cmp.Option{
544                         cmp.Transformer("λ", func(x int) float64 {
545                                 return math.NaN()
546                         }),
547                 },
548                 wantEqual: false,
549                 reason:    "dynamic checks should not panic for non-reflexive comparisons",
550         }, {
551                 label:     label + "/StringerMapKey",
552                 x:         map[*pb.Stringer]*pb.Stringer{{"hello"}: {"world"}},
553                 y:         map[*pb.Stringer]*pb.Stringer(nil),
554                 wantEqual: false,
555                 reason:    "stringer should be used to format the map key",
556         }, {
557                 label:     label + "/StringerBacktick",
558                 x:         []*pb.Stringer{{`multi\nline\nline\nline`}},
559                 wantEqual: false,
560                 reason:    "stringer should use backtick quoting if more readable",
561         }, {
562                 label: label + "/AvoidPanicAssignableConverter",
563                 x:     struct{ I Iface2 }{},
564                 y:     struct{ I Iface2 }{},
565                 opts: []cmp.Option{
566                         cmp.Comparer(func(x, y Iface1) bool {
567                                 return x == nil && y == nil
568                         }),
569                 },
570                 wantEqual: true,
571                 reason:    "function call using Go reflection should automatically convert assignable interfaces; see https://golang.org/issues/22143",
572         }, {
573                 label: label + "/AvoidPanicAssignableTransformer",
574                 x:     struct{ I Iface2 }{},
575                 y:     struct{ I Iface2 }{},
576                 opts: []cmp.Option{
577                         cmp.Transformer("λ", func(v Iface1) bool {
578                                 return v == nil
579                         }),
580                 },
581                 wantEqual: true,
582                 reason:    "function call using Go reflection should automatically convert assignable interfaces; see https://golang.org/issues/22143",
583         }, {
584                 label: label + "/AvoidPanicAssignableFilter",
585                 x:     struct{ I Iface2 }{},
586                 y:     struct{ I Iface2 }{},
587                 opts: []cmp.Option{
588                         cmp.FilterValues(func(x, y Iface1) bool {
589                                 return x == nil && y == nil
590                         }, cmp.Ignore()),
591                 },
592                 wantEqual: true,
593                 reason:    "function call using Go reflection should automatically convert assignable interfaces; see https://golang.org/issues/22143",
594         }, {
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"}},
598                 wantEqual: false,
599                 reason:    "dynamic map with differing types (but semantically equivalent values) should be inequal",
600         }, {
601                 label: label + "/MapKeyPointer",
602                 x: map[*int]string{
603                         new(int): "hello",
604                 },
605                 y: map[*int]string{
606                         new(int): "world",
607                 },
608                 wantEqual: false,
609                 reason:    "map keys should use shallow (rather than deep) pointer comparison",
610         }, {
611                 label: label + "/IgnoreSliceElements",
612                 x: [2][]int{
613                         {0, 0, 0, 1, 2, 3, 0, 0, 4, 5, 6, 7, 8, 0, 9, 0, 0},
614                         {0, 1, 0, 0, 0, 20},
615                 },
616                 y: [2][]int{
617                         {1, 2, 3, 0, 4, 5, 6, 7, 0, 8, 9, 0, 0, 0},
618                         {0, 0, 1, 2, 0, 0, 0},
619                 },
620                 opts: []cmp.Option{
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 {
624                                         return true
625                                 }
626                                 if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 {
627                                         return true
628                                 }
629                                 return false
630                         }, cmp.Ignore()),
631                 },
632                 wantEqual: false,
633                 reason:    "all zero slice elements are ignored (even if missing)",
634         }, {
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},
639                 },
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},
643                 },
644                 opts: []cmp.Option{
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 {
648                                         return true
649                                 }
650                                 if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 {
651                                         return true
652                                 }
653                                 return false
654                         }, cmp.Ignore()),
655                 },
656                 wantEqual: false,
657                 reason:    "all zero map entries are ignored (even if missing)",
658         }, {
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",
664         }, {
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",
670         }, {
671                 label: label + "/UnaddressableStruct",
672                 x:     struct{ s fmt.Stringer }{new(bytes.Buffer)},
673                 y:     struct{ s fmt.Stringer }{nil},
674                 opts: []cmp.Option{
675                         cmp.AllowUnexported(struct{ s fmt.Stringer }{}),
676                         cmp.FilterPath(func(p cmp.Path) bool {
677                                 if _, ok := p.Last().(cmp.StructField); !ok {
678                                         return false
679                                 }
680
681                                 t := p.Index(-1).Type()
682                                 vx, vy := p.Index(-1).Values()
683                                 pvx, pvy := p.Index(-2).Values()
684                                 switch {
685                                 case vx.Type() != t:
686                                         panic(fmt.Sprintf("inconsistent type: %v != %v", vx.Type(), t))
687                                 case vy.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()))
693                                 }
694                                 return true
695                         }, cmp.Ignore()),
696                 },
697                 wantEqual: true,
698                 reason:    "verify that exporter does not leak implementation details",
699         }, {
700                 label:     label + "/ErrorPanic",
701                 x:         io.EOF,
702                 y:         io.EOF,
703                 wantPanic: "consider using cmpopts.EquateErrors",
704                 reason:    "suggest cmpopts.EquateErrors when accessing unexported fields of error types",
705         }, {
706                 label:     label + "/ErrorEqual",
707                 x:         io.EOF,
708                 y:         io.EOF,
709                 opts:      []cmp.Option{cmpopts.EquateErrors()},
710                 wantEqual: true,
711                 reason:    "cmpopts.EquateErrors should equate these two errors as sentinel values",
712         }}
713 }
714
715 func transformerTests() []test {
716         type StringBytes struct {
717                 String string
718                 Bytes  []byte
719         }
720
721         const label = "Transformer"
722
723         transformOnce := func(name string, f interface{}) cmp.Option {
724                 xform := cmp.Transformer(name, f)
725                 return cmp.FilterPath(func(p cmp.Path) bool {
726                         for _, ps := range p {
727                                 if tr, ok := ps.(cmp.Transform); ok && tr.Option() == xform {
728                                         return false
729                                 }
730                         }
731                         return true
732                 }, xform)
733         }
734
735         return []test{{
736                 label: label + "/Uints",
737                 x:     uint8(0),
738                 y:     uint8(1),
739                 opts: []cmp.Option{
740                         cmp.Transformer("λ", func(in uint8) uint16 { return uint16(in) }),
741                         cmp.Transformer("λ", func(in uint16) uint32 { return uint32(in) }),
742                         cmp.Transformer("λ", func(in uint32) uint64 { return uint64(in) }),
743                 },
744                 wantEqual: false,
745                 reason:    "transform uint8 -> uint16 -> uint32 -> uint64",
746         }, {
747                 label: label + "/Ambiguous",
748                 x:     0,
749                 y:     1,
750                 opts: []cmp.Option{
751                         cmp.Transformer("λ", func(in int) int { return in / 2 }),
752                         cmp.Transformer("λ", func(in int) int { return in }),
753                 },
754                 wantPanic: "ambiguous set of applicable options",
755                 reason:    "both transformers apply on int",
756         }, {
757                 label: label + "/Filtered",
758                 x:     []int{0, -5, 0, -1},
759                 y:     []int{1, 3, 0, -5},
760                 opts: []cmp.Option{
761                         cmp.FilterValues(
762                                 func(x, y int) bool { return x+y >= 0 },
763                                 cmp.Transformer("λ", func(in int) int64 { return int64(in / 2) }),
764                         ),
765                         cmp.FilterValues(
766                                 func(x, y int) bool { return x+y < 0 },
767                                 cmp.Transformer("λ", func(in int) int64 { return int64(in) }),
768                         ),
769                 },
770                 wantEqual: false,
771                 reason:    "disjoint transformers filtered based on the values",
772         }, {
773                 label: label + "/DisjointOutput",
774                 x:     0,
775                 y:     1,
776                 opts: []cmp.Option{
777                         cmp.Transformer("λ", func(in int) interface{} {
778                                 if in == 0 {
779                                         return "zero"
780                                 }
781                                 return float64(in)
782                         }),
783                 },
784                 wantEqual: false,
785                 reason:    "output type differs based on input value",
786         }, {
787                 label: label + "/JSON",
788                 x: `{
789                   "firstName": "John",
790                   "lastName": "Smith",
791                   "age": 25,
792                   "isAlive": true,
793                   "address": {
794                     "city": "Los Angeles",
795                     "postalCode": "10021-3100",
796                     "state": "CA",
797                     "streetAddress": "21 2nd Street"
798                   },
799                   "phoneNumbers": [{
800                     "type": "home",
801                     "number": "212 555-4321"
802                   },{
803                     "type": "office",
804                     "number": "646 555-4567"
805                   },{
806                     "number": "123 456-7890",
807                     "type": "mobile"
808                   }],
809                   "children": []
810                 }`,
811                 y: `{"firstName":"John","lastName":"Smith","isAlive":true,"age":25,
812                         "address":{"streetAddress":"21 2nd Street","city":"New York",
813                         "state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home",
814                         "number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{
815                         "type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`,
816                 opts: []cmp.Option{
817                         transformOnce("ParseJSON", func(s string) (m map[string]interface{}) {
818                                 if err := json.Unmarshal([]byte(s), &m); err != nil {
819                                         panic(err)
820                                 }
821                                 return m
822                         }),
823                 },
824                 wantEqual: false,
825                 reason:    "transformer used to parse JSON input",
826         }, {
827                 label: label + "/AcyclicString",
828                 x:     StringBytes{String: "some\nmulti\nLine\nstring", Bytes: []byte("some\nmulti\nline\nbytes")},
829                 y:     StringBytes{String: "some\nmulti\nline\nstring", Bytes: []byte("some\nmulti\nline\nBytes")},
830                 opts: []cmp.Option{
831                         transformOnce("SplitString", func(s string) []string { return strings.Split(s, "\n") }),
832                         transformOnce("SplitBytes", func(b []byte) [][]byte { return bytes.Split(b, []byte("\n")) }),
833                 },
834                 wantEqual: false,
835                 reason:    "string -> []string and []byte -> [][]byte transformer only applied once",
836         }, {
837                 label: label + "/CyclicString",
838                 x:     "a\nb\nc\n",
839                 y:     "a\nb\nc\n",
840                 opts: []cmp.Option{
841                         cmp.Transformer("SplitLines", func(s string) []string { return strings.Split(s, "\n") }),
842                 },
843                 wantPanic: "recursive set of Transformers detected",
844                 reason:    "cyclic transformation from string -> []string -> string",
845         }, {
846                 label: label + "/CyclicComplex",
847                 x:     complex64(0),
848                 y:     complex64(0),
849                 opts: []cmp.Option{
850                         cmp.Transformer("T1", func(x complex64) complex128 { return complex128(x) }),
851                         cmp.Transformer("T2", func(x complex128) [2]float64 { return [2]float64{real(x), imag(x)} }),
852                         cmp.Transformer("T3", func(x float64) complex64 { return complex64(complex(x, 0)) }),
853                 },
854                 wantPanic: "recursive set of Transformers detected",
855                 reason:    "cyclic transformation from complex64 -> complex128 -> [2]float64 -> complex64",
856         }}
857 }
858
859 func reporterTests() []test {
860         const label = "Reporter"
861
862         type (
863                 MyString    string
864                 MyByte      byte
865                 MyBytes     []byte
866                 MyInt       int8
867                 MyInts      []int8
868                 MyUint      int16
869                 MyUints     []int16
870                 MyFloat     float32
871                 MyFloats    []float32
872                 MyComposite struct {
873                         StringA string
874                         StringB MyString
875                         BytesA  []byte
876                         BytesB  []MyByte
877                         BytesC  MyBytes
878                         IntsA   []int8
879                         IntsB   []MyInt
880                         IntsC   MyInts
881                         UintsA  []uint16
882                         UintsB  []MyUint
883                         UintsC  MyUints
884                         FloatsA []float32
885                         FloatsB []MyFloat
886                         FloatsC MyFloats
887                 }
888         )
889
890         return []test{{
891                 label:     label + "/PanicStringer",
892                 x:         struct{ X fmt.Stringer }{struct{ fmt.Stringer }{nil}},
893                 y:         struct{ X fmt.Stringer }{bytes.NewBuffer(nil)},
894                 wantEqual: false,
895                 reason:    "panic from fmt.Stringer should not crash the reporter",
896         }, {
897                 label:     label + "/PanicError",
898                 x:         struct{ X error }{struct{ error }{nil}},
899                 y:         struct{ X error }{errors.New("")},
900                 wantEqual: false,
901                 reason:    "panic from error should not crash the reporter",
902         }, {
903                 label:     label + "/AmbiguousType",
904                 x:         foo1.Bar{},
905                 y:         foo2.Bar{},
906                 wantEqual: false,
907                 reason:    "reporter should display the qualified type name to disambiguate between the two values",
908         }, {
909                 label: label + "/AmbiguousPointer",
910                 x:     newInt(0),
911                 y:     newInt(0),
912                 opts: []cmp.Option{
913                         cmp.Comparer(func(x, y *int) bool { return x == y }),
914                 },
915                 wantEqual: false,
916                 reason:    "reporter should display the address to disambiguate between the two values",
917         }, {
918                 label: label + "/AmbiguousPointerStruct",
919                 x:     struct{ I *int }{newInt(0)},
920                 y:     struct{ I *int }{newInt(0)},
921                 opts: []cmp.Option{
922                         cmp.Comparer(func(x, y *int) bool { return x == y }),
923                 },
924                 wantEqual: false,
925                 reason:    "reporter should display the address to disambiguate between the two struct fields",
926         }, {
927                 label: label + "/AmbiguousPointerSlice",
928                 x:     []*int{newInt(0)},
929                 y:     []*int{newInt(0)},
930                 opts: []cmp.Option{
931                         cmp.Comparer(func(x, y *int) bool { return x == y }),
932                 },
933                 wantEqual: false,
934                 reason:    "reporter should display the address to disambiguate between the two slice elements",
935         }, {
936                 label: label + "/AmbiguousPointerMap",
937                 x:     map[string]*int{"zero": newInt(0)},
938                 y:     map[string]*int{"zero": newInt(0)},
939                 opts: []cmp.Option{
940                         cmp.Comparer(func(x, y *int) bool { return x == y }),
941                 },
942                 wantEqual: false,
943                 reason:    "reporter should display the address to disambiguate between the two map values",
944         }, {
945                 label:     label + "/AmbiguousStringer",
946                 x:         Stringer("hello"),
947                 y:         newStringer("hello"),
948                 wantEqual: false,
949                 reason:    "reporter should avoid calling String to disambiguate between the two values",
950         }, {
951                 label:     label + "/AmbiguousStringerStruct",
952                 x:         struct{ S fmt.Stringer }{Stringer("hello")},
953                 y:         struct{ S fmt.Stringer }{newStringer("hello")},
954                 wantEqual: false,
955                 reason:    "reporter should avoid calling String to disambiguate between the two struct fields",
956         }, {
957                 label:     label + "/AmbiguousStringerSlice",
958                 x:         []fmt.Stringer{Stringer("hello")},
959                 y:         []fmt.Stringer{newStringer("hello")},
960                 wantEqual: false,
961                 reason:    "reporter should avoid calling String to disambiguate between the two slice elements",
962         }, {
963                 label:     label + "/AmbiguousStringerMap",
964                 x:         map[string]fmt.Stringer{"zero": Stringer("hello")},
965                 y:         map[string]fmt.Stringer{"zero": newStringer("hello")},
966                 wantEqual: false,
967                 reason:    "reporter should avoid calling String to disambiguate between the two map values",
968         }, {
969                 label: label + "/AmbiguousSliceHeader",
970                 x:     make([]int, 0, 5),
971                 y:     make([]int, 0, 1000),
972                 opts: []cmp.Option{
973                         cmp.Comparer(func(x, y []int) bool { return cap(x) == cap(y) }),
974                 },
975                 wantEqual: false,
976                 reason:    "reporter should display the slice header to disambiguate between the two slice values",
977         }, {
978                 label: label + "/AmbiguousStringerMapKey",
979                 x: map[interface{}]string{
980                         nil:               "nil",
981                         Stringer("hello"): "goodbye",
982                         foo1.Bar{"fizz"}:  "buzz",
983                 },
984                 y: map[interface{}]string{
985                         newStringer("hello"): "goodbye",
986                         foo2.Bar{"fizz"}:     "buzz",
987                 },
988                 wantEqual: false,
989                 reason:    "reporter should avoid calling String to disambiguate between the two map keys",
990         }, {
991                 label:     label + "/NonAmbiguousStringerMapKey",
992                 x:         map[interface{}]string{Stringer("hello"): "goodbye"},
993                 y:         map[interface{}]string{newStringer("fizz"): "buzz"},
994                 wantEqual: false,
995                 reason:    "reporter should call String as there is no ambiguity between the two map keys",
996         }, {
997                 label:     label + "/InvalidUTF8",
998                 x:         MyString("\xed\xa0\x80"),
999                 wantEqual: false,
1000                 reason:    "invalid UTF-8 should format as quoted string",
1001         }, {
1002                 label:     label + "/UnbatchedSlice",
1003                 x:         MyComposite{IntsA: []int8{11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
1004                 y:         MyComposite{IntsA: []int8{10, 11, 21, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
1005                 wantEqual: false,
1006                 reason:    "unbatched diffing desired since few elements differ",
1007         }, {
1008                 label:     label + "/BatchedSlice",
1009                 x:         MyComposite{IntsA: []int8{10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
1010                 y:         MyComposite{IntsA: []int8{12, 29, 13, 27, 22, 23, 17, 18, 19, 20, 21, 10, 26, 16, 25, 28, 11, 15, 24, 14}},
1011                 wantEqual: false,
1012                 reason:    "batched diffing desired since many elements differ",
1013         }, {
1014                 label:     label + "/BatchedWithComparer",
1015                 x:         MyComposite{BytesA: []byte{10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
1016                 y:         MyComposite{BytesA: []byte{12, 29, 13, 27, 22, 23, 17, 18, 19, 20, 21, 10, 26, 16, 25, 28, 11, 15, 24, 14}},
1017                 wantEqual: false,
1018                 opts: []cmp.Option{
1019                         cmp.Comparer(bytes.Equal),
1020                 },
1021                 reason: "batched diffing desired since many elements differ",
1022         }, {
1023                 label:     label + "/BatchedLong",
1024                 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}},
1025                 wantEqual: false,
1026                 reason:    "batched output desired for a single slice of primitives unique to one of the inputs",
1027         }, {
1028                 label: label + "/BatchedNamedAndUnnamed",
1029                 x: MyComposite{
1030                         BytesA:  []byte{1, 2, 3},
1031                         BytesB:  []MyByte{4, 5, 6},
1032                         BytesC:  MyBytes{7, 8, 9},
1033                         IntsA:   []int8{-1, -2, -3},
1034                         IntsB:   []MyInt{-4, -5, -6},
1035                         IntsC:   MyInts{-7, -8, -9},
1036                         UintsA:  []uint16{1000, 2000, 3000},
1037                         UintsB:  []MyUint{4000, 5000, 6000},
1038                         UintsC:  MyUints{7000, 8000, 9000},
1039                         FloatsA: []float32{1.5, 2.5, 3.5},
1040                         FloatsB: []MyFloat{4.5, 5.5, 6.5},
1041                         FloatsC: MyFloats{7.5, 8.5, 9.5},
1042                 },
1043                 y: MyComposite{
1044                         BytesA:  []byte{3, 2, 1},
1045                         BytesB:  []MyByte{6, 5, 4},
1046                         BytesC:  MyBytes{9, 8, 7},
1047                         IntsA:   []int8{-3, -2, -1},
1048                         IntsB:   []MyInt{-6, -5, -4},
1049                         IntsC:   MyInts{-9, -8, -7},
1050                         UintsA:  []uint16{3000, 2000, 1000},
1051                         UintsB:  []MyUint{6000, 5000, 4000},
1052                         UintsC:  MyUints{9000, 8000, 7000},
1053                         FloatsA: []float32{3.5, 2.5, 1.5},
1054                         FloatsB: []MyFloat{6.5, 5.5, 4.5},
1055                         FloatsC: MyFloats{9.5, 8.5, 7.5},
1056                 },
1057                 wantEqual: false,
1058                 reason:    "batched diffing available for both named and unnamed slices",
1059         }, {
1060                 label:     label + "/BinaryHexdump",
1061                 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]")},
1062                 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]")},
1063                 wantEqual: false,
1064                 reason:    "binary diff in hexdump form since data is binary data",
1065         }, {
1066                 label:     label + "/StringHexdump",
1067                 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")},
1068                 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")},
1069                 wantEqual: false,
1070                 reason:    "binary diff desired since string looks like binary data",
1071         }, {
1072                 label:     label + "/BinaryString",
1073                 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}`)},
1074                 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}`)},
1075                 wantEqual: false,
1076                 reason:    "batched textual diff desired since bytes looks like textual data",
1077         }, {
1078                 label:     label + "/TripleQuote",
1079                 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"},
1080                 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"},
1081                 wantEqual: false,
1082                 reason:    "use triple-quote syntax",
1083         }, {
1084                 label: label + "/TripleQuoteSlice",
1085                 x: []string{
1086                         "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",
1087                         "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",
1088                 },
1089                 y: []string{
1090                         "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",
1091                         "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",
1092                 },
1093                 wantEqual: false,
1094                 reason:    "use triple-quote syntax for slices of strings",
1095         }, {
1096                 label: label + "/TripleQuoteNamedTypes",
1097                 x: MyComposite{
1098                         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"),
1099                         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"),
1100                 },
1101                 y: MyComposite{
1102                         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"),
1103                         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"),
1104                 },
1105                 wantEqual: false,
1106                 reason:    "use triple-quote syntax for named types",
1107         }, {
1108                 label: label + "/TripleQuoteSliceNamedTypes",
1109                 x: []MyString{
1110                         "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",
1111                         "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",
1112                 },
1113                 y: []MyString{
1114                         "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",
1115                         "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",
1116                 },
1117                 wantEqual: false,
1118                 reason:    "use triple-quote syntax for slices of named strings",
1119         }, {
1120                 label:     label + "/TripleQuoteEndlines",
1121                 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",
1122                 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",
1123                 wantEqual: false,
1124                 reason:    "use triple-quote syntax",
1125         }, {
1126                 label:     label + "/AvoidTripleQuoteAmbiguousQuotes",
1127                 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",
1128                 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",
1129                 wantEqual: false,
1130                 reason:    "avoid triple-quote syntax due to presence of ambiguous triple quotes",
1131         }, {
1132                 label:     label + "/AvoidTripleQuoteAmbiguousEllipsis",
1133                 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",
1134                 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                 wantEqual: false,
1136                 reason:    "avoid triple-quote syntax due to presence of ambiguous ellipsis",
1137         }, {
1138                 label:     label + "/AvoidTripleQuoteNonPrintable",
1139                 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",
1140                 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",
1141                 wantEqual: false,
1142                 reason:    "use triple-quote syntax",
1143         }, {
1144                 label:     label + "/AvoidTripleQuoteIdenticalWhitespace",
1145                 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",
1146                 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",
1147                 wantEqual: false,
1148                 reason:    "avoid triple-quote syntax due to visual equivalence of differences",
1149         }, {
1150                 label: label + "/TripleQuoteStringer",
1151                 x: []fmt.Stringer{
1152                         bytes.NewBuffer([]byte("package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hello, playground\")\n}\n")),
1153                         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")),
1154                 },
1155                 y: []fmt.Stringer{
1156                         bytes.NewBuffer([]byte("package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hello, playground\")\n}\n")),
1157                         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")),
1158                 },
1159                 opts:      []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })},
1160                 wantEqual: false,
1161                 reason:    "multi-line String output should be formatted with triple quote",
1162         }, {
1163                 label:     label + "/LimitMaximumBytesDiffs",
1164                 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============="),
1165                 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============="),
1166                 wantEqual: false,
1167                 reason:    "total bytes difference output is truncated due to excessive number of differences",
1168         }, {
1169                 label:     label + "/LimitMaximumStringDiffs",
1170                 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",
1171                 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",
1172                 wantEqual: false,
1173                 reason:    "total string difference output is truncated due to excessive number of differences",
1174         }, {
1175                 label: label + "/LimitMaximumSliceDiffs",
1176                 x: func() (out []struct{ S string }) {
1177                         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") {
1178                                 out = append(out, struct{ S string }{s})
1179                         }
1180                         return out
1181                 }(),
1182                 y: func() (out []struct{ S string }) {
1183                         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") {
1184                                 out = append(out, struct{ S string }{s})
1185                         }
1186                         return out
1187                 }(),
1188                 wantEqual: false,
1189                 reason:    "total slice difference output is truncated due to excessive number of differences",
1190         }, {
1191                 label: label + "/MultilineString",
1192                 x: MyComposite{
1193                         StringA: strings.TrimPrefix(`
1194 Package cmp determines equality of values.
1195
1196 This package is intended to be a more powerful and safer alternative to
1197 reflect.DeepEqual for comparing whether two values are semantically equal.
1198
1199 The primary features of cmp are:
1200
1201 • When the default behavior of equality does not suit the needs of the test,
1202 custom equality functions can override the equality operation.
1203 For example, an equality function may report floats as equal so long as they
1204 are within some tolerance of each other.
1205
1206 • Types that have an Equal method may use that method to determine equality.
1207 This allows package authors to determine the equality operation for the types
1208 that they define.
1209
1210 • If no custom equality functions are used and no Equal method is defined,
1211 equality is determined by recursively comparing the primitive kinds on both
1212 values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported
1213 fields are not compared by default; they result in panics unless suppressed
1214 by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared
1215 using the AllowUnexported option.
1216 `, "\n"),
1217                 },
1218                 y: MyComposite{
1219                         StringA: strings.TrimPrefix(`
1220 Package cmp determines equality of value.
1221
1222 This package is intended to be a more powerful and safer alternative to
1223 reflect.DeepEqual for comparing whether two values are semantically equal.
1224
1225 The primary features of cmp are:
1226
1227 • When the default behavior of equality does not suit the needs of the test,
1228 custom equality functions can override the equality operation.
1229 For example, an equality function may report floats as equal so long as they
1230 are within some tolerance of each other.
1231
1232 • If no custom equality functions are used and no Equal method is defined,
1233 equality is determined by recursively comparing the primitive kinds on both
1234 values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported
1235 fields are not compared by default; they result in panics unless suppressed
1236 by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared
1237 using the AllowUnexported option.`, "\n"),
1238                 },
1239                 wantEqual: false,
1240                 reason:    "batched per-line diff desired since string looks like multi-line textual data",
1241         }, {
1242                 label: label + "/Slices",
1243                 x: MyComposite{
1244                         BytesA:  []byte{1, 2, 3},
1245                         BytesB:  []MyByte{4, 5, 6},
1246                         BytesC:  MyBytes{7, 8, 9},
1247                         IntsA:   []int8{-1, -2, -3},
1248                         IntsB:   []MyInt{-4, -5, -6},
1249                         IntsC:   MyInts{-7, -8, -9},
1250                         UintsA:  []uint16{1000, 2000, 3000},
1251                         UintsB:  []MyUint{4000, 5000, 6000},
1252                         UintsC:  MyUints{7000, 8000, 9000},
1253                         FloatsA: []float32{1.5, 2.5, 3.5},
1254                         FloatsB: []MyFloat{4.5, 5.5, 6.5},
1255                         FloatsC: MyFloats{7.5, 8.5, 9.5},
1256                 },
1257                 y:         MyComposite{},
1258                 wantEqual: false,
1259                 reason:    "batched diffing for non-nil slices and nil slices",
1260         }, {
1261                 label: label + "/EmptySlices",
1262                 x: MyComposite{
1263                         BytesA:  []byte{},
1264                         BytesB:  []MyByte{},
1265                         BytesC:  MyBytes{},
1266                         IntsA:   []int8{},
1267                         IntsB:   []MyInt{},
1268                         IntsC:   MyInts{},
1269                         UintsA:  []uint16{},
1270                         UintsB:  []MyUint{},
1271                         UintsC:  MyUints{},
1272                         FloatsA: []float32{},
1273                         FloatsB: []MyFloat{},
1274                         FloatsC: MyFloats{},
1275                 },
1276                 y:         MyComposite{},
1277                 wantEqual: false,
1278                 reason:    "batched diffing for empty slices and nil slices",
1279         }, {
1280                 label: label + "/LargeMapKey",
1281                 x: map[*[]byte]int{func() *[]byte {
1282                         b := make([]byte, 1<<20, 1<<20)
1283                         return &b
1284                 }(): 0},
1285                 y: map[*[]byte]int{func() *[]byte {
1286                         b := make([]byte, 1<<20, 1<<20)
1287                         return &b
1288                 }(): 0},
1289                 reason: "printing map keys should have some verbosity limit imposed",
1290         }}
1291 }
1292
1293 func embeddedTests() []test {
1294         const label = "EmbeddedStruct"
1295
1296         privateStruct := *new(ts.ParentStructA).PrivateStruct()
1297
1298         createStructA := func(i int) ts.ParentStructA {
1299                 s := ts.ParentStructA{}
1300                 s.PrivateStruct().Public = 1 + i
1301                 s.PrivateStruct().SetPrivate(2 + i)
1302                 return s
1303         }
1304
1305         createStructB := func(i int) ts.ParentStructB {
1306                 s := ts.ParentStructB{}
1307                 s.PublicStruct.Public = 1 + i
1308                 s.PublicStruct.SetPrivate(2 + i)
1309                 return s
1310         }
1311
1312         createStructC := func(i int) ts.ParentStructC {
1313                 s := ts.ParentStructC{}
1314                 s.PrivateStruct().Public = 1 + i
1315                 s.PrivateStruct().SetPrivate(2 + i)
1316                 s.Public = 3 + i
1317                 s.SetPrivate(4 + i)
1318                 return s
1319         }
1320
1321         createStructD := func(i int) ts.ParentStructD {
1322                 s := ts.ParentStructD{}
1323                 s.PublicStruct.Public = 1 + i
1324                 s.PublicStruct.SetPrivate(2 + i)
1325                 s.Public = 3 + i
1326                 s.SetPrivate(4 + i)
1327                 return s
1328         }
1329
1330         createStructE := func(i int) ts.ParentStructE {
1331                 s := ts.ParentStructE{}
1332                 s.PrivateStruct().Public = 1 + i
1333                 s.PrivateStruct().SetPrivate(2 + i)
1334                 s.PublicStruct.Public = 3 + i
1335                 s.PublicStruct.SetPrivate(4 + i)
1336                 return s
1337         }
1338
1339         createStructF := func(i int) ts.ParentStructF {
1340                 s := ts.ParentStructF{}
1341                 s.PrivateStruct().Public = 1 + i
1342                 s.PrivateStruct().SetPrivate(2 + i)
1343                 s.PublicStruct.Public = 3 + i
1344                 s.PublicStruct.SetPrivate(4 + i)
1345                 s.Public = 5 + i
1346                 s.SetPrivate(6 + i)
1347                 return s
1348         }
1349
1350         createStructG := func(i int) *ts.ParentStructG {
1351                 s := ts.NewParentStructG()
1352                 s.PrivateStruct().Public = 1 + i
1353                 s.PrivateStruct().SetPrivate(2 + i)
1354                 return s
1355         }
1356
1357         createStructH := func(i int) *ts.ParentStructH {
1358                 s := ts.NewParentStructH()
1359                 s.PublicStruct.Public = 1 + i
1360                 s.PublicStruct.SetPrivate(2 + i)
1361                 return s
1362         }
1363
1364         createStructI := func(i int) *ts.ParentStructI {
1365                 s := ts.NewParentStructI()
1366                 s.PrivateStruct().Public = 1 + i
1367                 s.PrivateStruct().SetPrivate(2 + i)
1368                 s.PublicStruct.Public = 3 + i
1369                 s.PublicStruct.SetPrivate(4 + i)
1370                 return s
1371         }
1372
1373         createStructJ := func(i int) *ts.ParentStructJ {
1374                 s := ts.NewParentStructJ()
1375                 s.PrivateStruct().Public = 1 + i
1376                 s.PrivateStruct().SetPrivate(2 + i)
1377                 s.PublicStruct.Public = 3 + i
1378                 s.PublicStruct.SetPrivate(4 + i)
1379                 s.Private().Public = 5 + i
1380                 s.Private().SetPrivate(6 + i)
1381                 s.Public.Public = 7 + i
1382                 s.Public.SetPrivate(8 + i)
1383                 return s
1384         }
1385
1386         // TODO(≥go1.10): Workaround for reflect bug (https://golang.org/issue/21122).
1387         wantPanicNotGo110 := func(s string) string {
1388                 if !flags.AtLeastGo110 {
1389                         return ""
1390                 }
1391                 return s
1392         }
1393
1394         return []test{{
1395                 label:     label + "/ParentStructA/PanicUnexported1",
1396                 x:         ts.ParentStructA{},
1397                 y:         ts.ParentStructA{},
1398                 wantPanic: "cannot handle unexported field",
1399                 reason:    "ParentStructA has an unexported field",
1400         }, {
1401                 label: label + "/ParentStructA/Ignored",
1402                 x:     ts.ParentStructA{},
1403                 y:     ts.ParentStructA{},
1404                 opts: []cmp.Option{
1405                         cmpopts.IgnoreUnexported(ts.ParentStructA{}),
1406                 },
1407                 wantEqual: true,
1408                 reason:    "the only field (which is unexported) of ParentStructA is ignored",
1409         }, {
1410                 label: label + "/ParentStructA/PanicUnexported2",
1411                 x:     createStructA(0),
1412                 y:     createStructA(0),
1413                 opts: []cmp.Option{
1414                         cmp.AllowUnexported(ts.ParentStructA{}),
1415                 },
1416                 wantPanic: "cannot handle unexported field",
1417                 reason:    "privateStruct also has unexported fields",
1418         }, {
1419                 label: label + "/ParentStructA/Equal",
1420                 x:     createStructA(0),
1421                 y:     createStructA(0),
1422                 opts: []cmp.Option{
1423                         cmp.AllowUnexported(ts.ParentStructA{}, privateStruct),
1424                 },
1425                 wantEqual: true,
1426                 reason:    "unexported fields of both ParentStructA and privateStruct are allowed",
1427         }, {
1428                 label: label + "/ParentStructA/Inequal",
1429                 x:     createStructA(0),
1430                 y:     createStructA(1),
1431                 opts: []cmp.Option{
1432                         cmp.AllowUnexported(ts.ParentStructA{}, privateStruct),
1433                 },
1434                 wantEqual: false,
1435                 reason:    "the two values differ on some fields",
1436         }, {
1437                 label: label + "/ParentStructB/PanicUnexported1",
1438                 x:     ts.ParentStructB{},
1439                 y:     ts.ParentStructB{},
1440                 opts: []cmp.Option{
1441                         cmpopts.IgnoreUnexported(ts.ParentStructB{}),
1442                 },
1443                 wantPanic: "cannot handle unexported field",
1444                 reason:    "PublicStruct has an unexported field",
1445         }, {
1446                 label: label + "/ParentStructB/Ignored",
1447                 x:     ts.ParentStructB{},
1448                 y:     ts.ParentStructB{},
1449                 opts: []cmp.Option{
1450                         cmpopts.IgnoreUnexported(ts.ParentStructB{}),
1451                         cmpopts.IgnoreUnexported(ts.PublicStruct{}),
1452                 },
1453                 wantEqual: true,
1454                 reason:    "unexported fields of both ParentStructB and PublicStruct are ignored",
1455         }, {
1456                 label: label + "/ParentStructB/PanicUnexported2",
1457                 x:     createStructB(0),
1458                 y:     createStructB(0),
1459                 opts: []cmp.Option{
1460                         cmp.AllowUnexported(ts.ParentStructB{}),
1461                 },
1462                 wantPanic: "cannot handle unexported field",
1463                 reason:    "PublicStruct also has unexported fields",
1464         }, {
1465                 label: label + "/ParentStructB/Equal",
1466                 x:     createStructB(0),
1467                 y:     createStructB(0),
1468                 opts: []cmp.Option{
1469                         cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}),
1470                 },
1471                 wantEqual: true,
1472                 reason:    "unexported fields of both ParentStructB and PublicStruct are allowed",
1473         }, {
1474                 label: label + "/ParentStructB/Inequal",
1475                 x:     createStructB(0),
1476                 y:     createStructB(1),
1477                 opts: []cmp.Option{
1478                         cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}),
1479                 },
1480                 wantEqual: false,
1481                 reason:    "the two values differ on some fields",
1482         }, {
1483                 label:     label + "/ParentStructC/PanicUnexported1",
1484                 x:         ts.ParentStructC{},
1485                 y:         ts.ParentStructC{},
1486                 wantPanic: "cannot handle unexported field",
1487                 reason:    "ParentStructC has unexported fields",
1488         }, {
1489                 label: label + "/ParentStructC/Ignored",
1490                 x:     ts.ParentStructC{},
1491                 y:     ts.ParentStructC{},
1492                 opts: []cmp.Option{
1493                         cmpopts.IgnoreUnexported(ts.ParentStructC{}),
1494                 },
1495                 wantEqual: true,
1496                 reason:    "unexported fields of ParentStructC are ignored",
1497         }, {
1498                 label: label + "/ParentStructC/PanicUnexported2",
1499                 x:     createStructC(0),
1500                 y:     createStructC(0),
1501                 opts: []cmp.Option{
1502                         cmp.AllowUnexported(ts.ParentStructC{}),
1503                 },
1504                 wantPanic: "cannot handle unexported field",
1505                 reason:    "privateStruct also has unexported fields",
1506         }, {
1507                 label: label + "/ParentStructC/Equal",
1508                 x:     createStructC(0),
1509                 y:     createStructC(0),
1510                 opts: []cmp.Option{
1511                         cmp.AllowUnexported(ts.ParentStructC{}, privateStruct),
1512                 },
1513                 wantEqual: true,
1514                 reason:    "unexported fields of both ParentStructC and privateStruct are allowed",
1515         }, {
1516                 label: label + "/ParentStructC/Inequal",
1517                 x:     createStructC(0),
1518                 y:     createStructC(1),
1519                 opts: []cmp.Option{
1520                         cmp.AllowUnexported(ts.ParentStructC{}, privateStruct),
1521                 },
1522                 wantEqual: false,
1523                 reason:    "the two values differ on some fields",
1524         }, {
1525                 label: label + "/ParentStructD/PanicUnexported1",
1526                 x:     ts.ParentStructD{},
1527                 y:     ts.ParentStructD{},
1528                 opts: []cmp.Option{
1529                         cmpopts.IgnoreUnexported(ts.ParentStructD{}),
1530                 },
1531                 wantPanic: "cannot handle unexported field",
1532                 reason:    "ParentStructD has unexported fields",
1533         }, {
1534                 label: label + "/ParentStructD/Ignored",
1535                 x:     ts.ParentStructD{},
1536                 y:     ts.ParentStructD{},
1537                 opts: []cmp.Option{
1538                         cmpopts.IgnoreUnexported(ts.ParentStructD{}),
1539                         cmpopts.IgnoreUnexported(ts.PublicStruct{}),
1540                 },
1541                 wantEqual: true,
1542                 reason:    "unexported fields of ParentStructD and PublicStruct are ignored",
1543         }, {
1544                 label: label + "/ParentStructD/PanicUnexported2",
1545                 x:     createStructD(0),
1546                 y:     createStructD(0),
1547                 opts: []cmp.Option{
1548                         cmp.AllowUnexported(ts.ParentStructD{}),
1549                 },
1550                 wantPanic: "cannot handle unexported field",
1551                 reason:    "PublicStruct also has unexported fields",
1552         }, {
1553                 label: label + "/ParentStructD/Equal",
1554                 x:     createStructD(0),
1555                 y:     createStructD(0),
1556                 opts: []cmp.Option{
1557                         cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}),
1558                 },
1559                 wantEqual: true,
1560                 reason:    "unexported fields of both ParentStructD and PublicStruct are allowed",
1561         }, {
1562                 label: label + "/ParentStructD/Inequal",
1563                 x:     createStructD(0),
1564                 y:     createStructD(1),
1565                 opts: []cmp.Option{
1566                         cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}),
1567                 },
1568                 wantEqual: false,
1569                 reason:    "the two values differ on some fields",
1570         }, {
1571                 label: label + "/ParentStructE/PanicUnexported1",
1572                 x:     ts.ParentStructE{},
1573                 y:     ts.ParentStructE{},
1574                 opts: []cmp.Option{
1575                         cmpopts.IgnoreUnexported(ts.ParentStructE{}),
1576                 },
1577                 wantPanic: "cannot handle unexported field",
1578                 reason:    "ParentStructE has unexported fields",
1579         }, {
1580                 label: label + "/ParentStructE/Ignored",
1581                 x:     ts.ParentStructE{},
1582                 y:     ts.ParentStructE{},
1583                 opts: []cmp.Option{
1584                         cmpopts.IgnoreUnexported(ts.ParentStructE{}),
1585                         cmpopts.IgnoreUnexported(ts.PublicStruct{}),
1586                 },
1587                 wantEqual: true,
1588                 reason:    "unexported fields of ParentStructE and PublicStruct are ignored",
1589         }, {
1590                 label: label + "/ParentStructE/PanicUnexported2",
1591                 x:     createStructE(0),
1592                 y:     createStructE(0),
1593                 opts: []cmp.Option{
1594                         cmp.AllowUnexported(ts.ParentStructE{}),
1595                 },
1596                 wantPanic: "cannot handle unexported field",
1597                 reason:    "PublicStruct and privateStruct also has unexported fields",
1598         }, {
1599                 label: label + "/ParentStructE/PanicUnexported3",
1600                 x:     createStructE(0),
1601                 y:     createStructE(0),
1602                 opts: []cmp.Option{
1603                         cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}),
1604                 },
1605                 wantPanic: "cannot handle unexported field",
1606                 reason:    "privateStruct also has unexported fields",
1607         }, {
1608                 label: label + "/ParentStructE/Equal",
1609                 x:     createStructE(0),
1610                 y:     createStructE(0),
1611                 opts: []cmp.Option{
1612                         cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct),
1613                 },
1614                 wantEqual: true,
1615                 reason:    "unexported fields of both ParentStructE, PublicStruct, and privateStruct are allowed",
1616         }, {
1617                 label: label + "/ParentStructE/Inequal",
1618                 x:     createStructE(0),
1619                 y:     createStructE(1),
1620                 opts: []cmp.Option{
1621                         cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct),
1622                 },
1623                 wantEqual: false,
1624                 reason:    "the two values differ on some fields",
1625         }, {
1626                 label: label + "/ParentStructF/PanicUnexported1",
1627                 x:     ts.ParentStructF{},
1628                 y:     ts.ParentStructF{},
1629                 opts: []cmp.Option{
1630                         cmpopts.IgnoreUnexported(ts.ParentStructF{}),
1631                 },
1632                 wantPanic: "cannot handle unexported field",
1633                 reason:    "ParentStructF has unexported fields",
1634         }, {
1635                 label: label + "/ParentStructF/Ignored",
1636                 x:     ts.ParentStructF{},
1637                 y:     ts.ParentStructF{},
1638                 opts: []cmp.Option{
1639                         cmpopts.IgnoreUnexported(ts.ParentStructF{}),
1640                         cmpopts.IgnoreUnexported(ts.PublicStruct{}),
1641                 },
1642                 wantEqual: true,
1643                 reason:    "unexported fields of ParentStructF and PublicStruct are ignored",
1644         }, {
1645                 label: label + "/ParentStructF/PanicUnexported2",
1646                 x:     createStructF(0),
1647                 y:     createStructF(0),
1648                 opts: []cmp.Option{
1649                         cmp.AllowUnexported(ts.ParentStructF{}),
1650                 },
1651                 wantPanic: "cannot handle unexported field",
1652                 reason:    "PublicStruct and privateStruct also has unexported fields",
1653         }, {
1654                 label: label + "/ParentStructF/PanicUnexported3",
1655                 x:     createStructF(0),
1656                 y:     createStructF(0),
1657                 opts: []cmp.Option{
1658                         cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}),
1659                 },
1660                 wantPanic: "cannot handle unexported field",
1661                 reason:    "privateStruct also has unexported fields",
1662         }, {
1663                 label: label + "/ParentStructF/Equal",
1664                 x:     createStructF(0),
1665                 y:     createStructF(0),
1666                 opts: []cmp.Option{
1667                         cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct),
1668                 },
1669                 wantEqual: true,
1670                 reason:    "unexported fields of both ParentStructF, PublicStruct, and privateStruct are allowed",
1671         }, {
1672                 label: label + "/ParentStructF/Inequal",
1673                 x:     createStructF(0),
1674                 y:     createStructF(1),
1675                 opts: []cmp.Option{
1676                         cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct),
1677                 },
1678                 wantEqual: false,
1679                 reason:    "the two values differ on some fields",
1680         }, {
1681                 label:     label + "/ParentStructG/PanicUnexported1",
1682                 x:         ts.ParentStructG{},
1683                 y:         ts.ParentStructG{},
1684                 wantPanic: wantPanicNotGo110("cannot handle unexported field"),
1685                 wantEqual: !flags.AtLeastGo110,
1686                 reason:    "ParentStructG has unexported fields",
1687         }, {
1688                 label: label + "/ParentStructG/Ignored",
1689                 x:     ts.ParentStructG{},
1690                 y:     ts.ParentStructG{},
1691                 opts: []cmp.Option{
1692                         cmpopts.IgnoreUnexported(ts.ParentStructG{}),
1693                 },
1694                 wantEqual: true,
1695                 reason:    "unexported fields of ParentStructG are ignored",
1696         }, {
1697                 label: label + "/ParentStructG/PanicUnexported2",
1698                 x:     createStructG(0),
1699                 y:     createStructG(0),
1700                 opts: []cmp.Option{
1701                         cmp.AllowUnexported(ts.ParentStructG{}),
1702                 },
1703                 wantPanic: "cannot handle unexported field",
1704                 reason:    "privateStruct also has unexported fields",
1705         }, {
1706                 label: label + "/ParentStructG/Equal",
1707                 x:     createStructG(0),
1708                 y:     createStructG(0),
1709                 opts: []cmp.Option{
1710                         cmp.AllowUnexported(ts.ParentStructG{}, privateStruct),
1711                 },
1712                 wantEqual: true,
1713                 reason:    "unexported fields of both ParentStructG and privateStruct are allowed",
1714         }, {
1715                 label: label + "/ParentStructG/Inequal",
1716                 x:     createStructG(0),
1717                 y:     createStructG(1),
1718                 opts: []cmp.Option{
1719                         cmp.AllowUnexported(ts.ParentStructG{}, privateStruct),
1720                 },
1721                 wantEqual: false,
1722                 reason:    "the two values differ on some fields",
1723         }, {
1724                 label:     label + "/ParentStructH/EqualNil",
1725                 x:         ts.ParentStructH{},
1726                 y:         ts.ParentStructH{},
1727                 wantEqual: true,
1728                 reason:    "PublicStruct is not compared because the pointer is nil",
1729         }, {
1730                 label:     label + "/ParentStructH/PanicUnexported1",
1731                 x:         createStructH(0),
1732                 y:         createStructH(0),
1733                 wantPanic: "cannot handle unexported field",
1734                 reason:    "PublicStruct has unexported fields",
1735         }, {
1736                 label: label + "/ParentStructH/Ignored",
1737                 x:     ts.ParentStructH{},
1738                 y:     ts.ParentStructH{},
1739                 opts: []cmp.Option{
1740                         cmpopts.IgnoreUnexported(ts.ParentStructH{}),
1741                 },
1742                 wantEqual: true,
1743                 reason:    "unexported fields of ParentStructH are ignored (it has none)",
1744         }, {
1745                 label: label + "/ParentStructH/PanicUnexported2",
1746                 x:     createStructH(0),
1747                 y:     createStructH(0),
1748                 opts: []cmp.Option{
1749                         cmp.AllowUnexported(ts.ParentStructH{}),
1750                 },
1751                 wantPanic: "cannot handle unexported field",
1752                 reason:    "PublicStruct also has unexported fields",
1753         }, {
1754                 label: label + "/ParentStructH/Equal",
1755                 x:     createStructH(0),
1756                 y:     createStructH(0),
1757                 opts: []cmp.Option{
1758                         cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}),
1759                 },
1760                 wantEqual: true,
1761                 reason:    "unexported fields of both ParentStructH and PublicStruct are allowed",
1762         }, {
1763                 label: label + "/ParentStructH/Inequal",
1764                 x:     createStructH(0),
1765                 y:     createStructH(1),
1766                 opts: []cmp.Option{
1767                         cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}),
1768                 },
1769                 wantEqual: false,
1770                 reason:    "the two values differ on some fields",
1771         }, {
1772                 label:     label + "/ParentStructI/PanicUnexported1",
1773                 x:         ts.ParentStructI{},
1774                 y:         ts.ParentStructI{},
1775                 wantPanic: wantPanicNotGo110("cannot handle unexported field"),
1776                 wantEqual: !flags.AtLeastGo110,
1777                 reason:    "ParentStructI has unexported fields",
1778         }, {
1779                 label: label + "/ParentStructI/Ignored1",
1780                 x:     ts.ParentStructI{},
1781                 y:     ts.ParentStructI{},
1782                 opts: []cmp.Option{
1783                         cmpopts.IgnoreUnexported(ts.ParentStructI{}),
1784                 },
1785                 wantEqual: true,
1786                 reason:    "unexported fields of ParentStructI are ignored",
1787         }, {
1788                 label: label + "/ParentStructI/PanicUnexported2",
1789                 x:     createStructI(0),
1790                 y:     createStructI(0),
1791                 opts: []cmp.Option{
1792                         cmpopts.IgnoreUnexported(ts.ParentStructI{}),
1793                 },
1794                 wantPanic: "cannot handle unexported field",
1795                 reason:    "PublicStruct and privateStruct also has unexported fields",
1796         }, {
1797                 label: label + "/ParentStructI/Ignored2",
1798                 x:     createStructI(0),
1799                 y:     createStructI(0),
1800                 opts: []cmp.Option{
1801                         cmpopts.IgnoreUnexported(ts.ParentStructI{}, ts.PublicStruct{}),
1802                 },
1803                 wantEqual: true,
1804                 reason:    "unexported fields of ParentStructI and PublicStruct are ignored",
1805         }, {
1806                 label: label + "/ParentStructI/PanicUnexported3",
1807                 x:     createStructI(0),
1808                 y:     createStructI(0),
1809                 opts: []cmp.Option{
1810                         cmp.AllowUnexported(ts.ParentStructI{}),
1811                 },
1812                 wantPanic: "cannot handle unexported field",
1813                 reason:    "PublicStruct and privateStruct also has unexported fields",
1814         }, {
1815                 label: label + "/ParentStructI/Equal",
1816                 x:     createStructI(0),
1817                 y:     createStructI(0),
1818                 opts: []cmp.Option{
1819                         cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct),
1820                 },
1821                 wantEqual: true,
1822                 reason:    "unexported fields of both ParentStructI, PublicStruct, and privateStruct are allowed",
1823         }, {
1824                 label: label + "/ParentStructI/Inequal",
1825                 x:     createStructI(0),
1826                 y:     createStructI(1),
1827                 opts: []cmp.Option{
1828                         cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct),
1829                 },
1830                 wantEqual: false,
1831                 reason:    "the two values differ on some fields",
1832         }, {
1833                 label:     label + "/ParentStructJ/PanicUnexported1",
1834                 x:         ts.ParentStructJ{},
1835                 y:         ts.ParentStructJ{},
1836                 wantPanic: "cannot handle unexported field",
1837                 reason:    "ParentStructJ has unexported fields",
1838         }, {
1839                 label: label + "/ParentStructJ/PanicUnexported2",
1840                 x:     ts.ParentStructJ{},
1841                 y:     ts.ParentStructJ{},
1842                 opts: []cmp.Option{
1843                         cmpopts.IgnoreUnexported(ts.ParentStructJ{}),
1844                 },
1845                 wantPanic: "cannot handle unexported field",
1846                 reason:    "PublicStruct and privateStruct also has unexported fields",
1847         }, {
1848                 label: label + "/ParentStructJ/Ignored",
1849                 x:     ts.ParentStructJ{},
1850                 y:     ts.ParentStructJ{},
1851                 opts: []cmp.Option{
1852                         cmpopts.IgnoreUnexported(ts.ParentStructJ{}, ts.PublicStruct{}),
1853                 },
1854                 wantEqual: true,
1855                 reason:    "unexported fields of ParentStructJ and PublicStruct are ignored",
1856         }, {
1857                 label: label + "/ParentStructJ/PanicUnexported3",
1858                 x:     createStructJ(0),
1859                 y:     createStructJ(0),
1860                 opts: []cmp.Option{
1861                         cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}),
1862                 },
1863                 wantPanic: "cannot handle unexported field",
1864                 reason:    "privateStruct also has unexported fields",
1865         }, {
1866                 label: label + "/ParentStructJ/Equal",
1867                 x:     createStructJ(0),
1868                 y:     createStructJ(0),
1869                 opts: []cmp.Option{
1870                         cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct),
1871                 },
1872                 wantEqual: true,
1873                 reason:    "unexported fields of both ParentStructJ, PublicStruct, and privateStruct are allowed",
1874         }, {
1875                 label: label + "/ParentStructJ/Inequal",
1876                 x:     createStructJ(0),
1877                 y:     createStructJ(1),
1878                 opts: []cmp.Option{
1879                         cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct),
1880                 },
1881                 wantEqual: false,
1882                 reason:    "the two values differ on some fields",
1883         }}
1884 }
1885
1886 func methodTests() []test {
1887         const label = "EqualMethod"
1888
1889         // A common mistake that the Equal method is on a pointer receiver,
1890         // but only a non-pointer value is present in the struct.
1891         // A transform can be used to forcibly reference the value.
1892         addrTransform := cmp.FilterPath(func(p cmp.Path) bool {
1893                 if len(p) == 0 {
1894                         return false
1895                 }
1896                 t := p[len(p)-1].Type()
1897                 if _, ok := t.MethodByName("Equal"); ok || t.Kind() == reflect.Ptr {
1898                         return false
1899                 }
1900                 if m, ok := reflect.PtrTo(t).MethodByName("Equal"); ok {
1901                         tf := m.Func.Type()
1902                         return !tf.IsVariadic() && tf.NumIn() == 2 && tf.NumOut() == 1 &&
1903                                 tf.In(0).AssignableTo(tf.In(1)) && tf.Out(0) == reflect.TypeOf(true)
1904                 }
1905                 return false
1906         }, cmp.Transformer("Addr", func(x interface{}) interface{} {
1907                 v := reflect.ValueOf(x)
1908                 vp := reflect.New(v.Type())
1909                 vp.Elem().Set(v)
1910                 return vp.Interface()
1911         }))
1912
1913         // For each of these types, there is an Equal method defined, which always
1914         // returns true, while the underlying data are fundamentally different.
1915         // Since the method should be called, these are expected to be equal.
1916         return []test{{
1917                 label:     label + "/StructA/ValueEqual",
1918                 x:         ts.StructA{X: "NotEqual"},
1919                 y:         ts.StructA{X: "not_equal"},
1920                 wantEqual: true,
1921                 reason:    "Equal method on StructA value called",
1922         }, {
1923                 label:     label + "/StructA/PointerEqual",
1924                 x:         &ts.StructA{X: "NotEqual"},
1925                 y:         &ts.StructA{X: "not_equal"},
1926                 wantEqual: true,
1927                 reason:    "Equal method on StructA pointer called",
1928         }, {
1929                 label:     label + "/StructB/ValueInequal",
1930                 x:         ts.StructB{X: "NotEqual"},
1931                 y:         ts.StructB{X: "not_equal"},
1932                 wantEqual: false,
1933                 reason:    "Equal method on StructB value not called",
1934         }, {
1935                 label:     label + "/StructB/ValueAddrEqual",
1936                 x:         ts.StructB{X: "NotEqual"},
1937                 y:         ts.StructB{X: "not_equal"},
1938                 opts:      []cmp.Option{addrTransform},
1939                 wantEqual: true,
1940                 reason:    "Equal method on StructB pointer called due to shallow copy transform",
1941         }, {
1942                 label:     label + "/StructB/PointerEqual",
1943                 x:         &ts.StructB{X: "NotEqual"},
1944                 y:         &ts.StructB{X: "not_equal"},
1945                 wantEqual: true,
1946                 reason:    "Equal method on StructB pointer called",
1947         }, {
1948                 label:     label + "/StructC/ValueEqual",
1949                 x:         ts.StructC{X: "NotEqual"},
1950                 y:         ts.StructC{X: "not_equal"},
1951                 wantEqual: true,
1952                 reason:    "Equal method on StructC value called",
1953         }, {
1954                 label:     label + "/StructC/PointerEqual",
1955                 x:         &ts.StructC{X: "NotEqual"},
1956                 y:         &ts.StructC{X: "not_equal"},
1957                 wantEqual: true,
1958                 reason:    "Equal method on StructC pointer called",
1959         }, {
1960                 label:     label + "/StructD/ValueInequal",
1961                 x:         ts.StructD{X: "NotEqual"},
1962                 y:         ts.StructD{X: "not_equal"},
1963                 wantEqual: false,
1964                 reason:    "Equal method on StructD value not called",
1965         }, {
1966                 label:     label + "/StructD/ValueAddrEqual",
1967                 x:         ts.StructD{X: "NotEqual"},
1968                 y:         ts.StructD{X: "not_equal"},
1969                 opts:      []cmp.Option{addrTransform},
1970                 wantEqual: true,
1971                 reason:    "Equal method on StructD pointer called due to shallow copy transform",
1972         }, {
1973                 label:     label + "/StructD/PointerEqual",
1974                 x:         &ts.StructD{X: "NotEqual"},
1975                 y:         &ts.StructD{X: "not_equal"},
1976                 wantEqual: true,
1977                 reason:    "Equal method on StructD pointer called",
1978         }, {
1979                 label:     label + "/StructE/ValueInequal",
1980                 x:         ts.StructE{X: "NotEqual"},
1981                 y:         ts.StructE{X: "not_equal"},
1982                 wantEqual: false,
1983                 reason:    "Equal method on StructE value not called",
1984         }, {
1985                 label:     label + "/StructE/ValueAddrEqual",
1986                 x:         ts.StructE{X: "NotEqual"},
1987                 y:         ts.StructE{X: "not_equal"},
1988                 opts:      []cmp.Option{addrTransform},
1989                 wantEqual: true,
1990                 reason:    "Equal method on StructE pointer called due to shallow copy transform",
1991         }, {
1992                 label:     label + "/StructE/PointerEqual",
1993                 x:         &ts.StructE{X: "NotEqual"},
1994                 y:         &ts.StructE{X: "not_equal"},
1995                 wantEqual: true,
1996                 reason:    "Equal method on StructE pointer called",
1997         }, {
1998                 label:     label + "/StructF/ValueInequal",
1999                 x:         ts.StructF{X: "NotEqual"},
2000                 y:         ts.StructF{X: "not_equal"},
2001                 wantEqual: false,
2002                 reason:    "Equal method on StructF value not called",
2003         }, {
2004                 label:     label + "/StructF/PointerEqual",
2005                 x:         &ts.StructF{X: "NotEqual"},
2006                 y:         &ts.StructF{X: "not_equal"},
2007                 wantEqual: true,
2008                 reason:    "Equal method on StructF pointer called",
2009         }, {
2010                 label:     label + "/StructA1/ValueEqual",
2011                 x:         ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"},
2012                 y:         ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"},
2013                 wantEqual: true,
2014                 reason:    "Equal method on StructA value called with equal X field",
2015         }, {
2016                 label:     label + "/StructA1/ValueInequal",
2017                 x:         ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"},
2018                 y:         ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"},
2019                 wantEqual: false,
2020                 reason:    "Equal method on StructA value called, but inequal X field",
2021         }, {
2022                 label:     label + "/StructA1/PointerEqual",
2023                 x:         &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"},
2024                 y:         &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"},
2025                 wantEqual: true,
2026                 reason:    "Equal method on StructA value called with equal X field",
2027         }, {
2028                 label:     label + "/StructA1/PointerInequal",
2029                 x:         &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"},
2030                 y:         &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"},
2031                 wantEqual: false,
2032                 reason:    "Equal method on StructA value called, but inequal X field",
2033         }, {
2034                 label:     label + "/StructB1/ValueEqual",
2035                 x:         ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"},
2036                 y:         ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"},
2037                 opts:      []cmp.Option{addrTransform},
2038                 wantEqual: true,
2039                 reason:    "Equal method on StructB pointer called due to shallow copy transform with equal X field",
2040         }, {
2041                 label:     label + "/StructB1/ValueInequal",
2042                 x:         ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"},
2043                 y:         ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"},
2044                 opts:      []cmp.Option{addrTransform},
2045                 wantEqual: false,
2046                 reason:    "Equal method on StructB pointer called due to shallow copy transform, but inequal X field",
2047         }, {
2048                 label:     label + "/StructB1/PointerEqual",
2049                 x:         &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"},
2050                 y:         &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"},
2051                 opts:      []cmp.Option{addrTransform},
2052                 wantEqual: true,
2053                 reason:    "Equal method on StructB pointer called due to shallow copy transform with equal X field",
2054         }, {
2055                 label:     label + "/StructB1/PointerInequal",
2056                 x:         &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"},
2057                 y:         &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"},
2058                 opts:      []cmp.Option{addrTransform},
2059                 wantEqual: false,
2060                 reason:    "Equal method on StructB pointer called due to shallow copy transform, but inequal X field",
2061         }, {
2062                 label:     label + "/StructC1/ValueEqual",
2063                 x:         ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"},
2064                 y:         ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"},
2065                 wantEqual: true,
2066                 reason:    "Equal method on StructC1 value called",
2067         }, {
2068                 label:     label + "/StructC1/PointerEqual",
2069                 x:         &ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"},
2070                 y:         &ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"},
2071                 wantEqual: true,
2072                 reason:    "Equal method on StructC1 pointer called",
2073         }, {
2074                 label:     label + "/StructD1/ValueInequal",
2075                 x:         ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
2076                 y:         ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
2077                 wantEqual: false,
2078                 reason:    "Equal method on StructD1 value not called",
2079         }, {
2080                 label:     label + "/StructD1/PointerAddrEqual",
2081                 x:         ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
2082                 y:         ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
2083                 opts:      []cmp.Option{addrTransform},
2084                 wantEqual: true,
2085                 reason:    "Equal method on StructD1 pointer called due to shallow copy transform",
2086         }, {
2087                 label:     label + "/StructD1/PointerEqual",
2088                 x:         &ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
2089                 y:         &ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
2090                 wantEqual: true,
2091                 reason:    "Equal method on StructD1 pointer called",
2092         }, {
2093                 label:     label + "/StructE1/ValueInequal",
2094                 x:         ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
2095                 y:         ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
2096                 wantEqual: false,
2097                 reason:    "Equal method on StructE1 value not called",
2098         }, {
2099                 label:     label + "/StructE1/ValueAddrEqual",
2100                 x:         ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
2101                 y:         ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
2102                 opts:      []cmp.Option{addrTransform},
2103                 wantEqual: true,
2104                 reason:    "Equal method on StructE1 pointer called due to shallow copy transform",
2105         }, {
2106                 label:     label + "/StructE1/PointerEqual",
2107                 x:         &ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
2108                 y:         &ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
2109                 wantEqual: true,
2110                 reason:    "Equal method on StructE1 pointer called",
2111         }, {
2112                 label:     label + "/StructF1/ValueInequal",
2113                 x:         ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"},
2114                 y:         ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"},
2115                 wantEqual: false,
2116                 reason:    "Equal method on StructF1 value not called",
2117         }, {
2118                 label:     label + "/StructF1/PointerEqual",
2119                 x:         &ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"},
2120                 y:         &ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"},
2121                 wantEqual: true,
2122                 reason:    "Equal method on StructF1 pointer called",
2123         }, {
2124                 label:     label + "/StructA2/ValueEqual",
2125                 x:         ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"},
2126                 y:         ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"},
2127                 wantEqual: true,
2128                 reason:    "Equal method on StructA pointer called with equal X field",
2129         }, {
2130                 label:     label + "/StructA2/ValueInequal",
2131                 x:         ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"},
2132                 y:         ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"},
2133                 wantEqual: false,
2134                 reason:    "Equal method on StructA pointer called, but inequal X field",
2135         }, {
2136                 label:     label + "/StructA2/PointerEqual",
2137                 x:         &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"},
2138                 y:         &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"},
2139                 wantEqual: true,
2140                 reason:    "Equal method on StructA pointer called with equal X field",
2141         }, {
2142                 label:     label + "/StructA2/PointerInequal",
2143                 x:         &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"},
2144                 y:         &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"},
2145                 wantEqual: false,
2146                 reason:    "Equal method on StructA pointer called, but inequal X field",
2147         }, {
2148                 label:     label + "/StructB2/ValueEqual",
2149                 x:         ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"},
2150                 y:         ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"},
2151                 wantEqual: true,
2152                 reason:    "Equal method on StructB pointer called with equal X field",
2153         }, {
2154                 label:     label + "/StructB2/ValueInequal",
2155                 x:         ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"},
2156                 y:         ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"},
2157                 wantEqual: false,
2158                 reason:    "Equal method on StructB pointer called, but inequal X field",
2159         }, {
2160                 label:     label + "/StructB2/PointerEqual",
2161                 x:         &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"},
2162                 y:         &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"},
2163                 wantEqual: true,
2164                 reason:    "Equal method on StructB pointer called with equal X field",
2165         }, {
2166                 label:     label + "/StructB2/PointerInequal",
2167                 x:         &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"},
2168                 y:         &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"},
2169                 wantEqual: false,
2170                 reason:    "Equal method on StructB pointer called, but inequal X field",
2171         }, {
2172                 label:     label + "/StructC2/ValueEqual",
2173                 x:         ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"},
2174                 y:         ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"},
2175                 wantEqual: true,
2176                 reason:    "Equal method called on StructC2 value due to forwarded StructC pointer",
2177         }, {
2178                 label:     label + "/StructC2/PointerEqual",
2179                 x:         &ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"},
2180                 y:         &ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"},
2181                 wantEqual: true,
2182                 reason:    "Equal method called on StructC2 pointer due to forwarded StructC pointer",
2183         }, {
2184                 label:     label + "/StructD2/ValueEqual",
2185                 x:         ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"},
2186                 y:         ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"},
2187                 wantEqual: true,
2188                 reason:    "Equal method called on StructD2 value due to forwarded StructD pointer",
2189         }, {
2190                 label:     label + "/StructD2/PointerEqual",
2191                 x:         &ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"},
2192                 y:         &ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"},
2193                 wantEqual: true,
2194                 reason:    "Equal method called on StructD2 pointer due to forwarded StructD pointer",
2195         }, {
2196                 label:     label + "/StructE2/ValueEqual",
2197                 x:         ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"},
2198                 y:         ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"},
2199                 wantEqual: true,
2200                 reason:    "Equal method called on StructE2 value due to forwarded StructE pointer",
2201         }, {
2202                 label:     label + "/StructE2/PointerEqual",
2203                 x:         &ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"},
2204                 y:         &ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"},
2205                 wantEqual: true,
2206                 reason:    "Equal method called on StructE2 pointer due to forwarded StructE pointer",
2207         }, {
2208                 label:     label + "/StructF2/ValueEqual",
2209                 x:         ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"},
2210                 y:         ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"},
2211                 wantEqual: true,
2212                 reason:    "Equal method called on StructF2 value due to forwarded StructF pointer",
2213         }, {
2214                 label:     label + "/StructF2/PointerEqual",
2215                 x:         &ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"},
2216                 y:         &ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"},
2217                 wantEqual: true,
2218                 reason:    "Equal method called on StructF2 pointer due to forwarded StructF pointer",
2219         }, {
2220                 label:     label + "/StructNo/Inequal",
2221                 x:         ts.StructNo{X: "NotEqual"},
2222                 y:         ts.StructNo{X: "not_equal"},
2223                 wantEqual: false,
2224                 reason:    "Equal method not called since StructNo is not assignable to InterfaceA",
2225         }, {
2226                 label:     label + "/AssignA/Equal",
2227                 x:         ts.AssignA(func() int { return 0 }),
2228                 y:         ts.AssignA(func() int { return 1 }),
2229                 wantEqual: true,
2230                 reason:    "Equal method called since named func is assignable to unnamed func",
2231         }, {
2232                 label:     label + "/AssignB/Equal",
2233                 x:         ts.AssignB(struct{ A int }{0}),
2234                 y:         ts.AssignB(struct{ A int }{1}),
2235                 wantEqual: true,
2236                 reason:    "Equal method called since named struct is assignable to unnamed struct",
2237         }, {
2238                 label:     label + "/AssignC/Equal",
2239                 x:         ts.AssignC(make(chan bool)),
2240                 y:         ts.AssignC(make(chan bool)),
2241                 wantEqual: true,
2242                 reason:    "Equal method called since named channel is assignable to unnamed channel",
2243         }, {
2244                 label:     label + "/AssignD/Equal",
2245                 x:         ts.AssignD(make(chan bool)),
2246                 y:         ts.AssignD(make(chan bool)),
2247                 wantEqual: true,
2248                 reason:    "Equal method called since named channel is assignable to unnamed channel",
2249         }}
2250 }
2251
2252 type (
2253         CycleAlpha struct {
2254                 Name   string
2255                 Bravos map[string]*CycleBravo
2256         }
2257         CycleBravo struct {
2258                 ID     int
2259                 Name   string
2260                 Mods   int
2261                 Alphas map[string]*CycleAlpha
2262         }
2263 )
2264
2265 func cycleTests() []test {
2266         const label = "Cycle"
2267
2268         type (
2269                 P *P
2270                 S []S
2271                 M map[int]M
2272         )
2273
2274         makeGraph := func() map[string]*CycleAlpha {
2275                 v := map[string]*CycleAlpha{
2276                         "Foo": &CycleAlpha{
2277                                 Name: "Foo",
2278                                 Bravos: map[string]*CycleBravo{
2279                                         "FooBravo": &CycleBravo{
2280                                                 Name: "FooBravo",
2281                                                 ID:   101,
2282                                                 Mods: 100,
2283                                                 Alphas: map[string]*CycleAlpha{
2284                                                         "Foo": nil, // cyclic reference
2285                                                 },
2286                                         },
2287                                 },
2288                         },
2289                         "Bar": &CycleAlpha{
2290                                 Name: "Bar",
2291                                 Bravos: map[string]*CycleBravo{
2292                                         "BarBuzzBravo": &CycleBravo{
2293                                                 Name: "BarBuzzBravo",
2294                                                 ID:   102,
2295                                                 Mods: 2,
2296                                                 Alphas: map[string]*CycleAlpha{
2297                                                         "Bar":  nil, // cyclic reference
2298                                                         "Buzz": nil, // cyclic reference
2299                                                 },
2300                                         },
2301                                         "BuzzBarBravo": &CycleBravo{
2302                                                 Name: "BuzzBarBravo",
2303                                                 ID:   103,
2304                                                 Mods: 0,
2305                                                 Alphas: map[string]*CycleAlpha{
2306                                                         "Bar":  nil, // cyclic reference
2307                                                         "Buzz": nil, // cyclic reference
2308                                                 },
2309                                         },
2310                                 },
2311                         },
2312                         "Buzz": &CycleAlpha{
2313                                 Name: "Buzz",
2314                                 Bravos: map[string]*CycleBravo{
2315                                         "BarBuzzBravo": nil, // cyclic reference
2316                                         "BuzzBarBravo": nil, // cyclic reference
2317                                 },
2318                         },
2319                 }
2320                 v["Foo"].Bravos["FooBravo"].Alphas["Foo"] = v["Foo"]
2321                 v["Bar"].Bravos["BarBuzzBravo"].Alphas["Bar"] = v["Bar"]
2322                 v["Bar"].Bravos["BarBuzzBravo"].Alphas["Buzz"] = v["Buzz"]
2323                 v["Bar"].Bravos["BuzzBarBravo"].Alphas["Bar"] = v["Bar"]
2324                 v["Bar"].Bravos["BuzzBarBravo"].Alphas["Buzz"] = v["Buzz"]
2325                 v["Buzz"].Bravos["BarBuzzBravo"] = v["Bar"].Bravos["BarBuzzBravo"]
2326                 v["Buzz"].Bravos["BuzzBarBravo"] = v["Bar"].Bravos["BuzzBarBravo"]
2327                 return v
2328         }
2329
2330         var tests []test
2331         type XY struct{ x, y interface{} }
2332         for _, tt := range []struct {
2333                 label     string
2334                 in        XY
2335                 wantEqual bool
2336                 reason    string
2337         }{{
2338                 label: "PointersEqual",
2339                 in: func() XY {
2340                         x := new(P)
2341                         *x = x
2342                         y := new(P)
2343                         *y = y
2344                         return XY{x, y}
2345                 }(),
2346                 wantEqual: true,
2347                 reason:    "equal pair of single-node pointers",
2348         }, {
2349                 label: "PointersInequal",
2350                 in: func() XY {
2351                         x := new(P)
2352                         *x = x
2353                         y1, y2 := new(P), new(P)
2354                         *y1 = y2
2355                         *y2 = y1
2356                         return XY{x, y1}
2357                 }(),
2358                 wantEqual: false,
2359                 reason:    "inequal pair of single-node and double-node pointers",
2360         }, {
2361                 label: "SlicesEqual",
2362                 in: func() XY {
2363                         x := S{nil}
2364                         x[0] = x
2365                         y := S{nil}
2366                         y[0] = y
2367                         return XY{x, y}
2368                 }(),
2369                 wantEqual: true,
2370                 reason:    "equal pair of single-node slices",
2371         }, {
2372                 label: "SlicesInequal",
2373                 in: func() XY {
2374                         x := S{nil}
2375                         x[0] = x
2376                         y1, y2 := S{nil}, S{nil}
2377                         y1[0] = y2
2378                         y2[0] = y1
2379                         return XY{x, y1}
2380                 }(),
2381                 wantEqual: false,
2382                 reason:    "inequal pair of single-node and double node slices",
2383         }, {
2384                 label: "MapsEqual",
2385                 in: func() XY {
2386                         x := M{0: nil}
2387                         x[0] = x
2388                         y := M{0: nil}
2389                         y[0] = y
2390                         return XY{x, y}
2391                 }(),
2392                 wantEqual: true,
2393                 reason:    "equal pair of single-node maps",
2394         }, {
2395                 label: "MapsInequal",
2396                 in: func() XY {
2397                         x := M{0: nil}
2398                         x[0] = x
2399                         y1, y2 := M{0: nil}, M{0: nil}
2400                         y1[0] = y2
2401                         y2[0] = y1
2402                         return XY{x, y1}
2403                 }(),
2404                 wantEqual: false,
2405                 reason:    "inequal pair of single-node and double-node maps",
2406         }, {
2407                 label:     "GraphEqual",
2408                 in:        XY{makeGraph(), makeGraph()},
2409                 wantEqual: true,
2410                 reason:    "graphs are equal since they have identical forms",
2411         }, {
2412                 label: "GraphInequalZeroed",
2413                 in: func() XY {
2414                         x := makeGraph()
2415                         y := makeGraph()
2416                         y["Foo"].Bravos["FooBravo"].ID = 0
2417                         y["Bar"].Bravos["BarBuzzBravo"].ID = 0
2418                         y["Bar"].Bravos["BuzzBarBravo"].ID = 0
2419                         return XY{x, y}
2420                 }(),
2421                 wantEqual: false,
2422                 reason:    "graphs are inequal because the ID fields are different",
2423         }, {
2424                 label: "GraphInequalStruct",
2425                 in: func() XY {
2426                         x := makeGraph()
2427                         y := makeGraph()
2428                         x["Buzz"].Bravos["BuzzBarBravo"] = &CycleBravo{
2429                                 Name: "BuzzBarBravo",
2430                                 ID:   103,
2431                         }
2432                         return XY{x, y}
2433                 }(),
2434                 wantEqual: false,
2435                 reason:    "graphs are inequal because they differ on a map element",
2436         }} {
2437                 tests = append(tests, test{
2438                         label:     label + "/" + tt.label,
2439                         x:         tt.in.x,
2440                         y:         tt.in.y,
2441                         wantEqual: tt.wantEqual,
2442                         reason:    tt.reason,
2443                 })
2444         }
2445         return tests
2446 }
2447
2448 func project1Tests() []test {
2449         const label = "Project1"
2450
2451         ignoreUnexported := cmpopts.IgnoreUnexported(
2452                 ts.EagleImmutable{},
2453                 ts.DreamerImmutable{},
2454                 ts.SlapImmutable{},
2455                 ts.GoatImmutable{},
2456                 ts.DonkeyImmutable{},
2457                 ts.LoveRadius{},
2458                 ts.SummerLove{},
2459                 ts.SummerLoveSummary{},
2460         )
2461
2462         createEagle := func() ts.Eagle {
2463                 return ts.Eagle{
2464                         Name:   "eagle",
2465                         Hounds: []string{"buford", "tannen"},
2466                         Desc:   "some description",
2467                         Dreamers: []ts.Dreamer{{}, {
2468                                 Name: "dreamer2",
2469                                 Animal: []interface{}{
2470                                         ts.Goat{
2471                                                 Target: "corporation",
2472                                                 Immutable: &ts.GoatImmutable{
2473                                                         ID:      "southbay",
2474                                                         State:   (*pb.Goat_States)(newInt(5)),
2475                                                         Started: now,
2476                                                 },
2477                                         },
2478                                         ts.Donkey{},
2479                                 },
2480                                 Amoeba: 53,
2481                         }},
2482                         Slaps: []ts.Slap{{
2483                                 Name: "slapID",
2484                                 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2485                                 Immutable: &ts.SlapImmutable{
2486                                         ID:       "immutableSlap",
2487                                         MildSlap: true,
2488                                         Started:  now,
2489                                         LoveRadius: &ts.LoveRadius{
2490                                                 Summer: &ts.SummerLove{
2491                                                         Summary: &ts.SummerLoveSummary{
2492                                                                 Devices:    []string{"foo", "bar", "baz"},
2493                                                                 ChangeType: []pb.SummerType{1, 2, 3},
2494                                                         },
2495                                                 },
2496                                         },
2497                                 },
2498                         }},
2499                         Immutable: &ts.EagleImmutable{
2500                                 ID:          "eagleID",
2501                                 Birthday:    now,
2502                                 MissingCall: (*pb.Eagle_MissingCalls)(newInt(55)),
2503                         },
2504                 }
2505         }
2506
2507         return []test{{
2508                 label: label + "/PanicUnexported",
2509                 x: ts.Eagle{Slaps: []ts.Slap{{
2510                         Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2511                 }}},
2512                 y: ts.Eagle{Slaps: []ts.Slap{{
2513                         Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2514                 }}},
2515                 wantPanic: "cannot handle unexported field",
2516                 reason:    "struct contains unexported fields",
2517         }, {
2518                 label: label + "/ProtoEqual",
2519                 x: ts.Eagle{Slaps: []ts.Slap{{
2520                         Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2521                 }}},
2522                 y: ts.Eagle{Slaps: []ts.Slap{{
2523                         Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2524                 }}},
2525                 opts:      []cmp.Option{cmp.Comparer(pb.Equal)},
2526                 wantEqual: true,
2527                 reason:    "simulated protobuf messages contain the same values",
2528         }, {
2529                 label: label + "/ProtoInequal",
2530                 x: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, {
2531                         Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
2532                 }}},
2533                 y: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, {
2534                         Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata2"}},
2535                 }}},
2536                 opts:      []cmp.Option{cmp.Comparer(pb.Equal)},
2537                 wantEqual: false,
2538                 reason:    "simulated protobuf messages contain different values",
2539         }, {
2540                 label:     label + "/Equal",
2541                 x:         createEagle(),
2542                 y:         createEagle(),
2543                 opts:      []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)},
2544                 wantEqual: true,
2545                 reason:    "equal because values are the same",
2546         }, {
2547                 label: label + "/Inequal",
2548                 x: func() ts.Eagle {
2549                         eg := createEagle()
2550                         eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.ID = "southbay2"
2551                         eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.State = (*pb.Goat_States)(newInt(6))
2552                         eg.Slaps[0].Immutable.MildSlap = false
2553                         return eg
2554                 }(),
2555                 y: func() ts.Eagle {
2556                         eg := createEagle()
2557                         devs := eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices
2558                         eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices = devs[:1]
2559                         return eg
2560                 }(),
2561                 opts:      []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)},
2562                 wantEqual: false,
2563                 reason:    "inequal because some values are different",
2564         }}
2565 }
2566
2567 type germSorter []*pb.Germ
2568
2569 func (gs germSorter) Len() int           { return len(gs) }
2570 func (gs germSorter) Less(i, j int) bool { return gs[i].String() < gs[j].String() }
2571 func (gs germSorter) Swap(i, j int)      { gs[i], gs[j] = gs[j], gs[i] }
2572
2573 func project2Tests() []test {
2574         const label = "Project2"
2575
2576         sortGerms := cmp.Transformer("Sort", func(in []*pb.Germ) []*pb.Germ {
2577                 out := append([]*pb.Germ(nil), in...) // Make copy
2578                 sort.Sort(germSorter(out))
2579                 return out
2580         })
2581
2582         equalDish := cmp.Comparer(func(x, y *ts.Dish) bool {
2583                 if x == nil || y == nil {
2584                         return x == nil && y == nil
2585                 }
2586                 px, err1 := x.Proto()
2587                 py, err2 := y.Proto()
2588                 if err1 != nil || err2 != nil {
2589                         return err1 == err2
2590                 }
2591                 return pb.Equal(px, py)
2592         })
2593
2594         createBatch := func() ts.GermBatch {
2595                 return ts.GermBatch{
2596                         DirtyGerms: map[int32][]*pb.Germ{
2597                                 17: {
2598                                         {Stringer: pb.Stringer{X: "germ1"}},
2599                                 },
2600                                 18: {
2601                                         {Stringer: pb.Stringer{X: "germ2"}},
2602                                         {Stringer: pb.Stringer{X: "germ3"}},
2603                                         {Stringer: pb.Stringer{X: "germ4"}},
2604                                 },
2605                         },
2606                         GermMap: map[int32]*pb.Germ{
2607                                 13: {Stringer: pb.Stringer{X: "germ13"}},
2608                                 21: {Stringer: pb.Stringer{X: "germ21"}},
2609                         },
2610                         DishMap: map[int32]*ts.Dish{
2611                                 0: ts.CreateDish(nil, io.EOF),
2612                                 1: ts.CreateDish(nil, io.ErrUnexpectedEOF),
2613                                 2: ts.CreateDish(&pb.Dish{Stringer: pb.Stringer{X: "dish"}}, nil),
2614                         },
2615                         HasPreviousResult: true,
2616                         DirtyID:           10,
2617                         GermStrain:        421,
2618                         InfectedAt:        now,
2619                 }
2620         }
2621
2622         return []test{{
2623                 label:     label + "/PanicUnexported",
2624                 x:         createBatch(),
2625                 y:         createBatch(),
2626                 wantPanic: "cannot handle unexported field",
2627                 reason:    "struct contains unexported fields",
2628         }, {
2629                 label:     label + "/Equal",
2630                 x:         createBatch(),
2631                 y:         createBatch(),
2632                 opts:      []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
2633                 wantEqual: true,
2634                 reason:    "equal because identical values are compared",
2635         }, {
2636                 label: label + "/InequalOrder",
2637                 x:     createBatch(),
2638                 y: func() ts.GermBatch {
2639                         gb := createBatch()
2640                         s := gb.DirtyGerms[18]
2641                         s[0], s[1], s[2] = s[1], s[2], s[0]
2642                         return gb
2643                 }(),
2644                 opts:      []cmp.Option{cmp.Comparer(pb.Equal), equalDish},
2645                 wantEqual: false,
2646                 reason:    "inequal because slice contains elements in differing order",
2647         }, {
2648                 label: label + "/EqualOrder",
2649                 x:     createBatch(),
2650                 y: func() ts.GermBatch {
2651                         gb := createBatch()
2652                         s := gb.DirtyGerms[18]
2653                         s[0], s[1], s[2] = s[1], s[2], s[0]
2654                         return gb
2655                 }(),
2656                 opts:      []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
2657                 wantEqual: true,
2658                 reason:    "equal because unordered slice is sorted using transformer",
2659         }, {
2660                 label: label + "/Inequal",
2661                 x: func() ts.GermBatch {
2662                         gb := createBatch()
2663                         delete(gb.DirtyGerms, 17)
2664                         gb.DishMap[1] = nil
2665                         return gb
2666                 }(),
2667                 y: func() ts.GermBatch {
2668                         gb := createBatch()
2669                         gb.DirtyGerms[18] = gb.DirtyGerms[18][:2]
2670                         gb.GermStrain = 22
2671                         return gb
2672                 }(),
2673                 opts:      []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
2674                 wantEqual: false,
2675                 reason:    "inequal because some values are different",
2676         }}
2677 }
2678
2679 func project3Tests() []test {
2680         const label = "Project3"
2681
2682         allowVisibility := cmp.AllowUnexported(ts.Dirt{})
2683
2684         ignoreLocker := cmpopts.IgnoreInterfaces(struct{ sync.Locker }{})
2685
2686         transformProtos := cmp.Transformer("λ", func(x pb.Dirt) *pb.Dirt {
2687                 return &x
2688         })
2689
2690         equalTable := cmp.Comparer(func(x, y ts.Table) bool {
2691                 tx, ok1 := x.(*ts.MockTable)
2692                 ty, ok2 := y.(*ts.MockTable)
2693                 if !ok1 || !ok2 {
2694                         panic("table type must be MockTable")
2695                 }
2696                 return cmp.Equal(tx.State(), ty.State())
2697         })
2698
2699         createDirt := func() (d ts.Dirt) {
2700                 d.SetTable(ts.CreateMockTable([]string{"a", "b", "c"}))
2701                 d.SetTimestamp(12345)
2702                 d.Discord = 554
2703                 d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "proto"}}
2704                 d.SetWizard(map[string]*pb.Wizard{
2705                         "harry": {Stringer: pb.Stringer{X: "potter"}},
2706                         "albus": {Stringer: pb.Stringer{X: "dumbledore"}},
2707                 })
2708                 d.SetLastTime(54321)
2709                 return d
2710         }
2711
2712         return []test{{
2713                 label:     label + "/PanicUnexported1",
2714                 x:         createDirt(),
2715                 y:         createDirt(),
2716                 wantPanic: "cannot handle unexported field",
2717                 reason:    "struct contains unexported fields",
2718         }, {
2719                 label:     label + "/PanicUnexported2",
2720                 x:         createDirt(),
2721                 y:         createDirt(),
2722                 opts:      []cmp.Option{allowVisibility, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
2723                 wantPanic: "cannot handle unexported field",
2724                 reason:    "struct contains references to simulated protobuf types with unexported fields",
2725         }, {
2726                 label:     label + "/Equal",
2727                 x:         createDirt(),
2728                 y:         createDirt(),
2729                 opts:      []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
2730                 wantEqual: true,
2731                 reason:    "transformer used to create reference to protobuf message so it works with pb.Equal",
2732         }, {
2733                 label: label + "/Inequal",
2734                 x: func() ts.Dirt {
2735                         d := createDirt()
2736                         d.SetTable(ts.CreateMockTable([]string{"a", "c"}))
2737                         d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "blah"}}
2738                         return d
2739                 }(),
2740                 y: func() ts.Dirt {
2741                         d := createDirt()
2742                         d.Discord = 500
2743                         d.SetWizard(map[string]*pb.Wizard{
2744                                 "harry": {Stringer: pb.Stringer{X: "otter"}},
2745                         })
2746                         return d
2747                 }(),
2748                 opts:      []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
2749                 wantEqual: false,
2750                 reason:    "inequal because some values are different",
2751         }}
2752 }
2753
2754 func project4Tests() []test {
2755         const label = "Project4"
2756
2757         allowVisibility := cmp.AllowUnexported(
2758                 ts.Cartel{},
2759                 ts.Headquarter{},
2760                 ts.Poison{},
2761         )
2762
2763         transformProtos := cmp.Transformer("λ", func(x pb.Restrictions) *pb.Restrictions {
2764                 return &x
2765         })
2766
2767         createCartel := func() ts.Cartel {
2768                 var p ts.Poison
2769                 p.SetPoisonType(5)
2770                 p.SetExpiration(now)
2771                 p.SetManufacturer("acme")
2772
2773                 var hq ts.Headquarter
2774                 hq.SetID(5)
2775                 hq.SetLocation("moon")
2776                 hq.SetSubDivisions([]string{"alpha", "bravo", "charlie"})
2777                 hq.SetMetaData(&pb.MetaData{Stringer: pb.Stringer{X: "metadata"}})
2778                 hq.SetPublicMessage([]byte{1, 2, 3, 4, 5})
2779                 hq.SetHorseBack("abcdef")
2780                 hq.SetStatus(44)
2781
2782                 var c ts.Cartel
2783                 c.Headquarter = hq
2784                 c.SetSource("mars")
2785                 c.SetCreationTime(now)
2786                 c.SetBoss("al capone")
2787                 c.SetPoisons([]*ts.Poison{&p})
2788
2789                 return c
2790         }
2791
2792         return []test{{
2793                 label:     label + "/PanicUnexported1",
2794                 x:         createCartel(),
2795                 y:         createCartel(),
2796                 wantPanic: "cannot handle unexported field",
2797                 reason:    "struct contains unexported fields",
2798         }, {
2799                 label:     label + "/PanicUnexported2",
2800                 x:         createCartel(),
2801                 y:         createCartel(),
2802                 opts:      []cmp.Option{allowVisibility, cmp.Comparer(pb.Equal)},
2803                 wantPanic: "cannot handle unexported field",
2804                 reason:    "struct contains references to simulated protobuf types with unexported fields",
2805         }, {
2806                 label:     label + "/Equal",
2807                 x:         createCartel(),
2808                 y:         createCartel(),
2809                 opts:      []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)},
2810                 wantEqual: true,
2811                 reason:    "transformer used to create reference to protobuf message so it works with pb.Equal",
2812         }, {
2813                 label: label + "/Inequal",
2814                 x: func() ts.Cartel {
2815                         d := createCartel()
2816                         var p1, p2 ts.Poison
2817                         p1.SetPoisonType(1)
2818                         p1.SetExpiration(now)
2819                         p1.SetManufacturer("acme")
2820                         p2.SetPoisonType(2)
2821                         p2.SetManufacturer("acme2")
2822                         d.SetPoisons([]*ts.Poison{&p1, &p2})
2823                         return d
2824                 }(),
2825                 y: func() ts.Cartel {
2826                         d := createCartel()
2827                         d.SetSubDivisions([]string{"bravo", "charlie"})
2828                         d.SetPublicMessage([]byte{1, 2, 4, 3, 5})
2829                         return d
2830                 }(),
2831                 opts:      []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)},
2832                 wantEqual: false,
2833                 reason:    "inequal because some values are different",
2834         }}
2835 }
2836
2837 // BenchmarkBytes benchmarks the performance of performing Equal or Diff on
2838 // large slices of bytes.
2839 func BenchmarkBytes(b *testing.B) {
2840         // Create a list of PathFilters that never apply, but are evaluated.
2841         const maxFilters = 5
2842         var filters cmp.Options
2843         errorIface := reflect.TypeOf((*error)(nil)).Elem()
2844         for i := 0; i <= maxFilters; i++ {
2845                 filters = append(filters, cmp.FilterPath(func(p cmp.Path) bool {
2846                         return p.Last().Type().AssignableTo(errorIface) // Never true
2847                 }, cmp.Ignore()))
2848         }
2849
2850         type benchSize struct {
2851                 label string
2852                 size  int64
2853         }
2854         for _, ts := range []benchSize{
2855                 {"4KiB", 1 << 12},
2856                 {"64KiB", 1 << 16},
2857                 {"1MiB", 1 << 20},
2858                 {"16MiB", 1 << 24},
2859         } {
2860                 bx := append(append(make([]byte, ts.size/2), 'x'), make([]byte, ts.size/2)...)
2861                 by := append(append(make([]byte, ts.size/2), 'y'), make([]byte, ts.size/2)...)
2862                 b.Run(ts.label, func(b *testing.B) {
2863                         // Iteratively add more filters that never apply, but are evaluated
2864                         // to measure the cost of simply evaluating each filter.
2865                         for i := 0; i <= maxFilters; i++ {
2866                                 b.Run(fmt.Sprintf("EqualFilter%d", i), func(b *testing.B) {
2867                                         b.ReportAllocs()
2868                                         b.SetBytes(2 * ts.size)
2869                                         for j := 0; j < b.N; j++ {
2870                                                 cmp.Equal(bx, by, filters[:i]...)
2871                                         }
2872                                 })
2873                         }
2874                         for i := 0; i <= maxFilters; i++ {
2875                                 b.Run(fmt.Sprintf("DiffFilter%d", i), func(b *testing.B) {
2876                                         b.ReportAllocs()
2877                                         b.SetBytes(2 * ts.size)
2878                                         for j := 0; j < b.N; j++ {
2879                                                 cmp.Diff(bx, by, filters[:i]...)
2880                                         }
2881                                 })
2882                         }
2883                 })
2884         }
2885 }