Source file src/cmd/compile/internal/test/eq_test.go

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

View as plain text