xref: /aosp_15_r20/external/go-cmp/cmp/cmpopts/util_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 cmpopts
6*88d15eacSSasha Smundak
7*88d15eacSSasha Smundakimport (
8*88d15eacSSasha Smundak	"bytes"
9*88d15eacSSasha Smundak	"errors"
10*88d15eacSSasha Smundak	"fmt"
11*88d15eacSSasha Smundak	"io"
12*88d15eacSSasha Smundak	"math"
13*88d15eacSSasha Smundak	"reflect"
14*88d15eacSSasha Smundak	"strings"
15*88d15eacSSasha Smundak	"sync"
16*88d15eacSSasha Smundak	"testing"
17*88d15eacSSasha Smundak	"time"
18*88d15eacSSasha Smundak
19*88d15eacSSasha Smundak	"github.com/google/go-cmp/cmp"
20*88d15eacSSasha Smundak)
21*88d15eacSSasha Smundak
22*88d15eacSSasha Smundaktype (
23*88d15eacSSasha Smundak	MyInt    int
24*88d15eacSSasha Smundak	MyInts   []int
25*88d15eacSSasha Smundak	MyFloat  float32
26*88d15eacSSasha Smundak	MyString string
27*88d15eacSSasha Smundak	MyTime   struct{ time.Time }
28*88d15eacSSasha Smundak	MyStruct struct {
29*88d15eacSSasha Smundak		A, B []int
30*88d15eacSSasha Smundak		C, D map[time.Time]string
31*88d15eacSSasha Smundak	}
32*88d15eacSSasha Smundak
33*88d15eacSSasha Smundak	Foo1 struct{ Alpha, Bravo, Charlie int }
34*88d15eacSSasha Smundak	Foo2 struct{ *Foo1 }
35*88d15eacSSasha Smundak	Foo3 struct{ *Foo2 }
36*88d15eacSSasha Smundak	Bar1 struct{ Foo3 }
37*88d15eacSSasha Smundak	Bar2 struct {
38*88d15eacSSasha Smundak		Bar1
39*88d15eacSSasha Smundak		*Foo3
40*88d15eacSSasha Smundak		Bravo float32
41*88d15eacSSasha Smundak	}
42*88d15eacSSasha Smundak	Bar3 struct {
43*88d15eacSSasha Smundak		Bar1
44*88d15eacSSasha Smundak		Bravo *Bar2
45*88d15eacSSasha Smundak		Delta struct{ Echo Foo1 }
46*88d15eacSSasha Smundak		*Foo3
47*88d15eacSSasha Smundak		Alpha string
48*88d15eacSSasha Smundak	}
49*88d15eacSSasha Smundak
50*88d15eacSSasha Smundak	privateStruct struct{ Public, private int }
51*88d15eacSSasha Smundak	PublicStruct  struct{ Public, private int }
52*88d15eacSSasha Smundak	ParentStruct  struct {
53*88d15eacSSasha Smundak		*privateStruct
54*88d15eacSSasha Smundak		*PublicStruct
55*88d15eacSSasha Smundak		Public  int
56*88d15eacSSasha Smundak		private int
57*88d15eacSSasha Smundak	}
58*88d15eacSSasha Smundak
59*88d15eacSSasha Smundak	Everything struct {
60*88d15eacSSasha Smundak		MyInt
61*88d15eacSSasha Smundak		MyFloat
62*88d15eacSSasha Smundak		MyTime
63*88d15eacSSasha Smundak		MyStruct
64*88d15eacSSasha Smundak		Bar3
65*88d15eacSSasha Smundak		ParentStruct
66*88d15eacSSasha Smundak	}
67*88d15eacSSasha Smundak
68*88d15eacSSasha Smundak	EmptyInterface interface{}
69*88d15eacSSasha Smundak)
70*88d15eacSSasha Smundak
71*88d15eacSSasha Smundakfunc TestOptions(t *testing.T) {
72*88d15eacSSasha Smundak	createBar3X := func() *Bar3 {
73*88d15eacSSasha Smundak		return &Bar3{
74*88d15eacSSasha Smundak			Bar1: Bar1{Foo3{&Foo2{&Foo1{Bravo: 2}}}},
75*88d15eacSSasha Smundak			Bravo: &Bar2{
76*88d15eacSSasha Smundak				Bar1:  Bar1{Foo3{&Foo2{&Foo1{Charlie: 7}}}},
77*88d15eacSSasha Smundak				Foo3:  &Foo3{&Foo2{&Foo1{Bravo: 5}}},
78*88d15eacSSasha Smundak				Bravo: 4,
79*88d15eacSSasha Smundak			},
80*88d15eacSSasha Smundak			Delta: struct{ Echo Foo1 }{Foo1{Charlie: 3}},
81*88d15eacSSasha Smundak			Foo3:  &Foo3{&Foo2{&Foo1{Alpha: 1}}},
82*88d15eacSSasha Smundak			Alpha: "alpha",
83*88d15eacSSasha Smundak		}
84*88d15eacSSasha Smundak	}
85*88d15eacSSasha Smundak	createBar3Y := func() *Bar3 {
86*88d15eacSSasha Smundak		return &Bar3{
87*88d15eacSSasha Smundak			Bar1: Bar1{Foo3{&Foo2{&Foo1{Bravo: 3}}}},
88*88d15eacSSasha Smundak			Bravo: &Bar2{
89*88d15eacSSasha Smundak				Bar1:  Bar1{Foo3{&Foo2{&Foo1{Charlie: 8}}}},
90*88d15eacSSasha Smundak				Foo3:  &Foo3{&Foo2{&Foo1{Bravo: 6}}},
91*88d15eacSSasha Smundak				Bravo: 5,
92*88d15eacSSasha Smundak			},
93*88d15eacSSasha Smundak			Delta: struct{ Echo Foo1 }{Foo1{Charlie: 4}},
94*88d15eacSSasha Smundak			Foo3:  &Foo3{&Foo2{&Foo1{Alpha: 2}}},
95*88d15eacSSasha Smundak			Alpha: "ALPHA",
96*88d15eacSSasha Smundak		}
97*88d15eacSSasha Smundak	}
98*88d15eacSSasha Smundak
99*88d15eacSSasha Smundak	tests := []struct {
100*88d15eacSSasha Smundak		label     string       // Test name
101*88d15eacSSasha Smundak		x, y      interface{}  // Input values to compare
102*88d15eacSSasha Smundak		opts      []cmp.Option // Input options
103*88d15eacSSasha Smundak		wantEqual bool         // Whether the inputs are equal
104*88d15eacSSasha Smundak		wantPanic bool         // Whether Equal should panic
105*88d15eacSSasha Smundak		reason    string       // The reason for the expected outcome
106*88d15eacSSasha Smundak	}{{
107*88d15eacSSasha Smundak		label:     "EquateEmpty",
108*88d15eacSSasha Smundak		x:         []int{},
109*88d15eacSSasha Smundak		y:         []int(nil),
110*88d15eacSSasha Smundak		wantEqual: false,
111*88d15eacSSasha Smundak		reason:    "not equal because empty non-nil and nil slice differ",
112*88d15eacSSasha Smundak	}, {
113*88d15eacSSasha Smundak		label:     "EquateEmpty",
114*88d15eacSSasha Smundak		x:         []int{},
115*88d15eacSSasha Smundak		y:         []int(nil),
116*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateEmpty()},
117*88d15eacSSasha Smundak		wantEqual: true,
118*88d15eacSSasha Smundak		reason:    "equal because EquateEmpty equates empty slices",
119*88d15eacSSasha Smundak	}, {
120*88d15eacSSasha Smundak		label:     "SortSlices",
121*88d15eacSSasha Smundak		x:         []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
122*88d15eacSSasha Smundak		y:         []int{1, 0, 5, 2, 8, 9, 4, 3, 6, 7},
123*88d15eacSSasha Smundak		wantEqual: false,
124*88d15eacSSasha Smundak		reason:    "not equal because element order differs",
125*88d15eacSSasha Smundak	}, {
126*88d15eacSSasha Smundak		label:     "SortSlices",
127*88d15eacSSasha Smundak		x:         []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
128*88d15eacSSasha Smundak		y:         []int{1, 0, 5, 2, 8, 9, 4, 3, 6, 7},
129*88d15eacSSasha Smundak		opts:      []cmp.Option{SortSlices(func(x, y int) bool { return x < y })},
130*88d15eacSSasha Smundak		wantEqual: true,
131*88d15eacSSasha Smundak		reason:    "equal because SortSlices sorts the slices",
132*88d15eacSSasha Smundak	}, {
133*88d15eacSSasha Smundak		label:     "SortSlices",
134*88d15eacSSasha Smundak		x:         []MyInt{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
135*88d15eacSSasha Smundak		y:         []MyInt{1, 0, 5, 2, 8, 9, 4, 3, 6, 7},
136*88d15eacSSasha Smundak		opts:      []cmp.Option{SortSlices(func(x, y int) bool { return x < y })},
137*88d15eacSSasha Smundak		wantEqual: false,
138*88d15eacSSasha Smundak		reason:    "not equal because MyInt is not the same type as int",
139*88d15eacSSasha Smundak	}, {
140*88d15eacSSasha Smundak		label:     "SortSlices",
141*88d15eacSSasha Smundak		x:         []float64{0, 1, 1, 2, 2, 2},
142*88d15eacSSasha Smundak		y:         []float64{2, 0, 2, 1, 2, 1},
143*88d15eacSSasha Smundak		opts:      []cmp.Option{SortSlices(func(x, y float64) bool { return x < y })},
144*88d15eacSSasha Smundak		wantEqual: true,
145*88d15eacSSasha Smundak		reason:    "equal even when sorted with duplicate elements",
146*88d15eacSSasha Smundak	}, {
147*88d15eacSSasha Smundak		label:     "SortSlices",
148*88d15eacSSasha Smundak		x:         []float64{0, 1, 1, 2, 2, 2, math.NaN(), 3, 3, 3, 3, 4, 4, 4, 4},
149*88d15eacSSasha Smundak		y:         []float64{2, 0, 4, 4, 3, math.NaN(), 4, 1, 3, 2, 3, 3, 4, 1, 2},
150*88d15eacSSasha Smundak		opts:      []cmp.Option{SortSlices(func(x, y float64) bool { return x < y })},
151*88d15eacSSasha Smundak		wantPanic: true,
152*88d15eacSSasha Smundak		reason:    "panics because SortSlices used with non-transitive less function",
153*88d15eacSSasha Smundak	}, {
154*88d15eacSSasha Smundak		label: "SortSlices",
155*88d15eacSSasha Smundak		x:     []float64{0, 1, 1, 2, 2, 2, math.NaN(), 3, 3, 3, 3, 4, 4, 4, 4},
156*88d15eacSSasha Smundak		y:     []float64{2, 0, 4, 4, 3, math.NaN(), 4, 1, 3, 2, 3, 3, 4, 1, 2},
157*88d15eacSSasha Smundak		opts: []cmp.Option{SortSlices(func(x, y float64) bool {
158*88d15eacSSasha Smundak			return (!math.IsNaN(x) && math.IsNaN(y)) || x < y
159*88d15eacSSasha Smundak		})},
160*88d15eacSSasha Smundak		wantEqual: false,
161*88d15eacSSasha Smundak		reason:    "no panics because SortSlices used with valid less function; not equal because NaN != NaN",
162*88d15eacSSasha Smundak	}, {
163*88d15eacSSasha Smundak		label: "SortSlices+EquateNaNs",
164*88d15eacSSasha Smundak		x:     []float64{0, 1, 1, 2, 2, 2, math.NaN(), 3, 3, 3, math.NaN(), 3, 4, 4, 4, 4},
165*88d15eacSSasha Smundak		y:     []float64{2, 0, 4, 4, 3, math.NaN(), 4, 1, 3, 2, 3, 3, 4, 1, math.NaN(), 2},
166*88d15eacSSasha Smundak		opts: []cmp.Option{
167*88d15eacSSasha Smundak			EquateNaNs(),
168*88d15eacSSasha Smundak			SortSlices(func(x, y float64) bool {
169*88d15eacSSasha Smundak				return (!math.IsNaN(x) && math.IsNaN(y)) || x < y
170*88d15eacSSasha Smundak			}),
171*88d15eacSSasha Smundak		},
172*88d15eacSSasha Smundak		wantEqual: true,
173*88d15eacSSasha Smundak		reason:    "no panics because SortSlices used with valid less function; equal because EquateNaNs is used",
174*88d15eacSSasha Smundak	}, {
175*88d15eacSSasha Smundak		label: "SortMaps",
176*88d15eacSSasha Smundak		x: map[time.Time]string{
177*88d15eacSSasha Smundak			time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC): "0th birthday",
178*88d15eacSSasha Smundak			time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC): "1st birthday",
179*88d15eacSSasha Smundak			time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC): "2nd birthday",
180*88d15eacSSasha Smundak		},
181*88d15eacSSasha Smundak		y: map[time.Time]string{
182*88d15eacSSasha Smundak			time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "0th birthday",
183*88d15eacSSasha Smundak			time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "1st birthday",
184*88d15eacSSasha Smundak			time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "2nd birthday",
185*88d15eacSSasha Smundak		},
186*88d15eacSSasha Smundak		wantEqual: false,
187*88d15eacSSasha Smundak		reason:    "not equal because timezones differ",
188*88d15eacSSasha Smundak	}, {
189*88d15eacSSasha Smundak		label: "SortMaps",
190*88d15eacSSasha Smundak		x: map[time.Time]string{
191*88d15eacSSasha Smundak			time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC): "0th birthday",
192*88d15eacSSasha Smundak			time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC): "1st birthday",
193*88d15eacSSasha Smundak			time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC): "2nd birthday",
194*88d15eacSSasha Smundak		},
195*88d15eacSSasha Smundak		y: map[time.Time]string{
196*88d15eacSSasha Smundak			time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "0th birthday",
197*88d15eacSSasha Smundak			time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "1st birthday",
198*88d15eacSSasha Smundak			time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "2nd birthday",
199*88d15eacSSasha Smundak		},
200*88d15eacSSasha Smundak		opts:      []cmp.Option{SortMaps(func(x, y time.Time) bool { return x.Before(y) })},
201*88d15eacSSasha Smundak		wantEqual: true,
202*88d15eacSSasha Smundak		reason:    "equal because SortMaps flattens to a slice where Time.Equal can be used",
203*88d15eacSSasha Smundak	}, {
204*88d15eacSSasha Smundak		label: "SortMaps",
205*88d15eacSSasha Smundak		x: map[MyTime]string{
206*88d15eacSSasha Smundak			{time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)}: "0th birthday",
207*88d15eacSSasha Smundak			{time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC)}: "1st birthday",
208*88d15eacSSasha Smundak			{time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC)}: "2nd birthday",
209*88d15eacSSasha Smundak		},
210*88d15eacSSasha Smundak		y: map[MyTime]string{
211*88d15eacSSasha Smundak			{time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local)}: "0th birthday",
212*88d15eacSSasha Smundak			{time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local)}: "1st birthday",
213*88d15eacSSasha Smundak			{time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local)}: "2nd birthday",
214*88d15eacSSasha Smundak		},
215*88d15eacSSasha Smundak		opts:      []cmp.Option{SortMaps(func(x, y time.Time) bool { return x.Before(y) })},
216*88d15eacSSasha Smundak		wantEqual: false,
217*88d15eacSSasha Smundak		reason:    "not equal because MyTime is not assignable to time.Time",
218*88d15eacSSasha Smundak	}, {
219*88d15eacSSasha Smundak		label: "SortMaps",
220*88d15eacSSasha Smundak		x:     map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""},
221*88d15eacSSasha Smundak		// => {0, 1, 2, 3, -1, -2, -3},
222*88d15eacSSasha Smundak		y: map[int]string{300: "", 200: "", 100: "", 0: "", 1: "", 2: "", 3: ""},
223*88d15eacSSasha Smundak		// => {0, 1, 2, 3, 100, 200, 300},
224*88d15eacSSasha Smundak		opts: []cmp.Option{SortMaps(func(a, b int) bool {
225*88d15eacSSasha Smundak			if -10 < a && a <= 0 {
226*88d15eacSSasha Smundak				a *= -100
227*88d15eacSSasha Smundak			}
228*88d15eacSSasha Smundak			if -10 < b && b <= 0 {
229*88d15eacSSasha Smundak				b *= -100
230*88d15eacSSasha Smundak			}
231*88d15eacSSasha Smundak			return a < b
232*88d15eacSSasha Smundak		})},
233*88d15eacSSasha Smundak		wantEqual: false,
234*88d15eacSSasha Smundak		reason:    "not equal because values differ even though SortMap provides valid ordering",
235*88d15eacSSasha Smundak	}, {
236*88d15eacSSasha Smundak		label: "SortMaps",
237*88d15eacSSasha Smundak		x:     map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""},
238*88d15eacSSasha Smundak		// => {0, 1, 2, 3, -1, -2, -3},
239*88d15eacSSasha Smundak		y: map[int]string{300: "", 200: "", 100: "", 0: "", 1: "", 2: "", 3: ""},
240*88d15eacSSasha Smundak		// => {0, 1, 2, 3, 100, 200, 300},
241*88d15eacSSasha Smundak		opts: []cmp.Option{
242*88d15eacSSasha Smundak			SortMaps(func(x, y int) bool {
243*88d15eacSSasha Smundak				if -10 < x && x <= 0 {
244*88d15eacSSasha Smundak					x *= -100
245*88d15eacSSasha Smundak				}
246*88d15eacSSasha Smundak				if -10 < y && y <= 0 {
247*88d15eacSSasha Smundak					y *= -100
248*88d15eacSSasha Smundak				}
249*88d15eacSSasha Smundak				return x < y
250*88d15eacSSasha Smundak			}),
251*88d15eacSSasha Smundak			cmp.Comparer(func(x, y int) bool {
252*88d15eacSSasha Smundak				if -10 < x && x <= 0 {
253*88d15eacSSasha Smundak					x *= -100
254*88d15eacSSasha Smundak				}
255*88d15eacSSasha Smundak				if -10 < y && y <= 0 {
256*88d15eacSSasha Smundak					y *= -100
257*88d15eacSSasha Smundak				}
258*88d15eacSSasha Smundak				return x == y
259*88d15eacSSasha Smundak			}),
260*88d15eacSSasha Smundak		},
261*88d15eacSSasha Smundak		wantEqual: true,
262*88d15eacSSasha Smundak		reason:    "equal because Comparer used to equate differences",
263*88d15eacSSasha Smundak	}, {
264*88d15eacSSasha Smundak		label: "SortMaps",
265*88d15eacSSasha Smundak		x:     map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""},
266*88d15eacSSasha Smundak		y:     map[int]string{},
267*88d15eacSSasha Smundak		opts: []cmp.Option{SortMaps(func(x, y int) bool {
268*88d15eacSSasha Smundak			return x < y && x >= 0 && y >= 0
269*88d15eacSSasha Smundak		})},
270*88d15eacSSasha Smundak		wantPanic: true,
271*88d15eacSSasha Smundak		reason:    "panics because SortMaps used with non-transitive less function",
272*88d15eacSSasha Smundak	}, {
273*88d15eacSSasha Smundak		label: "SortMaps",
274*88d15eacSSasha Smundak		x:     map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""},
275*88d15eacSSasha Smundak		y:     map[int]string{},
276*88d15eacSSasha Smundak		opts: []cmp.Option{SortMaps(func(x, y int) bool {
277*88d15eacSSasha Smundak			return math.Abs(float64(x)) < math.Abs(float64(y))
278*88d15eacSSasha Smundak		})},
279*88d15eacSSasha Smundak		wantPanic: true,
280*88d15eacSSasha Smundak		reason:    "panics because SortMaps used with partial less function",
281*88d15eacSSasha Smundak	}, {
282*88d15eacSSasha Smundak		label: "EquateEmpty+SortSlices+SortMaps",
283*88d15eacSSasha Smundak		x: MyStruct{
284*88d15eacSSasha Smundak			A: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
285*88d15eacSSasha Smundak			C: map[time.Time]string{
286*88d15eacSSasha Smundak				time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC): "0th birthday",
287*88d15eacSSasha Smundak				time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC): "1st birthday",
288*88d15eacSSasha Smundak			},
289*88d15eacSSasha Smundak			D: map[time.Time]string{},
290*88d15eacSSasha Smundak		},
291*88d15eacSSasha Smundak		y: MyStruct{
292*88d15eacSSasha Smundak			A: []int{1, 0, 5, 2, 8, 9, 4, 3, 6, 7},
293*88d15eacSSasha Smundak			B: []int{},
294*88d15eacSSasha Smundak			C: map[time.Time]string{
295*88d15eacSSasha Smundak				time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "0th birthday",
296*88d15eacSSasha Smundak				time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "1st birthday",
297*88d15eacSSasha Smundak			},
298*88d15eacSSasha Smundak		},
299*88d15eacSSasha Smundak		opts: []cmp.Option{
300*88d15eacSSasha Smundak			EquateEmpty(),
301*88d15eacSSasha Smundak			SortSlices(func(x, y int) bool { return x < y }),
302*88d15eacSSasha Smundak			SortMaps(func(x, y time.Time) bool { return x.Before(y) }),
303*88d15eacSSasha Smundak		},
304*88d15eacSSasha Smundak		wantEqual: true,
305*88d15eacSSasha Smundak		reason:    "no panics because EquateEmpty should compose with the sort options",
306*88d15eacSSasha Smundak	}, {
307*88d15eacSSasha Smundak		label:     "EquateApprox",
308*88d15eacSSasha Smundak		x:         3.09,
309*88d15eacSSasha Smundak		y:         3.10,
310*88d15eacSSasha Smundak		wantEqual: false,
311*88d15eacSSasha Smundak		reason:    "not equal because floats do not exactly matches",
312*88d15eacSSasha Smundak	}, {
313*88d15eacSSasha Smundak		label:     "EquateApprox",
314*88d15eacSSasha Smundak		x:         3.09,
315*88d15eacSSasha Smundak		y:         3.10,
316*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0, 0)},
317*88d15eacSSasha Smundak		wantEqual: false,
318*88d15eacSSasha Smundak		reason:    "not equal because EquateApprox(0 ,0) is equivalent to using ==",
319*88d15eacSSasha Smundak	}, {
320*88d15eacSSasha Smundak		label:     "EquateApprox",
321*88d15eacSSasha Smundak		x:         3.09,
322*88d15eacSSasha Smundak		y:         3.10,
323*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0.003, 0.009)},
324*88d15eacSSasha Smundak		wantEqual: false,
325*88d15eacSSasha Smundak		reason:    "not equal because EquateApprox is too strict",
326*88d15eacSSasha Smundak	}, {
327*88d15eacSSasha Smundak		label:     "EquateApprox",
328*88d15eacSSasha Smundak		x:         3.09,
329*88d15eacSSasha Smundak		y:         3.10,
330*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0, 0.011)},
331*88d15eacSSasha Smundak		wantEqual: true,
332*88d15eacSSasha Smundak		reason:    "equal because margin is loose enough to match",
333*88d15eacSSasha Smundak	}, {
334*88d15eacSSasha Smundak		label:     "EquateApprox",
335*88d15eacSSasha Smundak		x:         3.09,
336*88d15eacSSasha Smundak		y:         3.10,
337*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0.004, 0)},
338*88d15eacSSasha Smundak		wantEqual: true,
339*88d15eacSSasha Smundak		reason:    "equal because fraction is loose enough to match",
340*88d15eacSSasha Smundak	}, {
341*88d15eacSSasha Smundak		label:     "EquateApprox",
342*88d15eacSSasha Smundak		x:         3.09,
343*88d15eacSSasha Smundak		y:         3.10,
344*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0.004, 0.011)},
345*88d15eacSSasha Smundak		wantEqual: true,
346*88d15eacSSasha Smundak		reason:    "equal because both the margin and fraction are loose enough to match",
347*88d15eacSSasha Smundak	}, {
348*88d15eacSSasha Smundak		label:     "EquateApprox",
349*88d15eacSSasha Smundak		x:         float32(3.09),
350*88d15eacSSasha Smundak		y:         float64(3.10),
351*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0.004, 0)},
352*88d15eacSSasha Smundak		wantEqual: false,
353*88d15eacSSasha Smundak		reason:    "not equal because the types differ",
354*88d15eacSSasha Smundak	}, {
355*88d15eacSSasha Smundak		label:     "EquateApprox",
356*88d15eacSSasha Smundak		x:         float32(3.09),
357*88d15eacSSasha Smundak		y:         float32(3.10),
358*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0.004, 0)},
359*88d15eacSSasha Smundak		wantEqual: true,
360*88d15eacSSasha Smundak		reason:    "equal because EquateApprox also applies on float32s",
361*88d15eacSSasha Smundak	}, {
362*88d15eacSSasha Smundak		label:     "EquateApprox",
363*88d15eacSSasha Smundak		x:         []float64{math.Inf(+1), math.Inf(-1)},
364*88d15eacSSasha Smundak		y:         []float64{math.Inf(+1), math.Inf(-1)},
365*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0, 1)},
366*88d15eacSSasha Smundak		wantEqual: true,
367*88d15eacSSasha Smundak		reason:    "equal because we fall back on == which matches Inf (EquateApprox does not apply on Inf) ",
368*88d15eacSSasha Smundak	}, {
369*88d15eacSSasha Smundak		label:     "EquateApprox",
370*88d15eacSSasha Smundak		x:         []float64{math.Inf(+1), -1e100},
371*88d15eacSSasha Smundak		y:         []float64{+1e100, math.Inf(-1)},
372*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0, 1)},
373*88d15eacSSasha Smundak		wantEqual: false,
374*88d15eacSSasha Smundak		reason:    "not equal because we fall back on == where Inf != 1e100 (EquateApprox does not apply on Inf)",
375*88d15eacSSasha Smundak	}, {
376*88d15eacSSasha Smundak		label:     "EquateApprox",
377*88d15eacSSasha Smundak		x:         float64(+1e100),
378*88d15eacSSasha Smundak		y:         float64(-1e100),
379*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(math.Inf(+1), 0)},
380*88d15eacSSasha Smundak		wantEqual: true,
381*88d15eacSSasha Smundak		reason:    "equal because infinite fraction matches everything",
382*88d15eacSSasha Smundak	}, {
383*88d15eacSSasha Smundak		label:     "EquateApprox",
384*88d15eacSSasha Smundak		x:         float64(+1e100),
385*88d15eacSSasha Smundak		y:         float64(-1e100),
386*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0, math.Inf(+1))},
387*88d15eacSSasha Smundak		wantEqual: true,
388*88d15eacSSasha Smundak		reason:    "equal because infinite margin matches everything",
389*88d15eacSSasha Smundak	}, {
390*88d15eacSSasha Smundak		label:     "EquateApprox",
391*88d15eacSSasha Smundak		x:         math.Pi,
392*88d15eacSSasha Smundak		y:         math.Pi,
393*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0, 0)},
394*88d15eacSSasha Smundak		wantEqual: true,
395*88d15eacSSasha Smundak		reason:    "equal because EquateApprox(0, 0) is equivalent to ==",
396*88d15eacSSasha Smundak	}, {
397*88d15eacSSasha Smundak		label:     "EquateApprox",
398*88d15eacSSasha Smundak		x:         math.Pi,
399*88d15eacSSasha Smundak		y:         math.Nextafter(math.Pi, math.Inf(+1)),
400*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApprox(0, 0)},
401*88d15eacSSasha Smundak		wantEqual: false,
402*88d15eacSSasha Smundak		reason:    "not equal because EquateApprox(0, 0) is equivalent to ==",
403*88d15eacSSasha Smundak	}, {
404*88d15eacSSasha Smundak		label:     "EquateNaNs",
405*88d15eacSSasha Smundak		x:         []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)},
406*88d15eacSSasha Smundak		y:         []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)},
407*88d15eacSSasha Smundak		wantEqual: false,
408*88d15eacSSasha Smundak		reason:    "not equal because NaN != NaN",
409*88d15eacSSasha Smundak	}, {
410*88d15eacSSasha Smundak		label:     "EquateNaNs",
411*88d15eacSSasha Smundak		x:         []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)},
412*88d15eacSSasha Smundak		y:         []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)},
413*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateNaNs()},
414*88d15eacSSasha Smundak		wantEqual: true,
415*88d15eacSSasha Smundak		reason:    "equal because EquateNaNs allows NaN == NaN",
416*88d15eacSSasha Smundak	}, {
417*88d15eacSSasha Smundak		label:     "EquateNaNs",
418*88d15eacSSasha Smundak		x:         []float32{1.0, float32(math.NaN()), math.E, -0.0, +0.0},
419*88d15eacSSasha Smundak		y:         []float32{1.0, float32(math.NaN()), math.E, -0.0, +0.0},
420*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateNaNs()},
421*88d15eacSSasha Smundak		wantEqual: true,
422*88d15eacSSasha Smundak		reason:    "equal because EquateNaNs operates on float32",
423*88d15eacSSasha Smundak	}, {
424*88d15eacSSasha Smundak		label: "EquateApprox+EquateNaNs",
425*88d15eacSSasha Smundak		x:     []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1), 1.01, 5001},
426*88d15eacSSasha Smundak		y:     []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1), 1.02, 5002},
427*88d15eacSSasha Smundak		opts: []cmp.Option{
428*88d15eacSSasha Smundak			EquateNaNs(),
429*88d15eacSSasha Smundak			EquateApprox(0.01, 0),
430*88d15eacSSasha Smundak		},
431*88d15eacSSasha Smundak		wantEqual: true,
432*88d15eacSSasha Smundak		reason:    "equal because EquateNaNs and EquateApprox compose together",
433*88d15eacSSasha Smundak	}, {
434*88d15eacSSasha Smundak		label: "EquateApprox+EquateNaNs",
435*88d15eacSSasha Smundak		x:     []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.01, 5001},
436*88d15eacSSasha Smundak		y:     []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.02, 5002},
437*88d15eacSSasha Smundak		opts: []cmp.Option{
438*88d15eacSSasha Smundak			EquateNaNs(),
439*88d15eacSSasha Smundak			EquateApprox(0.01, 0),
440*88d15eacSSasha Smundak		},
441*88d15eacSSasha Smundak		wantEqual: false,
442*88d15eacSSasha Smundak		reason:    "not equal because EquateApprox and EquateNaNs do not apply on a named type",
443*88d15eacSSasha Smundak	}, {
444*88d15eacSSasha Smundak		label: "EquateApprox+EquateNaNs+Transform",
445*88d15eacSSasha Smundak		x:     []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.01, 5001},
446*88d15eacSSasha Smundak		y:     []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.02, 5002},
447*88d15eacSSasha Smundak		opts: []cmp.Option{
448*88d15eacSSasha Smundak			cmp.Transformer("", func(x MyFloat) float64 { return float64(x) }),
449*88d15eacSSasha Smundak			EquateNaNs(),
450*88d15eacSSasha Smundak			EquateApprox(0.01, 0),
451*88d15eacSSasha Smundak		},
452*88d15eacSSasha Smundak		wantEqual: true,
453*88d15eacSSasha Smundak		reason:    "equal because named type is transformed to float64",
454*88d15eacSSasha Smundak	}, {
455*88d15eacSSasha Smundak		label:     "EquateApproxTime",
456*88d15eacSSasha Smundak		x:         time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC),
457*88d15eacSSasha Smundak		y:         time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC),
458*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApproxTime(0)},
459*88d15eacSSasha Smundak		wantEqual: true,
460*88d15eacSSasha Smundak		reason:    "equal because times are identical",
461*88d15eacSSasha Smundak	}, {
462*88d15eacSSasha Smundak		label:     "EquateApproxTime",
463*88d15eacSSasha Smundak		x:         time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC),
464*88d15eacSSasha Smundak		y:         time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC),
465*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApproxTime(3 * time.Second)},
466*88d15eacSSasha Smundak		wantEqual: true,
467*88d15eacSSasha Smundak		reason:    "equal because time is exactly at the allowed margin",
468*88d15eacSSasha Smundak	}, {
469*88d15eacSSasha Smundak		label:     "EquateApproxTime",
470*88d15eacSSasha Smundak		x:         time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC),
471*88d15eacSSasha Smundak		y:         time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC),
472*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApproxTime(3 * time.Second)},
473*88d15eacSSasha Smundak		wantEqual: true,
474*88d15eacSSasha Smundak		reason:    "equal because time is exactly at the allowed margin (negative)",
475*88d15eacSSasha Smundak	}, {
476*88d15eacSSasha Smundak		label:     "EquateApproxTime",
477*88d15eacSSasha Smundak		x:         time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC),
478*88d15eacSSasha Smundak		y:         time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC),
479*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApproxTime(3*time.Second - 1)},
480*88d15eacSSasha Smundak		wantEqual: false,
481*88d15eacSSasha Smundak		reason:    "not equal because time is outside allowed margin",
482*88d15eacSSasha Smundak	}, {
483*88d15eacSSasha Smundak		label:     "EquateApproxTime",
484*88d15eacSSasha Smundak		x:         time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC),
485*88d15eacSSasha Smundak		y:         time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC),
486*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApproxTime(3*time.Second - 1)},
487*88d15eacSSasha Smundak		wantEqual: false,
488*88d15eacSSasha Smundak		reason:    "not equal because time is outside allowed margin (negative)",
489*88d15eacSSasha Smundak	}, {
490*88d15eacSSasha Smundak		label:     "EquateApproxTime",
491*88d15eacSSasha Smundak		x:         time.Time{},
492*88d15eacSSasha Smundak		y:         time.Time{},
493*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApproxTime(3 * time.Second)},
494*88d15eacSSasha Smundak		wantEqual: true,
495*88d15eacSSasha Smundak		reason:    "equal because both times are zero",
496*88d15eacSSasha Smundak	}, {
497*88d15eacSSasha Smundak		label:     "EquateApproxTime",
498*88d15eacSSasha Smundak		x:         time.Time{},
499*88d15eacSSasha Smundak		y:         time.Time{}.Add(1),
500*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApproxTime(3 * time.Second)},
501*88d15eacSSasha Smundak		wantEqual: false,
502*88d15eacSSasha Smundak		reason:    "not equal because zero time is always not equal not non-zero",
503*88d15eacSSasha Smundak	}, {
504*88d15eacSSasha Smundak		label:     "EquateApproxTime",
505*88d15eacSSasha Smundak		x:         time.Time{}.Add(1),
506*88d15eacSSasha Smundak		y:         time.Time{},
507*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApproxTime(3 * time.Second)},
508*88d15eacSSasha Smundak		wantEqual: false,
509*88d15eacSSasha Smundak		reason:    "not equal because zero time is always not equal not non-zero",
510*88d15eacSSasha Smundak	}, {
511*88d15eacSSasha Smundak		label:     "EquateApproxTime",
512*88d15eacSSasha Smundak		x:         time.Date(2409, 11, 10, 23, 0, 0, 0, time.UTC),
513*88d15eacSSasha Smundak		y:         time.Date(2000, 11, 10, 23, 0, 3, 0, time.UTC),
514*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateApproxTime(3 * time.Second)},
515*88d15eacSSasha Smundak		wantEqual: false,
516*88d15eacSSasha Smundak		reason:    "time difference overflows time.Duration",
517*88d15eacSSasha Smundak	}, {
518*88d15eacSSasha Smundak		label:     "EquateErrors",
519*88d15eacSSasha Smundak		x:         nil,
520*88d15eacSSasha Smundak		y:         nil,
521*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
522*88d15eacSSasha Smundak		wantEqual: true,
523*88d15eacSSasha Smundak		reason:    "nil values are equal",
524*88d15eacSSasha Smundak	}, {
525*88d15eacSSasha Smundak		label:     "EquateErrors",
526*88d15eacSSasha Smundak		x:         errors.New("EOF"),
527*88d15eacSSasha Smundak		y:         io.EOF,
528*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
529*88d15eacSSasha Smundak		wantEqual: false,
530*88d15eacSSasha Smundak		reason:    "user-defined EOF is not exactly equal",
531*88d15eacSSasha Smundak	}, {
532*88d15eacSSasha Smundak		label:     "EquateErrors",
533*88d15eacSSasha Smundak		x:         fmt.Errorf("wrapped: %w", io.EOF),
534*88d15eacSSasha Smundak		y:         io.EOF,
535*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
536*88d15eacSSasha Smundak		wantEqual: true,
537*88d15eacSSasha Smundak		reason:    "wrapped io.EOF is equal according to errors.Is",
538*88d15eacSSasha Smundak	}, {
539*88d15eacSSasha Smundak		label:     "EquateErrors",
540*88d15eacSSasha Smundak		x:         fmt.Errorf("wrapped: %w", io.EOF),
541*88d15eacSSasha Smundak		y:         io.EOF,
542*88d15eacSSasha Smundak		wantEqual: false,
543*88d15eacSSasha Smundak		reason:    "wrapped io.EOF is not equal without EquateErrors option",
544*88d15eacSSasha Smundak	}, {
545*88d15eacSSasha Smundak		label:     "EquateErrors",
546*88d15eacSSasha Smundak		x:         io.EOF,
547*88d15eacSSasha Smundak		y:         io.EOF,
548*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
549*88d15eacSSasha Smundak		wantEqual: true,
550*88d15eacSSasha Smundak		reason:    "sentinel errors are equal",
551*88d15eacSSasha Smundak	}, {
552*88d15eacSSasha Smundak		label:     "EquateErrors",
553*88d15eacSSasha Smundak		x:         io.EOF,
554*88d15eacSSasha Smundak		y:         AnyError,
555*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
556*88d15eacSSasha Smundak		wantEqual: true,
557*88d15eacSSasha Smundak		reason:    "AnyError is equal to any non-nil error",
558*88d15eacSSasha Smundak	}, {
559*88d15eacSSasha Smundak		label:     "EquateErrors",
560*88d15eacSSasha Smundak		x:         io.EOF,
561*88d15eacSSasha Smundak		y:         AnyError,
562*88d15eacSSasha Smundak		wantEqual: false,
563*88d15eacSSasha Smundak		reason:    "AnyError is not equal to any non-nil error without EquateErrors option",
564*88d15eacSSasha Smundak	}, {
565*88d15eacSSasha Smundak		label:     "EquateErrors",
566*88d15eacSSasha Smundak		x:         nil,
567*88d15eacSSasha Smundak		y:         AnyError,
568*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
569*88d15eacSSasha Smundak		wantEqual: false,
570*88d15eacSSasha Smundak		reason:    "AnyError is not equal to nil value",
571*88d15eacSSasha Smundak	}, {
572*88d15eacSSasha Smundak		label:     "EquateErrors",
573*88d15eacSSasha Smundak		x:         nil,
574*88d15eacSSasha Smundak		y:         nil,
575*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
576*88d15eacSSasha Smundak		wantEqual: true,
577*88d15eacSSasha Smundak		reason:    "nil values are equal",
578*88d15eacSSasha Smundak	}, {
579*88d15eacSSasha Smundak		label:     "EquateErrors",
580*88d15eacSSasha Smundak		x:         errors.New("EOF"),
581*88d15eacSSasha Smundak		y:         io.EOF,
582*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
583*88d15eacSSasha Smundak		wantEqual: false,
584*88d15eacSSasha Smundak		reason:    "user-defined EOF is not exactly equal",
585*88d15eacSSasha Smundak	}, {
586*88d15eacSSasha Smundak		label:     "EquateErrors",
587*88d15eacSSasha Smundak		x:         fmt.Errorf("wrapped: %w", io.EOF),
588*88d15eacSSasha Smundak		y:         io.EOF,
589*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
590*88d15eacSSasha Smundak		wantEqual: true,
591*88d15eacSSasha Smundak		reason:    "wrapped io.EOF is equal according to errors.Is",
592*88d15eacSSasha Smundak	}, {
593*88d15eacSSasha Smundak		label:     "EquateErrors",
594*88d15eacSSasha Smundak		x:         fmt.Errorf("wrapped: %w", io.EOF),
595*88d15eacSSasha Smundak		y:         io.EOF,
596*88d15eacSSasha Smundak		wantEqual: false,
597*88d15eacSSasha Smundak		reason:    "wrapped io.EOF is not equal without EquateErrors option",
598*88d15eacSSasha Smundak	}, {
599*88d15eacSSasha Smundak		label:     "EquateErrors",
600*88d15eacSSasha Smundak		x:         io.EOF,
601*88d15eacSSasha Smundak		y:         io.EOF,
602*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
603*88d15eacSSasha Smundak		wantEqual: true,
604*88d15eacSSasha Smundak		reason:    "sentinel errors are equal",
605*88d15eacSSasha Smundak	}, {
606*88d15eacSSasha Smundak		label:     "EquateErrors",
607*88d15eacSSasha Smundak		x:         io.EOF,
608*88d15eacSSasha Smundak		y:         AnyError,
609*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
610*88d15eacSSasha Smundak		wantEqual: true,
611*88d15eacSSasha Smundak		reason:    "AnyError is equal to any non-nil error",
612*88d15eacSSasha Smundak	}, {
613*88d15eacSSasha Smundak		label:     "EquateErrors",
614*88d15eacSSasha Smundak		x:         io.EOF,
615*88d15eacSSasha Smundak		y:         AnyError,
616*88d15eacSSasha Smundak		wantEqual: false,
617*88d15eacSSasha Smundak		reason:    "AnyError is not equal to any non-nil error without EquateErrors option",
618*88d15eacSSasha Smundak	}, {
619*88d15eacSSasha Smundak		label:     "EquateErrors",
620*88d15eacSSasha Smundak		x:         nil,
621*88d15eacSSasha Smundak		y:         AnyError,
622*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
623*88d15eacSSasha Smundak		wantEqual: false,
624*88d15eacSSasha Smundak		reason:    "AnyError is not equal to nil value",
625*88d15eacSSasha Smundak	}, {
626*88d15eacSSasha Smundak		label:     "EquateErrors",
627*88d15eacSSasha Smundak		x:         struct{ E error }{nil},
628*88d15eacSSasha Smundak		y:         struct{ E error }{nil},
629*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
630*88d15eacSSasha Smundak		wantEqual: true,
631*88d15eacSSasha Smundak		reason:    "nil values are equal",
632*88d15eacSSasha Smundak	}, {
633*88d15eacSSasha Smundak		label:     "EquateErrors",
634*88d15eacSSasha Smundak		x:         struct{ E error }{errors.New("EOF")},
635*88d15eacSSasha Smundak		y:         struct{ E error }{io.EOF},
636*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
637*88d15eacSSasha Smundak		wantEqual: false,
638*88d15eacSSasha Smundak		reason:    "user-defined EOF is not exactly equal",
639*88d15eacSSasha Smundak	}, {
640*88d15eacSSasha Smundak		label:     "EquateErrors",
641*88d15eacSSasha Smundak		x:         struct{ E error }{fmt.Errorf("wrapped: %w", io.EOF)},
642*88d15eacSSasha Smundak		y:         struct{ E error }{io.EOF},
643*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
644*88d15eacSSasha Smundak		wantEqual: true,
645*88d15eacSSasha Smundak		reason:    "wrapped io.EOF is equal according to errors.Is",
646*88d15eacSSasha Smundak	}, {
647*88d15eacSSasha Smundak		label:     "EquateErrors",
648*88d15eacSSasha Smundak		x:         struct{ E error }{fmt.Errorf("wrapped: %w", io.EOF)},
649*88d15eacSSasha Smundak		y:         struct{ E error }{io.EOF},
650*88d15eacSSasha Smundak		wantEqual: false,
651*88d15eacSSasha Smundak		reason:    "wrapped io.EOF is not equal without EquateErrors option",
652*88d15eacSSasha Smundak	}, {
653*88d15eacSSasha Smundak		label:     "EquateErrors",
654*88d15eacSSasha Smundak		x:         struct{ E error }{io.EOF},
655*88d15eacSSasha Smundak		y:         struct{ E error }{io.EOF},
656*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
657*88d15eacSSasha Smundak		wantEqual: true,
658*88d15eacSSasha Smundak		reason:    "sentinel errors are equal",
659*88d15eacSSasha Smundak	}, {
660*88d15eacSSasha Smundak		label:     "EquateErrors",
661*88d15eacSSasha Smundak		x:         struct{ E error }{io.EOF},
662*88d15eacSSasha Smundak		y:         struct{ E error }{AnyError},
663*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
664*88d15eacSSasha Smundak		wantEqual: true,
665*88d15eacSSasha Smundak		reason:    "AnyError is equal to any non-nil error",
666*88d15eacSSasha Smundak	}, {
667*88d15eacSSasha Smundak		label:     "EquateErrors",
668*88d15eacSSasha Smundak		x:         struct{ E error }{io.EOF},
669*88d15eacSSasha Smundak		y:         struct{ E error }{AnyError},
670*88d15eacSSasha Smundak		wantEqual: false,
671*88d15eacSSasha Smundak		reason:    "AnyError is not equal to any non-nil error without EquateErrors option",
672*88d15eacSSasha Smundak	}, {
673*88d15eacSSasha Smundak		label:     "EquateErrors",
674*88d15eacSSasha Smundak		x:         struct{ E error }{nil},
675*88d15eacSSasha Smundak		y:         struct{ E error }{AnyError},
676*88d15eacSSasha Smundak		opts:      []cmp.Option{EquateErrors()},
677*88d15eacSSasha Smundak		wantEqual: false,
678*88d15eacSSasha Smundak		reason:    "AnyError is not equal to nil value",
679*88d15eacSSasha Smundak	}, {
680*88d15eacSSasha Smundak		label:     "IgnoreFields",
681*88d15eacSSasha Smundak		x:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}},
682*88d15eacSSasha Smundak		y:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}},
683*88d15eacSSasha Smundak		wantEqual: false,
684*88d15eacSSasha Smundak		reason:    "not equal because values do not match in deeply embedded field",
685*88d15eacSSasha Smundak	}, {
686*88d15eacSSasha Smundak		label:     "IgnoreFields",
687*88d15eacSSasha Smundak		x:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}},
688*88d15eacSSasha Smundak		y:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}},
689*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreFields(Bar1{}, "Alpha")},
690*88d15eacSSasha Smundak		wantEqual: true,
691*88d15eacSSasha Smundak		reason:    "equal because IgnoreField ignores deeply embedded field: Alpha",
692*88d15eacSSasha Smundak	}, {
693*88d15eacSSasha Smundak		label:     "IgnoreFields",
694*88d15eacSSasha Smundak		x:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}},
695*88d15eacSSasha Smundak		y:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}},
696*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreFields(Bar1{}, "Foo1.Alpha")},
697*88d15eacSSasha Smundak		wantEqual: true,
698*88d15eacSSasha Smundak		reason:    "equal because IgnoreField ignores deeply embedded field: Foo1.Alpha",
699*88d15eacSSasha Smundak	}, {
700*88d15eacSSasha Smundak		label:     "IgnoreFields",
701*88d15eacSSasha Smundak		x:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}},
702*88d15eacSSasha Smundak		y:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}},
703*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreFields(Bar1{}, "Foo2.Alpha")},
704*88d15eacSSasha Smundak		wantEqual: true,
705*88d15eacSSasha Smundak		reason:    "equal because IgnoreField ignores deeply embedded field: Foo2.Alpha",
706*88d15eacSSasha Smundak	}, {
707*88d15eacSSasha Smundak		label:     "IgnoreFields",
708*88d15eacSSasha Smundak		x:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}},
709*88d15eacSSasha Smundak		y:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}},
710*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreFields(Bar1{}, "Foo3.Alpha")},
711*88d15eacSSasha Smundak		wantEqual: true,
712*88d15eacSSasha Smundak		reason:    "equal because IgnoreField ignores deeply embedded field: Foo3.Alpha",
713*88d15eacSSasha Smundak	}, {
714*88d15eacSSasha Smundak		label:     "IgnoreFields",
715*88d15eacSSasha Smundak		x:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}},
716*88d15eacSSasha Smundak		y:         Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}},
717*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreFields(Bar1{}, "Foo3.Foo2.Alpha")},
718*88d15eacSSasha Smundak		wantEqual: true,
719*88d15eacSSasha Smundak		reason:    "equal because IgnoreField ignores deeply embedded field: Foo3.Foo2.Alpha",
720*88d15eacSSasha Smundak	}, {
721*88d15eacSSasha Smundak		label:     "IgnoreFields",
722*88d15eacSSasha Smundak		x:         createBar3X(),
723*88d15eacSSasha Smundak		y:         createBar3Y(),
724*88d15eacSSasha Smundak		wantEqual: false,
725*88d15eacSSasha Smundak		reason:    "not equal because many deeply nested or embedded fields differ",
726*88d15eacSSasha Smundak	}, {
727*88d15eacSSasha Smundak		label:     "IgnoreFields",
728*88d15eacSSasha Smundak		x:         createBar3X(),
729*88d15eacSSasha Smundak		y:         createBar3Y(),
730*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreFields(Bar3{}, "Bar1", "Bravo", "Delta", "Foo3", "Alpha")},
731*88d15eacSSasha Smundak		wantEqual: true,
732*88d15eacSSasha Smundak		reason:    "equal because IgnoreFields ignores fields at the highest levels",
733*88d15eacSSasha Smundak	}, {
734*88d15eacSSasha Smundak		label: "IgnoreFields",
735*88d15eacSSasha Smundak		x:     createBar3X(),
736*88d15eacSSasha Smundak		y:     createBar3Y(),
737*88d15eacSSasha Smundak		opts: []cmp.Option{
738*88d15eacSSasha Smundak			IgnoreFields(Bar3{},
739*88d15eacSSasha Smundak				"Bar1.Foo3.Bravo",
740*88d15eacSSasha Smundak				"Bravo.Bar1.Foo3.Foo2.Foo1.Charlie",
741*88d15eacSSasha Smundak				"Bravo.Foo3.Foo2.Foo1.Bravo",
742*88d15eacSSasha Smundak				"Bravo.Bravo",
743*88d15eacSSasha Smundak				"Delta.Echo.Charlie",
744*88d15eacSSasha Smundak				"Foo3.Foo2.Foo1.Alpha",
745*88d15eacSSasha Smundak				"Alpha",
746*88d15eacSSasha Smundak			),
747*88d15eacSSasha Smundak		},
748*88d15eacSSasha Smundak		wantEqual: true,
749*88d15eacSSasha Smundak		reason:    "equal because IgnoreFields ignores fields using fully-qualified field",
750*88d15eacSSasha Smundak	}, {
751*88d15eacSSasha Smundak		label: "IgnoreFields",
752*88d15eacSSasha Smundak		x:     createBar3X(),
753*88d15eacSSasha Smundak		y:     createBar3Y(),
754*88d15eacSSasha Smundak		opts: []cmp.Option{
755*88d15eacSSasha Smundak			IgnoreFields(Bar3{},
756*88d15eacSSasha Smundak				"Bar1.Foo3.Bravo",
757*88d15eacSSasha Smundak				"Bravo.Foo3.Foo2.Foo1.Bravo",
758*88d15eacSSasha Smundak				"Bravo.Bravo",
759*88d15eacSSasha Smundak				"Delta.Echo.Charlie",
760*88d15eacSSasha Smundak				"Foo3.Foo2.Foo1.Alpha",
761*88d15eacSSasha Smundak				"Alpha",
762*88d15eacSSasha Smundak			),
763*88d15eacSSasha Smundak		},
764*88d15eacSSasha Smundak		wantEqual: false,
765*88d15eacSSasha Smundak		reason:    "not equal because one fully-qualified field is not ignored: Bravo.Bar1.Foo3.Foo2.Foo1.Charlie",
766*88d15eacSSasha Smundak	}, {
767*88d15eacSSasha Smundak		label:     "IgnoreFields",
768*88d15eacSSasha Smundak		x:         createBar3X(),
769*88d15eacSSasha Smundak		y:         createBar3Y(),
770*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreFields(Bar3{}, "Bar1", "Bravo", "Delta", "Alpha")},
771*88d15eacSSasha Smundak		wantEqual: false,
772*88d15eacSSasha Smundak		reason:    "not equal because highest-level field is not ignored: Foo3",
773*88d15eacSSasha Smundak	}, {
774*88d15eacSSasha Smundak		label: "IgnoreFields",
775*88d15eacSSasha Smundak		x: ParentStruct{
776*88d15eacSSasha Smundak			privateStruct: &privateStruct{private: 1},
777*88d15eacSSasha Smundak			PublicStruct:  &PublicStruct{private: 2},
778*88d15eacSSasha Smundak			private:       3,
779*88d15eacSSasha Smundak		},
780*88d15eacSSasha Smundak		y: ParentStruct{
781*88d15eacSSasha Smundak			privateStruct: &privateStruct{private: 10},
782*88d15eacSSasha Smundak			PublicStruct:  &PublicStruct{private: 20},
783*88d15eacSSasha Smundak			private:       30,
784*88d15eacSSasha Smundak		},
785*88d15eacSSasha Smundak		opts:      []cmp.Option{cmp.AllowUnexported(ParentStruct{}, PublicStruct{}, privateStruct{})},
786*88d15eacSSasha Smundak		wantEqual: false,
787*88d15eacSSasha Smundak		reason:    "not equal because unexported fields mismatch",
788*88d15eacSSasha Smundak	}, {
789*88d15eacSSasha Smundak		label: "IgnoreFields",
790*88d15eacSSasha Smundak		x: ParentStruct{
791*88d15eacSSasha Smundak			privateStruct: &privateStruct{private: 1},
792*88d15eacSSasha Smundak			PublicStruct:  &PublicStruct{private: 2},
793*88d15eacSSasha Smundak			private:       3,
794*88d15eacSSasha Smundak		},
795*88d15eacSSasha Smundak		y: ParentStruct{
796*88d15eacSSasha Smundak			privateStruct: &privateStruct{private: 10},
797*88d15eacSSasha Smundak			PublicStruct:  &PublicStruct{private: 20},
798*88d15eacSSasha Smundak			private:       30,
799*88d15eacSSasha Smundak		},
800*88d15eacSSasha Smundak		opts: []cmp.Option{
801*88d15eacSSasha Smundak			cmp.AllowUnexported(ParentStruct{}, PublicStruct{}, privateStruct{}),
802*88d15eacSSasha Smundak			IgnoreFields(ParentStruct{}, "PublicStruct.private", "privateStruct.private", "private"),
803*88d15eacSSasha Smundak		},
804*88d15eacSSasha Smundak		wantEqual: true,
805*88d15eacSSasha Smundak		reason:    "equal because mismatching unexported fields are ignored",
806*88d15eacSSasha Smundak	}, {
807*88d15eacSSasha Smundak		label:     "IgnoreTypes",
808*88d15eacSSasha Smundak		x:         []interface{}{5, "same"},
809*88d15eacSSasha Smundak		y:         []interface{}{6, "same"},
810*88d15eacSSasha Smundak		wantEqual: false,
811*88d15eacSSasha Smundak		reason:    "not equal because 5 != 6",
812*88d15eacSSasha Smundak	}, {
813*88d15eacSSasha Smundak		label:     "IgnoreTypes",
814*88d15eacSSasha Smundak		x:         []interface{}{5, "same"},
815*88d15eacSSasha Smundak		y:         []interface{}{6, "same"},
816*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreTypes(0)},
817*88d15eacSSasha Smundak		wantEqual: true,
818*88d15eacSSasha Smundak		reason:    "equal because ints are ignored",
819*88d15eacSSasha Smundak	}, {
820*88d15eacSSasha Smundak		label:     "IgnoreTypes+IgnoreInterfaces",
821*88d15eacSSasha Smundak		x:         []interface{}{5, "same", new(bytes.Buffer)},
822*88d15eacSSasha Smundak		y:         []interface{}{6, "same", new(bytes.Buffer)},
823*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreTypes(0)},
824*88d15eacSSasha Smundak		wantPanic: true,
825*88d15eacSSasha Smundak		reason:    "panics because bytes.Buffer has unexported fields",
826*88d15eacSSasha Smundak	}, {
827*88d15eacSSasha Smundak		label: "IgnoreTypes+IgnoreInterfaces",
828*88d15eacSSasha Smundak		x:     []interface{}{5, "same", new(bytes.Buffer)},
829*88d15eacSSasha Smundak		y:     []interface{}{6, "diff", new(bytes.Buffer)},
830*88d15eacSSasha Smundak		opts: []cmp.Option{
831*88d15eacSSasha Smundak			IgnoreTypes(0, ""),
832*88d15eacSSasha Smundak			IgnoreInterfaces(struct{ io.Reader }{}),
833*88d15eacSSasha Smundak		},
834*88d15eacSSasha Smundak		wantEqual: true,
835*88d15eacSSasha Smundak		reason:    "equal because bytes.Buffer is ignored by match on interface type",
836*88d15eacSSasha Smundak	}, {
837*88d15eacSSasha Smundak		label: "IgnoreTypes+IgnoreInterfaces",
838*88d15eacSSasha Smundak		x:     []interface{}{5, "same", new(bytes.Buffer)},
839*88d15eacSSasha Smundak		y:     []interface{}{6, "same", new(bytes.Buffer)},
840*88d15eacSSasha Smundak		opts: []cmp.Option{
841*88d15eacSSasha Smundak			IgnoreTypes(0, ""),
842*88d15eacSSasha Smundak			IgnoreInterfaces(struct {
843*88d15eacSSasha Smundak				io.Reader
844*88d15eacSSasha Smundak				io.Writer
845*88d15eacSSasha Smundak				fmt.Stringer
846*88d15eacSSasha Smundak			}{}),
847*88d15eacSSasha Smundak		},
848*88d15eacSSasha Smundak		wantEqual: true,
849*88d15eacSSasha Smundak		reason:    "equal because bytes.Buffer is ignored by match on multiple interface types",
850*88d15eacSSasha Smundak	}, {
851*88d15eacSSasha Smundak		label:     "IgnoreInterfaces",
852*88d15eacSSasha Smundak		x:         struct{ mu sync.Mutex }{},
853*88d15eacSSasha Smundak		y:         struct{ mu sync.Mutex }{},
854*88d15eacSSasha Smundak		wantPanic: true,
855*88d15eacSSasha Smundak		reason:    "panics because sync.Mutex has unexported fields",
856*88d15eacSSasha Smundak	}, {
857*88d15eacSSasha Smundak		label:     "IgnoreInterfaces",
858*88d15eacSSasha Smundak		x:         struct{ mu sync.Mutex }{},
859*88d15eacSSasha Smundak		y:         struct{ mu sync.Mutex }{},
860*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreInterfaces(struct{ sync.Locker }{})},
861*88d15eacSSasha Smundak		wantEqual: true,
862*88d15eacSSasha Smundak		reason:    "equal because IgnoreInterfaces applies on values (with pointer receiver)",
863*88d15eacSSasha Smundak	}, {
864*88d15eacSSasha Smundak		label:     "IgnoreInterfaces",
865*88d15eacSSasha Smundak		x:         struct{ mu *sync.Mutex }{},
866*88d15eacSSasha Smundak		y:         struct{ mu *sync.Mutex }{},
867*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreInterfaces(struct{ sync.Locker }{})},
868*88d15eacSSasha Smundak		wantEqual: true,
869*88d15eacSSasha Smundak		reason:    "equal because IgnoreInterfaces applies on pointers",
870*88d15eacSSasha Smundak	}, {
871*88d15eacSSasha Smundak		label:     "IgnoreUnexported",
872*88d15eacSSasha Smundak		x:         ParentStruct{Public: 1, private: 2},
873*88d15eacSSasha Smundak		y:         ParentStruct{Public: 1, private: -2},
874*88d15eacSSasha Smundak		opts:      []cmp.Option{cmp.AllowUnexported(ParentStruct{})},
875*88d15eacSSasha Smundak		wantEqual: false,
876*88d15eacSSasha Smundak		reason:    "not equal because ParentStruct.private differs with AllowUnexported",
877*88d15eacSSasha Smundak	}, {
878*88d15eacSSasha Smundak		label:     "IgnoreUnexported",
879*88d15eacSSasha Smundak		x:         ParentStruct{Public: 1, private: 2},
880*88d15eacSSasha Smundak		y:         ParentStruct{Public: 1, private: -2},
881*88d15eacSSasha Smundak		opts:      []cmp.Option{IgnoreUnexported(ParentStruct{})},
882*88d15eacSSasha Smundak		wantEqual: true,
883*88d15eacSSasha Smundak		reason:    "equal because IgnoreUnexported ignored ParentStruct.private",
884*88d15eacSSasha Smundak	}, {
885*88d15eacSSasha Smundak		label: "IgnoreUnexported",
886*88d15eacSSasha Smundak		x:     ParentStruct{Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4}},
887*88d15eacSSasha Smundak		y:     ParentStruct{Public: 1, private: -2, PublicStruct: &PublicStruct{Public: 3, private: 4}},
888*88d15eacSSasha Smundak		opts: []cmp.Option{
889*88d15eacSSasha Smundak			cmp.AllowUnexported(PublicStruct{}),
890*88d15eacSSasha Smundak			IgnoreUnexported(ParentStruct{}),
891*88d15eacSSasha Smundak		},
892*88d15eacSSasha Smundak		wantEqual: true,
893*88d15eacSSasha Smundak		reason:    "equal because ParentStruct.private is ignored",
894*88d15eacSSasha Smundak	}, {
895*88d15eacSSasha Smundak		label: "IgnoreUnexported",
896*88d15eacSSasha Smundak		x:     ParentStruct{Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4}},
897*88d15eacSSasha Smundak		y:     ParentStruct{Public: 1, private: -2, PublicStruct: &PublicStruct{Public: 3, private: -4}},
898*88d15eacSSasha Smundak		opts: []cmp.Option{
899*88d15eacSSasha Smundak			cmp.AllowUnexported(PublicStruct{}),
900*88d15eacSSasha Smundak			IgnoreUnexported(ParentStruct{}),
901*88d15eacSSasha Smundak		},
902*88d15eacSSasha Smundak		wantEqual: false,
903*88d15eacSSasha Smundak		reason:    "not equal because ParentStruct.PublicStruct.private differs and not ignored by IgnoreUnexported(ParentStruct{})",
904*88d15eacSSasha Smundak	}, {
905*88d15eacSSasha Smundak		label: "IgnoreUnexported",
906*88d15eacSSasha Smundak		x:     ParentStruct{Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4}},
907*88d15eacSSasha Smundak		y:     ParentStruct{Public: 1, private: -2, PublicStruct: &PublicStruct{Public: 3, private: -4}},
908*88d15eacSSasha Smundak		opts: []cmp.Option{
909*88d15eacSSasha Smundak			IgnoreUnexported(ParentStruct{}, PublicStruct{}),
910*88d15eacSSasha Smundak		},
911*88d15eacSSasha Smundak		wantEqual: true,
912*88d15eacSSasha Smundak		reason:    "equal because both ParentStruct.PublicStruct and ParentStruct.PublicStruct.private are ignored",
913*88d15eacSSasha Smundak	}, {
914*88d15eacSSasha Smundak		label: "IgnoreUnexported",
915*88d15eacSSasha Smundak		x:     ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}},
916*88d15eacSSasha Smundak		y:     ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: -3, private: -4}},
917*88d15eacSSasha Smundak		opts: []cmp.Option{
918*88d15eacSSasha Smundak			cmp.AllowUnexported(privateStruct{}, PublicStruct{}, ParentStruct{}),
919*88d15eacSSasha Smundak		},
920*88d15eacSSasha Smundak		wantEqual: false,
921*88d15eacSSasha Smundak		reason:    "not equal since ParentStruct.privateStruct differs",
922*88d15eacSSasha Smundak	}, {
923*88d15eacSSasha Smundak		label: "IgnoreUnexported",
924*88d15eacSSasha Smundak		x:     ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}},
925*88d15eacSSasha Smundak		y:     ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: -3, private: -4}},
926*88d15eacSSasha Smundak		opts: []cmp.Option{
927*88d15eacSSasha Smundak			cmp.AllowUnexported(privateStruct{}, PublicStruct{}),
928*88d15eacSSasha Smundak			IgnoreUnexported(ParentStruct{}),
929*88d15eacSSasha Smundak		},
930*88d15eacSSasha Smundak		wantEqual: true,
931*88d15eacSSasha Smundak		reason:    "equal because ParentStruct.privateStruct ignored by IgnoreUnexported(ParentStruct{})",
932*88d15eacSSasha Smundak	}, {
933*88d15eacSSasha Smundak		label: "IgnoreUnexported",
934*88d15eacSSasha Smundak		x:     ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}},
935*88d15eacSSasha Smundak		y:     ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: -4}},
936*88d15eacSSasha Smundak		opts: []cmp.Option{
937*88d15eacSSasha Smundak			cmp.AllowUnexported(PublicStruct{}, ParentStruct{}),
938*88d15eacSSasha Smundak			IgnoreUnexported(privateStruct{}),
939*88d15eacSSasha Smundak		},
940*88d15eacSSasha Smundak		wantEqual: true,
941*88d15eacSSasha Smundak		reason:    "equal because privateStruct.private ignored by IgnoreUnexported(privateStruct{})",
942*88d15eacSSasha Smundak	}, {
943*88d15eacSSasha Smundak		label: "IgnoreUnexported",
944*88d15eacSSasha Smundak		x:     ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}},
945*88d15eacSSasha Smundak		y:     ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: -3, private: -4}},
946*88d15eacSSasha Smundak		opts: []cmp.Option{
947*88d15eacSSasha Smundak			cmp.AllowUnexported(PublicStruct{}, ParentStruct{}),
948*88d15eacSSasha Smundak			IgnoreUnexported(privateStruct{}),
949*88d15eacSSasha Smundak		},
950*88d15eacSSasha Smundak		wantEqual: false,
951*88d15eacSSasha Smundak		reason:    "not equal because privateStruct.Public differs and not ignored by IgnoreUnexported(privateStruct{})",
952*88d15eacSSasha Smundak	}, {
953*88d15eacSSasha Smundak		label: "IgnoreFields+IgnoreTypes+IgnoreUnexported",
954*88d15eacSSasha Smundak		x: &Everything{
955*88d15eacSSasha Smundak			MyInt:   5,
956*88d15eacSSasha Smundak			MyFloat: 3.3,
957*88d15eacSSasha Smundak			MyTime:  MyTime{time.Now()},
958*88d15eacSSasha Smundak			Bar3:    *createBar3X(),
959*88d15eacSSasha Smundak			ParentStruct: ParentStruct{
960*88d15eacSSasha Smundak				Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4},
961*88d15eacSSasha Smundak			},
962*88d15eacSSasha Smundak		},
963*88d15eacSSasha Smundak		y: &Everything{
964*88d15eacSSasha Smundak			MyInt:   -5,
965*88d15eacSSasha Smundak			MyFloat: 3.3,
966*88d15eacSSasha Smundak			MyTime:  MyTime{time.Now()},
967*88d15eacSSasha Smundak			Bar3:    *createBar3Y(),
968*88d15eacSSasha Smundak			ParentStruct: ParentStruct{
969*88d15eacSSasha Smundak				Public: 1, private: -2, PublicStruct: &PublicStruct{Public: -3, private: -4},
970*88d15eacSSasha Smundak			},
971*88d15eacSSasha Smundak		},
972*88d15eacSSasha Smundak		opts: []cmp.Option{
973*88d15eacSSasha Smundak			IgnoreFields(Everything{}, "MyTime", "Bar3.Foo3"),
974*88d15eacSSasha Smundak			IgnoreFields(Bar3{}, "Bar1", "Bravo", "Delta", "Alpha"),
975*88d15eacSSasha Smundak			IgnoreTypes(MyInt(0), PublicStruct{}),
976*88d15eacSSasha Smundak			IgnoreUnexported(ParentStruct{}),
977*88d15eacSSasha Smundak		},
978*88d15eacSSasha Smundak		wantEqual: true,
979*88d15eacSSasha Smundak		reason:    "equal because all Ignore options can be composed together",
980*88d15eacSSasha Smundak	}, {
981*88d15eacSSasha Smundak		label: "IgnoreSliceElements",
982*88d15eacSSasha Smundak		x:     []int{1, 0, 2, 3, 0, 4, 0, 0},
983*88d15eacSSasha Smundak		y:     []int{0, 0, 0, 0, 1, 2, 3, 4},
984*88d15eacSSasha Smundak		opts: []cmp.Option{
985*88d15eacSSasha Smundak			IgnoreSliceElements(func(v int) bool { return v == 0 }),
986*88d15eacSSasha Smundak		},
987*88d15eacSSasha Smundak		wantEqual: true,
988*88d15eacSSasha Smundak		reason:    "equal because zero elements are ignored",
989*88d15eacSSasha Smundak	}, {
990*88d15eacSSasha Smundak		label: "IgnoreSliceElements",
991*88d15eacSSasha Smundak		x:     []MyInt{1, 0, 2, 3, 0, 4, 0, 0},
992*88d15eacSSasha Smundak		y:     []MyInt{0, 0, 0, 0, 1, 2, 3, 4},
993*88d15eacSSasha Smundak		opts: []cmp.Option{
994*88d15eacSSasha Smundak			IgnoreSliceElements(func(v int) bool { return v == 0 }),
995*88d15eacSSasha Smundak		},
996*88d15eacSSasha Smundak		wantEqual: false,
997*88d15eacSSasha Smundak		reason:    "not equal because MyInt is not assignable to int",
998*88d15eacSSasha Smundak	}, {
999*88d15eacSSasha Smundak		label: "IgnoreSliceElements",
1000*88d15eacSSasha Smundak		x:     MyInts{1, 0, 2, 3, 0, 4, 0, 0},
1001*88d15eacSSasha Smundak		y:     MyInts{0, 0, 0, 0, 1, 2, 3, 4},
1002*88d15eacSSasha Smundak		opts: []cmp.Option{
1003*88d15eacSSasha Smundak			IgnoreSliceElements(func(v int) bool { return v == 0 }),
1004*88d15eacSSasha Smundak		},
1005*88d15eacSSasha Smundak		wantEqual: true,
1006*88d15eacSSasha Smundak		reason:    "equal because the element type of MyInts is assignable to int",
1007*88d15eacSSasha Smundak	}, {
1008*88d15eacSSasha Smundak		label: "IgnoreSliceElements+EquateEmpty",
1009*88d15eacSSasha Smundak		x:     []MyInt{},
1010*88d15eacSSasha Smundak		y:     []MyInt{0, 0, 0, 0},
1011*88d15eacSSasha Smundak		opts: []cmp.Option{
1012*88d15eacSSasha Smundak			IgnoreSliceElements(func(v int) bool { return v == 0 }),
1013*88d15eacSSasha Smundak			EquateEmpty(),
1014*88d15eacSSasha Smundak		},
1015*88d15eacSSasha Smundak		wantEqual: false,
1016*88d15eacSSasha Smundak		reason:    "not equal because ignored elements does not imply empty slice",
1017*88d15eacSSasha Smundak	}, {
1018*88d15eacSSasha Smundak		label: "IgnoreMapEntries",
1019*88d15eacSSasha Smundak		x:     map[string]int{"one": 1, "TWO": 2, "three": 3, "FIVE": 5},
1020*88d15eacSSasha Smundak		y:     map[string]int{"one": 1, "three": 3, "TEN": 10},
1021*88d15eacSSasha Smundak		opts: []cmp.Option{
1022*88d15eacSSasha Smundak			IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }),
1023*88d15eacSSasha Smundak		},
1024*88d15eacSSasha Smundak		wantEqual: true,
1025*88d15eacSSasha Smundak		reason:    "equal because uppercase keys are ignored",
1026*88d15eacSSasha Smundak	}, {
1027*88d15eacSSasha Smundak		label: "IgnoreMapEntries",
1028*88d15eacSSasha Smundak		x:     map[MyString]int{"one": 1, "TWO": 2, "three": 3, "FIVE": 5},
1029*88d15eacSSasha Smundak		y:     map[MyString]int{"one": 1, "three": 3, "TEN": 10},
1030*88d15eacSSasha Smundak		opts: []cmp.Option{
1031*88d15eacSSasha Smundak			IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }),
1032*88d15eacSSasha Smundak		},
1033*88d15eacSSasha Smundak		wantEqual: false,
1034*88d15eacSSasha Smundak		reason:    "not equal because MyString is not assignable to string",
1035*88d15eacSSasha Smundak	}, {
1036*88d15eacSSasha Smundak		label: "IgnoreMapEntries",
1037*88d15eacSSasha Smundak		x:     map[string]MyInt{"one": 1, "TWO": 2, "three": 3, "FIVE": 5},
1038*88d15eacSSasha Smundak		y:     map[string]MyInt{"one": 1, "three": 3, "TEN": 10},
1039*88d15eacSSasha Smundak		opts: []cmp.Option{
1040*88d15eacSSasha Smundak			IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }),
1041*88d15eacSSasha Smundak		},
1042*88d15eacSSasha Smundak		wantEqual: false,
1043*88d15eacSSasha Smundak		reason:    "not equal because MyInt is not assignable to int",
1044*88d15eacSSasha Smundak	}, {
1045*88d15eacSSasha Smundak		label: "IgnoreMapEntries+EquateEmpty",
1046*88d15eacSSasha Smundak		x:     map[string]MyInt{"ONE": 1, "TWO": 2, "THREE": 3},
1047*88d15eacSSasha Smundak		y:     nil,
1048*88d15eacSSasha Smundak		opts: []cmp.Option{
1049*88d15eacSSasha Smundak			IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }),
1050*88d15eacSSasha Smundak			EquateEmpty(),
1051*88d15eacSSasha Smundak		},
1052*88d15eacSSasha Smundak		wantEqual: false,
1053*88d15eacSSasha Smundak		reason:    "not equal because ignored entries does not imply empty map",
1054*88d15eacSSasha Smundak	}, {
1055*88d15eacSSasha Smundak		label: "AcyclicTransformer",
1056*88d15eacSSasha Smundak		x:     "a\nb\nc\nd",
1057*88d15eacSSasha Smundak		y:     "a\nb\nd\nd",
1058*88d15eacSSasha Smundak		opts: []cmp.Option{
1059*88d15eacSSasha Smundak			AcyclicTransformer("", func(s string) []string { return strings.Split(s, "\n") }),
1060*88d15eacSSasha Smundak		},
1061*88d15eacSSasha Smundak		wantEqual: false,
1062*88d15eacSSasha Smundak		reason:    "not equal because 3rd line differs, but should not recurse infinitely",
1063*88d15eacSSasha Smundak	}, {
1064*88d15eacSSasha Smundak		label: "AcyclicTransformer",
1065*88d15eacSSasha Smundak		x:     []string{"foo", "Bar", "BAZ"},
1066*88d15eacSSasha Smundak		y:     []string{"Foo", "BAR", "baz"},
1067*88d15eacSSasha Smundak		opts: []cmp.Option{
1068*88d15eacSSasha Smundak			AcyclicTransformer("", strings.ToUpper),
1069*88d15eacSSasha Smundak		},
1070*88d15eacSSasha Smundak		wantEqual: true,
1071*88d15eacSSasha Smundak		reason:    "equal because of strings.ToUpper; AcyclicTransformer unnecessary, but check this still works",
1072*88d15eacSSasha Smundak	}, {
1073*88d15eacSSasha Smundak		label: "AcyclicTransformer",
1074*88d15eacSSasha Smundak		x:     "this is a sentence",
1075*88d15eacSSasha Smundak		y:     "this   			is a 			sentence",
1076*88d15eacSSasha Smundak		opts: []cmp.Option{
1077*88d15eacSSasha Smundak			AcyclicTransformer("", strings.Fields),
1078*88d15eacSSasha Smundak		},
1079*88d15eacSSasha Smundak		wantEqual: true,
1080*88d15eacSSasha Smundak		reason:    "equal because acyclic transformer splits on any contiguous whitespace",
1081*88d15eacSSasha Smundak	}}
1082*88d15eacSSasha Smundak
1083*88d15eacSSasha Smundak	for _, tt := range tests {
1084*88d15eacSSasha Smundak		t.Run(tt.label, func(t *testing.T) {
1085*88d15eacSSasha Smundak			var gotEqual bool
1086*88d15eacSSasha Smundak			var gotPanic string
1087*88d15eacSSasha Smundak			func() {
1088*88d15eacSSasha Smundak				defer func() {
1089*88d15eacSSasha Smundak					if ex := recover(); ex != nil {
1090*88d15eacSSasha Smundak						gotPanic = fmt.Sprint(ex)
1091*88d15eacSSasha Smundak					}
1092*88d15eacSSasha Smundak				}()
1093*88d15eacSSasha Smundak				gotEqual = cmp.Equal(tt.x, tt.y, tt.opts...)
1094*88d15eacSSasha Smundak			}()
1095*88d15eacSSasha Smundak			switch {
1096*88d15eacSSasha Smundak			case tt.reason == "":
1097*88d15eacSSasha Smundak				t.Errorf("reason must be provided")
1098*88d15eacSSasha Smundak			case gotPanic == "" && tt.wantPanic:
1099*88d15eacSSasha Smundak				t.Errorf("expected Equal panic\nreason: %s", tt.reason)
1100*88d15eacSSasha Smundak			case gotPanic != "" && !tt.wantPanic:
1101*88d15eacSSasha Smundak				t.Errorf("unexpected Equal panic: got %v\nreason: %v", gotPanic, tt.reason)
1102*88d15eacSSasha Smundak			case gotEqual != tt.wantEqual:
1103*88d15eacSSasha Smundak				t.Errorf("Equal = %v, want %v\nreason: %v", gotEqual, tt.wantEqual, tt.reason)
1104*88d15eacSSasha Smundak			}
1105*88d15eacSSasha Smundak		})
1106*88d15eacSSasha Smundak	}
1107*88d15eacSSasha Smundak}
1108*88d15eacSSasha Smundak
1109*88d15eacSSasha Smundakfunc TestPanic(t *testing.T) {
1110*88d15eacSSasha Smundak	args := func(x ...interface{}) []interface{} { return x }
1111*88d15eacSSasha Smundak	tests := []struct {
1112*88d15eacSSasha Smundak		label     string        // Test name
1113*88d15eacSSasha Smundak		fnc       interface{}   // Option function to call
1114*88d15eacSSasha Smundak		args      []interface{} // Arguments to pass in
1115*88d15eacSSasha Smundak		wantPanic string        // Expected panic message
1116*88d15eacSSasha Smundak		reason    string        // The reason for the expected outcome
1117*88d15eacSSasha Smundak	}{{
1118*88d15eacSSasha Smundak		label:  "EquateApprox",
1119*88d15eacSSasha Smundak		fnc:    EquateApprox,
1120*88d15eacSSasha Smundak		args:   args(0.0, 0.0),
1121*88d15eacSSasha Smundak		reason: "zero margin and fraction is equivalent to exact equality",
1122*88d15eacSSasha Smundak	}, {
1123*88d15eacSSasha Smundak		label:     "EquateApprox",
1124*88d15eacSSasha Smundak		fnc:       EquateApprox,
1125*88d15eacSSasha Smundak		args:      args(-0.1, 0.0),
1126*88d15eacSSasha Smundak		wantPanic: "margin or fraction must be a non-negative number",
1127*88d15eacSSasha Smundak		reason:    "negative inputs are invalid",
1128*88d15eacSSasha Smundak	}, {
1129*88d15eacSSasha Smundak		label:     "EquateApprox",
1130*88d15eacSSasha Smundak		fnc:       EquateApprox,
1131*88d15eacSSasha Smundak		args:      args(0.0, -0.1),
1132*88d15eacSSasha Smundak		wantPanic: "margin or fraction must be a non-negative number",
1133*88d15eacSSasha Smundak		reason:    "negative inputs are invalid",
1134*88d15eacSSasha Smundak	}, {
1135*88d15eacSSasha Smundak		label:     "EquateApprox",
1136*88d15eacSSasha Smundak		fnc:       EquateApprox,
1137*88d15eacSSasha Smundak		args:      args(math.NaN(), 0.0),
1138*88d15eacSSasha Smundak		wantPanic: "margin or fraction must be a non-negative number",
1139*88d15eacSSasha Smundak		reason:    "NaN inputs are invalid",
1140*88d15eacSSasha Smundak	}, {
1141*88d15eacSSasha Smundak		label:  "EquateApprox",
1142*88d15eacSSasha Smundak		fnc:    EquateApprox,
1143*88d15eacSSasha Smundak		args:   args(1.0, 0.0),
1144*88d15eacSSasha Smundak		reason: "fraction of 1.0 or greater is valid",
1145*88d15eacSSasha Smundak	}, {
1146*88d15eacSSasha Smundak		label:  "EquateApprox",
1147*88d15eacSSasha Smundak		fnc:    EquateApprox,
1148*88d15eacSSasha Smundak		args:   args(0.0, math.Inf(+1)),
1149*88d15eacSSasha Smundak		reason: "margin of infinity is valid",
1150*88d15eacSSasha Smundak	}, {
1151*88d15eacSSasha Smundak		label:     "EquateApproxTime",
1152*88d15eacSSasha Smundak		fnc:       EquateApproxTime,
1153*88d15eacSSasha Smundak		args:      args(time.Duration(-1)),
1154*88d15eacSSasha Smundak		wantPanic: "margin must be a non-negative number",
1155*88d15eacSSasha Smundak		reason:    "negative duration is invalid",
1156*88d15eacSSasha Smundak	}, {
1157*88d15eacSSasha Smundak		label:     "SortSlices",
1158*88d15eacSSasha Smundak		fnc:       SortSlices,
1159*88d15eacSSasha Smundak		args:      args(strings.Compare),
1160*88d15eacSSasha Smundak		wantPanic: "invalid less function",
1161*88d15eacSSasha Smundak		reason:    "func(x, y string) int is wrong signature for less",
1162*88d15eacSSasha Smundak	}, {
1163*88d15eacSSasha Smundak		label:     "SortSlices",
1164*88d15eacSSasha Smundak		fnc:       SortSlices,
1165*88d15eacSSasha Smundak		args:      args((func(_, _ int) bool)(nil)),
1166*88d15eacSSasha Smundak		wantPanic: "invalid less function",
1167*88d15eacSSasha Smundak		reason:    "nil value is not valid",
1168*88d15eacSSasha Smundak	}, {
1169*88d15eacSSasha Smundak		label:     "SortMaps",
1170*88d15eacSSasha Smundak		fnc:       SortMaps,
1171*88d15eacSSasha Smundak		args:      args(strings.Compare),
1172*88d15eacSSasha Smundak		wantPanic: "invalid less function",
1173*88d15eacSSasha Smundak		reason:    "func(x, y string) int is wrong signature for less",
1174*88d15eacSSasha Smundak	}, {
1175*88d15eacSSasha Smundak		label:     "SortMaps",
1176*88d15eacSSasha Smundak		fnc:       SortMaps,
1177*88d15eacSSasha Smundak		args:      args((func(_, _ int) bool)(nil)),
1178*88d15eacSSasha Smundak		wantPanic: "invalid less function",
1179*88d15eacSSasha Smundak		reason:    "nil value is not valid",
1180*88d15eacSSasha Smundak	}, {
1181*88d15eacSSasha Smundak		label:     "IgnoreFields",
1182*88d15eacSSasha Smundak		fnc:       IgnoreFields,
1183*88d15eacSSasha Smundak		args:      args(Foo1{}, ""),
1184*88d15eacSSasha Smundak		wantPanic: "name must not be empty",
1185*88d15eacSSasha Smundak		reason:    "empty selector is invalid",
1186*88d15eacSSasha Smundak	}, {
1187*88d15eacSSasha Smundak		label:     "IgnoreFields",
1188*88d15eacSSasha Smundak		fnc:       IgnoreFields,
1189*88d15eacSSasha Smundak		args:      args(Foo1{}, "."),
1190*88d15eacSSasha Smundak		wantPanic: "name must not be empty",
1191*88d15eacSSasha Smundak		reason:    "single dot selector is invalid",
1192*88d15eacSSasha Smundak	}, {
1193*88d15eacSSasha Smundak		label:  "IgnoreFields",
1194*88d15eacSSasha Smundak		fnc:    IgnoreFields,
1195*88d15eacSSasha Smundak		args:   args(Foo1{}, ".Alpha"),
1196*88d15eacSSasha Smundak		reason: "dot-prefix is okay since Foo1.Alpha reads naturally",
1197*88d15eacSSasha Smundak	}, {
1198*88d15eacSSasha Smundak		label:     "IgnoreFields",
1199*88d15eacSSasha Smundak		fnc:       IgnoreFields,
1200*88d15eacSSasha Smundak		args:      args(Foo1{}, "Alpha."),
1201*88d15eacSSasha Smundak		wantPanic: "name must not be empty",
1202*88d15eacSSasha Smundak		reason:    "dot-suffix is invalid",
1203*88d15eacSSasha Smundak	}, {
1204*88d15eacSSasha Smundak		label:     "IgnoreFields",
1205*88d15eacSSasha Smundak		fnc:       IgnoreFields,
1206*88d15eacSSasha Smundak		args:      args(Foo1{}, "Alpha "),
1207*88d15eacSSasha Smundak		wantPanic: "does not exist",
1208*88d15eacSSasha Smundak		reason:    "identifiers must not have spaces",
1209*88d15eacSSasha Smundak	}, {
1210*88d15eacSSasha Smundak		label:     "IgnoreFields",
1211*88d15eacSSasha Smundak		fnc:       IgnoreFields,
1212*88d15eacSSasha Smundak		args:      args(Foo1{}, "Zulu"),
1213*88d15eacSSasha Smundak		wantPanic: "does not exist",
1214*88d15eacSSasha Smundak		reason:    "name of non-existent field is invalid",
1215*88d15eacSSasha Smundak	}, {
1216*88d15eacSSasha Smundak		label:     "IgnoreFields",
1217*88d15eacSSasha Smundak		fnc:       IgnoreFields,
1218*88d15eacSSasha Smundak		args:      args(Foo1{}, "Alpha.NoExist"),
1219*88d15eacSSasha Smundak		wantPanic: "must be a struct",
1220*88d15eacSSasha Smundak		reason:    "cannot select into a non-struct",
1221*88d15eacSSasha Smundak	}, {
1222*88d15eacSSasha Smundak		label:     "IgnoreFields",
1223*88d15eacSSasha Smundak		fnc:       IgnoreFields,
1224*88d15eacSSasha Smundak		args:      args(&Foo1{}, "Alpha"),
1225*88d15eacSSasha Smundak		wantPanic: "must be a non-pointer struct",
1226*88d15eacSSasha Smundak		reason:    "the type must be a struct (not pointer to a struct)",
1227*88d15eacSSasha Smundak	}, {
1228*88d15eacSSasha Smundak		label:  "IgnoreFields",
1229*88d15eacSSasha Smundak		fnc:    IgnoreFields,
1230*88d15eacSSasha Smundak		args:   args(struct{ privateStruct }{}, "privateStruct"),
1231*88d15eacSSasha Smundak		reason: "privateStruct field permitted since it is the default name of the embedded type",
1232*88d15eacSSasha Smundak	}, {
1233*88d15eacSSasha Smundak		label:  "IgnoreFields",
1234*88d15eacSSasha Smundak		fnc:    IgnoreFields,
1235*88d15eacSSasha Smundak		args:   args(struct{ privateStruct }{}, "Public"),
1236*88d15eacSSasha Smundak		reason: "Public field permitted since it is a forwarded field that is exported",
1237*88d15eacSSasha Smundak	}, {
1238*88d15eacSSasha Smundak		label:     "IgnoreFields",
1239*88d15eacSSasha Smundak		fnc:       IgnoreFields,
1240*88d15eacSSasha Smundak		args:      args(struct{ privateStruct }{}, "private"),
1241*88d15eacSSasha Smundak		wantPanic: "does not exist",
1242*88d15eacSSasha Smundak		reason:    "private field not permitted since it is a forwarded field that is unexported",
1243*88d15eacSSasha Smundak	}, {
1244*88d15eacSSasha Smundak		label:  "IgnoreTypes",
1245*88d15eacSSasha Smundak		fnc:    IgnoreTypes,
1246*88d15eacSSasha Smundak		reason: "empty input is valid",
1247*88d15eacSSasha Smundak	}, {
1248*88d15eacSSasha Smundak		label:     "IgnoreTypes",
1249*88d15eacSSasha Smundak		fnc:       IgnoreTypes,
1250*88d15eacSSasha Smundak		args:      args(nil),
1251*88d15eacSSasha Smundak		wantPanic: "cannot determine type",
1252*88d15eacSSasha Smundak		reason:    "input must not be nil value",
1253*88d15eacSSasha Smundak	}, {
1254*88d15eacSSasha Smundak		label:  "IgnoreTypes",
1255*88d15eacSSasha Smundak		fnc:    IgnoreTypes,
1256*88d15eacSSasha Smundak		args:   args(0, 0, 0),
1257*88d15eacSSasha Smundak		reason: "duplicate inputs of the same type is valid",
1258*88d15eacSSasha Smundak	}, {
1259*88d15eacSSasha Smundak		label:     "IgnoreInterfaces",
1260*88d15eacSSasha Smundak		fnc:       IgnoreInterfaces,
1261*88d15eacSSasha Smundak		args:      args(nil),
1262*88d15eacSSasha Smundak		wantPanic: "input must be an anonymous struct",
1263*88d15eacSSasha Smundak		reason:    "input must not be nil value",
1264*88d15eacSSasha Smundak	}, {
1265*88d15eacSSasha Smundak		label:     "IgnoreInterfaces",
1266*88d15eacSSasha Smundak		fnc:       IgnoreInterfaces,
1267*88d15eacSSasha Smundak		args:      args(Foo1{}),
1268*88d15eacSSasha Smundak		wantPanic: "input must be an anonymous struct",
1269*88d15eacSSasha Smundak		reason:    "input must not be a named struct type",
1270*88d15eacSSasha Smundak	}, {
1271*88d15eacSSasha Smundak		label:     "IgnoreInterfaces",
1272*88d15eacSSasha Smundak		fnc:       IgnoreInterfaces,
1273*88d15eacSSasha Smundak		args:      args(struct{ _ io.Reader }{}),
1274*88d15eacSSasha Smundak		wantPanic: "struct cannot have named fields",
1275*88d15eacSSasha Smundak		reason:    "input must not have named fields",
1276*88d15eacSSasha Smundak	}, {
1277*88d15eacSSasha Smundak		label:     "IgnoreInterfaces",
1278*88d15eacSSasha Smundak		fnc:       IgnoreInterfaces,
1279*88d15eacSSasha Smundak		args:      args(struct{ Foo1 }{}),
1280*88d15eacSSasha Smundak		wantPanic: "embedded field must be an interface type",
1281*88d15eacSSasha Smundak		reason:    "field types must be interfaces",
1282*88d15eacSSasha Smundak	}, {
1283*88d15eacSSasha Smundak		label:     "IgnoreInterfaces",
1284*88d15eacSSasha Smundak		fnc:       IgnoreInterfaces,
1285*88d15eacSSasha Smundak		args:      args(struct{ EmptyInterface }{}),
1286*88d15eacSSasha Smundak		wantPanic: "cannot ignore empty interface",
1287*88d15eacSSasha Smundak		reason:    "field types must not be the empty interface",
1288*88d15eacSSasha Smundak	}, {
1289*88d15eacSSasha Smundak		label: "IgnoreInterfaces",
1290*88d15eacSSasha Smundak		fnc:   IgnoreInterfaces,
1291*88d15eacSSasha Smundak		args: args(struct {
1292*88d15eacSSasha Smundak			io.Reader
1293*88d15eacSSasha Smundak			io.Writer
1294*88d15eacSSasha Smundak			io.Closer
1295*88d15eacSSasha Smundak			io.ReadWriteCloser
1296*88d15eacSSasha Smundak		}{}),
1297*88d15eacSSasha Smundak		reason: "multiple interfaces may be specified, even if they overlap",
1298*88d15eacSSasha Smundak	}, {
1299*88d15eacSSasha Smundak		label:  "IgnoreUnexported",
1300*88d15eacSSasha Smundak		fnc:    IgnoreUnexported,
1301*88d15eacSSasha Smundak		reason: "empty input is valid",
1302*88d15eacSSasha Smundak	}, {
1303*88d15eacSSasha Smundak		label:     "IgnoreUnexported",
1304*88d15eacSSasha Smundak		fnc:       IgnoreUnexported,
1305*88d15eacSSasha Smundak		args:      args(nil),
1306*88d15eacSSasha Smundak		wantPanic: "must be a non-pointer struct",
1307*88d15eacSSasha Smundak		reason:    "input must not be nil value",
1308*88d15eacSSasha Smundak	}, {
1309*88d15eacSSasha Smundak		label:     "IgnoreUnexported",
1310*88d15eacSSasha Smundak		fnc:       IgnoreUnexported,
1311*88d15eacSSasha Smundak		args:      args(&Foo1{}),
1312*88d15eacSSasha Smundak		wantPanic: "must be a non-pointer struct",
1313*88d15eacSSasha Smundak		reason:    "input must be a struct type (not a pointer to a struct)",
1314*88d15eacSSasha Smundak	}, {
1315*88d15eacSSasha Smundak		label:  "IgnoreUnexported",
1316*88d15eacSSasha Smundak		fnc:    IgnoreUnexported,
1317*88d15eacSSasha Smundak		args:   args(Foo1{}, struct{ x, X int }{}),
1318*88d15eacSSasha Smundak		reason: "input may be named or unnamed structs",
1319*88d15eacSSasha Smundak	}, {
1320*88d15eacSSasha Smundak		label:     "AcyclicTransformer",
1321*88d15eacSSasha Smundak		fnc:       AcyclicTransformer,
1322*88d15eacSSasha Smundak		args:      args("", "not a func"),
1323*88d15eacSSasha Smundak		wantPanic: "invalid transformer function",
1324*88d15eacSSasha Smundak		reason:    "AcyclicTransformer has same input requirements as Transformer",
1325*88d15eacSSasha Smundak	}}
1326*88d15eacSSasha Smundak
1327*88d15eacSSasha Smundak	for _, tt := range tests {
1328*88d15eacSSasha Smundak		t.Run(tt.label, func(t *testing.T) {
1329*88d15eacSSasha Smundak			// Prepare function arguments.
1330*88d15eacSSasha Smundak			vf := reflect.ValueOf(tt.fnc)
1331*88d15eacSSasha Smundak			var vargs []reflect.Value
1332*88d15eacSSasha Smundak			for i, arg := range tt.args {
1333*88d15eacSSasha Smundak				if arg == nil {
1334*88d15eacSSasha Smundak					tf := vf.Type()
1335*88d15eacSSasha Smundak					if i == tf.NumIn()-1 && tf.IsVariadic() {
1336*88d15eacSSasha Smundak						vargs = append(vargs, reflect.Zero(tf.In(i).Elem()))
1337*88d15eacSSasha Smundak					} else {
1338*88d15eacSSasha Smundak						vargs = append(vargs, reflect.Zero(tf.In(i)))
1339*88d15eacSSasha Smundak					}
1340*88d15eacSSasha Smundak				} else {
1341*88d15eacSSasha Smundak					vargs = append(vargs, reflect.ValueOf(arg))
1342*88d15eacSSasha Smundak				}
1343*88d15eacSSasha Smundak			}
1344*88d15eacSSasha Smundak
1345*88d15eacSSasha Smundak			// Call the function and capture any panics.
1346*88d15eacSSasha Smundak			var gotPanic string
1347*88d15eacSSasha Smundak			func() {
1348*88d15eacSSasha Smundak				defer func() {
1349*88d15eacSSasha Smundak					if ex := recover(); ex != nil {
1350*88d15eacSSasha Smundak						if s, ok := ex.(string); ok {
1351*88d15eacSSasha Smundak							gotPanic = s
1352*88d15eacSSasha Smundak						} else {
1353*88d15eacSSasha Smundak							panic(ex)
1354*88d15eacSSasha Smundak						}
1355*88d15eacSSasha Smundak					}
1356*88d15eacSSasha Smundak				}()
1357*88d15eacSSasha Smundak				vf.Call(vargs)
1358*88d15eacSSasha Smundak			}()
1359*88d15eacSSasha Smundak
1360*88d15eacSSasha Smundak			switch {
1361*88d15eacSSasha Smundak			case tt.reason == "":
1362*88d15eacSSasha Smundak				t.Errorf("reason must be provided")
1363*88d15eacSSasha Smundak			case tt.wantPanic == "" && gotPanic != "":
1364*88d15eacSSasha Smundak				t.Errorf("unexpected panic message: %s\nreason: %s", gotPanic, tt.reason)
1365*88d15eacSSasha Smundak			case tt.wantPanic != "" && !strings.Contains(gotPanic, tt.wantPanic):
1366*88d15eacSSasha Smundak				t.Errorf("panic message:\ngot:  %s\nwant: %s\nreason: %s", gotPanic, tt.wantPanic, tt.reason)
1367*88d15eacSSasha Smundak			}
1368*88d15eacSSasha Smundak		})
1369*88d15eacSSasha Smundak	}
1370*88d15eacSSasha Smundak}
1371