Skip to content

Commit 4a801cd

Browse files
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent ec0077c commit 4a801cd

File tree

1 file changed

+102
-45
lines changed

1 file changed

+102
-45
lines changed

src/cmd/cover/cover_test.go

+102-45
Original file line numberDiff line numberDiff line change
@@ -19,43 +19,103 @@ import (
1919
"path/filepath"
2020
"regexp"
2121
"strings"
22+
"sync"
2223
"testing"
2324
)
2425

2526
const (
2627
// Data directory, also the package directory for the test.
2728
testdata = "testdata"
28-
29-
// Binaries we compile.
30-
testcover = "./testcover.exe"
3129
)
3230

3331
var (
34-
// Files we use.
32+
// Input files.
3533
testMain = filepath.Join(testdata, "main.go")
3634
testTest = filepath.Join(testdata, "test.go")
37-
coverInput = filepath.Join(testdata, "test_line.go")
38-
coverOutput = filepath.Join(testdata, "test_cover.go")
3935
coverProfile = filepath.Join(testdata, "profile.cov")
4036

4137
// The HTML test files are in a separate directory
4238
// so they are a complete package.
43-
htmlProfile = filepath.Join(testdata, "html", "html.cov")
44-
htmlHTML = filepath.Join(testdata, "html", "html.html")
45-
htmlGolden = filepath.Join(testdata, "html", "html.golden")
39+
htmlGolden = filepath.Join(testdata, "html", "html.golden")
40+
41+
// Temporary files.
42+
tmpTestMain string
43+
coverInput string
44+
coverOutput string
45+
htmlProfile string
46+
htmlHTML string
47+
)
48+
49+
var (
50+
// testTempDir is a temporary directory created in TestMain.
51+
testTempDir string
52+
53+
// testcover is a newly built version of the cover program.
54+
testcover string
55+
56+
// testcoverErr records an error building testcover.
57+
testcoverErr error
58+
59+
// testcoverOnce is used to build testcover once.
60+
testcoverOnce sync.Once
4661
)
4762

4863
var debug = flag.Bool("debug", false, "keep rewritten files for debugging")
4964

65+
// We use TestMain to set up a temporary directory and remove it when
66+
// the tests are done.
67+
func TestMain(m *testing.M) {
68+
dir, err := ioutil.TempDir("", "gotestcover")
69+
if err != nil {
70+
fmt.Fprintln(os.Stderr, err)
71+
os.Exit(1)
72+
}
73+
74+
testTempDir = dir
75+
76+
tmpTestMain = filepath.Join(dir, "main.go")
77+
coverInput = filepath.Join(dir, "test_line.go")
78+
coverOutput = filepath.Join(dir, "test_cover.go")
79+
htmlProfile = filepath.Join(dir, "html.cov")
80+
htmlHTML = filepath.Join(dir, "html.html")
81+
82+
status := m.Run()
83+
84+
if !*debug {
85+
os.RemoveAll(dir)
86+
}
87+
88+
os.Exit(status)
89+
}
90+
91+
// buildCover builds a version of the cover program for testing.
92+
// This ensures that "go test cmd/cover" tests the current cmd/cover.
93+
func buildCover(t *testing.T) {
94+
t.Helper()
95+
testenv.MustHaveGoBuild(t)
96+
testcoverOnce.Do(func() {
97+
testcover = filepath.Join(testTempDir, "testcover.exe")
98+
t.Logf("running [go build -o %s]", testcover)
99+
out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", testcover).CombinedOutput()
100+
t.Logf("%s", out)
101+
testcoverErr = err
102+
})
103+
if testcoverErr != nil {
104+
t.Fatal("failed to build testcover program:", testcoverErr)
105+
}
106+
}
107+
50108
// Run this shell script, but do it in Go so it can be run by "go test".
51109
//
52110
// replace the word LINE with the line number < testdata/test.go > testdata/test_line.go
53-
// go build -o ./testcover
54-
// ./testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go
111+
// go build -o testcover
112+
// testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go
55113
// go run ./testdata/main.go ./testdata/test.go
56114
//
57115
func TestCover(t *testing.T) {
58-
testenv.MustHaveGoBuild(t)
116+
t.Parallel()
117+
testenv.MustHaveGoRun(t)
118+
buildCover(t)
59119

60120
// Read in the test file (testTest) and write it, with LINEs specified, to coverInput.
61121
file, err := ioutil.ReadFile(testTest)
@@ -81,29 +141,22 @@ func TestCover(t *testing.T) {
81141
t.Fatal(err)
82142
}
83143

84-
// defer removal of test_line.go
85-
if !*debug {
86-
defer os.Remove(coverInput)
87-
}
88-
89-
// go build -o testcover
90-
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", testcover)
144+
// testcover -mode=count -var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest -o ./testdata/test_cover.go testdata/test_line.go
145+
cmd := exec.Command(testcover, "-mode=count", "-var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest", "-o", coverOutput, coverInput)
91146
run(cmd, t)
92147

93-
// defer removal of testcover
94-
defer os.Remove(testcover)
95-
96-
// ./testcover -mode=count -var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest -o ./testdata/test_cover.go testdata/test_line.go
97-
cmd = exec.Command(testcover, "-mode=count", "-var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest", "-o", coverOutput, coverInput)
98-
run(cmd, t)
99-
100-
// defer removal of ./testdata/test_cover.go
101-
if !*debug {
102-
defer os.Remove(coverOutput)
148+
// Copy testmain to testTempDir, so that it is in the same directory
149+
// as coverOutput.
150+
b, err := ioutil.ReadFile(testMain)
151+
if err != nil {
152+
t.Fatal(err)
153+
}
154+
if err := ioutil.WriteFile(tmpTestMain, b, 0444); err != nil {
155+
t.Fatal(err)
103156
}
104157

105158
// go run ./testdata/main.go ./testdata/test.go
106-
cmd = exec.Command(testenv.GoToolPath(t), "run", testMain, coverOutput)
159+
cmd = exec.Command(testenv.GoToolPath(t), "run", tmpTestMain, coverOutput)
107160
run(cmd, t)
108161

109162
file, err = ioutil.ReadFile(coverOutput)
@@ -131,6 +184,9 @@ func TestCover(t *testing.T) {
131184
// above those declarations, even if they are not part of the block of
132185
// documentation comments.
133186
func TestDirectives(t *testing.T) {
187+
t.Parallel()
188+
buildCover(t)
189+
134190
// Read the source file and find all the directives. We'll keep
135191
// track of whether each one has been seen in the output.
136192
testDirectives := filepath.Join(testdata, "directives.go")
@@ -140,8 +196,8 @@ func TestDirectives(t *testing.T) {
140196
}
141197
sourceDirectives := findDirectives(source)
142198

143-
// go tool cover -mode=atomic ./testdata/directives.go
144-
cmd := exec.Command(testenv.GoToolPath(t), "tool", "cover", "-mode=atomic", testDirectives)
199+
// testcover -mode=atomic ./testdata/directives.go
200+
cmd := exec.Command(testcover, "-mode=atomic", testDirectives)
145201
cmd.Stderr = os.Stderr
146202
output, err := cmd.Output()
147203
if err != nil {
@@ -247,8 +303,10 @@ func findDirectives(source []byte) []directiveInfo {
247303
// Makes sure that `cover -func=profile.cov` reports accurate coverage.
248304
// Issue #20515.
249305
func TestCoverFunc(t *testing.T) {
250-
// go tool cover -func ./testdata/profile.cov
251-
cmd := exec.Command(testenv.GoToolPath(t), "tool", "cover", "-func", coverProfile)
306+
t.Parallel()
307+
buildCover(t)
308+
// testcover -func ./testdata/profile.cov
309+
cmd := exec.Command(testcover, "-func", coverProfile)
252310
out, err := cmd.Output()
253311
if err != nil {
254312
if ee, ok := err.(*exec.ExitError); ok {
@@ -266,19 +324,14 @@ func TestCoverFunc(t *testing.T) {
266324
// Check that cover produces correct HTML.
267325
// Issue #25767.
268326
func TestCoverHTML(t *testing.T) {
269-
testenv.MustHaveGoBuild(t)
270-
if !*debug {
271-
defer os.Remove(testcover)
272-
defer os.Remove(htmlProfile)
273-
defer os.Remove(htmlHTML)
274-
}
275-
// go build -o testcover
276-
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", testcover)
277-
run(cmd, t)
327+
t.Parallel()
328+
testenv.MustHaveGoRun(t)
329+
buildCover(t)
330+
278331
// go test -coverprofile testdata/html/html.cov cmd/cover/testdata/html
279-
cmd = exec.Command(testenv.GoToolPath(t), "test", "-coverprofile", htmlProfile, "cmd/cover/testdata/html")
332+
cmd := exec.Command(testenv.GoToolPath(t), "test", "-coverprofile", htmlProfile, "cmd/cover/testdata/html")
280333
run(cmd, t)
281-
// ./testcover -html testdata/html/html.cov -o testdata/html/html.html
334+
// testcover -html testdata/html/html.cov -o testdata/html/html.html
282335
cmd = exec.Command(testcover, "-html", htmlProfile, "-o", htmlHTML)
283336
run(cmd, t)
284337

@@ -303,6 +356,9 @@ func TestCoverHTML(t *testing.T) {
303356
in = false
304357
}
305358
}
359+
if scan.Err() != nil {
360+
t.Error(scan.Err())
361+
}
306362
golden, err := ioutil.ReadFile(htmlGolden)
307363
if err != nil {
308364
t.Fatalf("reading golden file: %v", err)
@@ -331,6 +387,7 @@ func TestCoverHTML(t *testing.T) {
331387

332388
func run(c *exec.Cmd, t *testing.T) {
333389
t.Helper()
390+
t.Log("running", c.Args)
334391
c.Stdout = os.Stdout
335392
c.Stderr = os.Stderr
336393
err := c.Run()

0 commit comments

Comments
 (0)