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