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 }