From 45e7e668440e79717e950162e6d42fb8773a109a Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 31 Jul 2018 12:40:20 -0700 Subject: [PATCH] cmd/compile: unify compilation of compiler tests Before this CL we would build&run each test file individually. Building the test takes most of the time, a significant fraction of a second. Running the tests are really fast. After this CL, we build all the tests at once, then run each individually. We only have to run the compiler&linker once (or twice, for softfloat architectures) instead of once per test. While we're here, organize these tests to fit a bit more into the standard testing framework. This is just the organizational CL that changes the testing framework and migrates 2 tests. Future tests will follow. R=go1.12 Update #26469 Change-Id: I1a1e7338c054b51f0c1c4c539d48d3d046b08b7d Reviewed-on: https://go-review.googlesource.com/126995 Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot Reviewed-by: David Chase --- src/cmd/compile/internal/gc/ssa_test.go | 125 +++++++++++++++++- .../gc/testdata/{break.go => break_test.go} | 17 +-- src/cmd/compile/internal/gc/testdata/short.go | 60 --------- .../internal/gc/testdata/short_test.go | 57 ++++++++ 4 files changed, 184 insertions(+), 75 deletions(-) rename src/cmd/compile/internal/gc/testdata/{break.go => break_test.go} (93%) delete mode 100644 src/cmd/compile/internal/gc/testdata/short.go create mode 100644 src/cmd/compile/internal/gc/testdata/short_test.go diff --git a/src/cmd/compile/internal/gc/ssa_test.go b/src/cmd/compile/internal/gc/ssa_test.go index 73110ea65a..9f927262ca 100644 --- a/src/cmd/compile/internal/gc/ssa_test.go +++ b/src/cmd/compile/internal/gc/ssa_test.go @@ -6,11 +6,16 @@ package gc import ( "bytes" + "fmt" + "go/ast" + "go/parser" + "go/token" "internal/testenv" "io/ioutil" "os" "os/exec" "path/filepath" + "runtime" "strings" "testing" ) @@ -104,11 +109,123 @@ func TestGenFlowGraph(t *testing.T) { runGenTest(t, "flowgraph_generator1.go", "ssa_fg_tmp1") } -// TestShortCircuit tests OANDAND and OOROR expressions and short circuiting. -func TestShortCircuit(t *testing.T) { runTest(t, "short.go") } +// TestCode runs all the tests in the testdata directory as subtests. +// These tests are special because we want to run them with different +// compiler flags set (and thus they can't just be _test.go files in +// this directory). +func TestCode(t *testing.T) { + testenv.MustHaveGoBuild(t) + gotool := testenv.GoToolPath(t) + + // Make a temporary directory to work in. + tmpdir, err := ioutil.TempDir("", "TestCode") + if err != nil { + t.Fatalf("Failed to create temporary directory: %v", err) + } + defer os.RemoveAll(tmpdir) + + // Find all the test functions (and the files containing them). + var srcs []string // files containing Test functions + type test struct { + name string // TestFoo + usesFloat bool // might use float operations + } + var tests []test + files, err := ioutil.ReadDir("testdata") + if err != nil { + t.Fatalf("can't read testdata directory: %v", err) + } + for _, f := range files { + if !strings.HasSuffix(f.Name(), "_test.go") { + continue + } + text, err := ioutil.ReadFile(filepath.Join("testdata", f.Name())) + if err != nil { + t.Fatalf("can't read testdata/%s: %v", f.Name(), err) + } + fset := token.NewFileSet() + code, err := parser.ParseFile(fset, f.Name(), text, 0) + if err != nil { + t.Fatalf("can't parse testdata/%s: %v", f.Name(), err) + } + srcs = append(srcs, filepath.Join("testdata", f.Name())) + foundTest := false + for _, d := range code.Decls { + fd, ok := d.(*ast.FuncDecl) + if !ok { + continue + } + if !strings.HasPrefix(fd.Name.Name, "Test") { + continue + } + if fd.Recv != nil { + continue + } + if fd.Type.Results != nil { + continue + } + if len(fd.Type.Params.List) != 1 { + continue + } + p := fd.Type.Params.List[0] + if len(p.Names) != 1 { + continue + } + s, ok := p.Type.(*ast.StarExpr) + if !ok { + continue + } + sel, ok := s.X.(*ast.SelectorExpr) + if !ok { + continue + } + base, ok := sel.X.(*ast.Ident) + if !ok { + continue + } + if base.Name != "testing" { + continue + } + if sel.Sel.Name != "T" { + continue + } + // Found a testing function. + tests = append(tests, test{name: fd.Name.Name, usesFloat: bytes.Contains(text, []byte("float"))}) + foundTest = true + } + if !foundTest { + t.Fatalf("test file testdata/%s has no tests in it", f.Name()) + } + } -// TestBreakContinue tests that continue and break statements do what they say. -func TestBreakContinue(t *testing.T) { runTest(t, "break.go") } + flags := []string{""} + if runtime.GOARCH == "arm" || runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" { + flags = append(flags, ",softfloat") + } + for _, flag := range flags { + args := []string{"test", "-c", "-gcflags=-d=ssa/check/on" + flag, "-o", filepath.Join(tmpdir, "code.test")} + args = append(args, srcs...) + out, err := exec.Command(gotool, args...).CombinedOutput() + if err != nil || len(out) != 0 { + t.Fatalf("Build failed: %v\n%s\n", err, out) + } + + // Now we have a test binary. Run it with all the tests as subtests of this one. + for _, test := range tests { + test := test + if flag == ",softfloat" && !test.usesFloat { + // No point in running the soft float version if the test doesn't use floats. + continue + } + t.Run(fmt.Sprintf("%s%s", test.name[4:], flag), func(t *testing.T) { + out, err := exec.Command(filepath.Join(tmpdir, "code.test"), "-test.run="+test.name).CombinedOutput() + if err != nil || string(out) != "PASS\n" { + t.Errorf("Failed:\n%s\n", out) + } + }) + } + } +} // TestTypeAssertion tests type assertions. func TestTypeAssertion(t *testing.T) { runTest(t, "assert.go") } diff --git a/src/cmd/compile/internal/gc/testdata/break.go b/src/cmd/compile/internal/gc/testdata/break_test.go similarity index 93% rename from src/cmd/compile/internal/gc/testdata/break.go rename to src/cmd/compile/internal/gc/testdata/break_test.go index 855ef70049..50245dfd31 100644 --- a/src/cmd/compile/internal/gc/testdata/break.go +++ b/src/cmd/compile/internal/gc/testdata/break_test.go @@ -1,5 +1,3 @@ -// run - // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -8,6 +6,8 @@ package main +import "testing" + func continuePlain_ssa() int { var n int for i := 0; i < 10; i++ { @@ -214,7 +214,8 @@ Done: return n } -func main() { +// TestBreakContinue tests that continue and break statements do what they say. +func TestBreakContinue(t *testing.T) { tests := [...]struct { name string fn func() int @@ -241,15 +242,9 @@ func main() { // no select tests; they're identical to switch } - var failed bool for _, test := range tests { - if got := test.fn(); test.fn() != test.want { - print(test.name, "()=", got, ", want ", test.want, "\n") - failed = true + if got := test.fn(); got != test.want { + t.Errorf("%s()=%d, want %d", test.name, got, test.want) } } - - if failed { - panic("failed") - } } diff --git a/src/cmd/compile/internal/gc/testdata/short.go b/src/cmd/compile/internal/gc/testdata/short.go deleted file mode 100644 index fcec1baf09..0000000000 --- a/src/cmd/compile/internal/gc/testdata/short.go +++ /dev/null @@ -1,60 +0,0 @@ -// run - -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Tests short circuiting. - -package main - -func and_ssa(arg1, arg2 bool) bool { - return arg1 && rightCall(arg2) -} - -func or_ssa(arg1, arg2 bool) bool { - return arg1 || rightCall(arg2) -} - -var rightCalled bool - -//go:noinline -func rightCall(v bool) bool { - rightCalled = true - return v - panic("unreached") -} - -func testAnd(arg1, arg2, wantRes bool) { testShortCircuit("AND", arg1, arg2, and_ssa, arg1, wantRes) } -func testOr(arg1, arg2, wantRes bool) { testShortCircuit("OR", arg1, arg2, or_ssa, !arg1, wantRes) } - -func testShortCircuit(opName string, arg1, arg2 bool, fn func(bool, bool) bool, wantRightCall, wantRes bool) { - rightCalled = false - got := fn(arg1, arg2) - if rightCalled != wantRightCall { - println("failed for", arg1, opName, arg2, "; rightCalled=", rightCalled, "want=", wantRightCall) - failed = true - } - if wantRes != got { - println("failed for", arg1, opName, arg2, "; res=", got, "want=", wantRes) - failed = true - } -} - -var failed = false - -func main() { - testAnd(false, false, false) - testAnd(false, true, false) - testAnd(true, false, false) - testAnd(true, true, true) - - testOr(false, false, false) - testOr(false, true, true) - testOr(true, false, true) - testOr(true, true, true) - - if failed { - panic("failed") - } -} diff --git a/src/cmd/compile/internal/gc/testdata/short_test.go b/src/cmd/compile/internal/gc/testdata/short_test.go new file mode 100644 index 0000000000..7a743b5d19 --- /dev/null +++ b/src/cmd/compile/internal/gc/testdata/short_test.go @@ -0,0 +1,57 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Tests short circuiting. + +package main + +import "testing" + +func and_ssa(arg1, arg2 bool) bool { + return arg1 && rightCall(arg2) +} + +func or_ssa(arg1, arg2 bool) bool { + return arg1 || rightCall(arg2) +} + +var rightCalled bool + +//go:noinline +func rightCall(v bool) bool { + rightCalled = true + return v + panic("unreached") +} + +func testAnd(t *testing.T, arg1, arg2, wantRes bool) { + testShortCircuit(t, "AND", arg1, arg2, and_ssa, arg1, wantRes) +} +func testOr(t *testing.T, arg1, arg2, wantRes bool) { + testShortCircuit(t, "OR", arg1, arg2, or_ssa, !arg1, wantRes) +} + +func testShortCircuit(t *testing.T, opName string, arg1, arg2 bool, fn func(bool, bool) bool, wantRightCall, wantRes bool) { + rightCalled = false + got := fn(arg1, arg2) + if rightCalled != wantRightCall { + t.Errorf("failed for %t %s %t; rightCalled=%t want=%t", arg1, opName, arg2, rightCalled, wantRightCall) + } + if wantRes != got { + t.Errorf("failed for %t %s %t; res=%t want=%t", arg1, opName, arg2, got, wantRes) + } +} + +// TestShortCircuit tests OANDAND and OOROR expressions and short circuiting. +func TestShortCircuit(t *testing.T) { + testAnd(t, false, false, false) + testAnd(t, false, true, false) + testAnd(t, true, false, false) + testAnd(t, true, true, true) + + testOr(t, false, false, false) + testOr(t, false, true, true) + testOr(t, true, false, true) + testOr(t, true, true, true) +} -- 2.50.0