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