github.com/jd-ly/tools@v0.5.7/go/ssa/interp/interp_test.go (about)

     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  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/jd-ly/tools/go/loader"
    31  	"github.com/jd-ly/tools/go/ssa"
    32  	"github.com/jd-ly/tools/go/ssa/interp"
    33  	"github.com/jd-ly/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.
    41  var 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/.
   109  var 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  
   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.")
   131  	}
   132  
   133  	t.Logf("Input: %s\n", input)
   134  
   135  	start := time.Now()
   136  
   137  	ctx := build.Default    // copy
   138  	ctx.GOROOT = "testdata" // fake goroot
   139  	ctx.GOOS = "linux"
   140  	ctx.GOARCH = "amd64"
   141  
   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)
   145  		return false
   146  	}
   147  
   148  	conf.Import("runtime")
   149  
   150  	// Print a helpful hint if we don't make it to the end.
   151  	var hint string
   152  	defer func() {
   153  		if hint != "" {
   154  			fmt.Println("FAIL")
   155  			fmt.Println(hint)
   156  		} else {
   157  			fmt.Println("PASS")
   158  		}
   159  
   160  		interp.CapturedOutput = nil
   161  	}()
   162  
   163  	hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build github.com/jd-ly/tools/cmd/ssadump && ./ssadump -test -build=CFP %s\n", input)
   164  
   165  	iprog, err := conf.Load()
   166  	if err != nil {
   167  		t.Errorf("conf.Load(%s) failed: %s", input, err)
   168  		return false
   169  	}
   170  
   171  	prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
   172  	prog.Build()
   173  
   174  	mainPkg := prog.Package(iprog.Created[0].Pkg)
   175  	if mainPkg == nil {
   176  		t.Fatalf("not a main package: %s", input)
   177  	}
   178  
   179  	interp.CapturedOutput = new(bytes.Buffer)
   180  
   181  	hint = fmt.Sprintf("To trace execution, run:\n%% go build github.com/jd-ly/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{})
   183  	if exitCode != 0 {
   184  		t.Fatalf("interpreting %s: exit code was %d", input, exitCode)
   185  	}
   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)
   189  	}
   190  
   191  	hint = "" // call off the hounds
   192  
   193  	if false {
   194  		t.Log(input, time.Since(start)) // test profiling
   195  	}
   196  
   197  	return true
   198  }
   199  
   200  func printFailures(failures []string) {
   201  	if failures != nil {
   202  		fmt.Println("The following tests failed:")
   203  		for _, f := range failures {
   204  			fmt.Printf("\t%s\n", f)
   205  		}
   206  	}
   207  }
   208  
   209  // TestTestdataFiles runs the interpreter on testdata/*.go.
   210  func TestTestdataFiles(t *testing.T) {
   211  	cwd, err := os.Getwd()
   212  	if err != nil {
   213  		log.Fatal(err)
   214  	}
   215  
   216  	var failures []string
   217  	for _, input := range testdataTests {
   218  		if !run(t, filepath.Join(cwd, "testdata", input)) {
   219  			failures = append(failures, input)
   220  		}
   221  	}
   222  	printFailures(failures)
   223  }
   224  
   225  // TestGorootTest runs the interpreter on $GOROOT/test/*.go.
   226  func TestGorootTest(t *testing.T) {
   227  	var failures []string
   228  
   229  	for _, input := range gorootTestTests {
   230  		if !run(t, filepath.Join(build.Default.GOROOT, "test", input)) {
   231  			failures = append(failures, input)
   232  		}
   233  	}
   234  	printFailures(failures)
   235  }