|
| 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