github.com/rgonomic/rgo@v0.2.2-0.20220708095818-4747f0905d6e/internal/rgo/testdata/testgen.go (about) 1 // Copyright ©2020 The rgonomic Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:generate go run ./testgen.go 6 7 // The testgen command constructs small single file packages with no-op, but 8 // buildable source for testing the internal/pkg code. 9 package main 10 11 import ( 12 "bytes" 13 "fmt" 14 "go/format" 15 "go/types" 16 "log" 17 "os" 18 "os/exec" 19 "path/filepath" 20 "text/template" 21 ) 22 23 var crafted = []pkg{ 24 { 25 Name: "mixed", 26 Path: "github.com/rgonomic/rgo/internal/rgo/testdata", 27 Types: []string{"T int", "S1 string"}, 28 Funcs: []fn{ 29 {In: []string{"int"}, Out: []string{"int", "int"}, Named: false}, 30 {In: []string{"int"}, Out: []string{"float64", "int"}, Named: true}, 31 {In: []string{"int"}}, 32 {In: []string{"T", "S1"}, Out: []string{"S1"}}, 33 }, 34 }, 35 { 36 Name: "slice_of_slices", 37 Path: "github.com/rgonomic/rgo/internal/rgo/testdata", 38 Funcs: []fn{ 39 {In: []string{"[][]float64"}, Out: []string{"[][]float64"}, Named: false}, 40 }, 41 }, 42 { 43 Name: "map_of_slices", 44 Path: "github.com/rgonomic/rgo/internal/rgo/testdata", 45 Funcs: []fn{ 46 {In: []string{"map[string][]float64"}, Out: []string{"map[string][]float64"}, Named: false}, 47 }, 48 }, 49 } 50 51 type pkg struct { 52 Name string 53 UID int 54 Path string 55 Types []string 56 Funcs []fn 57 } 58 59 type fn struct { 60 pkg *pkg 61 In []string // Input parameter types. 62 Out []string // Output parameter types. 63 Named bool // Whether the output parameter types are named. 64 } 65 66 func builtins() []pkg { 67 skip := map[types.BasicKind]bool{ 68 types.Uintptr: true, 69 types.Uint64: true, 70 types.Int64: true, 71 } 72 var pkgs []pkg 73 for t := types.Bool; t <= types.String; t++ { 74 if skip[t] { 75 continue 76 } 77 pkgs = addTypeTest(pkgs, types.Typ[t]) 78 } 79 pkgs = addTypeTest(pkgs, types.Universe.Lookup("byte").Type().(*types.Basic)) 80 pkgs = addTypeTest(pkgs, types.Universe.Lookup("rune").Type().(*types.Basic)) 81 82 return pkgs 83 } 84 85 func addTypeTest(dst []pkg, typ *types.Basic) []pkg { 86 name := typ.String() 87 88 // Generate scalar value test functions. 89 dst = append(dst, 90 pkg{Name: fmt.Sprintf("%s_in", name), Funcs: []fn{ 91 {In: []string{name}}}}, 92 pkg{Name: fmt.Sprintf("%s_out", name), Funcs: []fn{ 93 {Out: []string{name}}}}, 94 pkg{Name: fmt.Sprintf("%s_out_named", name), Funcs: []fn{ 95 {Out: []string{name}, Named: true}}}, 96 ) 97 98 dst = append(dst, 99 pkg{Name: fmt.Sprintf("%s_slice_in", name), Funcs: []fn{ 100 {In: []string{"[]" + name}}}}, 101 pkg{Name: fmt.Sprintf("%s_slice_out", name), Funcs: []fn{ 102 {Out: []string{"[]" + name}}}}, 103 pkg{Name: fmt.Sprintf("%s_slice_out_named", name), Funcs: []fn{ 104 {Out: []string{"[]" + name}, Named: true}}}, 105 ) 106 107 // Generate array value test functions. 108 dst = append(dst, 109 pkg{Name: fmt.Sprintf("%s_array_in", name), Funcs: []fn{ 110 {In: []string{"[4]" + name}}}}, 111 pkg{Name: fmt.Sprintf("%s_array_out", name), Funcs: []fn{ 112 {Out: []string{"[4]" + name}}}}, 113 pkg{Name: fmt.Sprintf("%s_array_out_named", name), Funcs: []fn{ 114 {Out: []string{"[4]" + name}, Named: true}}}, 115 ) 116 117 // Generate struct value test functions. 118 st := fmt.Sprintf(`struct{F1 %[1]s; F2 %[1]s "rgo:\"Rname\""}`, name) 119 dst = append(dst, 120 pkg{Name: fmt.Sprintf("struct_%s_in", name), Funcs: []fn{ 121 {In: []string{st}}}}, 122 pkg{Name: fmt.Sprintf("struct_%s_out", name), Funcs: []fn{ 123 {Out: []string{st}}}}, 124 pkg{Name: fmt.Sprintf("struct_%s_out_named", name), Funcs: []fn{ 125 {Out: []string{st}, Named: true}}}, 126 ) 127 128 // Generate map[string]T value test functions. 129 mt := fmt.Sprintf("map[string]%s", name) 130 dst = append(dst, 131 pkg{Name: fmt.Sprintf("string_%s_map_in", name), Funcs: []fn{ 132 {In: []string{mt}}}}, 133 pkg{Name: fmt.Sprintf("string_%s_map_out", name), Funcs: []fn{ 134 {Out: []string{mt}}}}, 135 pkg{Name: fmt.Sprintf("string_%s_map_out_named", name), Funcs: []fn{ 136 {Out: []string{mt}, Named: true}}}, 137 ) 138 139 return dst 140 } 141 142 func main() { 143 suff := make(map[string]int) 144 for _, cases := range [][]pkg{builtins(), crafted} { 145 for _, c := range cases { 146 c.UID = suff[c.Name] 147 suff[c.Name]++ 148 for i := range c.Funcs { 149 c.Funcs[i].pkg = &c 150 } 151 152 pkg := fmt.Sprintf("%s_%d", c.Name, c.UID) 153 err := os.Mkdir(pkg, 0o755) 154 if err != nil && !os.IsExist(err) { 155 log.Fatalf("failed to create testing package dir: %v", err) 156 } 157 f, err := os.Create(filepath.Join(pkg, c.Name+".go")) 158 if err != nil { 159 log.Fatalf("failed to create testing source file: %v", err) 160 } 161 162 var buf bytes.Buffer 163 err = src.Execute(&buf, c) 164 if err != nil { 165 log.Fatalf("failed to execute template: %v", err) 166 } 167 b, err := format.Source(buf.Bytes()) 168 if err != nil { 169 log.Fatalf("failed to format source: %v", err) 170 } 171 _, err = f.Write(b) 172 if err != nil { 173 log.Fatalf("failed to write source: %v", err) 174 } 175 176 err = f.Close() 177 if err != nil { 178 log.Fatalf("failed to close testing source file: %v", err) 179 } 180 181 err = os.Remove(filepath.Join(pkg, "go.mod")) 182 if err != nil && !os.IsNotExist(err) { 183 log.Fatalf("failed to remove go.mod file: %v", err) 184 } 185 cmd := exec.Command("go", "mod", "init", pkg) 186 cmd.Dir = filepath.Join(".", pkg) 187 err = cmd.Run() 188 if err != nil { 189 log.Fatalf("failed create go.mod file: %v", err) 190 } 191 } 192 } 193 } 194 195 var src = template.Must(template.New("Go source").Parse(`// Code generated by "go generate github.com/rgonomic/rgo/internal/pkg/testdata"; DO NOT EDIT. 196 197 package {{.Name}}_{{.UID}} 198 {{if .Types}} 199 type ( 200 {{- range $i, $t := .Types}} 201 {{$t}}{{end}} 202 ){{end}} 203 {{- range $i, $fn := .Funcs}} 204 205 // Test{{$i}} does things with {{$fn.In}} and returns {{$fn.Out}}. 206 func Test{{$i}}({{if $fn.In}}{{range $j, $p := $fn.In -}} 207 {{- if ne $j 0}}, {{end}}par{{$j}} {{$p -}} 208 {{- end}}{{end}}){{if $fn.Out}} ({{range $j, $p := $fn.Out -}} 209 {{- if ne $j 0}}, {{end}}{{if $fn.Named}}res{{$j}} {{end}}{{$p}}{{end}}){{end}} { {{if not $fn.Named}}{{range $j, $p := $fn.Out}} 210 var res{{$j}} {{$p}}{{end}}{{end}} 211 {{if $fn.Out}} return {{range $j, $p := $fn.Out -}} 212 {{- if ne $j 0}}, {{end}}res{{$j}}{{end}} 213 {{end}}}{{end}} 214 `))