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  }