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.
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.
15 // We no longer attempt to interpret any real standard packages such as
16 // fmt or testing, as it proved too fragile.
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"
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.
40 // If a test in this list fails spuriously, remove it.
41 var gorootTestTests = []string{
89 "nul1.go", // doesn't actually assert anything (errorcheckoutput)
95 "blank.go", // partly disabled
105 "crlf.go", // doesn't actually assert anything (runoutput)
108 // These are files in go.tools/go/ssa/interp/testdata/.
109 var testdataTests = []string{
126 func run(t *testing.T, input string) bool {
127 // The recover2 test case is broken on Go 1.14+. See golang/go#34089.
128 // TODO(matloob): Fix this.
129 if filepath.Base(input) == "recover2.go" {
130 t.Skip("The recover2.go test is broken in go1.14+. See golang.org/issue/34089.")
133 t.Logf("Input: %s\n", input)
137 ctx := build.Default // copy
138 ctx.GOROOT = "testdata" // fake goroot
142 conf := loader.Config{Build: &ctx}
143 if _, err := conf.FromArgs([]string{input}, true); err != nil {
144 t.Errorf("FromArgs(%s) failed: %s", input, err)
148 conf.Import("runtime")
150 // Print a helpful hint if we don't make it to the end.
160 interp.CapturedOutput = nil
163 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 iprog, err := conf.Load()
167 t.Errorf("conf.Load(%s) failed: %s", input, err)
171 prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
174 mainPkg := prog.Package(iprog.Created[0].Pkg)
176 t.Fatalf("not a main package: %s", input)
179 interp.CapturedOutput = new(bytes.Buffer)
181 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)
182 exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{WordSize: 8, MaxAlign: 8}, input, []string{})
184 t.Fatalf("interpreting %s: exit code was %d", input, exitCode)
186 // $GOROOT/test tests use this convention:
187 if strings.Contains(interp.CapturedOutput.String(), "BUG") {
188 t.Fatalf("interpreting %s: exited zero but output contained 'BUG'", input)
191 hint = "" // call off the hounds
194 t.Log(input, time.Since(start)) // test profiling
200 func printFailures(failures []string) {
202 fmt.Println("The following tests failed:")
203 for _, f := range failures {
204 fmt.Printf("\t%s\n", f)
209 // TestTestdataFiles runs the interpreter on testdata/*.go.
210 func TestTestdataFiles(t *testing.T) {
211 cwd, err := os.Getwd()
216 var failures []string
217 for _, input := range testdataTests {
218 if !run(t, filepath.Join(cwd, "testdata", input)) {
219 failures = append(failures, input)
222 printFailures(failures)
225 // TestGorootTest runs the interpreter on $GOROOT/test/*.go.
226 func TestGorootTest(t *testing.T) {
227 var failures []string
229 for _, input := range gorootTestTests {
230 if !run(t, filepath.Join(build.Default.GOROOT, "test", input)) {
231 failures = append(failures, input)
234 printFailures(failures)