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:
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)}
|
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
|
||||||
|
@ -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)}
|
||||||
}
|
}
|
||||||
|
@ -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)}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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":
|
||||||
|
Loading…
Reference in New Issue
Block a user