Skip to content

Commit e47cab1

Browse files
committed
cmd/go: fix objdir for run actions for -cover no-test packages
As of CL 495447 we now synthesize coverage data (including coverage profiles) for packages that have no tests, if they are included in a "go test -cover" run. The code that set up the "run" actions for such tests wasn't setting the objdir for the action, which meant that the coverage profile temp file fragment ("_cover_.out") was being created in the dir where the test was run, and in addition the same fragment could be written to by more than one package (which could lead to a corrupted file). This CL updates the code to properly set the objdir, and to create the dir when needed. Updates #24570. Fixes #63356. Change-Id: Iffe131cf50f07ce91085b816a039308e0ca84776 Reviewed-on: https://go-review.googlesource.com/c/go/+/532555 Reviewed-by: Russ Cox <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 3a69dcd commit e47cab1

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed

src/cmd/go/internal/test/test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,7 @@ func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts,
10591059
Mode: "test run",
10601060
Actor: new(runTestActor),
10611061
Deps: []*work.Action{build},
1062+
Objdir: b.NewObjdir(),
10621063
Package: p,
10631064
IgnoreFail: true, // run (prepare output) even if build failed
10641065
}
@@ -1385,12 +1386,18 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
13851386
}
13861387

13871388
coverProfTempFile := func(a *work.Action) string {
1389+
if a.Objdir == "" {
1390+
panic("internal error: objdir not set in coverProfTempFile")
1391+
}
13881392
return a.Objdir + "_cover_.out"
13891393
}
13901394

13911395
if p := a.Package; len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
13921396
reportNoTestFiles := true
13931397
if cfg.BuildCover && cfg.Experiment.CoverageRedesign {
1398+
if err := b.Mkdir(a.Objdir); err != nil {
1399+
return err
1400+
}
13941401
mf, err := work.BuildActionCoverMetaFile(a)
13951402
if err != nil {
13961403
return err
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
2+
# Testcase for #63356. In this bug we're doing a "go test -coverprofile"
3+
# run for a collection of packages, mostly independent (hence tests can
4+
# be done in parallel) and in the original bug, temp coverage profile
5+
# files were not being properly qualified and were colliding, resulting
6+
# in a corrupted final profile. Actual content of the packages doesn't
7+
# especially matter as long as we have a mix of packages with tests and
8+
# multiple packages without tests.
9+
10+
[short] skip
11+
12+
# Kick off test.
13+
go test -p=10 -vet=off -count=1 -coverprofile=cov.p ./...
14+
15+
# Make sure resulting profile is digestible.
16+
go tool cover -func=cov.p
17+
18+
# No extraneous extra files please.
19+
! exists _cover_.out
20+
21+
-- a/a.go --
22+
package a
23+
24+
func init() {
25+
println("package 'a' init: launch the missiles!")
26+
}
27+
28+
func AFunc() int {
29+
return 42
30+
}
31+
-- a/a_test.go --
32+
package a
33+
34+
import "testing"
35+
36+
func TestA(t *testing.T) {
37+
if AFunc() != 42 {
38+
t.Fatalf("bad!")
39+
}
40+
}
41+
-- aa/aa.go --
42+
package aa
43+
44+
import "M/it"
45+
46+
func AA(y int) int {
47+
c := it.Conc{}
48+
x := it.Callee(&c)
49+
println(x, y)
50+
return 0
51+
}
52+
-- aa/aa_test.go --
53+
package aa
54+
55+
import "testing"
56+
57+
func TestMumble(t *testing.T) {
58+
AA(3)
59+
}
60+
-- b/b.go --
61+
package b
62+
63+
func init() {
64+
println("package 'b' init: release the kraken")
65+
}
66+
67+
func BFunc() int {
68+
return -42
69+
}
70+
-- b/b_test.go --
71+
package b
72+
73+
import "testing"
74+
75+
func TestB(t *testing.T) {
76+
if BFunc() != -42 {
77+
t.Fatalf("bad!")
78+
}
79+
}
80+
-- deadstuff/deadstuff.go --
81+
package deadstuff
82+
83+
func downStreamOfPanic(x int) {
84+
panic("bad")
85+
if x < 10 {
86+
println("foo")
87+
}
88+
}
89+
-- deadstuff/deadstuff_test.go --
90+
package deadstuff
91+
92+
import "testing"
93+
94+
func TestMumble(t *testing.T) {
95+
defer func() {
96+
if x := recover(); x != nil {
97+
println("recovered")
98+
}
99+
}()
100+
downStreamOfPanic(10)
101+
}
102+
-- go.mod --
103+
module M
104+
105+
go 1.21
106+
-- it/it.go --
107+
package it
108+
109+
type Ctr interface {
110+
Count() int
111+
}
112+
113+
type Conc struct {
114+
X int
115+
}
116+
117+
func (c *Conc) Count() int {
118+
return c.X
119+
}
120+
121+
func DoCall(c *Conc) {
122+
c2 := Callee(c)
123+
println(c2.Count())
124+
}
125+
126+
func Callee(ii Ctr) Ctr {
127+
q := ii.Count()
128+
return &Conc{X: q}
129+
}
130+
-- main/main.go --
131+
package main
132+
133+
import (
134+
"M/a"
135+
"M/b"
136+
)
137+
138+
func MFunc() string {
139+
return "42"
140+
}
141+
142+
func M2Func() int {
143+
return a.AFunc() + b.BFunc()
144+
}
145+
146+
func init() {
147+
println("package 'main' init")
148+
}
149+
150+
func main() {
151+
println(a.AFunc() + b.BFunc())
152+
}
153+
-- main/main_test.go --
154+
package main
155+
156+
import "testing"
157+
158+
func TestMain(t *testing.T) {
159+
if MFunc() != "42" {
160+
t.Fatalf("bad!")
161+
}
162+
if M2Func() != 0 {
163+
t.Fatalf("also bad!")
164+
}
165+
}
166+
-- n/n.go --
167+
package n
168+
169+
type N int
170+
-- onlytest/mumble_test.go --
171+
package onlytest
172+
173+
import "testing"
174+
175+
func TestFoo(t *testing.T) {
176+
t.Logf("Whee\n")
177+
}
178+
-- x/x.go --
179+
package x
180+
181+
func XFunc() int {
182+
return 2 * 2
183+
}
184+
-- xinternal/i.go --
185+
package i
186+
187+
func I() int { return 32 }
188+
-- xinternal/q/q.go --
189+
package q
190+
191+
func Q() int {
192+
return 42
193+
}

0 commit comments

Comments
 (0)