1
0
mirror of https://github.com/golang/go synced 2024-11-18 21:34:46 -07:00

go.tools/ssa: don't synthesize an empty "testmain" package.

Also, only examine functions defined in *_test.go files.

Added tests for empty and nonempty behaviour of CreateTestMainPackage.
(Required some surgery to interp_test.)

R=gri, gri
CC=golang-dev
https://golang.org/cl/25570044
This commit is contained in:
Alan Donovan 2013-11-13 16:05:13 -05:00
parent 3f686cae84
commit cadc2255fe
4 changed files with 152 additions and 42 deletions

View File

@ -155,7 +155,9 @@ var gorootSrcPkgTests = []string{
// "strings.go strings/search.go strings/search_test.go", // "strings.go strings/search.go strings/search_test.go",
} }
func run(t *testing.T, dir, input string) bool { type successPredicate func(exitcode int, output string) error
func run(t *testing.T, dir, input string, success successPredicate) bool {
fmt.Printf("Input: %s\n", input) fmt.Printf("Input: %s\n", input)
start := time.Now() start := time.Now()
@ -189,7 +191,7 @@ func run(t *testing.T, dir, input string) bool {
}() }()
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)
mainInfo := imp.CreatePackage("main", files...) mainInfo := imp.CreatePackage(files[0].Name.Name, files...)
if _, err := imp.LoadPackage("runtime"); err != nil { if _, err := imp.LoadPackage("runtime"); err != nil {
t.Errorf("LoadPackage(runtime) failed: %s", err) t.Errorf("LoadPackage(runtime) failed: %s", err)
@ -204,21 +206,27 @@ func run(t *testing.T, dir, input string) bool {
mainPkg := prog.Package(mainInfo.Pkg) mainPkg := prog.Package(mainInfo.Pkg)
if mainPkg.Func("main") == nil { if mainPkg.Func("main") == nil {
mainPkg = prog.CreateTestMainPackage(mainPkg) testmainPkg := prog.CreateTestMainPackage(mainPkg)
if testmainPkg == nil {
t.Errorf("CreateTestMainPackage(%s) returned nil", mainPkg)
return false
}
if testmainPkg.Func("main") == nil {
t.Errorf("synthetic testmain package has no main")
return false
}
mainPkg = testmainPkg
} }
var out bytes.Buffer var out bytes.Buffer
interp.CapturedOutput = &out 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 { exitCode := interp.Interpret(mainPkg, 0, inputs[0], []string{})
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". // The definition of success varies with each file.
if strings.Contains(out.String(), "BUG") { if err := success(exitCode, out.String()); err != nil {
t.Errorf("interp.Interpret(%s) exited zero but output contained 'BUG'", inputs) t.Errorf("interp.Interpret(%s) failed: %s", inputs, err)
return false return false
} }
@ -233,30 +241,7 @@ func run(t *testing.T, dir, input string) bool {
const slash = string(os.PathSeparator) const slash = string(os.PathSeparator)
// TestInterp runs the interpreter on a selection of small Go programs. func printFailures(failures []string) {
func TestInterp(t *testing.T) {
var failures []string
for _, input := range testdataTests {
if !run(t, "testdata"+slash, input) {
failures = append(failures, input)
}
}
if !testing.Short() {
for _, input := range gorootTestTests {
if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input) {
failures = append(failures, input)
}
}
for _, input := range gorootSrcPkgTests {
if !run(t, filepath.Join(build.Default.GOROOT, "src/pkg")+slash, input) {
failures = append(failures, input)
}
}
}
if failures != nil { if failures != nil {
fmt.Println("The following tests failed:") fmt.Println("The following tests failed:")
for _, f := range failures { for _, f := range failures {
@ -264,3 +249,92 @@ func TestInterp(t *testing.T) {
} }
} }
} }
// The "normal" success predicate.
func exitsZero(exitcode int, _ string) error {
if exitcode != 0 {
return fmt.Errorf("exit code was %d", exitcode)
}
return nil
}
// TestTestdataFiles runs the interpreter on testdata/*.go.
func TestTestdataFiles(t *testing.T) {
var failures []string
for _, input := range testdataTests {
if !run(t, "testdata"+slash, input, exitsZero) {
failures = append(failures, input)
}
}
printFailures(failures)
}
// TestGorootTest runs the interpreter on $GOROOT/test/*.go.
func TestGorootTest(t *testing.T) {
if testing.Short() {
return // too slow (~30s)
}
var failures []string
// $GOROOT/tests are also considered a failure if they print "BUG".
success := func(exitcode int, output string) error {
if exitcode != 0 {
return fmt.Errorf("exit code was %d", exitcode)
}
if strings.Contains(output, "BUG") {
return fmt.Errorf("exited zero but output contained 'BUG'")
}
return nil
}
for _, input := range gorootTestTests {
if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input, success) {
failures = append(failures, input)
}
}
for _, input := range gorootSrcPkgTests {
if !run(t, filepath.Join(build.Default.GOROOT, "src/pkg")+slash, input, success) {
failures = append(failures, input)
}
}
printFailures(failures)
}
// TestTestmainPackage runs the interpreter on a synthetic "testmain" package.
func TestTestmainPackage(t *testing.T) {
success := func(exitcode int, output string) error {
if exitcode == 0 {
return fmt.Errorf("unexpected success")
}
if !strings.Contains(output, "FAIL: TestFoo") {
return fmt.Errorf("missing failure log for TestFoo")
}
if !strings.Contains(output, "FAIL: TestBar") {
return fmt.Errorf("missing failure log for TestBar")
}
// TODO(adonovan): test benchmarks too
return nil
}
run(t, "testdata"+slash, "a_test.go", success)
}
// CreateTestMainPackage should return nil if there were no tests.
func TestNullTestmainPackage(t *testing.T) {
imp := importer.New(&importer.Config{Build: build.Default})
files, err := importer.ParseFiles(imp.Fset, ".", "testdata/b_test.go")
if err != nil {
t.Fatalf("ParseFiles failed: %s", err)
}
mainInfo := imp.CreatePackage("b", files...)
prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
if err := prog.CreatePackages(imp); err != nil {
t.Fatalf("CreatePackages failed: %s", err)
}
mainPkg := prog.Package(mainInfo.Pkg)
if mainPkg.Func("main") != nil {
t.Fatalf("unexpected main function")
}
if prog.CreateTestMainPackage(mainPkg) != nil {
t.Fatalf("CreateTestMainPackage returned non-nil")
}
}

17
ssa/interp/testdata/a_test.go vendored Normal file
View File

@ -0,0 +1,17 @@
package a
import "testing"
func TestFoo(t *testing.T) {
t.Error("foo")
}
func TestBar(t *testing.T) {
t.Error("bar")
}
func BenchmarkWiz(b *testing.B) {
b.Error("wiz")
}
// Don't test Examples since that testing package needs pipe(2) for that.

11
ssa/interp/testdata/b_test.go vendored Normal file
View File

@ -0,0 +1,11 @@
package b
import "testing"
func NotATest(t *testing.T) {
t.Error("foo")
}
func NotABenchmark(b *testing.B) {
b.Error("wiz")
}

View File

@ -42,7 +42,7 @@ func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package {
Prog: prog, Prog: prog,
} }
init.startBody() init.startBody()
var expfuncs []*Function // all exported functions of pkgs, unordered var expfuncs []*Function // all exported functions of *_test.go in pkgs, unordered
for _, pkg := range pkgs { for _, pkg := range pkgs {
// Initialize package to test. // Initialize package to test.
var v Call var v Call
@ -52,7 +52,9 @@ func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package {
// Enumerate its possible tests/benchmarks. // Enumerate its possible tests/benchmarks.
for _, mem := range pkg.Members { for _, mem := range pkg.Members {
if f, ok := mem.(*Function); ok && ast.IsExported(f.Name()) { if f, ok := mem.(*Function); ok &&
ast.IsExported(f.Name()) &&
strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") {
expfuncs = append(expfuncs, f) expfuncs = append(expfuncs, f)
} }
} }
@ -107,12 +109,18 @@ func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package {
main.startBody() main.startBody()
var c Call var c Call
c.Call.Value = testingMain c.Call.Value = testingMain
c.Call.Args = []Value{
matcher, tests := testMainSlice(main, expfuncs, "Test", testingMainParams.At(1).Type())
testMainSlice(main, expfuncs, "Test", testingMainParams.At(1).Type()), benchmarks := testMainSlice(main, expfuncs, "Benchmark", testingMainParams.At(2).Type())
testMainSlice(main, expfuncs, "Benchmark", testingMainParams.At(2).Type()), examples := testMainSlice(main, expfuncs, "Example", testingMainParams.At(3).Type())
testMainSlice(main, expfuncs, "Example", testingMainParams.At(3).Type()), _, noTests := tests.(*Const) // i.e. nil slice
_, noBenchmarks := benchmarks.(*Const)
_, noExamples := examples.(*Const)
if noTests && noBenchmarks && noExamples {
return nil
} }
c.Call.Args = []Value{matcher, tests, benchmarks, examples}
// Emit: testing.Main(nil, tests, benchmarks, examples) // Emit: testing.Main(nil, tests, benchmarks, examples)
emitTailCall(main, &c) emitTailCall(main, &c)
main.finishBody() main.finishBody()
@ -122,6 +130,7 @@ func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package {
if prog.mode&LogPackages != 0 { if prog.mode&LogPackages != 0 {
testmain.DumpTo(os.Stderr) testmain.DumpTo(os.Stderr)
} }
if prog.mode&SanityCheckFunctions != 0 { if prog.mode&SanityCheckFunctions != 0 {
sanityCheckPackage(testmain) sanityCheckPackage(testmain)
} }
@ -141,7 +150,6 @@ func testMainSlice(fn *Function, expfuncs []*Function, prefix string, slice type
var testfuncs []*Function var testfuncs []*Function
for _, f := range expfuncs { for _, f := range expfuncs {
// TODO(adonovan): only look at members defined in *_test.go files.
if isTest(f.Name(), prefix) && types.IsIdentical(f.Signature, tFunc) { if isTest(f.Name(), prefix) && types.IsIdentical(f.Signature, tFunc) {
testfuncs = append(testfuncs, f) testfuncs = append(testfuncs, f)
} }