Logf(format string, args ...any)
Name() string
Setenv(key, value string)
+ SetGOMAXPROCS(n int)
Skip(args ...any)
SkipNow()
Skipf(format string, args ...any)
// may be called simultaneously from multiple goroutines.
type T struct {
common
- isEnvSet bool
- context *testContext // For running tests and subtests.
+ isEnvSet bool
+ isGOMAXPROCSSet bool
+ context *testContext // For running tests and subtests.
}
func (c *common) private() {}
}
}
+// SetGOMAXPROCS calls runtime.GOMAXPROCS(n) and uses Cleanup to
+// restore the value of GOMAXPROCS after the test.
+//
+// Because GOMAXPROCS affects the whole process, it cannot be used
+// in parallel tests or tests with parallel ancestors.
+func (c *common) SetGOMAXPROCS(n int) {
+ c.checkFuzzFn("SetGOMAXPROCS")
+ prev := runtime.GOMAXPROCS(n)
+ c.Cleanup(func() {
+ runtime.GOMAXPROCS(prev)
+ })
+}
+
// panicHanding controls the panic handling used by runCleanup.
type panicHandling int
if t.isEnvSet {
panic("testing: t.Parallel called after t.Setenv; cannot set environment variables in parallel tests")
}
+ if t.isGOMAXPROCSSet {
+ panic("testing: t.Parallel called after t.SetGOMAXPROCS; cannot set GOMAXPROCS in parallel tests")
+ }
t.isParallel = true
if t.parent.barrier == nil {
// T.Parallel has no effect when fuzzing.
t.common.Setenv(key, value)
}
+// SetGOMAXPROCS calls runtime.GOMAXPROCS(n) and uses Cleanup to
+// restore the value of GOMAXPROCS after the test.
+//
+// Because GOMAXPROCS affects the whole process, it cannot be used
+// in parallel tests or tests with parallel ancestors.
+func (t *T) SetGOMAXPROCS(n int) {
+ // 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 SetGOMAXPROCS 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.SetGOMAXPROCS called after t.Parallel; cannot set GOMAXPROCS in parallel tests")
+ }
+
+ t.isGOMAXPROCSSet = true
+
+ t.common.SetGOMAXPROCS(n)
+}
+
// InternalTest is an internal type but exported because it is cross-package;
// it is part of the implementation of the "go test" command.
type InternalTest struct {
"os/exec"
"path/filepath"
"regexp"
+ "runtime"
"slices"
"strings"
"sync"
})
}
+func TestSetGOMAXPROCS(t *testing.T) {
+ if runtime.GOARCH == "wasm" {
+ t.Skip("not supported on wasm yet")
+ }
+ tests := []struct {
+ name string
+ newP int
+ }{
+ {
+ name: "overriding value",
+ newP: 1,
+ },
+ }
+
+ for _, test := range tests {
+ p := runtime.GOMAXPROCS(0)
+ t.Run(test.name, func(t *testing.T) {
+ t.SetGOMAXPROCS(test.newP + 1)
+ if runtime.GOMAXPROCS(0) != test.newP+1 {
+ t.Fatalf("unexpected value after t.SetGOMAXPROCS: got %d, want %d", runtime.GOMAXPROCS(0), test.newP+1)
+ }
+ })
+ if runtime.GOMAXPROCS(0) != p {
+ t.Fatalf("unexpected value after t.SetGOMAXPROCS cleanup: got %d, want %d", runtime.GOMAXPROCS(0), p)
+ }
+ }
+}
+
+func TestSetGOMAXPROCSWithParallelAfterSetGOMAXPROCS(t *testing.T) {
+ if runtime.GOARCH == "wasm" {
+ t.Skip("not supported on wasm yet")
+ }
+ defer func() {
+ want := "testing: t.Parallel called after t.SetGOMAXPROCS; cannot set GOMAXPROCS in parallel tests"
+ if got := recover(); got != want {
+ t.Fatalf("expected panic; got %#v want %q", got, want)
+ }
+ }()
+ p := runtime.GOMAXPROCS(0)
+ t.SetGOMAXPROCS(p + 1)
+ t.Parallel()
+}
+
+func TestSetGOMAXPROCSWithParallelBeforeSetGOMAXPROCS(t *testing.T) {
+ if runtime.GOARCH == "wasm" {
+ t.Skip("not supported on wasm yet")
+ }
+ defer func() {
+ want := "testing: t.SetGOMAXPROCS called after t.Parallel; cannot set GOMAXPROCS in parallel tests"
+ if got := recover(); got != want {
+ t.Fatalf("expected panic; got %#v want %q", got, want)
+ }
+ }()
+ t.Parallel()
+ p := runtime.GOMAXPROCS(0)
+ t.SetGOMAXPROCS(p + 1)
+}
+
+func TestSetGOMAXPROCSWithParallelParentBeforeSetGOMAXPROCS(t *testing.T) {
+ if runtime.GOARCH == "wasm" {
+ t.Skip("not supported on wasm yet")
+ }
+ t.Parallel()
+ t.Run("child", func(t *testing.T) {
+ defer func() {
+ want := "testing: t.SetGOMAXPROCS called after t.Parallel; cannot set GOMAXPROCS in parallel tests"
+ if got := recover(); got != want {
+ t.Fatalf("expected panic; got %#v want %q", got, want)
+ }
+ }()
+
+ p := runtime.GOMAXPROCS(0)
+ t.SetGOMAXPROCS(p + 1)
+ })
+}
+
+func TestSetGOMAXPROCSWithParallelGrandParentBeforeSetGOMAXPROCS(t *testing.T) {
+ if runtime.GOARCH == "wasm" {
+ t.Skip("not supported on wasm yet")
+ }
+ t.Parallel()
+ t.Run("child", func(t *testing.T) {
+ t.Run("grand-child", func(t *testing.T) {
+ defer func() {
+ want := "testing: t.SetGOMAXPROCS called after t.Parallel; cannot set GOMAXPROCS in parallel tests"
+ if got := recover(); got != want {
+ t.Fatalf("expected panic; got %#v want %q", got, want)
+ }
+ }()
+
+ p := runtime.GOMAXPROCS(0)
+ t.SetGOMAXPROCS(p + 1)
+ })
+ })
+}
+
// testingTrueInInit is part of TestTesting.
var testingTrueInInit = false