// Copyright 2025 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Tests of generated equality functions. package test import ( "reflect" "testing" "unsafe" ) //go:noinline func checkEq(t *testing.T, x, y any) { // Make sure we don't inline the equality test. if x != y { t.Errorf("%#v != %#v, wanted equal", x, y) } } //go:noinline func checkNe(t *testing.T, x, y any) { // Make sure we don't inline the equality test. if x == y { t.Errorf("%#v == %#v, wanted not equal", x, y) } } //go:noinline func checkPanic(t *testing.T, x, y any) { defer func() { if recover() == nil { t.Errorf("%#v == %#v didn't panic", x, y) } }() _ = x == y } type fooComparable struct { x int } func (f fooComparable) foo() { } type fooIncomparable struct { b func() } func (i fooIncomparable) foo() { } type eqResult int const ( eq eqResult = iota ne panic_ ) func (x eqResult) String() string { return []string{eq: "eq", ne: "ne", panic_: "panic"}[x] } // testEq returns eq if x==y, ne if x!=y, or panic_ if the comparison panics. func testEq(x, y any) (r eqResult) { defer func() { if e := recover(); e != nil { r = panic_ } }() r = ne if x == y { r = eq } return } // testCompare make two instances of struct type typ, then // assigns its len(vals) fields one value from each slice in vals. // Then it checks the results against a "manual" comparison field // by field. func testCompare(t *testing.T, typ reflect.Type, vals [][]any) { if len(vals) != typ.NumField() { t.Fatalf("bad test, have %d fields in the list, but %d fields in the type", len(vals), typ.NumField()) } x := reflect.New(typ).Elem() y := reflect.New(typ).Elem() ps := powerSet(vals) // all possible settings of fields of the test type. for _, xf := range ps { // Pick fields for x for _, yf := range ps { // Pick fields for y // Make x and y from their chosen fields. for i, f := range xf { x.Field(i).Set(reflect.ValueOf(f)) } for i, f := range yf { y.Field(i).Set(reflect.ValueOf(f)) } // Compute what we want the result to be. want := eq for i := range len(vals) { if c := testEq(xf[i], yf[i]); c != eq { want = c break } } // Compute actual result using generated equality function. got := testEq(x.Interface(), y.Interface()) if got != want { t.Errorf("%#v == %#v, got %s want %s\n", x, y, got, want) } } } } // powerset returns all possible sequences of choosing one // element from each entry in s. // For instance, if s = {{1,2}, {a,b}}, then // it returns {{1,a},{1,b},{2,a},{2,b}}. func powerSet(s [][]any) [][]any { if len(s) == 0 { return [][]any{{}} } p := powerSet(s[:len(s)-1]) // powerset from first len(s)-1 entries var r [][]any for _, head := range p { // add one more entry. for _, v := range s[len(s)-1] { x := make([]any, 0, len(s)) x = append(x, head...) x = append(x, v) r = append(r, x) } } return r } func TestCompareKinds1(t *testing.T) { type S struct { X0 int8 X1 int16 X2 int32 X3 int64 X4 float32 X5 float64 } testCompare(t, reflect.TypeOf(S{}), [][]any{ {int8(0), int8(1)}, {int16(0), int16(1), int16(1 << 14)}, {int32(0), int32(1), int32(1 << 30)}, {int64(0), int64(1), int64(1 << 62)}, {float32(0), float32(1.0)}, {0.0, 1.0}, }) } func TestCompareKinds2(t *testing.T) { type S struct { X0 uint8 X1 uint16 X2 uint32 X3 uint64 X4 uintptr X5 bool } testCompare(t, reflect.TypeOf(S{}), [][]any{ {uint8(0), uint8(1)}, {uint16(0), uint16(1), uint16(1 << 15)}, {uint32(0), uint32(1), uint32(1 << 31)}, {uint64(0), uint64(1), uint64(1 << 63)}, {uintptr(0), uintptr(1)}, {false, true}, }) } func TestCompareKinds3(t *testing.T) { type S struct { X0 complex64 X1 complex128 X2 *byte X3 chan int X4 unsafe.Pointer } testCompare(t, reflect.TypeOf(S{}), [][]any{ {complex64(1 + 1i), complex64(1 + 2i), complex64(2 + 1i)}, {complex128(1 + 1i), complex128(1 + 2i), complex128(2 + 1i)}, {new(byte), new(byte)}, {make(chan int), make(chan int)}, {unsafe.Pointer(new(byte)), unsafe.Pointer(new(byte))}, }) } func TestCompareOrdering(t *testing.T) { type S struct { A string E any B string } testCompare(t, reflect.TypeOf(S{}), [][]any{ {"a", "b", "cc"}, {3, []byte{0}, []byte{1}}, {"a", "b", "cc"}, }) } func TestCompareInterfaces(t *testing.T) { type S struct { A any B fooer } testCompare(t, reflect.TypeOf(S{}), [][]any{ {3, []byte{0}}, {fooComparable{x: 3}, fooIncomparable{b: nil}}, }) } func TestCompareSkip(t *testing.T) { type S struct { A int8 B int16 } type S2 struct { A int8 padding int8 B int16 } x := S{A: 1, B: 3} y := S{A: 1, B: 3} (*S2)(unsafe.Pointer(&x)).padding = 88 (*S2)(unsafe.Pointer(&y)).padding = 99 want := eq if got := testEq(x, y); got != want { t.Errorf("%#v == %#v, got %s want %s", x, y, got, want) } } func TestCompareMemequal(t *testing.T) { type S struct { s1 string d [100]byte s2 string } var x, y S checkEq(t, x, y) y.d[0] = 1 checkNe(t, x, y) y.d[0] = 0 y.d[99] = 1 checkNe(t, x, y) } func TestComparePanic(t *testing.T) { type S struct { X0 string X1 any X2 string X3 fooer X4 string } testCompare(t, reflect.TypeOf(S{}), [][]any{ {"a", "b", "cc"}, // length equal, as well as length unequal {3, []byte{1}}, // comparable and incomparable {"a", "b", "cc"}, // length equal, as well as length unequal {fooComparable{x: 3}, fooIncomparable{b: nil}}, // comparable and incomparable {"a", "b", "cc"}, // length equal, as well as length unequal }) } func TestCompareArray(t *testing.T) { type S struct { X0 string X1 [100]string X2 string } x := S{X0: "a", X2: "b"} y := x checkEq(t, x, y) x.X0 = "c" checkNe(t, x, y) x.X0 = "a" x.X2 = "c" checkNe(t, x, y) x.X2 = "b" checkEq(t, x, y) for i := 0; i < 100; i++ { x.X1[i] = "d" checkNe(t, x, y) y.X1[i] = "e" checkNe(t, x, y) x.X1[i] = "" y.X1[i] = "" checkEq(t, x, y) } }