Skip to content

Commit e432b4f

Browse files
randall77gopherbot
authored andcommitted
cmd/compile: more generated equality function tests
Change-Id: I05cd103ea8d8bbee0ad907ff3e594de273d49e86 Reviewed-on: https://go-review.googlesource.com/c/go/+/725600 Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Auto-Submit: Keith Randall <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]>
1 parent c1acdcb commit e432b4f

File tree

1 file changed

+298
-0
lines changed

1 file changed

+298
-0
lines changed
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Tests of generated equality functions.
6+
7+
package test
8+
9+
import (
10+
"reflect"
11+
"testing"
12+
"unsafe"
13+
)
14+
15+
//go:noinline
16+
func checkEq(t *testing.T, x, y any) {
17+
// Make sure we don't inline the equality test.
18+
if x != y {
19+
t.Errorf("%#v != %#v, wanted equal", x, y)
20+
}
21+
}
22+
23+
//go:noinline
24+
func checkNe(t *testing.T, x, y any) {
25+
// Make sure we don't inline the equality test.
26+
if x == y {
27+
t.Errorf("%#v == %#v, wanted not equal", x, y)
28+
}
29+
}
30+
31+
//go:noinline
32+
func checkPanic(t *testing.T, x, y any) {
33+
defer func() {
34+
if recover() == nil {
35+
t.Errorf("%#v == %#v didn't panic", x, y)
36+
}
37+
}()
38+
_ = x == y
39+
}
40+
41+
type fooComparable struct {
42+
x int
43+
}
44+
45+
func (f fooComparable) foo() {
46+
}
47+
48+
type fooIncomparable struct {
49+
b func()
50+
}
51+
52+
func (i fooIncomparable) foo() {
53+
}
54+
55+
type eqResult int
56+
57+
const (
58+
eq eqResult = iota
59+
ne
60+
panic_
61+
)
62+
63+
func (x eqResult) String() string {
64+
return []string{eq: "eq", ne: "ne", panic_: "panic"}[x]
65+
}
66+
67+
// testEq returns eq if x==y, ne if x!=y, or panic_ if the comparison panics.
68+
func testEq(x, y any) (r eqResult) {
69+
defer func() {
70+
if e := recover(); e != nil {
71+
r = panic_
72+
}
73+
}()
74+
r = ne
75+
if x == y {
76+
r = eq
77+
}
78+
return
79+
}
80+
81+
// testCompare make two instances of struct type typ, then
82+
// assigns its len(vals) fields one value from each slice in vals.
83+
// Then it checks the results against a "manual" comparison field
84+
// by field.
85+
func testCompare(t *testing.T, typ reflect.Type, vals [][]any) {
86+
if len(vals) != typ.NumField() {
87+
t.Fatalf("bad test, have %d fields in the list, but %d fields in the type", len(vals), typ.NumField())
88+
}
89+
90+
x := reflect.New(typ).Elem()
91+
y := reflect.New(typ).Elem()
92+
ps := powerSet(vals) // all possible settings of fields of the test type.
93+
for _, xf := range ps { // Pick fields for x
94+
for _, yf := range ps { // Pick fields for y
95+
// Make x and y from their chosen fields.
96+
for i, f := range xf {
97+
x.Field(i).Set(reflect.ValueOf(f))
98+
}
99+
for i, f := range yf {
100+
y.Field(i).Set(reflect.ValueOf(f))
101+
}
102+
// Compute what we want the result to be.
103+
want := eq
104+
for i := range len(vals) {
105+
if c := testEq(xf[i], yf[i]); c != eq {
106+
want = c
107+
break
108+
}
109+
}
110+
// Compute actual result using generated equality function.
111+
got := testEq(x.Interface(), y.Interface())
112+
if got != want {
113+
t.Errorf("%#v == %#v, got %s want %s\n", x, y, got, want)
114+
}
115+
}
116+
}
117+
}
118+
119+
// powerset returns all possible sequences of choosing one
120+
// element from each entry in s.
121+
// For instance, if s = {{1,2}, {a,b}}, then
122+
// it returns {{1,a},{1,b},{2,a},{2,b}}.
123+
func powerSet(s [][]any) [][]any {
124+
if len(s) == 0 {
125+
return [][]any{{}}
126+
}
127+
p := powerSet(s[:len(s)-1]) // powerset from first len(s)-1 entries
128+
var r [][]any
129+
for _, head := range p {
130+
// add one more entry.
131+
for _, v := range s[len(s)-1] {
132+
x := make([]any, 0, len(s))
133+
x = append(x, head...)
134+
x = append(x, v)
135+
r = append(r, x)
136+
}
137+
}
138+
return r
139+
}
140+
141+
func TestCompareKinds1(t *testing.T) {
142+
type S struct {
143+
X0 int8
144+
X1 int16
145+
X2 int32
146+
X3 int64
147+
X4 float32
148+
X5 float64
149+
}
150+
testCompare(t, reflect.TypeOf(S{}), [][]any{
151+
{int8(0), int8(1)},
152+
{int16(0), int16(1), int16(1 << 14)},
153+
{int32(0), int32(1), int32(1 << 30)},
154+
{int64(0), int64(1), int64(1 << 62)},
155+
{float32(0), float32(1.0)},
156+
{0.0, 1.0},
157+
})
158+
}
159+
func TestCompareKinds2(t *testing.T) {
160+
type S struct {
161+
X0 uint8
162+
X1 uint16
163+
X2 uint32
164+
X3 uint64
165+
X4 uintptr
166+
X5 bool
167+
}
168+
testCompare(t, reflect.TypeOf(S{}), [][]any{
169+
{uint8(0), uint8(1)},
170+
{uint16(0), uint16(1), uint16(1 << 15)},
171+
{uint32(0), uint32(1), uint32(1 << 31)},
172+
{uint64(0), uint64(1), uint64(1 << 63)},
173+
{uintptr(0), uintptr(1)},
174+
{false, true},
175+
})
176+
}
177+
func TestCompareKinds3(t *testing.T) {
178+
type S struct {
179+
X0 complex64
180+
X1 complex128
181+
X2 *byte
182+
X3 chan int
183+
X4 unsafe.Pointer
184+
}
185+
testCompare(t, reflect.TypeOf(S{}), [][]any{
186+
{complex64(1 + 1i), complex64(1 + 2i), complex64(2 + 1i)},
187+
{complex128(1 + 1i), complex128(1 + 2i), complex128(2 + 1i)},
188+
{new(byte), new(byte)},
189+
{make(chan int), make(chan int)},
190+
{unsafe.Pointer(new(byte)), unsafe.Pointer(new(byte))},
191+
})
192+
}
193+
194+
func TestCompareOrdering(t *testing.T) {
195+
type S struct {
196+
A string
197+
E any
198+
B string
199+
}
200+
201+
testCompare(t, reflect.TypeOf(S{}), [][]any{
202+
{"a", "b", "cc"},
203+
{3, []byte{0}, []byte{1}},
204+
{"a", "b", "cc"},
205+
})
206+
}
207+
func TestCompareInterfaces(t *testing.T) {
208+
type S struct {
209+
A any
210+
B fooer
211+
}
212+
testCompare(t, reflect.TypeOf(S{}), [][]any{
213+
{3, []byte{0}},
214+
{fooComparable{x: 3}, fooIncomparable{b: nil}},
215+
})
216+
}
217+
218+
func TestCompareSkip(t *testing.T) {
219+
type S struct {
220+
A int8
221+
B int16
222+
}
223+
type S2 struct {
224+
A int8
225+
padding int8
226+
B int16
227+
}
228+
x := S{A: 1, B: 3}
229+
y := S{A: 1, B: 3}
230+
(*S2)(unsafe.Pointer(&x)).padding = 88
231+
(*S2)(unsafe.Pointer(&y)).padding = 99
232+
233+
want := eq
234+
if got := testEq(x, y); got != want {
235+
t.Errorf("%#v == %#v, got %s want %s", x, y, got, want)
236+
}
237+
}
238+
239+
func TestCompareMemequal(t *testing.T) {
240+
type S struct {
241+
s1 string
242+
d [100]byte
243+
s2 string
244+
}
245+
var x, y S
246+
247+
checkEq(t, x, y)
248+
y.d[0] = 1
249+
checkNe(t, x, y)
250+
y.d[0] = 0
251+
y.d[99] = 1
252+
checkNe(t, x, y)
253+
}
254+
255+
func TestComparePanic(t *testing.T) {
256+
type S struct {
257+
X0 string
258+
X1 any
259+
X2 string
260+
X3 fooer
261+
X4 string
262+
}
263+
testCompare(t, reflect.TypeOf(S{}), [][]any{
264+
{"a", "b", "cc"}, // length equal, as well as length unequal
265+
{3, []byte{1}}, // comparable and incomparable
266+
{"a", "b", "cc"}, // length equal, as well as length unequal
267+
{fooComparable{x: 3}, fooIncomparable{b: nil}}, // comparable and incomparable
268+
{"a", "b", "cc"}, // length equal, as well as length unequal
269+
})
270+
}
271+
272+
func TestCompareArray(t *testing.T) {
273+
type S struct {
274+
X0 string
275+
X1 [100]string
276+
X2 string
277+
}
278+
x := S{X0: "a", X2: "b"}
279+
y := x
280+
checkEq(t, x, y)
281+
x.X0 = "c"
282+
checkNe(t, x, y)
283+
x.X0 = "a"
284+
x.X2 = "c"
285+
checkNe(t, x, y)
286+
x.X2 = "b"
287+
checkEq(t, x, y)
288+
289+
for i := 0; i < 100; i++ {
290+
x.X1[i] = "d"
291+
checkNe(t, x, y)
292+
y.X1[i] = "e"
293+
checkNe(t, x, y)
294+
x.X1[i] = ""
295+
y.X1[i] = ""
296+
checkEq(t, x, y)
297+
}
298+
}

0 commit comments

Comments
 (0)