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

View as plain text