aboutsummaryrefslogtreecommitdiff
path: root/src/testing/synctest/synctest_test.go
diff options
context:
space:
mode:
authorDamien Neil <dneil@google.com>2025-05-12 11:15:08 -0700
committerGopher Robot <gobot@golang.org>2025-05-20 15:46:03 -0700
commit49a660e22cb349cf13ef0a2f6214c6fdd75afda0 (patch)
treeef5e4c8825712a119dc37929cb372747b8d21230 /src/testing/synctest/synctest_test.go
parent609197b406ce8d9efd39bd3984b2cade74df35a6 (diff)
downloadgo-49a660e22cb349cf13ef0a2f6214c6fdd75afda0.tar.xz
testing/synctest: add Test
Add a synctest.Test function, superseding the experimental synctest.Run function. Promote the testing/synctest package out of experimental status. For #67434 For #73567 Change-Id: I3c5ba030860d90fe2ddb517a2f3536efd60181a9 Reviewed-on: https://go-review.googlesource.com/c/go/+/671961 Auto-Submit: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com>
Diffstat (limited to 'src/testing/synctest/synctest_test.go')
-rw-r--r--src/testing/synctest/synctest_test.go149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/testing/synctest/synctest_test.go b/src/testing/synctest/synctest_test.go
new file mode 100644
index 0000000000..4897df999e
--- /dev/null
+++ b/src/testing/synctest/synctest_test.go
@@ -0,0 +1,149 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package synctest_test
+
+import (
+ "fmt"
+ "internal/testenv"
+ "os"
+ "regexp"
+ "testing"
+ "testing/synctest"
+)
+
+// Tests for interactions between synctest bubbles and the testing package.
+// Other bubble behaviors are tested in internal/synctest.
+
+func TestSuccess(t *testing.T) {
+ synctest.Test(t, func(t *testing.T) {
+ })
+}
+
+func TestFatal(t *testing.T) {
+ runTest(t, func() {
+ synctest.Test(t, func(t *testing.T) {
+ t.Fatal("fatal")
+ })
+ }, `^=== RUN TestFatal
+ synctest_test.go:.* fatal
+--- FAIL: TestFatal.*
+FAIL
+$`)
+}
+
+func TestError(t *testing.T) {
+ runTest(t, func() {
+ synctest.Test(t, func(t *testing.T) {
+ t.Error("error")
+ })
+ }, `^=== RUN TestError
+ synctest_test.go:.* error
+--- FAIL: TestError.*
+FAIL
+$`)
+}
+
+func TestSkip(t *testing.T) {
+ runTest(t, func() {
+ synctest.Test(t, func(t *testing.T) {
+ t.Skip("skip")
+ })
+ }, `^=== RUN TestSkip
+ synctest_test.go:.* skip
+--- PASS: TestSkip.*
+PASS
+$`)
+}
+
+func TestCleanup(t *testing.T) {
+ done := false
+ synctest.Test(t, func(t *testing.T) {
+ ch := make(chan struct{})
+ t.Cleanup(func() {
+ // This cleanup function should execute inside the test's bubble.
+ // (If it doesn't the runtime will panic.)
+ close(ch)
+ })
+ // synctest.Test will wait for this goroutine to exit before returning.
+ // The cleanup function signals the goroutine to exit before the wait starts.
+ go func() {
+ <-ch
+ done = true
+ }()
+ })
+ if !done {
+ t.Fatalf("background goroutine did not return")
+ }
+}
+
+func TestContext(t *testing.T) {
+ state := "not started"
+ synctest.Test(t, func(t *testing.T) {
+ go func() {
+ state = "waiting on context"
+ <-t.Context().Done()
+ state = "done"
+ }()
+ // Wait blocks until the goroutine above is blocked on t.Context().Done().
+ synctest.Wait()
+ if got, want := state, "waiting on context"; got != want {
+ t.Fatalf("state = %q, want %q", got, want)
+ }
+ })
+ // t.Context() is canceled before the test completes,
+ // and synctest.Test does not return until the goroutine has set its state to "done".
+ if got, want := state, "done"; got != want {
+ t.Fatalf("state = %q, want %q", got, want)
+ }
+}
+
+func TestDeadline(t *testing.T) {
+ synctest.Test(t, func(t *testing.T) {
+ defer wantPanic(t, "testing: t.Deadline called inside synctest bubble")
+ _, _ = t.Deadline()
+ })
+}
+
+func TestParallel(t *testing.T) {
+ synctest.Test(t, func(t *testing.T) {
+ defer wantPanic(t, "testing: t.Parallel called inside synctest bubble")
+ t.Parallel()
+ })
+}
+
+func TestRun(t *testing.T) {
+ synctest.Test(t, func(t *testing.T) {
+ defer wantPanic(t, "testing: t.Run called inside synctest bubble")
+ t.Run("subtest", func(t *testing.T) {
+ })
+ })
+}
+
+func wantPanic(t *testing.T, want string) {
+ if e := recover(); e != nil {
+ if got := fmt.Sprint(e); got != want {
+ t.Errorf("got panic message %q, want %q", got, want)
+ }
+ } else {
+ t.Errorf("got no panic, want one")
+ }
+}
+
+func runTest(t *testing.T, f func(), pattern string) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ f()
+ return
+ }
+ t.Helper()
+ re := regexp.MustCompile(pattern)
+ testenv.MustHaveExec(t)
+ cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^"+t.Name()+"$", "-test.v", "-test.count=1")
+ cmd = testenv.CleanCmdEnv(cmd)
+ cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
+ out, _ := cmd.CombinedOutput()
+ if !re.Match(out) {
+ t.Errorf("got output:\n%s\nwant matching:\n%s", out, pattern)
+ }
+}