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