1// Copyright 2015 The Go 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// This program generates a test to verify that the standard arithmetic 6// operators properly handle some special cases. The test file should be 7// generated with a known working version of go. 8// launch with `go run arithBoundaryGen.go` a file called arithBoundary.go 9// will be written into the parent directory containing the tests 10 11package main 12 13import ( 14 "bytes" 15 "fmt" 16 "go/format" 17 "io/ioutil" 18 "log" 19 "text/template" 20) 21 22// used for interpolation in a text template 23type tmplData struct { 24 Name, Stype, Symbol string 25} 26 27// used to work around an issue with the mod symbol being 28// interpreted as part of a format string 29func (s tmplData) SymFirst() string { 30 return string(s.Symbol[0]) 31} 32 33// ucast casts an unsigned int to the size in s 34func ucast(i uint64, s sizedTestData) uint64 { 35 switch s.name { 36 case "uint32": 37 return uint64(uint32(i)) 38 case "uint16": 39 return uint64(uint16(i)) 40 case "uint8": 41 return uint64(uint8(i)) 42 } 43 return i 44} 45 46// icast casts a signed int to the size in s 47func icast(i int64, s sizedTestData) int64 { 48 switch s.name { 49 case "int32": 50 return int64(int32(i)) 51 case "int16": 52 return int64(int16(i)) 53 case "int8": 54 return int64(int8(i)) 55 } 56 return i 57} 58 59type sizedTestData struct { 60 name string 61 sn string 62 u []uint64 63 i []int64 64} 65 66// values to generate tests. these should include the smallest and largest values, along 67// with any other values that might cause issues. we generate n^2 tests for each size to 68// cover all cases. 69var szs = []sizedTestData{ 70 sizedTestData{name: "uint64", sn: "64", u: []uint64{0, 1, 4294967296, 0xffffFFFFffffFFFF}}, 71 sizedTestData{name: "int64", sn: "64", i: []int64{-0x8000000000000000, -0x7FFFFFFFFFFFFFFF, 72 -4294967296, -1, 0, 1, 4294967296, 0x7FFFFFFFFFFFFFFE, 0x7FFFFFFFFFFFFFFF}}, 73 74 sizedTestData{name: "uint32", sn: "32", u: []uint64{0, 1, 4294967295}}, 75 sizedTestData{name: "int32", sn: "32", i: []int64{-0x80000000, -0x7FFFFFFF, -1, 0, 76 1, 0x7FFFFFFF}}, 77 78 sizedTestData{name: "uint16", sn: "16", u: []uint64{0, 1, 65535}}, 79 sizedTestData{name: "int16", sn: "16", i: []int64{-32768, -32767, -1, 0, 1, 32766, 32767}}, 80 81 sizedTestData{name: "uint8", sn: "8", u: []uint64{0, 1, 255}}, 82 sizedTestData{name: "int8", sn: "8", i: []int64{-128, -127, -1, 0, 1, 126, 127}}, 83} 84 85type op struct { 86 name, symbol string 87} 88 89// ops that we will be generating tests for 90var ops = []op{op{"add", "+"}, op{"sub", "-"}, op{"div", "/"}, op{"mod", "%%"}, op{"mul", "*"}} 91 92func main() { 93 w := new(bytes.Buffer) 94 fmt.Fprintf(w, "// Code generated by gen/arithBoundaryGen.go. DO NOT EDIT.\n\n") 95 fmt.Fprintf(w, "package main;\n") 96 fmt.Fprintf(w, "import \"testing\"\n") 97 98 for _, sz := range []int{64, 32, 16, 8} { 99 fmt.Fprintf(w, "type utd%d struct {\n", sz) 100 fmt.Fprintf(w, " a,b uint%d\n", sz) 101 fmt.Fprintf(w, " add,sub,mul,div,mod uint%d\n", sz) 102 fmt.Fprintf(w, "}\n") 103 104 fmt.Fprintf(w, "type itd%d struct {\n", sz) 105 fmt.Fprintf(w, " a,b int%d\n", sz) 106 fmt.Fprintf(w, " add,sub,mul,div,mod int%d\n", sz) 107 fmt.Fprintf(w, "}\n") 108 } 109 110 // the function being tested 111 testFunc, err := template.New("testFunc").Parse( 112 `//go:noinline 113 func {{.Name}}_{{.Stype}}_ssa(a, b {{.Stype}}) {{.Stype}} { 114 return a {{.SymFirst}} b 115} 116`) 117 if err != nil { 118 panic(err) 119 } 120 121 // generate our functions to be tested 122 for _, s := range szs { 123 for _, o := range ops { 124 fd := tmplData{o.name, s.name, o.symbol} 125 err = testFunc.Execute(w, fd) 126 if err != nil { 127 panic(err) 128 } 129 } 130 } 131 132 // generate the test data 133 for _, s := range szs { 134 if len(s.u) > 0 { 135 fmt.Fprintf(w, "var %s_data []utd%s = []utd%s{", s.name, s.sn, s.sn) 136 for _, i := range s.u { 137 for _, j := range s.u { 138 fmt.Fprintf(w, "utd%s{a: %d, b: %d, add: %d, sub: %d, mul: %d", s.sn, i, j, ucast(i+j, s), ucast(i-j, s), ucast(i*j, s)) 139 if j != 0 { 140 fmt.Fprintf(w, ", div: %d, mod: %d", ucast(i/j, s), ucast(i%j, s)) 141 } 142 fmt.Fprint(w, "},\n") 143 } 144 } 145 fmt.Fprintf(w, "}\n") 146 } else { 147 // TODO: clean up this duplication 148 fmt.Fprintf(w, "var %s_data []itd%s = []itd%s{", s.name, s.sn, s.sn) 149 for _, i := range s.i { 150 for _, j := range s.i { 151 fmt.Fprintf(w, "itd%s{a: %d, b: %d, add: %d, sub: %d, mul: %d", s.sn, i, j, icast(i+j, s), icast(i-j, s), icast(i*j, s)) 152 if j != 0 { 153 fmt.Fprintf(w, ", div: %d, mod: %d", icast(i/j, s), icast(i%j, s)) 154 } 155 fmt.Fprint(w, "},\n") 156 } 157 } 158 fmt.Fprintf(w, "}\n") 159 } 160 } 161 162 fmt.Fprintf(w, "//TestArithmeticBoundary tests boundary results for arithmetic operations.\n") 163 fmt.Fprintf(w, "func TestArithmeticBoundary(t *testing.T) {\n\n") 164 165 verify, err := template.New("tst").Parse( 166 `if got := {{.Name}}_{{.Stype}}_ssa(v.a, v.b); got != v.{{.Name}} { 167 t.Errorf("{{.Name}}_{{.Stype}} %d{{.Symbol}}%d = %d, wanted %d\n",v.a,v.b,got,v.{{.Name}}) 168} 169`) 170 171 for _, s := range szs { 172 fmt.Fprintf(w, "for _, v := range %s_data {\n", s.name) 173 174 for _, o := range ops { 175 // avoid generating tests that divide by zero 176 if o.name == "div" || o.name == "mod" { 177 fmt.Fprint(w, "if v.b != 0 {") 178 } 179 180 err = verify.Execute(w, tmplData{o.name, s.name, o.symbol}) 181 182 if o.name == "div" || o.name == "mod" { 183 fmt.Fprint(w, "\n}\n") 184 } 185 186 if err != nil { 187 panic(err) 188 } 189 190 } 191 fmt.Fprint(w, " }\n") 192 } 193 194 fmt.Fprintf(w, "}\n") 195 196 // gofmt result 197 b := w.Bytes() 198 src, err := format.Source(b) 199 if err != nil { 200 fmt.Printf("%s\n", b) 201 panic(err) 202 } 203 204 // write to file 205 err = ioutil.WriteFile("../arithBoundary_test.go", src, 0666) 206 if err != nil { 207 log.Fatalf("can't write output: %v\n", err) 208 } 209} 210