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 }