1
0
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:
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)}
}
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

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 {
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)}
}

View File

@ -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)}
}

View File

@ -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 {

View File

@ -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":