Skip to content

Commit

Permalink
testing: fail if T.Setenv is called via T.Run in a parallel test
Browse files Browse the repository at this point in the history
The existing implementation can call to T.Setenv in T.Run even after
calling to T.Parallel, so I changed it to cause a panic in that case.

Fixes #55128

Change-Id: Ib89d998ff56f00f96a5ca218af071bd35fdae53a
Reviewed-on: https://go-review.googlesource.com/c/go/+/431101
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>
  • Loading branch information
noi authored and gopherbot committed Sep 28, 2022
1 parent 7f7f27f commit d6ca244
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 4 deletions.
20 changes: 16 additions & 4 deletions src/testing/testing.go
Expand Up @@ -547,6 +547,7 @@ type common struct {
hasSub atomic.Bool // whether there are sub-benchmarks.
raceErrors int // Number of races detected during test.
runner string // Function name of tRunner running the test.
isParallel bool // Whether the test is parallel.

parent *common
level int // Nesting depth of test or benchmark.
Expand Down Expand Up @@ -823,9 +824,8 @@ var _ TB = (*B)(nil)
// may be called simultaneously from multiple goroutines.
type T struct {
common
isParallel bool
isEnvSet bool
context *testContext // For running tests and subtests.
isEnvSet bool
context *testContext // For running tests and subtests.
}

func (c *common) private() {}
Expand Down Expand Up @@ -1326,7 +1326,19 @@ func (t *T) Parallel() {
//
// This cannot be used in parallel tests.
func (t *T) Setenv(key, value string) {
if t.isParallel {
// Non-parallel subtests that have parallel ancestors may still
// run in parallel with other tests: they are only non-parallel
// with respect to the other subtests of the same parent.
// Since SetEnv affects the whole process, we need to disallow it
// if the current test or any parent is parallel.
isParallel := false
for c := &t.common; c != nil; c = c.parent {
if c.isParallel {
isParallel = true
break
}
}
if isParallel {
panic("testing: t.Setenv called after t.Parallel; cannot set environment variables in parallel tests")
}

Expand Down
32 changes: 32 additions & 0 deletions src/testing/testing_test.go
Expand Up @@ -200,3 +200,35 @@ func TestSetenvWithParallelBeforeSetenv(t *testing.T) {

t.Setenv("GO_TEST_KEY_1", "value")
}

func TestSetenvWithParallelParentBeforeSetenv(t *testing.T) {
t.Parallel()

t.Run("child", func(t *testing.T) {
defer func() {
want := "testing: t.Setenv called after t.Parallel; cannot set environment variables in parallel tests"
if got := recover(); got != want {
t.Fatalf("expected panic; got %#v want %q", got, want)
}
}()

t.Setenv("GO_TEST_KEY_1", "value")
})
}

func TestSetenvWithParallelGrandParentBeforeSetenv(t *testing.T) {
t.Parallel()

t.Run("child", func(t *testing.T) {
t.Run("grand-child", func(t *testing.T) {
defer func() {
want := "testing: t.Setenv called after t.Parallel; cannot set environment variables in parallel tests"
if got := recover(); got != want {
t.Fatalf("expected panic; got %#v want %q", got, want)
}
}()

t.Setenv("GO_TEST_KEY_1", "value")
})
})
}

0 comments on commit d6ca244

Please sign in to comment.