github.com/notti/go-dynamic@v0.0.0-20190619201224-fc443047424c/test/build.go (about) 1 // Test generator and tests 2 package main 3 4 import ( 5 "fmt" 6 "log" 7 "os" 8 "strings" 9 "text/template" 10 ) 11 12 type datatype int 13 14 const ( 15 void datatype = iota 16 i8 17 u8 18 i16 19 u16 20 i32 21 u32 22 i64 23 u64 24 f32 25 f64 26 byteSlice 27 ) 28 29 type datatypeMap struct { 30 golang, C, Cgo, Cgop string 31 } 32 33 var mappings = map[datatype]datatypeMap{ 34 void: {"", "void", "", ""}, 35 i8: {"int8", "char", "C.char", ""}, 36 u8: {"uint8", "unsigned char", "C.uchar", ""}, 37 i16: {"int16", "short", "C.short", ""}, 38 u16: {"uint16", "unsigned short", "C.ushort", ""}, 39 i32: {"int32", "int", "C.int", ""}, 40 u32: {"uint32", "unsigned int", "C.uint", ""}, 41 i64: {"int64", "long long", "C.longlong", ""}, 42 u64: {"uint64", "unsigned long long", "C.ulonglong", ""}, 43 f32: {"float32", "float", "C.float", ""}, 44 f64: {"float64", "double", "C.double", ""}, 45 byteSlice: {"[]byte", "char *", "unsafe.Pointer", "*C.char"}, 46 } 47 48 type Value struct { 49 Type datatype 50 CData interface{} 51 GoData interface{} 52 } 53 54 func (v Value) Go() string { 55 return mappings[v.Type].golang 56 } 57 58 func (v Value) C() string { 59 return mappings[v.Type].C 60 } 61 62 func (v Value) Void() bool { 63 return v.Type == void 64 } 65 66 type Argument struct { 67 Name string 68 Value 69 } 70 71 func (a Argument) Go() string { 72 return a.Name + " " + mappings[a.Type].golang 73 } 74 75 func (a Argument) C() string { 76 return mappings[a.Type].C + " " + a.Name 77 } 78 79 type Arguments []Argument 80 81 func (a Arguments) Go() string { 82 args := make([]string, len(a)) 83 for i, arg := range a { 84 args[i] = arg.Go() 85 } 86 return strings.Join(args, ", ") 87 } 88 89 func (a Arguments) C() string { 90 args := make([]string, len(a)) 91 for i, arg := range a { 92 args[i] = arg.C() 93 } 94 return strings.Join(args, ", ") 95 } 96 97 func (a Arguments) Call() string { 98 args := make([]string, len(a)) 99 for i, arg := range a { 100 if mappings[arg.Type].Cgo == "unsafe.Pointer" { 101 args[i] = fmt.Sprintf("(%s)(%s(&%s[0]))", mappings[arg.Type].Cgop, mappings[arg.Type].Cgo, arg.Name) 102 } else { 103 args[i] = fmt.Sprintf("%s(%s)", mappings[arg.Type].Cgo, arg.Name) 104 } 105 } 106 return strings.Join(args, ", ") 107 } 108 109 func (a Arguments) Value() string { 110 args := make([]string, len(a)) 111 for i, arg := range a { 112 if arg.GoData != nil { 113 args[i] = fmt.Sprint(arg.GoData) 114 } else { 115 args[i] = fmt.Sprint(arg.CData) 116 } 117 } 118 return strings.Join(args, ", ") 119 } 120 121 type Test struct { 122 Name string 123 Ret Value 124 Arguments Arguments 125 CgoPre, CgoPost string 126 NOCGOPre, NOCGOPost string 127 Bench bool 128 Multitest bool 129 } 130 131 func (t Test) NOCGO() string { 132 args := make([]string, len(t.Arguments)) 133 for i, arg := range t.Arguments { 134 args[i] = arg.Go() 135 } 136 ret := "(" + strings.Join(args, ", ") + ")" 137 if !t.Ret.Void() { 138 ret += " " + t.Ret.Go() 139 } 140 return ret 141 } 142 143 func (t Test) DataInit() string { 144 args := make([]string, len(t.Arguments)) 145 for i, arg := range t.Arguments { 146 if arg.GoData == nil { 147 args[i] = fmt.Sprint(arg.CData) 148 } else { 149 args[i] = fmt.Sprint(arg.GoData) 150 } 151 } 152 return strings.Join(args, ", ") 153 } 154 155 func (t Test) TestName() string { 156 return "Test" + strings.Title(t.Name) 157 } 158 159 func (t Test) BenchmarkName() string { 160 return "Benchmark" + strings.Title(t.Name) 161 } 162 163 var ccode = template.Must(template.New("ccode").Parse(`#include <stdio.h> 164 165 {{range .}}{{.Ret.C}} {{.Name}}({{.Arguments.C}}) { 166 return{{with .Ret.CData}} {{.}}{{end}}; 167 } 168 169 {{end}} 170 `)) 171 172 var bridge = template.Must(template.New("bridge").Parse(`package testlib 173 174 {{range .}}// {{.Ret.C}} {{.Name}}({{.Arguments.C}}); 175 {{end}}import "C" 176 177 import "unsafe" 178 179 var _ = unsafe.Sizeof(0) 180 181 {{range .}}func {{.Name}}({{.Arguments.Go}}) {{.Ret.Go}} { 182 {{if not .Ret.Void}}return {{.Ret.Go}}({{end}}C.{{.Name}}({{.Arguments.Call}}){{if not .Ret.Void}}){{end}} 183 } 184 185 {{end}} 186 187 `)) 188 189 var testingCgo = template.Must(template.New("testingCgo").Parse(`package testlib 190 191 import ( 192 "testing" 193 ) 194 195 {{range .}}func {{.TestName}}(t *testing.T) { 196 {{.CgoPre}} 197 {{if .Ret.Void}}{{.Name}}({{.Arguments.Value}}){{else}}ret := {{.Name}}({{.Arguments.Value}}) 198 if ret != {{.Ret.GoData}} { 199 t.Fatalf("Expected %v, but got %v\n", {{.Ret.GoData}}, ret) 200 }{{end}} 201 {{.CgoPost}} 202 } 203 {{if .Bench}} 204 func {{.BenchmarkName}}(b *testing.B) { 205 {{.CgoPre}} 206 b.ResetTimer() 207 for i := 0; i < b.N; i++ { 208 {{.Name}}({{.Arguments.Value}}) 209 } 210 } 211 {{end}} 212 {{end}} 213 `)) 214 215 var testingNOCGO = template.Must(template.New("testingNOCGO").Parse(`package testlib 216 217 import ( 218 "log" 219 "os" 220 "runtime" 221 "testing" 222 223 "github.com/notti/nocgo" 224 ) 225 226 {{range .}} 227 228 var {{.Name}}Func func{{.NOCGO}} 229 230 {{if .Multitest}}func {{.TestName}}Multi(t *testing.T) { 231 for i:=0; i < 100; i++ { 232 t.Run("{{.TestName}}Multi", func(t *testing.T) { 233 t.Parallel() 234 {{.NOCGOPre}} 235 {{if not .Ret.Void}}ret := {{end}}{{.Name}}Func({{.DataInit}}){{if not .Ret.Void}} 236 if ret != {{.Ret.GoData}} { 237 t.Fatalf("Expected %v, but got %v\n", {{.Ret.GoData}}, ret) 238 }{{end}} 239 {{.NOCGOPost}} 240 }) 241 } 242 } 243 {{else}}func {{.TestName}}(t *testing.T) { 244 {{.NOCGOPre}} 245 {{if not .Ret.Void}}ret := {{end}}{{.Name}}Func({{.DataInit}}){{if not .Ret.Void}} 246 if ret != {{.Ret.GoData}} { 247 t.Fatalf("Expected %v, but got %v\n", {{.Ret.GoData}}, ret) 248 }{{end}} 249 {{.NOCGOPost}} 250 }{{end}} 251 {{if .Bench}} 252 func {{.BenchmarkName}}(b *testing.B) { 253 {{.NOCGOPre}} 254 b.ResetTimer() 255 for i := 0; i < b.N; i++ { 256 {{.Name}}Func({{.DataInit}}) 257 } 258 } 259 {{end}}{{end}} 260 261 func TestMain(m *testing.M) { 262 var lib string 263 switch runtime.GOARCH { 264 case "386": 265 lib = "libcalltest32.so.1" 266 case "amd64": 267 lib = "libcalltest64.so.1" 268 default: 269 log.Fatalln("Unknown arch ", runtime.GOARCH) 270 } 271 272 l, err := nocgo.Open(lib) 273 if err != nil { 274 log.Fatal(err) 275 } 276 277 {{range .}}if err := l.Func("{{.Name}}", &{{.Name}}Func); err != nil { 278 log.Fatal(err) 279 } 280 281 {{end}} 282 283 os.Exit(m.Run()) 284 } 285 `)) 286 287 func main() { 288 cfile, err := os.Create("testlib/test.c") 289 if err != nil { 290 log.Fatalln("Couldn't open c file") 291 } 292 bridgefile, err := os.Create("testlib/cgo_bridge.go") 293 if err != nil { 294 log.Fatalln("Couldn't open bridge file") 295 } 296 testingCgofile, err := os.Create("testlib/cgo_test.go") 297 if err != nil { 298 log.Fatalln("Couldn't open bridge file") 299 } 300 testingNOCGOfile, err := os.Create("nocgo/nocgo_test.go") 301 if err != nil { 302 log.Fatalln("Couldn't open bridge file") 303 } 304 tests := []Test{ 305 { 306 Name: "empty", 307 Ret: Value{void, nil, nil}, 308 Bench: true, 309 }, 310 { 311 Name: "int1", 312 Ret: Value{i8, "10", "10"}, 313 }, 314 { 315 Name: "int2", 316 Ret: Value{i8, "-10", "-10"}, 317 }, 318 { 319 Name: "int3", 320 Ret: Value{u8, "10", "10"}, 321 }, 322 { 323 Name: "int4", 324 Ret: Value{u8, "-10", "246"}, 325 }, 326 { 327 Name: "int5", 328 Ret: Value{u8, "a+b", "44"}, 329 Arguments: Arguments{ 330 {"a", Value{u8, "100", nil}}, 331 {"b", Value{u8, "200", nil}}, 332 }, 333 }, 334 { 335 Name: "int6", 336 Ret: Value{u64, "a", "100"}, 337 Arguments: Arguments{ 338 {"a", Value{u8, "100", nil}}, 339 }, 340 }, 341 { 342 Name: "int7", 343 Ret: Value{u8, "a", "100"}, 344 Arguments: Arguments{ 345 {"a", Value{u64, "100", nil}}, 346 }, 347 }, 348 { 349 Name: "intBig1", 350 Ret: Value{u64, "81985529216486895", "uint64(81985529216486895)"}, 351 }, 352 { 353 Name: "intBig2", 354 Ret: Value{u64, "a", "uint64(81985529216486895)"}, 355 Arguments: Arguments{ 356 {"a", Value{u64, "81985529216486895", nil}}, 357 }, 358 }, 359 { 360 Name: "float1", 361 Ret: Value{f32, "10.5", "10.5"}, 362 }, 363 { 364 Name: "float2", 365 Ret: Value{f64, "10.5", "10.5"}, 366 Bench: true, 367 }, 368 { 369 Name: "stackSpill1", 370 Ret: Value{i8, "a+b+c+d+e+f+g+h", "8"}, 371 Arguments: Arguments{ 372 {"a", Value{i8, "1", nil}}, 373 {"b", Value{i8, "1", nil}}, 374 {"c", Value{i8, "1", nil}}, 375 {"d", Value{i8, "1", nil}}, 376 {"e", Value{i8, "1", nil}}, 377 {"f", Value{i8, "1", nil}}, 378 {"g", Value{i8, "1", nil}}, 379 {"h", Value{i8, "1", nil}}, 380 }, 381 }, 382 { 383 Name: "stackSpill2", 384 Ret: Value{f32, "a+b+c+d+e+f+g+h+i+j", "10"}, 385 Arguments: Arguments{ 386 {"a", Value{f32, "1", nil}}, 387 {"b", Value{f32, "1", nil}}, 388 {"c", Value{f32, "1", nil}}, 389 {"d", Value{f32, "1", nil}}, 390 {"e", Value{f32, "1", nil}}, 391 {"f", Value{f32, "1", nil}}, 392 {"g", Value{f32, "1", nil}}, 393 {"h", Value{f32, "1", nil}}, 394 {"i", Value{f32, "1", nil}}, 395 {"j", Value{f32, "1", nil}}, 396 }, 397 }, 398 { 399 Name: "stackSpill3", 400 Ret: Value{i8, "ia+ib+ic+id+ie+f+ig+ih+fa+fb+fc+fd+fe+ff+fg+fh+fi+fj", "18"}, 401 Arguments: Arguments{ 402 {"ia", Value{i8, "1", nil}}, 403 {"ib", Value{i8, "1", nil}}, 404 {"ic", Value{i8, "1", nil}}, 405 {"id", Value{i8, "1", nil}}, 406 {"ie", Value{i8, "1", nil}}, 407 {"f", Value{i8, "1", nil}}, 408 {"ig", Value{i8, "1", nil}}, 409 {"ih", Value{i8, "1", nil}}, 410 {"fa", Value{f32, "1", nil}}, 411 {"fb", Value{f32, "1", nil}}, 412 {"fc", Value{f32, "1", nil}}, 413 {"fd", Value{f32, "1", nil}}, 414 {"fe", Value{f32, "1", nil}}, 415 {"ff", Value{f32, "1", nil}}, 416 {"fg", Value{f32, "1", nil}}, 417 {"fh", Value{f32, "1", nil}}, 418 {"fi", Value{f32, "1", nil}}, 419 {"fj", Value{f32, "1", nil}}, 420 }, 421 Bench: true, 422 }, 423 { 424 Name: "stackSpill4", 425 Ret: Value{i8, "ia+ib+ic+id+ie+f+ig+ih+fa+fb+fc+fd+fe+ff+fg+fh+fi+fj", "18"}, 426 Arguments: Arguments{ 427 {"ia", Value{i8, "1", nil}}, 428 {"fa", Value{f32, "1", nil}}, 429 {"ib", Value{i8, "1", nil}}, 430 {"fb", Value{f32, "1", nil}}, 431 {"ic", Value{i8, "1", nil}}, 432 {"fc", Value{f32, "1", nil}}, 433 {"id", Value{i8, "1", nil}}, 434 {"fd", Value{f32, "1", nil}}, 435 {"ie", Value{i8, "1", nil}}, 436 {"fe", Value{f32, "1", nil}}, 437 {"f", Value{i8, "1", nil}}, 438 {"ff", Value{f32, "1", nil}}, 439 {"ig", Value{i8, "1", nil}}, 440 {"fg", Value{f32, "1", nil}}, 441 {"ih", Value{i8, "1", nil}}, 442 {"fh", Value{f32, "1", nil}}, 443 {"fi", Value{f32, "1", nil}}, 444 {"fj", Value{f32, "1", nil}}, 445 }, 446 }, 447 { 448 Name: "funcall1", 449 Ret: Value{i32, `sprintf(s, "test from C: %d %1.1f %s\n", a, b, c)`, "27"}, 450 Arguments: Arguments{ 451 {"s", Value{byteSlice, nil, "buf"}}, 452 {"a", Value{i8, "-1", nil}}, 453 {"b", Value{f32, "1.5", nil}}, 454 {"c", Value{byteSlice, nil, `[]byte("gotest\000")`}}, 455 }, 456 CgoPre: "buf := make([]byte, 1024)", 457 CgoPost: ` if string(buf[:ret]) != "test from C: -1 1.5 gotest\n" { 458 t.Fatalf("Expected \"test from C: -1 1.5 gotest\n\", but got \"%s\"", string(buf[:ret])) 459 }`, 460 NOCGOPre: "buf := make([]byte, 1024)", 461 NOCGOPost: ` if string(buf[:ret]) != "test from C: -1 1.5 gotest\n" { 462 t.Fatalf("Expected \"test from C: -1 1.5 gotest\n\", but got \"%s\"", string(buf[:ret])) 463 }`, 464 Multitest: true, 465 }, 466 } 467 if err := ccode.Execute(cfile, tests); err != nil { 468 log.Fatal(err) 469 } 470 cfile.Close() 471 if err := bridge.Execute(bridgefile, tests); err != nil { 472 log.Fatal(err) 473 } 474 bridgefile.Close() 475 if err := testingCgo.Execute(testingCgofile, tests); err != nil { 476 log.Fatal(err) 477 } 478 testingCgofile.Close() 479 if err := testingNOCGO.Execute(testingNOCGOfile, tests); err != nil { 480 log.Fatal(err) 481 } 482 testingNOCGOfile.Close() 483 }