1
0
mirror of https://github.com/golang/go synced 2024-11-19 05:44:40 -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:
Alan Donovan 2013-10-08 14:35:39 -04:00
parent 068f017092
commit cd908f1108
5 changed files with 60 additions and 25 deletions

View File

@ -330,6 +330,15 @@ func ext۰syscall۰RawSyscall(fn *ssa.Function, args []value) value {
return tuple{uintptr(0), uintptr(0), uintptr(syscall.ENOSYS)} 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): // The set of remaining native functions we need to implement (as needed):
// crypto/aes/cipher_asm.go:10:func hasAsm() bool // crypto/aes/cipher_asm.go:10:func hasAsm() bool

View File

@ -37,13 +37,8 @@ func ext۰syscall۰ReadDirent(fn *ssa.Function, args []value) value {
func ext۰syscall۰Stat(fn *ssa.Function, args []value) value { func ext۰syscall۰Stat(fn *ssa.Function, args []value) value {
panic("syscall.Stat not yet implemented") panic("syscall.Stat not yet implemented")
} }
func ext۰syscall۰Write(fn *ssa.Function, args []value) value { func ext۰syscall۰Write(fn *ssa.Function, args []value) value {
p := args[1].([]value) // func Write(fd int, p []byte) (n int, err error)
b := make([]byte, 0, len(p)) n, err := write(args[0].(int), valueToBytes(args[1]))
for i := range p {
b = append(b, p[i].(byte))
}
n, err := syscall.Write(args[0].(int), b)
return tuple{n, wrapError(err)} return tuple{n, wrapError(err)}
} }

View File

@ -12,15 +12,6 @@ import (
"code.google.com/p/go.tools/ssa" "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) { func fillStat(st *syscall.Stat_t, stat structure) {
stat[0] = st.Dev stat[0] = st.Dev
stat[1] = st.Ino 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 ext۰syscall۰Write(fn *ssa.Function, args []value) value {
// func Write(fd int, p []byte) (n int, err error) // 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)} return tuple{n, wrapError(err)}
} }

View File

@ -7,6 +7,7 @@
package interp_test package interp_test
import ( import (
"bytes"
"fmt" "fmt"
"go/build" "go/build"
"os" "os"
@ -64,7 +65,7 @@ var gorootTestTests = []string{
"simassign.go", "simassign.go",
"iota.go", "iota.go",
"nilptr2.go", "nilptr2.go",
"goprint.go", // doesn't actually assert anything "goprint.go", // doesn't actually assert anything (cmpout)
"utf.go", "utf.go",
"method.go", "method.go",
"char_lit.go", "char_lit.go",
@ -77,13 +78,13 @@ var gorootTestTests = []string{
"reorder.go", "reorder.go",
"method3.go", "method3.go",
"literal.go", "literal.go",
"nul1.go", "nul1.go", // doesn't actually assert anything (errorcheckoutput)
"zerodivide.go", "zerodivide.go",
"convert.go", "convert.go",
"convT2X.go", "convT2X.go",
"initialize.go", "initialize.go",
"ddd.go", "ddd.go",
"blank.go", // partly disabled; TODO(adonovan): skip blank fields in struct{_} equivalence. "blank.go", // partly disabled
"map.go", "map.go",
"closedchan.go", "closedchan.go",
"divide.go", "divide.go",
@ -93,7 +94,7 @@ var gorootTestTests = []string{
"recover.go", // partly disabled; TODO(adonovan): fix. "recover.go", // partly disabled; TODO(adonovan): fix.
"typeswitch1.go", "typeswitch1.go",
"floatcmp.go", "floatcmp.go",
"crlf.go", // doesn't actually assert anything "crlf.go", // doesn't actually assert anything (runoutput)
// Slow tests follow. // Slow tests follow.
"bom.go", // ~1.7s "bom.go", // ~1.7s
"gc1.go", // ~1.7s "gc1.go", // ~1.7s
@ -182,6 +183,8 @@ func run(t *testing.T, dir, input string) bool {
} else { } else {
fmt.Println("PASS") 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) 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 := prog.Package(mainInfo.Pkg)
mainPkg.CreateTestMainFunction() // (no-op if main already exists) 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) 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 { if exitCode := interp.Interpret(mainPkg, 0, inputs[0], []string{}); exitCode != 0 {
t.Errorf("interp.Interpret(%s) exited with code %d, want zero", inputs, exitCode) t.Errorf("interp.Interpret(%s) exited with code %d, want zero", inputs, exitCode)
return false 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 hint = "" // call off the hounds
if false { if false {

View File

@ -5,10 +5,13 @@
package interp package interp
import ( import (
"bytes"
"fmt" "fmt"
"go/token" "go/token"
"runtime" "runtime"
"strings" "strings"
"sync"
"syscall"
"unsafe" "unsafe"
"code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/exact"
@ -906,6 +909,29 @@ func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value {
return v 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, // callBuiltin interprets a call to builtin fn with arguments args,
// returning its result. // returning its result.
func callBuiltin(caller *frame, callpos token.Pos, fn *ssa.Builtin, args []value) value { 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{}) case "print", "println": // print(anytype, ...interface{})
ln := fn.Name() == "println" ln := fn.Name() == "println"
fmt.Print(toString(args[0])) var buf bytes.Buffer
buf.WriteString(toString(args[0]))
if len(args) == 2 { if len(args) == 2 {
for _, arg := range args[1].([]value) { for _, arg := range args[1].([]value) {
if ln { if ln {
fmt.Print(" ") buf.WriteRune(' ')
} }
fmt.Print(toString(arg)) buf.WriteString(toString(arg))
} }
} }
if ln { if ln {
fmt.Println() buf.WriteRune('\n')
} }
write(1, buf.Bytes())
return nil return nil
case "len": case "len":