Skip to content

Commit 2e794c2

Browse files
bynovmvdan
authored andcommitted
testing: add TB.Setenv
Add a new method TB.Setenv that'll set environment variables only for the isolated lifetime of the test, and will clean up and unset these variables when the test ends. This method disables the test or benchmark from running in parallel. Fixes #41260 Change-Id: I0a18f094ec1c6ec3157b4b12993ea3075e2e9867 GitHub-Last-Rev: 0ca12fa GitHub-Pull-Request: #41857 Reviewed-on: https://go-review.googlesource.com/c/go/+/260577 Trust: Daniel Martí <[email protected]> Trust: Emmanuel Odeke <[email protected]> Run-TryBot: Daniel Martí <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: roger peppe <[email protected]>
1 parent 2217e89 commit 2e794c2

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

src/testing/testing.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ var _ TB = (*B)(nil)
667667
type T struct {
668668
common
669669
isParallel bool
670+
isEnvSet bool
670671
context *testContext // For running tests and subtests.
671672
}
672673

@@ -964,6 +965,29 @@ func (c *common) TempDir() string {
964965
return dir
965966
}
966967

968+
// Setenv calls os.Setenv(key, value) and uses Cleanup to
969+
// restore the environment variable to its original value
970+
// after the test.
971+
//
972+
// This cannot be used in parallel tests.
973+
func (c *common) Setenv(key, value string) {
974+
prevValue, ok := os.LookupEnv(key)
975+
976+
if err := os.Setenv(key, value); err != nil {
977+
c.Fatalf("cannot set environment variable: %v", err)
978+
}
979+
980+
if ok {
981+
c.Cleanup(func() {
982+
os.Setenv(key, prevValue)
983+
})
984+
} else {
985+
c.Cleanup(func() {
986+
os.Unsetenv(key)
987+
})
988+
}
989+
}
990+
967991
// panicHanding is an argument to runCleanup.
968992
type panicHandling int
969993

@@ -1035,6 +1059,9 @@ func (t *T) Parallel() {
10351059
if t.isParallel {
10361060
panic("testing: t.Parallel called multiple times")
10371061
}
1062+
if t.isEnvSet {
1063+
panic("testing: t.Parallel called after t.Setenv; cannot set environment variables in parallel tests")
1064+
}
10381065
t.isParallel = true
10391066

10401067
// We don't want to include the time we spend waiting for serial tests
@@ -1068,6 +1095,21 @@ func (t *T) Parallel() {
10681095
t.raceErrors += -race.Errors()
10691096
}
10701097

1098+
// Setenv calls os.Setenv(key, value) and uses Cleanup to
1099+
// restore the environment variable to its original value
1100+
// after the test.
1101+
//
1102+
// This cannot be used in parallel tests.
1103+
func (t *T) Setenv(key, value string) {
1104+
if t.isParallel {
1105+
panic("testing: t.Setenv called after t.Parallel; cannot set environment variables in parallel tests")
1106+
}
1107+
1108+
t.isEnvSet = true
1109+
1110+
t.common.Setenv(key, value)
1111+
}
1112+
10711113
// InternalTest is an internal type but exported because it is cross-package;
10721114
// it is part of the implementation of the "go test" command.
10731115
type InternalTest struct {

src/testing/testing_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,86 @@ func testTempDir(t *testing.T) {
109109
t.Errorf("unexpected %d files in TempDir: %v", len(files), files)
110110
}
111111
}
112+
113+
func TestSetenv(t *testing.T) {
114+
tests := []struct {
115+
name string
116+
key string
117+
initialValueExists bool
118+
initialValue string
119+
newValue string
120+
}{
121+
{
122+
name: "initial value exists",
123+
key: "GO_TEST_KEY_1",
124+
initialValueExists: true,
125+
initialValue: "111",
126+
newValue: "222",
127+
},
128+
{
129+
name: "initial value exists but empty",
130+
key: "GO_TEST_KEY_2",
131+
initialValueExists: true,
132+
initialValue: "",
133+
newValue: "222",
134+
},
135+
{
136+
name: "initial value is not exists",
137+
key: "GO_TEST_KEY_3",
138+
initialValueExists: false,
139+
initialValue: "",
140+
newValue: "222",
141+
},
142+
}
143+
144+
for _, test := range tests {
145+
if test.initialValueExists {
146+
if err := os.Setenv(test.key, test.initialValue); err != nil {
147+
t.Fatalf("unable to set env: got %v", err)
148+
}
149+
} else {
150+
os.Unsetenv(test.key)
151+
}
152+
153+
t.Run(test.name, func(t *testing.T) {
154+
t.Setenv(test.key, test.newValue)
155+
if os.Getenv(test.key) != test.newValue {
156+
t.Fatalf("unexpected value after t.Setenv: got %s, want %s", os.Getenv(test.key), test.newValue)
157+
}
158+
})
159+
160+
got, exists := os.LookupEnv(test.key)
161+
if got != test.initialValue {
162+
t.Fatalf("unexpected value after t.Setenv cleanup: got %s, want %s", got, test.initialValue)
163+
}
164+
if exists != test.initialValueExists {
165+
t.Fatalf("unexpected value after t.Setenv cleanup: got %t, want %t", exists, test.initialValueExists)
166+
}
167+
}
168+
}
169+
170+
func TestSetenvWithParallelAfterSetenv(t *testing.T) {
171+
defer func() {
172+
want := "testing: t.Parallel called after t.Setenv; cannot set environment variables in parallel tests"
173+
if got := recover(); got != want {
174+
t.Fatalf("expected panic; got %#v want %q", got, want)
175+
}
176+
}()
177+
178+
t.Setenv("GO_TEST_KEY_1", "value")
179+
180+
t.Parallel()
181+
}
182+
183+
func TestSetenvWithParallelBeforeSetenv(t *testing.T) {
184+
defer func() {
185+
want := "testing: t.Setenv called after t.Parallel; cannot set environment variables in parallel tests"
186+
if got := recover(); got != want {
187+
t.Fatalf("expected panic; got %#v want %q", got, want)
188+
}
189+
}()
190+
191+
t.Parallel()
192+
193+
t.Setenv("GO_TEST_KEY_1", "value")
194+
}

0 commit comments

Comments
 (0)