github.com/grailbio/bigslice@v0.0.0-20230519005545-30c4c12152ad/func_test.go (about)

     1  // Copyright 2019 GRAIL, Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache 2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  package bigslice
     6  
     7  import (
     8  	"testing"
     9  	"unsafe"
    10  
    11  	"github.com/grailbio/testutil/expect"
    12  	"github.com/grailbio/testutil/h"
    13  )
    14  
    15  type testStruct0 struct{ Field0 int }
    16  
    17  // testStruct1 exists to avoid the problem of registering the same struct twice
    18  // with gob. As a convenience, bigslice.Func registers its argument types.
    19  // However, if you pass the same struct as a value and a pointer, we attempt to
    20  // register the same type twice with different names, e.g.
    21  // "github.com/grailbio/bigslice.testStruct0" and "*bigslice.testStruct0". This
    22  // causes a panic in gob. Instead, we just use a different type altogether for
    23  // our pointer-to-struct argument.
    24  type testStruct1 struct{ Field1 int }
    25  
    26  // Disable unused checking for testInterface, as we're just using it to make
    27  // sure that our func typechecking works properly, and we don't need to
    28  // call it to do so.
    29  // nolint:unused
    30  type testInterface interface{ FuncTestMethod() }
    31  type testInterfaceImpl struct{}
    32  
    33  func (s *testInterfaceImpl) FuncTestMethod() {}
    34  
    35  var fnTestNilFuncArgs = Func(
    36  	func(int, string, []string, map[int]int,
    37  		testStruct0, *testStruct1, unsafe.Pointer,
    38  		testInterface) Slice {
    39  
    40  		return Const(1, []int{})
    41  	})
    42  
    43  // TestNilFuncArgs verifies that Func invocation handles untyped nil arguments
    44  // properly.
    45  func TestNilFuncArgs(t *testing.T) {
    46  	ts0 := testStruct0{Field0: 0}
    47  	pts1 := &testStruct1{Field1: 0}
    48  	upts1 := unsafe.Pointer(pts1)
    49  	ptii := &testInterfaceImpl{}
    50  	for _, c := range []struct {
    51  		name string
    52  		args []interface{}
    53  		ok   bool
    54  	}{
    55  		{
    56  			name: "all non-nil",
    57  			args: []interface{}{
    58  				0, "", []string{}, map[int]int{0: 0},
    59  				ts0, pts1, upts1, ptii,
    60  			},
    61  			ok: true,
    62  		},
    63  		{
    64  			name: "nil for types that can be nil",
    65  			args: []interface{}{
    66  				0, "", nil, nil,
    67  				ts0, nil, nil, nil,
    68  			},
    69  			ok: true,
    70  		},
    71  		{
    72  			name: "nil for int",
    73  			args: []interface{}{
    74  				nil, "", []string{}, map[int]int{0: 0},
    75  				ts0, pts1, upts1, ptii,
    76  			},
    77  			ok: false,
    78  		},
    79  		{
    80  			name: "nil for string",
    81  			args: []interface{}{
    82  				0, nil, []string{}, map[int]int{0: 0},
    83  				ts0, pts1, upts1, ptii,
    84  			},
    85  			ok: false,
    86  		},
    87  		{
    88  			name: "nil for struct",
    89  			args: []interface{}{
    90  				0, "", []string{}, map[int]int{0: 0},
    91  				nil, pts1, upts1, ptii,
    92  			},
    93  			ok: false,
    94  		},
    95  	} {
    96  		t.Run(c.name, func(t *testing.T) {
    97  			var matcher *h.Matcher
    98  			if c.ok {
    99  				matcher = h.Panics(h.Nil())
   100  			} else {
   101  				// Expect a panic with a message saying something about
   102  				// nil-ness.
   103  				matcher = h.Panics(h.HasSubstr("nil"))
   104  			}
   105  			invoke := func() { fnTestNilFuncArgs.Invocation("", c.args...) }
   106  			expect.That(t, invoke, matcher)
   107  
   108  			apply := func() { fnTestNilFuncArgs.Apply(c.args...) }
   109  			expect.That(t, apply, matcher)
   110  		})
   111  	}
   112  }
   113  
   114  func TestFuncLocationsDiff(t *testing.T) {
   115  	for _, c := range []struct {
   116  		lhs  []string
   117  		rhs  []string
   118  		diff []string
   119  	}{
   120  		{nil, nil, nil},
   121  		{[]string{"a"}, []string{"a"}, nil},
   122  		{
   123  			[]string{},
   124  			[]string{"a"},
   125  			[]string{"+ a"},
   126  		},
   127  		{
   128  			[]string{"a", "b"},
   129  			[]string{"a"},
   130  			[]string{"a", "- b"},
   131  		},
   132  		{
   133  			[]string{"a", "b"},
   134  			[]string{"b"},
   135  			[]string{"- a", "b"},
   136  		},
   137  		{
   138  			[]string{"a"},
   139  			[]string{"a", "b"},
   140  			[]string{"a", "+ b"},
   141  		},
   142  		{
   143  			[]string{"a", "c"},
   144  			[]string{"a", "b", "c", "d"},
   145  			[]string{"a", "+ b", "c", "+ d"},
   146  		},
   147  		{
   148  			[]string{"a", "b", "d"},
   149  			[]string{"a", "c", "d"},
   150  			[]string{"a", "- b", "+ c", "d"},
   151  		},
   152  		{
   153  			[]string{"a", "b", "c"},
   154  			[]string{"a", "c", "d", "e"},
   155  			[]string{"a", "- b", "c", "+ d", "+ e"},
   156  		},
   157  	} {
   158  		expect.That(t, FuncLocationsDiff(c.lhs, c.rhs), h.EQ(c.diff))
   159  	}
   160  }