...

Source file src/golang.org/x/tools/go/ssa/interp/interp_test.go

Documentation: golang.org/x/tools/go/ssa/interp

     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  
     5  package 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  
    18  import (
    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  	"unsafe"
    31  
    32  	"golang.org/x/tools/go/loader"
    33  	"golang.org/x/tools/go/ssa"
    34  	"golang.org/x/tools/go/ssa/interp"
    35  	"golang.org/x/tools/go/ssa/ssautil"
    36  	"golang.org/x/tools/internal/testenv"
    37  )
    38  
    39  // Each line contains a space-separated list of $GOROOT/test/
    40  // filenames comprising the main package of a program.
    41  // They are ordered quickest-first, roughly.
    42  //
    43  // If a test in this list fails spuriously, remove it.
    44  var gorootTestTests = []string{
    45  	"235.go",
    46  	"alias1.go",
    47  	"func5.go",
    48  	"func6.go",
    49  	"func7.go",
    50  	"func8.go",
    51  	"helloworld.go",
    52  	"varinit.go",
    53  	"escape3.go",
    54  	"initcomma.go",
    55  	"cmp.go",
    56  	"compos.go",
    57  	"turing.go",
    58  	"indirect.go",
    59  	"complit.go",
    60  	"for.go",
    61  	"struct0.go",
    62  	"intcvt.go",
    63  	"printbig.go",
    64  	"deferprint.go",
    65  	"escape.go",
    66  	"range.go",
    67  	"const4.go",
    68  	"float_lit.go",
    69  	"bigalg.go",
    70  	"decl.go",
    71  	"if.go",
    72  	"named.go",
    73  	"bigmap.go",
    74  	"func.go",
    75  	"reorder2.go",
    76  	"gc.go",
    77  	"simassign.go",
    78  	"iota.go",
    79  	"nilptr2.go",
    80  	"utf.go",
    81  	"method.go",
    82  	"char_lit.go",
    83  	"env.go",
    84  	"int_lit.go",
    85  	"string_lit.go",
    86  	"defer.go",
    87  	"typeswitch.go",
    88  	"stringrange.go",
    89  	"reorder.go",
    90  	"method3.go",
    91  	"literal.go",
    92  	"nul1.go", // doesn't actually assert anything (errorcheckoutput)
    93  	"zerodivide.go",
    94  	"convert.go",
    95  	"convT2X.go",
    96  	"switch.go",
    97  	"ddd.go",
    98  	"blank.go", // partly disabled
    99  	"closedchan.go",
   100  	"divide.go",
   101  	"rename.go",
   102  	"nil.go",
   103  	"recover1.go",
   104  	"recover2.go",
   105  	"recover3.go",
   106  	"typeswitch1.go",
   107  	"floatcmp.go",
   108  	"crlf.go", // doesn't actually assert anything (runoutput)
   109  }
   110  
   111  // These are files in go.tools/go/ssa/interp/testdata/.
   112  var testdataTests = []string{
   113  	"boundmeth.go",
   114  	"complit.go",
   115  	"convert.go",
   116  	"coverage.go",
   117  	"deepequal.go",
   118  	"defer.go",
   119  	"fieldprom.go",
   120  	"forvarlifetime_old.go",
   121  	"ifaceconv.go",
   122  	"ifaceprom.go",
   123  	"initorder.go",
   124  	"methprom.go",
   125  	"mrvchain.go",
   126  	"range.go",
   127  	"recover.go",
   128  	"reflect.go",
   129  	"slice2arrayptr.go",
   130  	"static.go",
   131  	"width32.go",
   132  	"rangevarlifetime_old.go",
   133  	"fixedbugs/issue52342.go",
   134  	"fixedbugs/issue55115.go",
   135  	"fixedbugs/issue52835.go",
   136  	"fixedbugs/issue55086.go",
   137  	"typeassert.go",
   138  	"zeros.go",
   139  }
   140  
   141  func init() {
   142  	// GOROOT/test used to assume that GOOS and GOARCH were explicitly set in the
   143  	// environment, so do that here for TestGorootTest.
   144  	os.Setenv("GOOS", runtime.GOOS)
   145  	os.Setenv("GOARCH", runtime.GOARCH)
   146  }
   147  
   148  func run(t *testing.T, input string, goroot string) {
   149  	// The recover2 test case is broken on Go 1.14+. See golang/go#34089.
   150  	// TODO(matloob): Fix this.
   151  	if filepath.Base(input) == "recover2.go" {
   152  		t.Skip("The recover2.go test is broken in go1.14+. See golang.org/issue/34089.")
   153  	}
   154  
   155  	t.Logf("Input: %s\n", input)
   156  
   157  	start := time.Now()
   158  
   159  	ctx := build.Default // copy
   160  	ctx.GOROOT = goroot
   161  	ctx.GOOS = runtime.GOOS
   162  	ctx.GOARCH = runtime.GOARCH
   163  	if filepath.Base(input) == "width32.go" && unsafe.Sizeof(int(0)) > 4 {
   164  		t.Skipf("skipping: width32.go checks behavior for a 32-bit int")
   165  	}
   166  
   167  	gover := ""
   168  	if p := testenv.Go1Point(); p > 0 {
   169  		gover = fmt.Sprintf("go1.%d", p)
   170  	}
   171  
   172  	conf := loader.Config{Build: &ctx, TypeChecker: types.Config{GoVersion: gover}}
   173  	if _, err := conf.FromArgs([]string{input}, true); err != nil {
   174  		t.Fatalf("FromArgs(%s) failed: %s", input, err)
   175  	}
   176  
   177  	conf.Import("runtime")
   178  
   179  	// Print a helpful hint if we don't make it to the end.
   180  	var hint string
   181  	defer func() {
   182  		if hint != "" {
   183  			fmt.Println("FAIL")
   184  			fmt.Println(hint)
   185  		} else {
   186  			fmt.Println("PASS")
   187  		}
   188  
   189  		interp.CapturedOutput = nil
   190  	}()
   191  
   192  	hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -test -build=CFP %s\n", input)
   193  
   194  	iprog, err := conf.Load()
   195  	if err != nil {
   196  		t.Fatalf("conf.Load(%s) failed: %s", input, err)
   197  	}
   198  
   199  	bmode := ssa.InstantiateGenerics | ssa.SanityCheckFunctions
   200  	// bmode |= ssa.PrintFunctions // enable for debugging
   201  	prog := ssautil.CreateProgram(iprog, bmode)
   202  	prog.Build()
   203  
   204  	mainPkg := prog.Package(iprog.Created[0].Pkg)
   205  	if mainPkg == nil {
   206  		t.Fatalf("not a main package: %s", input)
   207  	}
   208  
   209  	interp.CapturedOutput = new(bytes.Buffer)
   210  
   211  	sizes := types.SizesFor("gc", ctx.GOARCH)
   212  	if sizes.Sizeof(types.Typ[types.Int]) < 4 {
   213  		panic("bogus SizesFor")
   214  	}
   215  	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)
   216  	var imode interp.Mode // default mode
   217  	// imode |= interp.DisableRecover // enable for debugging
   218  	// imode |= interp.EnableTracing // enable for debugging
   219  	exitCode := interp.Interpret(mainPkg, imode, sizes, input, []string{})
   220  	if exitCode != 0 {
   221  		t.Fatalf("interpreting %s: exit code was %d", input, exitCode)
   222  	}
   223  	// $GOROOT/test tests use this convention:
   224  	if strings.Contains(interp.CapturedOutput.String(), "BUG") {
   225  		t.Fatalf("interpreting %s: exited zero but output contained 'BUG'", input)
   226  	}
   227  
   228  	hint = "" // call off the hounds
   229  
   230  	if false {
   231  		t.Log(input, time.Since(start)) // test profiling
   232  	}
   233  }
   234  
   235  // makeGoroot copies testdata/src into the "src" directory of a temporary
   236  // location to mimic GOROOT/src, and adds a file "runtime/consts.go" containing
   237  // declarations for GOOS and GOARCH that match the GOOS and GOARCH of this test.
   238  //
   239  // It returns the directory that should be used for GOROOT.
   240  func makeGoroot(t *testing.T) string {
   241  	goroot := t.TempDir()
   242  	src := filepath.Join(goroot, "src")
   243  
   244  	err := filepath.Walk("testdata/src", func(path string, info os.FileInfo, err error) error {
   245  		if err != nil {
   246  			return err
   247  		}
   248  
   249  		rel, err := filepath.Rel("testdata/src", path)
   250  		if err != nil {
   251  			return err
   252  		}
   253  		targ := filepath.Join(src, rel)
   254  
   255  		if info.IsDir() {
   256  			return os.Mkdir(targ, info.Mode().Perm()|0700)
   257  		}
   258  
   259  		b, err := os.ReadFile(path)
   260  		if err != nil {
   261  			return err
   262  		}
   263  		return os.WriteFile(targ, b, info.Mode().Perm())
   264  	})
   265  	if err != nil {
   266  		t.Fatal(err)
   267  	}
   268  
   269  	constsGo := fmt.Sprintf(`package runtime
   270  const GOOS = %q
   271  const GOARCH = %q
   272  `, runtime.GOOS, runtime.GOARCH)
   273  	err = os.WriteFile(filepath.Join(src, "runtime/consts.go"), []byte(constsGo), 0644)
   274  	if err != nil {
   275  		t.Fatal(err)
   276  	}
   277  
   278  	return goroot
   279  }
   280  
   281  // TestTestdataFiles runs the interpreter on testdata/*.go.
   282  func TestTestdataFiles(t *testing.T) {
   283  	goroot := makeGoroot(t)
   284  	cwd, err := os.Getwd()
   285  	if err != nil {
   286  		log.Fatal(err)
   287  	}
   288  	for _, input := range testdataTests {
   289  		t.Run(input, func(t *testing.T) {
   290  			run(t, filepath.Join(cwd, "testdata", input), goroot)
   291  		})
   292  	}
   293  }
   294  
   295  // TestGorootTest runs the interpreter on $GOROOT/test/*.go.
   296  func TestGorootTest(t *testing.T) {
   297  	goroot := makeGoroot(t)
   298  	for _, input := range gorootTestTests {
   299  		t.Run(input, func(t *testing.T) {
   300  			run(t, filepath.Join(build.Default.GOROOT, "test", input), goroot)
   301  		})
   302  	}
   303  }
   304  
   305  // TestTypeparamTest runs the interpreter on runnable examples
   306  // in $GOROOT/test/typeparam/*.go.
   307  
   308  func TestTypeparamTest(t *testing.T) {
   309  	goroot := makeGoroot(t)
   310  
   311  	// Skip known failures for the given reason.
   312  	// TODO(taking): Address these.
   313  	skip := map[string]string{
   314  		"chans.go":      "interp tests do not support runtime.SetFinalizer",
   315  		"issue23536.go": "unknown reason",
   316  		"issue48042.go": "interp tests do not handle reflect.Value.SetInt",
   317  		"issue47716.go": "interp tests do not handle unsafe.Sizeof",
   318  		"issue50419.go": "interp tests do not handle dispatch to String() correctly",
   319  		"issue51733.go": "interp does not handle unsafe casts",
   320  		"ordered.go":    "math.NaN() comparisons not being handled correctly",
   321  		"orderedmap.go": "interp tests do not support runtime.SetFinalizer",
   322  		"stringer.go":   "unknown reason",
   323  		"issue48317.go": "interp tests do not support encoding/json",
   324  		"issue48318.go": "interp tests do not support encoding/json",
   325  		"issue58513.go": "interp tests do not support runtime.Caller",
   326  	}
   327  	// Collect all of the .go files in dir that are runnable.
   328  	dir := filepath.Join(build.Default.GOROOT, "test", "typeparam")
   329  	list, err := os.ReadDir(dir)
   330  	if err != nil {
   331  		t.Fatal(err)
   332  	}
   333  	for _, entry := range list {
   334  		if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
   335  			continue // Consider standalone go files.
   336  		}
   337  		t.Run(entry.Name(), func(t *testing.T) {
   338  			input := filepath.Join(dir, entry.Name())
   339  			src, err := os.ReadFile(input)
   340  			if err != nil {
   341  				t.Fatal(err)
   342  			}
   343  
   344  			// Only build test files that can be compiled, or compiled and run.
   345  			if !bytes.HasPrefix(src, []byte("// run")) || bytes.HasPrefix(src, []byte("// rundir")) {
   346  				t.Logf("Not a `// run` file: %s", entry.Name())
   347  				return
   348  			}
   349  
   350  			if reason := skip[entry.Name()]; reason != "" {
   351  				t.Skipf("skipping: %s", reason)
   352  			}
   353  
   354  			run(t, input, goroot)
   355  		})
   356  	}
   357  }
   358  

View as plain text