mirror of
https://github.com/golang/go
synced 2024-11-19 02:54:42 -07:00
go.tools/ssa/interp: capture stdout/err of target programs and check for "BUG".
The $GOROOT/tests may print "BUG" on failure but do not necessarily exit zero, so we must capture their output too. Details: - make plan9 use unix's valueToBytes function (now in externals.go) - direct the target's syscall.Write and print/println built-ins to a new utility, write(). This may capture the output into a global variable. R=gri, r CC=golang-dev https://golang.org/cl/14550044
This commit is contained in:
parent
068f017092
commit
cd908f1108
@ -330,6 +330,15 @@ func ext۰syscall۰RawSyscall(fn *ssa.Function, args []value) value {
|
||||
return tuple{uintptr(0), uintptr(0), uintptr(syscall.ENOSYS)}
|
||||
}
|
||||
|
||||
func valueToBytes(v value) []byte {
|
||||
in := v.([]value)
|
||||
b := make([]byte, len(in))
|
||||
for i := range in {
|
||||
b[i] = in[i].(byte)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// The set of remaining native functions we need to implement (as needed):
|
||||
|
||||
// crypto/aes/cipher_asm.go:10:func hasAsm() bool
|
||||
|
@ -37,13 +37,8 @@ func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value {
|
||||
func ext۰syscall۰Stat(fn *ssa.Function, args []value) value {
|
||||
panic("syscall.Stat not yet implemented")
|
||||
}
|
||||
|
||||
func ext۰syscall۰Write(fn *ssa.Function, args []value) value {
|
||||
p := args[1].([]value)
|
||||
b := make([]byte, 0, len(p))
|
||||
for i := range p {
|
||||
b = append(b, p[i].(byte))
|
||||
}
|
||||
n, err := syscall.Write(args[0].(int), b)
|
||||
// func Write(fd int, p []byte) (n int, err error)
|
||||
n, err := write(args[0].(int), valueToBytes(args[1]))
|
||||
return tuple{n, wrapError(err)}
|
||||
}
|
||||
|
@ -12,15 +12,6 @@ import (
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
)
|
||||
|
||||
func valueToBytes(v value) []byte {
|
||||
in := v.([]value)
|
||||
b := make([]byte, len(in))
|
||||
for i := range in {
|
||||
b[i] = in[i].(byte)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func fillStat(st *syscall.Stat_t, stat structure) {
|
||||
stat[0] = st.Dev
|
||||
stat[1] = st.Ino
|
||||
@ -132,6 +123,6 @@ func ext۰syscall۰Stat(fn *ssa.Function, args []value) value {
|
||||
|
||||
func ext۰syscall۰Write(fn *ssa.Function, args []value) value {
|
||||
// func Write(fd int, p []byte) (n int, err error)
|
||||
n, err := syscall.Write(args[0].(int), valueToBytes(args[1]))
|
||||
n, err := write(args[0].(int), valueToBytes(args[1]))
|
||||
return tuple{n, wrapError(err)}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
package interp_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
@ -64,7 +65,7 @@ var gorootTestTests = []string{
|
||||
"simassign.go",
|
||||
"iota.go",
|
||||
"nilptr2.go",
|
||||
"goprint.go", // doesn't actually assert anything
|
||||
"goprint.go", // doesn't actually assert anything (cmpout)
|
||||
"utf.go",
|
||||
"method.go",
|
||||
"char_lit.go",
|
||||
@ -77,13 +78,13 @@ var gorootTestTests = []string{
|
||||
"reorder.go",
|
||||
"method3.go",
|
||||
"literal.go",
|
||||
"nul1.go",
|
||||
"nul1.go", // doesn't actually assert anything (errorcheckoutput)
|
||||
"zerodivide.go",
|
||||
"convert.go",
|
||||
"convT2X.go",
|
||||
"initialize.go",
|
||||
"ddd.go",
|
||||
"blank.go", // partly disabled; TODO(adonovan): skip blank fields in struct{_} equivalence.
|
||||
"blank.go", // partly disabled
|
||||
"map.go",
|
||||
"closedchan.go",
|
||||
"divide.go",
|
||||
@ -93,7 +94,7 @@ var gorootTestTests = []string{
|
||||
"recover.go", // partly disabled; TODO(adonovan): fix.
|
||||
"typeswitch1.go",
|
||||
"floatcmp.go",
|
||||
"crlf.go", // doesn't actually assert anything
|
||||
"crlf.go", // doesn't actually assert anything (runoutput)
|
||||
// Slow tests follow.
|
||||
"bom.go", // ~1.7s
|
||||
"gc1.go", // ~1.7s
|
||||
@ -182,6 +183,8 @@ func run(t *testing.T, dir, input string) bool {
|
||||
} else {
|
||||
fmt.Println("PASS")
|
||||
}
|
||||
|
||||
interp.CapturedOutput = nil
|
||||
}()
|
||||
|
||||
hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build code.google.com/p/go.tools/cmd/ssadump && ./ssadump -build=CFP %s\n", input)
|
||||
@ -197,12 +200,21 @@ func run(t *testing.T, dir, input string) bool {
|
||||
mainPkg := prog.Package(mainInfo.Pkg)
|
||||
mainPkg.CreateTestMainFunction() // (no-op if main already exists)
|
||||
|
||||
var out bytes.Buffer
|
||||
interp.CapturedOutput = &out
|
||||
|
||||
hint = fmt.Sprintf("To trace execution, run:\n%% go build code.google.com/p/go.tools/cmd/ssadump && ./ssadump -build=C -run --interp=T %s\n", input)
|
||||
if exitCode := interp.Interpret(mainPkg, 0, inputs[0], []string{}); exitCode != 0 {
|
||||
t.Errorf("interp.Interpret(%s) exited with code %d, want zero", inputs, exitCode)
|
||||
return false
|
||||
}
|
||||
|
||||
// $GOROOT/tests are considered a failure if they print "BUG".
|
||||
if strings.Contains(out.String(), "BUG") {
|
||||
t.Errorf("interp.Interpret(%s) exited zero but output contained 'BUG'", inputs)
|
||||
return false
|
||||
}
|
||||
|
||||
hint = "" // call off the hounds
|
||||
|
||||
if false {
|
||||
|
@ -5,10 +5,13 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
@ -906,6 +909,29 @@ func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value {
|
||||
return v
|
||||
}
|
||||
|
||||
// If CapturedOutput is non-nil, all writes by the interpreted program
|
||||
// to file descriptors 1 and 2 will also be written to CapturedOutput.
|
||||
//
|
||||
// (The $GOROOT/test system requires that the test be considered a
|
||||
// failure if "BUG" appears in the combined stdout/stderr output, even
|
||||
// if it exits zero. This is a global variable shared by all
|
||||
// interpreters in the same process.)
|
||||
//
|
||||
var CapturedOutput *bytes.Buffer
|
||||
var capturedOutputMu sync.Mutex
|
||||
|
||||
// write writes bytes b to the target program's file descriptor fd.
|
||||
// The print/println built-ins and the write() system call funnel
|
||||
// through here so they can be captured by the test driver.
|
||||
func write(fd int, b []byte) (int, error) {
|
||||
if CapturedOutput != nil && (fd == 1 || fd == 2) {
|
||||
capturedOutputMu.Lock()
|
||||
CapturedOutput.Write(b) // ignore errors
|
||||
capturedOutputMu.Unlock()
|
||||
}
|
||||
return syscall.Write(fd, b)
|
||||
}
|
||||
|
||||
// callBuiltin interprets a call to builtin fn with arguments args,
|
||||
// returning its result.
|
||||
func callBuiltin(caller *frame, callpos token.Pos, fn *ssa.Builtin, args []value) value {
|
||||
@ -948,18 +974,20 @@ func callBuiltin(caller *frame, callpos token.Pos, fn *ssa.Builtin, args []value
|
||||
|
||||
case "print", "println": // print(anytype, ...interface{})
|
||||
ln := fn.Name() == "println"
|
||||
fmt.Print(toString(args[0]))
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(toString(args[0]))
|
||||
if len(args) == 2 {
|
||||
for _, arg := range args[1].([]value) {
|
||||
if ln {
|
||||
fmt.Print(" ")
|
||||
buf.WriteRune(' ')
|
||||
}
|
||||
fmt.Print(toString(arg))
|
||||
buf.WriteString(toString(arg))
|
||||
}
|
||||
}
|
||||
if ln {
|
||||
fmt.Println()
|
||||
buf.WriteRune('\n')
|
||||
}
|
||||
write(1, buf.Bytes())
|
||||
return nil
|
||||
|
||||
case "len":
|
||||
|
Loading…
Reference in New Issue
Block a user