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 "strings" 27 "testing" 28 "time" 29 30 "golang.org/x/tools/go/loader" 31 "golang.org/x/tools/go/ssa" 32 "golang.org/x/tools/go/ssa/interp" 33 "golang.org/x/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. 41var 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/. 109var testdataTests = []string{ 110 "boundmeth.go", 111 "complit.go", 112 "coverage.go", 113 "defer.go", 114 "fieldprom.go", 115 "ifaceconv.go", 116 "ifaceprom.go", 117 "initorder.go", 118 "methprom.go", 119 "mrvchain.go", 120 "range.go", 121 "recover.go", 122 "reflect.go", 123 "static.go", 124} 125 126func run(t *testing.T, input string) bool { 127 t.Logf("Input: %s\n", input) 128 129 start := time.Now() 130 131 ctx := build.Default // copy 132 ctx.GOROOT = "testdata" // fake goroot 133 ctx.GOOS = "linux" 134 ctx.GOARCH = "amd64" 135 136 conf := loader.Config{Build: &ctx} 137 if _, err := conf.FromArgs([]string{input}, true); err != nil { 138 t.Errorf("FromArgs(%s) failed: %s", input, err) 139 return false 140 } 141 142 conf.Import("runtime") 143 144 // Print a helpful hint if we don't make it to the end. 145 var hint string 146 defer func() { 147 if hint != "" { 148 fmt.Println("FAIL") 149 fmt.Println(hint) 150 } else { 151 fmt.Println("PASS") 152 } 153 154 interp.CapturedOutput = nil 155 }() 156 157 hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -test -build=CFP %s\n", input) 158 159 iprog, err := conf.Load() 160 if err != nil { 161 t.Errorf("conf.Load(%s) failed: %s", input, err) 162 return false 163 } 164 165 prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions) 166 prog.Build() 167 168 mainPkg := prog.Package(iprog.Created[0].Pkg) 169 if mainPkg == nil { 170 t.Fatalf("not a main package: %s", input) 171 } 172 173 interp.CapturedOutput = new(bytes.Buffer) 174 175 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) 176 exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{WordSize: 8, MaxAlign: 8}, input, []string{}) 177 if exitCode != 0 { 178 t.Fatalf("interpreting %s: exit code was %d", input, exitCode) 179 } 180 // $GOROOT/test tests use this convention: 181 if strings.Contains(interp.CapturedOutput.String(), "BUG") { 182 t.Fatalf("interpreting %s: exited zero but output contained 'BUG'", input) 183 } 184 185 hint = "" // call off the hounds 186 187 if false { 188 t.Log(input, time.Since(start)) // test profiling 189 } 190 191 return true 192} 193 194func printFailures(failures []string) { 195 if failures != nil { 196 fmt.Println("The following tests failed:") 197 for _, f := range failures { 198 fmt.Printf("\t%s\n", f) 199 } 200 } 201} 202 203// TestTestdataFiles runs the interpreter on testdata/*.go. 204func TestTestdataFiles(t *testing.T) { 205 cwd, err := os.Getwd() 206 if err != nil { 207 log.Fatal(err) 208 } 209 210 var failures []string 211 for _, input := range testdataTests { 212 if !run(t, filepath.Join(cwd, "testdata", input)) { 213 failures = append(failures, input) 214 } 215 } 216 printFailures(failures) 217} 218 219// TestGorootTest runs the interpreter on $GOROOT/test/*.go. 220func TestGorootTest(t *testing.T) { 221 var failures []string 222 223 for _, input := range gorootTestTests { 224 if !run(t, filepath.Join(build.Default.GOROOT, "test", input)) { 225 failures = append(failures, input) 226 } 227 } 228 printFailures(failures) 229} 230