blob: 28ebf5f8054fb5dfa31bfa313bc3065511b18d73 [file] [log] [blame]
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package interp_test
// This test runs the SSA interpreter over sample Go programs.
// Because the interpreter requires intrinsics for assembly
// functions and many low-level runtime routines, it is inherently
// not robust to evolutionary change in the standard library.
// Therefore the test cases are restricted to programs that
// use a fake standard library in testdata/src containing a tiny
// subset of simple functions useful for writing assertions.
// We no longer attempt to interpret any real standard packages such as
// fmt or testing, as it proved too fragile.
import (
// Each line contains a space-separated list of $GOROOT/test/
// filenames comprising the main package of a program.
// They are ordered quickest-first, roughly.
// If a test in this list fails spuriously, remove it.
var gorootTestTests = []string{
"nul1.go", // doesn't actually assert anything (errorcheckoutput)
"blank.go", // partly disabled
"crlf.go", // doesn't actually assert anything (runoutput)
// These are files in
var testdataTests = []string{
func run(t *testing.T, input string) bool {
// The recover2 test case is broken on Go 1.14+. See golang/go#34089.
// TODO(matloob): Fix this.
if filepath.Base(input) == "recover2.go" {
t.Skip("The recover2.go test is broken in go1.14+. See")
t.Logf("Input: %s\n", input)
start := time.Now()
ctx := build.Default // copy
ctx.GOROOT = "testdata" // fake goroot
ctx.GOOS = "linux"
ctx.GOARCH = "amd64"
conf := loader.Config{Build: &ctx}
if _, err := conf.FromArgs([]string{input}, true); err != nil {
t.Errorf("FromArgs(%s) failed: %s", input, err)
return false
// Print a helpful hint if we don't make it to the end.
var hint string
defer func() {
if hint != "" {
} else {
interp.CapturedOutput = nil
hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build && ./ssadump -test -build=CFP %s\n", input)
iprog, err := conf.Load()
if err != nil {
t.Errorf("conf.Load(%s) failed: %s", input, err)
return false
prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
mainPkg := prog.Package(iprog.Created[0].Pkg)
if mainPkg == nil {
t.Fatalf("not a main package: %s", input)
interp.CapturedOutput = new(bytes.Buffer)
hint = fmt.Sprintf("To trace execution, run:\n%% go build && ./ssadump -build=C -test -run --interp=T %s\n", input)
exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{WordSize: 8, MaxAlign: 8}, input, []string{})
if exitCode != 0 {
t.Fatalf("interpreting %s: exit code was %d", input, exitCode)
// $GOROOT/test tests use this convention:
if strings.Contains(interp.CapturedOutput.String(), "BUG") {
t.Fatalf("interpreting %s: exited zero but output contained 'BUG'", input)
hint = "" // call off the hounds
if false {
t.Log(input, time.Since(start)) // test profiling
return true
func printFailures(failures []string) {
if failures != nil {
fmt.Println("The following tests failed:")
for _, f := range failures {
fmt.Printf("\t%s\n", f)
// TestTestdataFiles runs the interpreter on testdata/*.go.
func TestTestdataFiles(t *testing.T) {
cwd, err := os.Getwd()
if err != nil {
var failures []string
for _, input := range testdataTests {
if !run(t, filepath.Join(cwd, "testdata", input)) {
failures = append(failures, input)
// TestGorootTest runs the interpreter on $GOROOT/test/*.go.
func TestGorootTest(t *testing.T) {
var failures []string
for _, input := range gorootTestTests {
if !run(t, filepath.Join(build.Default.GOROOT, "test", input)) {
failures = append(failures, input)