Commit 4a801cdd authored by Ian Lance Taylor's avatar Ian Lance Taylor

cmd/cover: run tests in parallel, don't change source directory

This speeds up the cmd/cover testsuite by about 40% on my laptop.

Updates #26473
Updates #28386

Change-Id: I853b1b3b8c98dc89440f7b7bf5c0ade1d3d66802
Reviewed-on: https://go-review.googlesource.com/c/152817
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent ec0077c5
...@@ -19,43 +19,103 @@ import ( ...@@ -19,43 +19,103 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"sync"
"testing" "testing"
) )
const ( const (
// Data directory, also the package directory for the test. // Data directory, also the package directory for the test.
testdata = "testdata" testdata = "testdata"
// Binaries we compile.
testcover = "./testcover.exe"
) )
var ( var (
// Files we use. // Input files.
testMain = filepath.Join(testdata, "main.go") testMain = filepath.Join(testdata, "main.go")
testTest = filepath.Join(testdata, "test.go") testTest = filepath.Join(testdata, "test.go")
coverInput = filepath.Join(testdata, "test_line.go")
coverOutput = filepath.Join(testdata, "test_cover.go")
coverProfile = filepath.Join(testdata, "profile.cov") coverProfile = filepath.Join(testdata, "profile.cov")
// The HTML test files are in a separate directory // The HTML test files are in a separate directory
// so they are a complete package. // so they are a complete package.
htmlProfile = filepath.Join(testdata, "html", "html.cov") htmlGolden = filepath.Join(testdata, "html", "html.golden")
htmlHTML = filepath.Join(testdata, "html", "html.html")
htmlGolden = filepath.Join(testdata, "html", "html.golden") // Temporary files.
tmpTestMain string
coverInput string
coverOutput string
htmlProfile string
htmlHTML string
)
var (
// testTempDir is a temporary directory created in TestMain.
testTempDir string
// testcover is a newly built version of the cover program.
testcover string
// testcoverErr records an error building testcover.
testcoverErr error
// testcoverOnce is used to build testcover once.
testcoverOnce sync.Once
) )
var debug = flag.Bool("debug", false, "keep rewritten files for debugging") var debug = flag.Bool("debug", false, "keep rewritten files for debugging")
// We use TestMain to set up a temporary directory and remove it when
// the tests are done.
func TestMain(m *testing.M) {
dir, err := ioutil.TempDir("", "gotestcover")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
testTempDir = dir
tmpTestMain = filepath.Join(dir, "main.go")
coverInput = filepath.Join(dir, "test_line.go")
coverOutput = filepath.Join(dir, "test_cover.go")
htmlProfile = filepath.Join(dir, "html.cov")
htmlHTML = filepath.Join(dir, "html.html")
status := m.Run()
if !*debug {
os.RemoveAll(dir)
}
os.Exit(status)
}
// buildCover builds a version of the cover program for testing.
// This ensures that "go test cmd/cover" tests the current cmd/cover.
func buildCover(t *testing.T) {
t.Helper()
testenv.MustHaveGoBuild(t)
testcoverOnce.Do(func() {
testcover = filepath.Join(testTempDir, "testcover.exe")
t.Logf("running [go build -o %s]", testcover)
out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", testcover).CombinedOutput()
t.Logf("%s", out)
testcoverErr = err
})
if testcoverErr != nil {
t.Fatal("failed to build testcover program:", testcoverErr)
}
}
// Run this shell script, but do it in Go so it can be run by "go test". // Run this shell script, but do it in Go so it can be run by "go test".
// //
// replace the word LINE with the line number < testdata/test.go > testdata/test_line.go // replace the word LINE with the line number < testdata/test.go > testdata/test_line.go
// go build -o ./testcover // go build -o testcover
// ./testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go // testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go
// go run ./testdata/main.go ./testdata/test.go // go run ./testdata/main.go ./testdata/test.go
// //
func TestCover(t *testing.T) { func TestCover(t *testing.T) {
testenv.MustHaveGoBuild(t) t.Parallel()
testenv.MustHaveGoRun(t)
buildCover(t)
// Read in the test file (testTest) and write it, with LINEs specified, to coverInput. // Read in the test file (testTest) and write it, with LINEs specified, to coverInput.
file, err := ioutil.ReadFile(testTest) file, err := ioutil.ReadFile(testTest)
...@@ -81,29 +141,22 @@ func TestCover(t *testing.T) { ...@@ -81,29 +141,22 @@ func TestCover(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// defer removal of test_line.go // testcover -mode=count -var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest -o ./testdata/test_cover.go testdata/test_line.go
if !*debug { cmd := exec.Command(testcover, "-mode=count", "-var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest", "-o", coverOutput, coverInput)
defer os.Remove(coverInput)
}
// go build -o testcover
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", testcover)
run(cmd, t) run(cmd, t)
// defer removal of testcover // Copy testmain to testTempDir, so that it is in the same directory
defer os.Remove(testcover) // as coverOutput.
b, err := ioutil.ReadFile(testMain)
// ./testcover -mode=count -var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest -o ./testdata/test_cover.go testdata/test_line.go if err != nil {
cmd = exec.Command(testcover, "-mode=count", "-var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest", "-o", coverOutput, coverInput) t.Fatal(err)
run(cmd, t) }
if err := ioutil.WriteFile(tmpTestMain, b, 0444); err != nil {
// defer removal of ./testdata/test_cover.go t.Fatal(err)
if !*debug {
defer os.Remove(coverOutput)
} }
// go run ./testdata/main.go ./testdata/test.go // go run ./testdata/main.go ./testdata/test.go
cmd = exec.Command(testenv.GoToolPath(t), "run", testMain, coverOutput) cmd = exec.Command(testenv.GoToolPath(t), "run", tmpTestMain, coverOutput)
run(cmd, t) run(cmd, t)
file, err = ioutil.ReadFile(coverOutput) file, err = ioutil.ReadFile(coverOutput)
...@@ -131,6 +184,9 @@ func TestCover(t *testing.T) { ...@@ -131,6 +184,9 @@ func TestCover(t *testing.T) {
// above those declarations, even if they are not part of the block of // above those declarations, even if they are not part of the block of
// documentation comments. // documentation comments.
func TestDirectives(t *testing.T) { func TestDirectives(t *testing.T) {
t.Parallel()
buildCover(t)
// Read the source file and find all the directives. We'll keep // Read the source file and find all the directives. We'll keep
// track of whether each one has been seen in the output. // track of whether each one has been seen in the output.
testDirectives := filepath.Join(testdata, "directives.go") testDirectives := filepath.Join(testdata, "directives.go")
...@@ -140,8 +196,8 @@ func TestDirectives(t *testing.T) { ...@@ -140,8 +196,8 @@ func TestDirectives(t *testing.T) {
} }
sourceDirectives := findDirectives(source) sourceDirectives := findDirectives(source)
// go tool cover -mode=atomic ./testdata/directives.go // testcover -mode=atomic ./testdata/directives.go
cmd := exec.Command(testenv.GoToolPath(t), "tool", "cover", "-mode=atomic", testDirectives) cmd := exec.Command(testcover, "-mode=atomic", testDirectives)
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
output, err := cmd.Output() output, err := cmd.Output()
if err != nil { if err != nil {
...@@ -247,8 +303,10 @@ func findDirectives(source []byte) []directiveInfo { ...@@ -247,8 +303,10 @@ func findDirectives(source []byte) []directiveInfo {
// Makes sure that `cover -func=profile.cov` reports accurate coverage. // Makes sure that `cover -func=profile.cov` reports accurate coverage.
// Issue #20515. // Issue #20515.
func TestCoverFunc(t *testing.T) { func TestCoverFunc(t *testing.T) {
// go tool cover -func ./testdata/profile.cov t.Parallel()
cmd := exec.Command(testenv.GoToolPath(t), "tool", "cover", "-func", coverProfile) buildCover(t)
// testcover -func ./testdata/profile.cov
cmd := exec.Command(testcover, "-func", coverProfile)
out, err := cmd.Output() out, err := cmd.Output()
if err != nil { if err != nil {
if ee, ok := err.(*exec.ExitError); ok { if ee, ok := err.(*exec.ExitError); ok {
...@@ -266,19 +324,14 @@ func TestCoverFunc(t *testing.T) { ...@@ -266,19 +324,14 @@ func TestCoverFunc(t *testing.T) {
// Check that cover produces correct HTML. // Check that cover produces correct HTML.
// Issue #25767. // Issue #25767.
func TestCoverHTML(t *testing.T) { func TestCoverHTML(t *testing.T) {
testenv.MustHaveGoBuild(t) t.Parallel()
if !*debug { testenv.MustHaveGoRun(t)
defer os.Remove(testcover) buildCover(t)
defer os.Remove(htmlProfile)
defer os.Remove(htmlHTML)
}
// go build -o testcover
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", testcover)
run(cmd, t)
// go test -coverprofile testdata/html/html.cov cmd/cover/testdata/html // go test -coverprofile testdata/html/html.cov cmd/cover/testdata/html
cmd = exec.Command(testenv.GoToolPath(t), "test", "-coverprofile", htmlProfile, "cmd/cover/testdata/html") cmd := exec.Command(testenv.GoToolPath(t), "test", "-coverprofile", htmlProfile, "cmd/cover/testdata/html")
run(cmd, t) run(cmd, t)
// ./testcover -html testdata/html/html.cov -o testdata/html/html.html // testcover -html testdata/html/html.cov -o testdata/html/html.html
cmd = exec.Command(testcover, "-html", htmlProfile, "-o", htmlHTML) cmd = exec.Command(testcover, "-html", htmlProfile, "-o", htmlHTML)
run(cmd, t) run(cmd, t)
...@@ -303,6 +356,9 @@ func TestCoverHTML(t *testing.T) { ...@@ -303,6 +356,9 @@ func TestCoverHTML(t *testing.T) {
in = false in = false
} }
} }
if scan.Err() != nil {
t.Error(scan.Err())
}
golden, err := ioutil.ReadFile(htmlGolden) golden, err := ioutil.ReadFile(htmlGolden)
if err != nil { if err != nil {
t.Fatalf("reading golden file: %v", err) t.Fatalf("reading golden file: %v", err)
...@@ -331,6 +387,7 @@ func TestCoverHTML(t *testing.T) { ...@@ -331,6 +387,7 @@ func TestCoverHTML(t *testing.T) {
func run(c *exec.Cmd, t *testing.T) { func run(c *exec.Cmd, t *testing.T) {
t.Helper() t.Helper()
t.Log("running", c.Args)
c.Stdout = os.Stdout c.Stdout = os.Stdout
c.Stderr = os.Stderr c.Stderr = os.Stderr
err := c.Run() err := c.Run()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment