github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/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/powerman/golang-tools/go/loader"
    31  	"github.com/powerman/golang-tools/go/ssa"
    32  	"github.com/powerman/golang-tools/go/ssa/interp"
    33  	"github.com/powerman/golang-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  	"convert.go",
   113  	"coverage.go",
   114  	"deepequal.go",
   115  	"defer.go",
   116  	"fieldprom.go",
   117  	"ifaceconv.go",
   118  	"ifaceprom.go",
   119  	"initorder.go",
   120  	"methprom.go",
   121  	"mrvchain.go",
   122  	"range.go",
   123  	"recover.go",
   124  	"reflect.go",
   125  	"static.go",
   126  	"width32.go",
   127  }
   128  
   129  // Specific GOARCH to use for a test case in go.tools/go/ssa/interp/testdata/.
   130  // Defaults to amd64 otherwise.
   131  var testdataArchs = map[string]string{
   132  	"width32.go": "386",
   133  }
   134  
   135  func run(t *testing.T, input string) bool {
   136  	// The recover2 test case is broken on Go 1.14+. See golang/go#34089.
   137  	// TODO(matloob): Fix this.
   138  	if filepath.Base(input) == "recover2.go" {
   139  		t.Skip("The recover2.go test is broken in go1.14+. See golang.org/issue/34089.")
   140  	}
   141  
   142  	t.Logf("Input: %s\n", input)
   143  
   144  	start := time.Now()
   145  
   146  	ctx := build.Default    // copy
   147  	ctx.GOROOT = "testdata" // fake goroot
   148  	ctx.GOOS = "linux"
   149  	ctx.GOARCH = "amd64"
   150  	if arch, ok := testdataArchs[filepath.Base(input)]; ok {
   151  		ctx.GOARCH = arch
   152  	}
   153  
   154  	conf := loader.Config{Build: &ctx}
   155  	if _, err := conf.FromArgs([]string{input}, true); err != nil {
   156  		t.Errorf("FromArgs(%s) failed: %s", input, err)
   157  		return false
   158  	}
   159  
   160  	conf.Import("runtime")
   161  
   162  	// Print a helpful hint if we don't make it to the end.
   163  	var hint string
   164  	defer func() {
   165  		if hint != "" {
   166  			fmt.Println("FAIL")
   167  			fmt.Println(hint)
   168  		} else {
   169  			fmt.Println("PASS")
   170  		}
   171  
   172  		interp.CapturedOutput = nil
   173  	}()
   174  
   175  	hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build github.com/powerman/golang-tools/cmd/ssadump && ./ssadump -test -build=CFP %s\n", input)
   176  
   177  	iprog, err := conf.Load()
   178  	if err != nil {
   179  		t.Errorf("conf.Load(%s) failed: %s", input, err)
   180  		return false
   181  	}
   182  
   183  	prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
   184  	prog.Build()
   185  
   186  	mainPkg := prog.Package(iprog.Created[0].Pkg)
   187  	if mainPkg == nil {
   188  		t.Fatalf("not a main package: %s", input)
   189  	}
   190  
   191  	interp.CapturedOutput = new(bytes.Buffer)
   192  
   193  	sizes := types.SizesFor("gc", ctx.GOARCH)
   194  	hint = fmt.Sprintf("To trace execution, run:\n%% go build github.com/powerman/golang-tools/cmd/ssadump && ./ssadump -build=C -test -run --interp=T %s\n", input)
   195  	exitCode := interp.Interpret(mainPkg, 0, sizes, input, []string{})
   196  	if exitCode != 0 {
   197  		t.Fatalf("interpreting %s: exit code was %d", input, exitCode)
   198  	}
   199  	// $GOROOT/test tests use this convention:
   200  	if strings.Contains(interp.CapturedOutput.String(), "BUG") {
   201  		t.Fatalf("interpreting %s: exited zero but output contained 'BUG'", input)
   202  	}
   203  
   204  	hint = "" // call off the hounds
   205  
   206  	if false {
   207  		t.Log(input, time.Since(start)) // test profiling
   208  	}
   209  
   210  	return true
   211  }
   212  
   213  func printFailures(failures []string) {
   214  	if failures != nil {
   215  		fmt.Println("The following tests failed:")
   216  		for _, f := range failures {
   217  			fmt.Printf("\t%s\n", f)
   218  		}
   219  	}
   220  }
   221  
   222  // TestTestdataFiles runs the interpreter on testdata/*.go.
   223  func TestTestdataFiles(t *testing.T) {
   224  	cwd, err := os.Getwd()
   225  	if err != nil {
   226  		log.Fatal(err)
   227  	}
   228  
   229  	var failures []string
   230  	for _, input := range testdataTests {
   231  		if !run(t, filepath.Join(cwd, "testdata", input)) {
   232  			failures = append(failures, input)
   233  		}
   234  	}
   235  	printFailures(failures)
   236  }
   237  
   238  // TestGorootTest runs the interpreter on $GOROOT/test/*.go.
   239  func TestGorootTest(t *testing.T) {
   240  	var failures []string
   241  
   242  	for _, input := range gorootTestTests {
   243  		if !run(t, filepath.Join(build.Default.GOROOT, "test", input)) {
   244  			failures = append(failures, input)
   245  		}
   246  	}
   247  	printFailures(failures)
   248  }