github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/misc/genstd/mapping_test.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/parser" 7 "os" 8 "path/filepath" 9 "sync" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 const testdataDir = "github.com/gnolang/gno/misc/genstd/testdata/" 17 18 var initWD = func() string { 19 d, err := os.Getwd() 20 if err != nil { 21 panic(err) 22 } 23 return d 24 }() 25 26 func chdir(t *testing.T, s string) { 27 t.Helper() 28 29 os.Chdir(filepath.Join(initWD, s)) 30 t.Cleanup(func() { 31 os.Chdir(initWD) 32 dirsOnce = sync.Once{} 33 memoGitRoot, memoRelPath = "", "" 34 }) 35 } 36 37 func Test_linkFunctions(t *testing.T) { 38 chdir(t, "testdata/linkFunctions") 39 40 pkgs, err := walkStdlibs(".") 41 require.NoError(t, err) 42 43 mappings := linkFunctions(pkgs) 44 require.Len(t, mappings, 8) 45 46 const ( 47 ret = 1 << iota 48 param 49 machine 50 ) 51 str := func(i int) string { 52 s := "Fn" 53 if i&machine != 0 { 54 s += "Machine" 55 } 56 if i¶m != 0 { 57 s += "Param" 58 } 59 if i&ret != 0 { 60 s += "Ret" 61 } 62 return s 63 } 64 65 for i, v := range mappings { 66 exp := str(i) 67 assert.Equal(t, exp, v.GnoFunc) 68 assert.Equal(t, exp, v.GoFunc) 69 assert.Equal(t, "std", v.GnoImportPath) 70 assert.Equal(t, testdataDir+"linkFunctions/std", v.GoImportPath) 71 72 assert.Equal(t, i&machine != 0, v.MachineParam, "MachineParam should match expected value") 73 if i¶m != 0 { 74 // require, otherwise the following would panic 75 require.Len(t, v.Params, 1) 76 p := v.Params[0] 77 assert.Equal(t, "int", p.GnoType()) 78 assert.Equal(t, "int", p.GoQualifiedName()) 79 assert.False(t, p.IsTypedValue) 80 } else { 81 assert.Len(t, v.Params, 0) 82 } 83 if i&ret != 0 { 84 // require, otherwise the following would panic 85 require.Len(t, v.Results, 1) 86 p := v.Results[0] 87 assert.Equal(t, "int", p.GnoType()) 88 assert.Equal(t, "int", p.GoQualifiedName()) 89 assert.False(t, p.IsTypedValue) 90 } else { 91 assert.Len(t, v.Results, 0) 92 } 93 } 94 } 95 96 func Test_linkFunctions_unexp(t *testing.T) { 97 chdir(t, "testdata/linkFunctions_unexp") 98 99 pkgs, err := walkStdlibs(".") 100 require.NoError(t, err) 101 102 mappings := linkFunctions(pkgs) 103 require.Len(t, mappings, 2) 104 105 assert.Equal(t, false, mappings[0].MachineParam) 106 assert.Equal(t, "t1", mappings[0].GnoFunc) 107 assert.Equal(t, "X_t1", mappings[0].GoFunc) 108 109 assert.Equal(t, true, mappings[1].MachineParam) 110 assert.Equal(t, "t2", mappings[1].GnoFunc) 111 assert.Equal(t, "X_t2", mappings[1].GoFunc) 112 } 113 114 func Test_linkFunctions_TypedValue(t *testing.T) { 115 chdir(t, "testdata/linkFunctions_TypedValue") 116 117 pkgs, err := walkStdlibs(".") 118 require.NoError(t, err) 119 120 mappings := linkFunctions(pkgs) 121 require.Len(t, mappings, 3) 122 123 assert.Equal(t, false, mappings[0].MachineParam) 124 assert.Equal(t, "TVParam", mappings[0].GnoFunc) 125 assert.Equal(t, "TVParam", mappings[0].GoFunc) 126 assert.Len(t, mappings[0].Results, 0) 127 _ = assert.Len(t, mappings[0].Params, 1) && 128 assert.Equal(t, true, mappings[0].Params[0].IsTypedValue) && 129 assert.Equal(t, "struct{m1 map[string]interface{}}", mappings[0].Params[0].GnoType()) 130 131 assert.Equal(t, false, mappings[1].MachineParam) 132 assert.Equal(t, "TVResult", mappings[1].GnoFunc) 133 assert.Equal(t, "TVResult", mappings[1].GoFunc) 134 assert.Len(t, mappings[1].Params, 0) 135 _ = assert.Len(t, mappings[1].Results, 1) && 136 assert.Equal(t, true, mappings[1].Results[0].IsTypedValue) && 137 assert.Equal(t, "interface{S() map[int]Banker}", mappings[1].Results[0].GnoType()) 138 139 assert.Equal(t, true, mappings[2].MachineParam) 140 assert.Equal(t, "TVFull", mappings[2].GnoFunc) 141 assert.Equal(t, "TVFull", mappings[2].GoFunc) 142 assert.Len(t, mappings[2].Params, 1) 143 assert.Len(t, mappings[2].Results, 1) 144 } 145 146 func Test_linkFunctions_noMatch(t *testing.T) { 147 chdir(t, "testdata/linkFunctions_noMatch") 148 149 pkgs, err := walkStdlibs(".") 150 require.NoError(t, err) 151 152 defer func() { 153 r := recover() 154 assert.NotNil(t, r) 155 assert.Contains(t, fmt.Sprint(r), "no matching go function declaration") 156 }() 157 158 linkFunctions(pkgs) 159 } 160 161 func Test_linkFunctions_noMatchSig(t *testing.T) { 162 chdir(t, "testdata/linkFunctions_noMatchSig") 163 164 pkgs, err := walkStdlibs(".") 165 require.NoError(t, err) 166 167 defer func() { 168 r := recover() 169 assert.NotNil(t, r) 170 assert.Contains(t, fmt.Sprint(r), "doesn't match signature of go function") 171 }() 172 173 linkFunctions(pkgs) 174 } 175 176 // typesEqual - separate tests. 177 178 var typesEqualMapping = &mapping{ 179 GnoImportPath: "std", 180 GnoFunc: "Fn", 181 GoImportPath: "github.com/gnolang/gno/gnovm/stdlibs/std", 182 GoFunc: "Fn", 183 goImports: []*ast.ImportSpec{ 184 { 185 Name: &ast.Ident{Name: "gno"}, 186 Path: &ast.BasicLit{Value: `"github.com/gnolang/gno/gnovm/pkg/gnolang"`}, 187 }, 188 { 189 Path: &ast.BasicLit{Value: `"github.com/gnolang/gno/tm2/pkg/crypto"`}, 190 }, 191 }, 192 gnoImports: []*ast.ImportSpec{ 193 { 194 Path: &ast.BasicLit{Value: `"std"`}, 195 }, 196 { 197 Path: &ast.BasicLit{Value: `"math"`}, 198 }, 199 }, 200 } 201 202 func Test_typesEqual(t *testing.T) { 203 tt := []struct { 204 gnoe, goe string 205 errContains string 206 }{ 207 {"int", "int", ""}, 208 {"*[11][]rune", "*[11][ ]rune", ""}, 209 210 {"madeup", "madeup", "non-builtin type"}, 211 212 {"int", "string", "does not match"}, 213 {"*int", "int", "does not match"}, 214 {"string", "*string", "does not match"}, 215 {"*string", "*int", "does not match"}, 216 217 {"[]int", "[1]int", "does not match"}, 218 {"[1]int", "[]int", "does not match"}, 219 {"[2]int", "[2]string", "does not match"}, 220 // valid, but unsupported (only BasicLits) 221 {"[(11)]int", "[(11)]string", "does not match"}, 222 223 // even though mathematically equal, for simplicity we don't implement 224 // "true" basic lit equivalence 225 {"[8]int", "[0x8]int", "does not match"}, 226 } 227 228 for idx, tv := range tt { 229 t.Run(fmt.Sprintf("%02d_%s", idx, tv.gnoe), func(t *testing.T) { 230 gnoe, err := parser.ParseExpr(tv.gnoe) 231 require.NoError(t, err) 232 goe, err := parser.ParseExpr(tv.goe) 233 require.NoError(t, err) 234 235 err = typesEqualMapping.typesEqual(gnoe, goe) 236 if tv.errContains == "" { 237 assert.NoError(t, err) 238 } else { 239 _ = assert.Error(t, err) && 240 assert.Contains(t, err.Error(), tv.errContains) 241 } 242 }) 243 } 244 } 245 246 func Test_typesEqual_panic(t *testing.T) { 247 tt := []struct { 248 gnoe, goe string 249 panic string 250 }{ 251 {"map[string]string", "map[string]string", "not implemented"}, 252 {"func(s string)", "func(s string)", "not implemented"}, 253 {"interface{}", "interface{}", "not implemented"}, 254 {"struct{}", "struct{}", "not implemented"}, 255 {"1 + 2", "1 + 2", "invalid expression"}, 256 } 257 258 for _, tv := range tt { 259 t.Run(tv.gnoe, func(t *testing.T) { 260 gnoe, err := parser.ParseExpr(tv.gnoe) 261 require.NoError(t, err) 262 goe, err := parser.ParseExpr(tv.goe) 263 require.NoError(t, err) 264 265 defer func() { 266 r := recover() 267 if tv.panic == "" { 268 assert.Nil(t, r) 269 } else { 270 assert.Contains(t, fmt.Sprint(r), tv.panic) 271 } 272 }() 273 274 result := typesEqualMapping.typesEqual(gnoe, goe) 275 assert.Nil(t, result) 276 }) 277 } 278 }