Skip to content

Commit 49a1a01

Browse files
cespareBryan C. Mills
authored and
Bryan C. Mills
committed
cmd/go: move automatic testing.Init call into generated test code
In CL 173722, we moved the flag registration in the testing package into an Init function. In order to avoid needing changes to user code, we called Init automatically as part of testing.MainStart. However, that isn't early enough if flag.Parse is called before the tests run, as part of package initialization. Fix this by injecting a bit of code to call testing.Init into test packages. This runs before any other initialization code in the user's test package, so testing.Init will be called before any user code can call flag.Parse. Fixes #31859 Change-Id: Ib42cd8d3819150c49a3cecf7eef2472319d0c7e9 Reviewed-on: https://go-review.googlesource.com/c/go/+/176098 Run-TryBot: Caleb Spare <cespare@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
1 parent 5833aa5 commit 49a1a01

File tree

10 files changed

+163
-44
lines changed

10 files changed

+163
-44
lines changed

src/cmd/go/go_test.go

-6
Original file line numberDiff line numberDiff line change
@@ -3176,12 +3176,6 @@ func TestGoTestFooTestWorks(t *testing.T) {
31763176
tg.run("test", "testdata/standalone_test.go")
31773177
}
31783178

3179-
func TestGoTestTestMainSeesTestingFlags(t *testing.T) {
3180-
tg := testgo(t)
3181-
defer tg.cleanup()
3182-
tg.run("test", "testdata/standalone_testmain_flag_test.go")
3183-
}
3184-
31853179
// Issue 22388
31863180
func TestGoTestMainWithWrongSignature(t *testing.T) {
31873181
tg := testgo(t)

src/cmd/go/internal/list/list.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ func runList(cmd *base.Command, args []string) {
459459
}
460460
if pmain != nil {
461461
pkgs = append(pkgs, pmain)
462-
data := *pmain.Internal.TestmainGo
462+
data := pmain.Internal.TestmainGo
463463
h := cache.NewHash("testmain")
464464
h.Write([]byte("testmain\n"))
465465
h.Write(data)

src/cmd/go/internal/load/pkg.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ type PackageInternal struct {
177177
OmitDebug bool // tell linker not to write debug information
178178
GobinSubdir bool // install target would be subdir of GOBIN
179179
BuildInfo string // add this info to package main
180-
TestmainGo *[]byte // content for _testmain.go
180+
TestinginitGo []byte // content for _testinginit.go
181+
TestmainGo []byte // content for _testmain.go
181182

182183
Asmflags []string // -asmflags for this package
183184
Gcflags []string // -gcflags for this package

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

+41-3
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ func TestPackagesAndErrors(p *Package, cover *TestCover) (pmain, ptest, pxtest *
102102
var stk ImportStack
103103
stk.Push(p.ImportPath + " (test)")
104104
rawTestImports := str.StringList(p.TestImports)
105+
var ptestImportsTesting, pxtestImportsTesting bool
105106
for i, path := range p.TestImports {
106107
p1 := loadImport(pre, path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], ResolveImport)
107108
if str.Contains(p1.Deps, p.ImportPath) || p1.ImportPath == p.ImportPath {
@@ -116,6 +117,9 @@ func TestPackagesAndErrors(p *Package, cover *TestCover) (pmain, ptest, pxtest *
116117
}
117118
p.TestImports[i] = p1.ImportPath
118119
imports = append(imports, p1)
120+
if path == "testing" {
121+
ptestImportsTesting = true
122+
}
119123
}
120124
stk.Pop()
121125
stk.Push(p.ImportPath + "_test")
@@ -129,6 +133,9 @@ func TestPackagesAndErrors(p *Package, cover *TestCover) (pmain, ptest, pxtest *
129133
ximports = append(ximports, p1)
130134
}
131135
p.XTestImports[i] = p1.ImportPath
136+
if path == "testing" {
137+
pxtestImportsTesting = true
138+
}
132139
}
133140
stk.Pop()
134141

@@ -138,6 +145,9 @@ func TestPackagesAndErrors(p *Package, cover *TestCover) (pmain, ptest, pxtest *
138145
*ptest = *p
139146
ptest.Error = ptestErr
140147
ptest.ForTest = p.ImportPath
148+
if ptestImportsTesting {
149+
ptest.Internal.TestinginitGo = formatTestinginit(p)
150+
}
141151
ptest.GoFiles = nil
142152
ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...)
143153
ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...)
@@ -201,6 +211,9 @@ func TestPackagesAndErrors(p *Package, cover *TestCover) (pmain, ptest, pxtest *
201211
Gccgoflags: p.Internal.Gccgoflags,
202212
},
203213
}
214+
if pxtestImportsTesting {
215+
pxtest.Internal.TestinginitGo = formatTestinginit(pxtest)
216+
}
204217
if pxtestNeedsPtest {
205218
pxtest.Internal.Imports = append(pxtest.Internal.Imports, ptest)
206219
}
@@ -323,9 +336,7 @@ func TestPackagesAndErrors(p *Package, cover *TestCover) (pmain, ptest, pxtest *
323336
if err != nil && pmain.Error == nil {
324337
pmain.Error = &PackageError{Err: err.Error()}
325338
}
326-
if data != nil {
327-
pmain.Internal.TestmainGo = &data
328-
}
339+
pmain.Internal.TestmainGo = data
329340

330341
return pmain, ptest, pxtest
331342
}
@@ -473,6 +484,15 @@ func loadTestFuncs(ptest *Package) (*testFuncs, error) {
473484
return t, err
474485
}
475486

487+
// formatTestinginit returns the content of the _testinginit.go file for p.
488+
func formatTestinginit(p *Package) []byte {
489+
var buf bytes.Buffer
490+
if err := testinginitTmpl.Execute(&buf, p); err != nil {
491+
panic("testinginit template execution failed") // shouldn't be possible
492+
}
493+
return buf.Bytes()
494+
}
495+
476496
// formatTestmain returns the content of the _testmain.go file for t.
477497
func formatTestmain(t *testFuncs) ([]byte, error) {
478498
var buf bytes.Buffer
@@ -602,6 +622,24 @@ func checkTestFunc(fn *ast.FuncDecl, arg string) error {
602622
return nil
603623
}
604624

625+
var testinginitTmpl = lazytemplate.New("init", `
626+
package {{.Name}}
627+
628+
{{/* Avoid a name collision with a name "testing" in user code. */}}
629+
import testing_xxxxxxxxxxxx "testing"
630+
631+
{{/*
632+
Call testing.Init before any other user initialization code runs.
633+
(This file is passed to the compiler first.)
634+
This provides the illusion of the old behavior where testing flags
635+
were registered as part of the testing package's initialization.
636+
*/}}
637+
var _ = func() bool {
638+
testing_xxxxxxxxxxxx.Init()
639+
return true
640+
}()
641+
`)
642+
605643
var testmainTmpl = lazytemplate.New("main", `
606644
package main
607645

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
835835
if !cfg.BuildN {
836836
// writeTestmain writes _testmain.go,
837837
// using the test description gathered in t.
838-
if err := ioutil.WriteFile(testDir+"_testmain.go", *pmain.Internal.TestmainGo, 0666); err != nil {
838+
if err := ioutil.WriteFile(testDir+"_testmain.go", pmain.Internal.TestmainGo, 0666); err != nil {
839839
return nil, nil, nil, err
840840
}
841841
}

src/cmd/go/internal/work/exec.go

+9
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,15 @@ func (b *Builder) build(a *Action) (err error) {
523523
}
524524
}
525525

526+
// Write out the _testinginit.go file for any test packages that import "testing".
527+
if a.Package.Internal.TestinginitGo != nil {
528+
initfile := objdir + "_testinginit.go"
529+
if err := b.writeFile(initfile, a.Package.Internal.TestinginitGo); err != nil {
530+
return err
531+
}
532+
gofiles = append([]string{initfile}, gofiles...)
533+
}
534+
526535
// Run cgo.
527536
if a.Package.UsesCgo() || a.Package.UsesSwig() {
528537
// In a package using cgo, cgo compiles the C, C++ and assembly files with gcc.

src/cmd/go/testdata/flag_test.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
15
package flag_test
26

37
import (
48
"flag"
5-
"log"
69
"testing"
710
)
811

912
var v = flag.Int("v", 0, "v flag")
1013

11-
// Run this as go test pkg -v=7
14+
// Run this as go test pkg -args -v=7
1215
func TestVFlagIsSet(t *testing.T) {
1316
if *v != 7 {
14-
log.Fatal("v flag not set")
17+
t.Fatal("v flag not set")
1518
}
1619
}
+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Tests for automatic testing.Init calls when using 'go test'.
2+
3+
env GO111MODULE=on
4+
5+
# A TestMain should be able to access testing flags if it calls flag.Parse
6+
# without needing to use testing.Init.
7+
go test testmain_flag_test.go
8+
9+
# Test code can use the name 'testing' without colliding with generated
10+
# testinginit code.
11+
go test testing_collision_test.go
12+
13+
# Tests running under 'go test' should observe that testing.Init is called
14+
# before any user package initialization code runs.
15+
go test ./testinitflag
16+
17+
-- testmain_flag_test.go --
18+
package testmain_flag_test
19+
20+
import (
21+
"flag"
22+
"fmt"
23+
"os"
24+
"testing"
25+
)
26+
27+
func TestMain(m *testing.M) {
28+
flag.Parse()
29+
found := false
30+
flag.VisitAll(func(f *flag.Flag) {
31+
if f.Name == "test.count" {
32+
found = true
33+
}
34+
})
35+
if !found {
36+
fmt.Println("testing flags not registered")
37+
os.Exit(1)
38+
}
39+
os.Exit(m.Run())
40+
}
41+
42+
func TestX(t *testing.T) {}
43+
44+
-- testing_collision_test.go --
45+
package testing_collision_test
46+
47+
import testing2 "testing"
48+
49+
var testing = 3
50+
51+
func TestX(t *testing2.T) {}
52+
53+
-- go.mod --
54+
module m
55+
56+
-- testinitflag/init.go --
57+
package testinitflag
58+
59+
import "flag"
60+
61+
func TestFlagsInitialized() bool {
62+
found := false
63+
flag.VisitAll(func(f *flag.Flag) {
64+
if f.Name == "test.count" {
65+
found = true
66+
}
67+
})
68+
return found
69+
}
70+
71+
-- testinitflag/init_test.go --
72+
package testinitflag
73+
74+
import "testing"
75+
76+
var testingInitAtInitialization = TestFlagsInitialized()
77+
78+
func TestInit(t *testing.T) {
79+
if !testingInitAtInitialization {
80+
t.Fatal("testing.Init not called before package initialization")
81+
}
82+
}
83+
84+
-- testinitflag/external_test.go --
85+
package testinitflag_test
86+
87+
import (
88+
"testing"
89+
"m/testinitflag"
90+
)
91+
92+
var testingInitAtInitialization = testinitflag.TestFlagsInitialized()
93+
94+
func TestInitExternal(t *testing.T) {
95+
if !testingInitAtInitialization {
96+
t.Fatal("testing.Init not called before package initialization")
97+
}
98+
}

src/cmd/go/testdata/standalone_testmain_flag_test.go

-29
This file was deleted.

src/testing/testing.go

+5
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,11 @@ type testDeps interface {
10781078
// It is not meant to be called directly and is not subject to the Go 1 compatibility document.
10791079
// It may change signature from release to release.
10801080
func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
1081+
// In most cases, Init has already been called by the testinginit code
1082+
// that 'go test' injects into test packages.
1083+
// Call it again here to handle cases such as:
1084+
// - test packages that don't import "testing" (such as example-only packages)
1085+
// - direct use of MainStart (though that isn't well-supported)
10811086
Init()
10821087
return &M{
10831088
deps: deps,

0 commit comments

Comments
 (0)