Source file src/testing/synctest/synctest_test.go

     1  // Copyright 2025 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  
     5  package synctest_test
     6  
     7  import (
     8  	"fmt"
     9  	"internal/testenv"
    10  	"os"
    11  	"regexp"
    12  	"testing"
    13  	"testing/synctest"
    14  )
    15  
    16  // Tests for interactions between synctest bubbles and the testing package.
    17  // Other bubble behaviors are tested in internal/synctest.
    18  
    19  func TestSuccess(t *testing.T) {
    20  	synctest.Test(t, func(t *testing.T) {
    21  	})
    22  }
    23  
    24  func TestFatal(t *testing.T) {
    25  	runTest(t, nil, func() {
    26  		synctest.Test(t, func(t *testing.T) {
    27  			t.Fatal("fatal")
    28  		})
    29  	}, `^--- FAIL: TestFatal.*
    30      synctest_test.go:.* fatal
    31  FAIL
    32  $`)
    33  }
    34  
    35  func TestError(t *testing.T) {
    36  	runTest(t, nil, func() {
    37  		synctest.Test(t, func(t *testing.T) {
    38  			t.Error("error")
    39  		})
    40  	}, `^--- FAIL: TestError.*
    41      synctest_test.go:.* error
    42  FAIL
    43  $`)
    44  }
    45  
    46  func TestVerboseError(t *testing.T) {
    47  	runTest(t, []string{"-test.v"}, func() {
    48  		synctest.Test(t, func(t *testing.T) {
    49  			t.Error("error")
    50  		})
    51  	}, `^=== RUN   TestVerboseError
    52      synctest_test.go:.* error
    53  --- FAIL: TestVerboseError.*
    54  FAIL
    55  $`)
    56  }
    57  
    58  func TestSkip(t *testing.T) {
    59  	runTest(t, nil, func() {
    60  		synctest.Test(t, func(t *testing.T) {
    61  			t.Skip("skip")
    62  		})
    63  	}, `^PASS
    64  $`)
    65  }
    66  
    67  func TestVerboseSkip(t *testing.T) {
    68  	runTest(t, []string{"-test.v"}, func() {
    69  		synctest.Test(t, func(t *testing.T) {
    70  			t.Skip("skip")
    71  		})
    72  	}, `^=== RUN   TestVerboseSkip
    73      synctest_test.go:.* skip
    74  --- PASS: TestVerboseSkip.*
    75  PASS
    76  $`)
    77  }
    78  
    79  func TestCleanup(t *testing.T) {
    80  	done := false
    81  	synctest.Test(t, func(t *testing.T) {
    82  		ch := make(chan struct{})
    83  		t.Cleanup(func() {
    84  			// This cleanup function should execute inside the test's bubble.
    85  			// (If it doesn't the runtime will panic.)
    86  			close(ch)
    87  		})
    88  		// synctest.Test will wait for this goroutine to exit before returning.
    89  		// The cleanup function signals the goroutine to exit before the wait starts.
    90  		go func() {
    91  			<-ch
    92  			done = true
    93  		}()
    94  	})
    95  	if !done {
    96  		t.Fatalf("background goroutine did not return")
    97  	}
    98  }
    99  
   100  func TestContext(t *testing.T) {
   101  	state := "not started"
   102  	synctest.Test(t, func(t *testing.T) {
   103  		go func() {
   104  			state = "waiting on context"
   105  			<-t.Context().Done()
   106  			state = "done"
   107  		}()
   108  		// Wait blocks until the goroutine above is blocked on t.Context().Done().
   109  		synctest.Wait()
   110  		if got, want := state, "waiting on context"; got != want {
   111  			t.Fatalf("state = %q, want %q", got, want)
   112  		}
   113  	})
   114  	// t.Context() is canceled before the test completes,
   115  	// and synctest.Test does not return until the goroutine has set its state to "done".
   116  	if got, want := state, "done"; got != want {
   117  		t.Fatalf("state = %q, want %q", got, want)
   118  	}
   119  }
   120  
   121  func TestDeadline(t *testing.T) {
   122  	synctest.Test(t, func(t *testing.T) {
   123  		defer wantPanic(t, "testing: t.Deadline called inside synctest bubble")
   124  		_, _ = t.Deadline()
   125  	})
   126  }
   127  
   128  func TestParallel(t *testing.T) {
   129  	synctest.Test(t, func(t *testing.T) {
   130  		defer wantPanic(t, "testing: t.Parallel called inside synctest bubble")
   131  		t.Parallel()
   132  	})
   133  }
   134  
   135  func TestRun(t *testing.T) {
   136  	synctest.Test(t, func(t *testing.T) {
   137  		defer wantPanic(t, "testing: t.Run called inside synctest bubble")
   138  		t.Run("subtest", func(t *testing.T) {
   139  		})
   140  	})
   141  }
   142  
   143  func wantPanic(t *testing.T, want string) {
   144  	if e := recover(); e != nil {
   145  		if got := fmt.Sprint(e); got != want {
   146  			t.Errorf("got panic message %q, want %q", got, want)
   147  		}
   148  	} else {
   149  		t.Errorf("got no panic, want one")
   150  	}
   151  }
   152  
   153  func runTest(t *testing.T, args []string, f func(), pattern string) {
   154  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   155  		f()
   156  		return
   157  	}
   158  	t.Helper()
   159  	re := regexp.MustCompile(pattern)
   160  	testenv.MustHaveExec(t)
   161  	cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^"+regexp.QuoteMeta(t.Name())+"$", "-test.count=1")
   162  	cmd.Args = append(cmd.Args, args...)
   163  	cmd = testenv.CleanCmdEnv(cmd)
   164  	cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
   165  	out, _ := cmd.CombinedOutput()
   166  	if !re.Match(out) {
   167  		t.Errorf("got output:\n%s\nwant matching:\n%s", out, pattern)
   168  	}
   169  }
   170  

View as plain text