github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/ssa/interp/interp_test.go (about) 1 // Copyright 2013 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 package interp_test 6 7 // This test runs the SSA interpreter over sample Go programs. 8 // Because the interpreter requires intrinsics for assembly 9 // functions and many low-level runtime routines, it is inherently 10 // not robust to evolutionary change in the standard library. 11 // Therefore the test cases are restricted to programs that 12 // use a fake standard library in testdata/src containing a tiny 13 // subset of simple functions useful for writing assertions. 14 // 15 // We no longer attempt to interpret any real standard packages such as 16 // fmt or testing, as it proved too fragile. 17 18 import ( 19 "bytes" 20 "fmt" 21 "go/build" 22 "go/types" 23 "log" 24 "os" 25 "path/filepath" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/powerman/golang-tools/go/loader" 31 "github.com/powerman/golang-tools/go/ssa" 32 "github.com/powerman/golang-tools/go/ssa/interp" 33 "github.com/powerman/golang-tools/go/ssa/ssautil" 34 ) 35 36 // Each line contains a space-separated list of $GOROOT/test/ 37 // filenames comprising the main package of a program. 38 // They are ordered quickest-first, roughly. 39 // 40 // If a test in this list fails spuriously, remove it. 41 var gorootTestTests = []string{ 42 "235.go", 43 "alias1.go", 44 "func5.go", 45 "func6.go", 46 "func7.go", 47 "func8.go", 48 "helloworld.go", 49 "varinit.go", 50 "escape3.go", 51 "initcomma.go", 52 "cmp.go", 53 "compos.go", 54 "turing.go", 55 "indirect.go", 56 "complit.go", 57 "for.go", 58 "struct0.go", 59 "intcvt.go", 60 "printbig.go", 61 "deferprint.go", 62 "escape.go", 63 "range.go", 64 "const4.go", 65 "float_lit.go", 66 "bigalg.go", 67 "decl.go", 68 "if.go", 69 "named.go", 70 "bigmap.go", 71 "func.go", 72 "reorder2.go", 73 "gc.go", 74 "simassign.go", 75 "iota.go", 76 "nilptr2.go", 77 "utf.go", 78 "method.go", 79 "char_lit.go", 80 "env.go", 81 "int_lit.go", 82 "string_lit.go", 83 "defer.go", 84 "typeswitch.go", 85 "stringrange.go", 86 "reorder.go", 87 "method3.go", 88 "literal.go", 89 "nul1.go", // doesn't actually assert anything (errorcheckoutput) 90 "zerodivide.go", 91 "convert.go", 92 "convT2X.go", 93 "switch.go", 94 "ddd.go", 95 "blank.go", // partly disabled 96 "closedchan.go", 97 "divide.go", 98 "rename.go", 99 "nil.go", 100 "recover1.go", 101 "recover2.go", 102 "recover3.go", 103 "typeswitch1.go", 104 "floatcmp.go", 105 "crlf.go", // doesn't actually assert anything (runoutput) 106 } 107 108 // These are files in go.tools/go/ssa/interp/testdata/. 109 var testdataTests = []string{ 110 "boundmeth.go", 111 "complit.go", 112 "convert.go", 113 "coverage.go", 114 "deepequal.go", 115 "defer.go", 116 "fieldprom.go", 117 "ifaceconv.go", 118 "ifaceprom.go", 119 "initorder.go", 120 "methprom.go", 121 "mrvchain.go", 122 "range.go", 123 "recover.go", 124 "reflect.go", 125 "static.go", 126 "width32.go", 127 } 128 129 // Specific GOARCH to use for a test case in go.tools/go/ssa/interp/testdata/. 130 // Defaults to amd64 otherwise. 131 var testdataArchs = map[string]string{ 132 "width32.go": "386", 133 } 134 135 func run(t *testing.T, input string) bool { 136 // The recover2 test case is broken on Go 1.14+. See golang/go#34089. 137 // TODO(matloob): Fix this. 138 if filepath.Base(input) == "recover2.go" { 139 t.Skip("The recover2.go test is broken in go1.14+. See golang.org/issue/34089.") 140 } 141 142 t.Logf("Input: %s\n", input) 143 144 start := time.Now() 145 146 ctx := build.Default // copy 147 ctx.GOROOT = "testdata" // fake goroot 148 ctx.GOOS = "linux" 149 ctx.GOARCH = "amd64" 150 if arch, ok := testdataArchs[filepath.Base(input)]; ok { 151 ctx.GOARCH = arch 152 } 153 154 conf := loader.Config{Build: &ctx} 155 if _, err := conf.FromArgs([]string{input}, true); err != nil { 156 t.Errorf("FromArgs(%s) failed: %s", input, err) 157 return false 158 } 159 160 conf.Import("runtime") 161 162 // Print a helpful hint if we don't make it to the end. 163 var hint string 164 defer func() { 165 if hint != "" { 166 fmt.Println("FAIL") 167 fmt.Println(hint) 168 } else { 169 fmt.Println("PASS") 170 } 171 172 interp.CapturedOutput = nil 173 }() 174 175 hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build github.com/powerman/golang-tools/cmd/ssadump && ./ssadump -test -build=CFP %s\n", input) 176 177 iprog, err := conf.Load() 178 if err != nil { 179 t.Errorf("conf.Load(%s) failed: %s", input, err) 180 return false 181 } 182 183 prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions) 184 prog.Build() 185 186 mainPkg := prog.Package(iprog.Created[0].Pkg) 187 if mainPkg == nil { 188 t.Fatalf("not a main package: %s", input) 189 } 190 191 interp.CapturedOutput = new(bytes.Buffer) 192 193 sizes := types.SizesFor("gc", ctx.GOARCH) 194 hint = fmt.Sprintf("To trace execution, run:\n%% go build github.com/powerman/golang-tools/cmd/ssadump && ./ssadump -build=C -test -run --interp=T %s\n", input) 195 exitCode := interp.Interpret(mainPkg, 0, sizes, input, []string{}) 196 if exitCode != 0 { 197 t.Fatalf("interpreting %s: exit code was %d", input, exitCode) 198 } 199 // $GOROOT/test tests use this convention: 200 if strings.Contains(interp.CapturedOutput.String(), "BUG") { 201 t.Fatalf("interpreting %s: exited zero but output contained 'BUG'", input) 202 } 203 204 hint = "" // call off the hounds 205 206 if false { 207 t.Log(input, time.Since(start)) // test profiling 208 } 209 210 return true 211 } 212 213 func printFailures(failures []string) { 214 if failures != nil { 215 fmt.Println("The following tests failed:") 216 for _, f := range failures { 217 fmt.Printf("\t%s\n", f) 218 } 219 } 220 } 221 222 // TestTestdataFiles runs the interpreter on testdata/*.go. 223 func TestTestdataFiles(t *testing.T) { 224 cwd, err := os.Getwd() 225 if err != nil { 226 log.Fatal(err) 227 } 228 229 var failures []string 230 for _, input := range testdataTests { 231 if !run(t, filepath.Join(cwd, "testdata", input)) { 232 failures = append(failures, input) 233 } 234 } 235 printFailures(failures) 236 } 237 238 // TestGorootTest runs the interpreter on $GOROOT/test/*.go. 239 func TestGorootTest(t *testing.T) { 240 var failures []string 241 242 for _, input := range gorootTestTests { 243 if !run(t, filepath.Join(build.Default.GOROOT, "test", input)) { 244 failures = append(failures, input) 245 } 246 } 247 printFailures(failures) 248 }