diff --git a/internal/assertions/condition_test.go b/internal/assertions/condition_test.go index bbf76d43d..0eaf07033 100644 --- a/internal/assertions/condition_test.go +++ b/internal/assertions/condition_test.go @@ -295,11 +295,25 @@ func TestConditionEventuallyNoLeak(t *testing.T) { }) } -//nolint:gocognit,gocyclo,cyclop,maintidx // subtests are actually not complex func TestConditionEventuallyWith(t *testing.T) { t.Parallel() - t.Run("should complete with false", func(t *testing.T) { + t.Run("should complete with false", testEventuallyWithShouldCompleteWithFalse()) + t.Run("should complete with true", testEventuallyWithShouldCompleteWithTrue()) + t.Run("should complete with fail, on a nanosecond tick", testEventuallyWithShouldCompleteWithFail()) + t.Run("should complete with fail, with latest failed condition", testEventuallyWithShouldCompleteWithFailLatest()) + t.Run("should complete with success, with the ticker never used", testEventuallyWithShouldCompleteWithSuccess()) + t.Run("collect.FailNow only fails the current tick (poller retries)", testEventuallyWithFailNowRetries()) + t.Run("collect.FailNow allows convergence on a later tick", testEventuallyWithFailNowConverges()) + t.Run("collect.Cancel aborts the whole assertion immediately", testEventuallyWithCancelAborts()) + t.Run("collect.Cancelf aborts with a custom message", testEventuallyWithCancelfAborts()) +} + +// ===================================================== +// Sub tests EventuallyWith +// =====================================================. +func testEventuallyWithShouldCompleteWithFalse() func(*testing.T) { + return func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) @@ -323,9 +337,11 @@ func TestConditionEventuallyWith(t *testing.T) { if counter < expectedCalls-1 || counter > expectedCalls+1 { // it may be 4, 5 or 6 depending on how the test schedules t.Errorf("expected %d calls to the condition, but got %d", expectedCalls, counter) } - }) + } +} - t.Run("should complete with true", func(t *testing.T) { +func testEventuallyWithShouldCompleteWithTrue() func(*testing.T) { + return func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) @@ -345,9 +361,11 @@ func TestConditionEventuallyWith(t *testing.T) { if expectedCalls != counter { t.Errorf("expected condition to be called %d times, got %d", expectedCalls, counter) } - }) + } +} - t.Run("should complete with fail, on a nanosecond tick", func(t *testing.T) { +func testEventuallyWithShouldCompleteWithFail() func(*testing.T) { + return func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) @@ -363,9 +381,11 @@ func TestConditionEventuallyWith(t *testing.T) { if len(mock.errors) != expectedErrors { t.Errorf("expected %d errors (1 from condition, 2 from Eventually), got %d", expectedErrors, len(mock.errors)) } - }) + } +} - t.Run("should complete with fail, with latest failed condition", func(t *testing.T) { +func testEventuallyWithShouldCompleteWithFailLatest() func(*testing.T) { + return func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) @@ -393,9 +413,11 @@ func TestConditionEventuallyWith(t *testing.T) { if len(mock.errors) != expectedErrors { t.Errorf("expected %d errors (1 from condition, 2 from Eventually), got %d", expectedErrors, len(mock.errors)) } - }) + } +} - t.Run("should complete with success, with the ticker never used", func(t *testing.T) { +func testEventuallyWithShouldCompleteWithSuccess() func(*testing.T) { + return func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) @@ -406,9 +428,11 @@ func TestConditionEventuallyWith(t *testing.T) { if !EventuallyWith(mock, condition, testTimeout, time.Second) { t.Error("expected EventuallyWith to return true") } - }) + } +} - t.Run("collect.FailNow only fails the current tick (poller retries)", func(t *testing.T) { +func testEventuallyWithFailNowRetries() func(*testing.T) { + return func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) @@ -432,9 +456,11 @@ func TestConditionEventuallyWith(t *testing.T) { if got < 2 { t.Errorf("expected the condition to be retried multiple times, got %d call(s)", got) } - }) + } +} - t.Run("collect.FailNow allows convergence on a later tick", func(t *testing.T) { +func testEventuallyWithFailNowConverges() func(*testing.T) { + return func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) @@ -458,9 +484,11 @@ func TestConditionEventuallyWith(t *testing.T) { if len(mock.errors) != 0 { t.Errorf("expected no errors reported on parent t after success, got %d: %v", len(mock.errors), mock.errors) } - }) + } +} - t.Run("collect.Cancel aborts the whole assertion immediately", func(t *testing.T) { +func testEventuallyWithCancelAborts() func(*testing.T) { + return func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) @@ -491,9 +519,11 @@ func TestConditionEventuallyWith(t *testing.T) { if len(mock.errors) == 0 { t.Error("expected at least one error reported on parent t after Cancel") } - }) + } +} - t.Run("collect.Cancelf aborts with a custom message", func(t *testing.T) { +func testEventuallyWithCancelfAborts() func(*testing.T) { + return func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) @@ -532,7 +562,7 @@ func TestConditionEventuallyWith(t *testing.T) { if !foundCustom { t.Errorf("expected custom Cancelf message in errors, got: %v", mock.errors) } - }) + } } func TestConditionPollUntilTimeout(t *testing.T) { diff --git a/internal/assertions/http_test.go b/internal/assertions/http_test.go index 1c8d146b9..9b45df300 100644 --- a/internal/assertions/http_test.go +++ b/internal/assertions/http_test.go @@ -252,8 +252,9 @@ func httpFailCases() iter.Seq[failCase] { // ============================================================================ func httpHelloName(w http.ResponseWriter, r *http.Request) { + r.Body = http.MaxBytesReader(w, r.Body, 100) name := r.FormValue("name") - _, _ = fmt.Fprintf(w, "Hello, %s!", name) //nolint:gosec // gosec false positive: G705: XSS via taint analysis + _, _ = fmt.Fprintf(w, "Hello, %s!", name) //nolint:gosec // G705: XSS via taint analysis but okay in tests } func httpOK(w http.ResponseWriter, _ *http.Request) { diff --git a/internal/assertions/safety.go b/internal/assertions/safety.go index 75b8a0432..7cac1a6ba 100644 --- a/internal/assertions/safety.go +++ b/internal/assertions/safety.go @@ -11,6 +11,8 @@ import ( "github.com/go-openapi/testify/v2/internal/leak" ) +const linuxOS = "linux" + // NoGoRoutineLeak ensures that no goroutine did leak from inside the tested function. // // NOTE: only the go routines spawned from inside the tested function are checked for leaks. @@ -101,7 +103,7 @@ func NoFileDescriptorLeak(t T, tested func(), msgAndArgs ...any) bool { h.Helper() } - if runtime.GOOS != "linux" { //nolint:goconst // well-known runtime value + if runtime.GOOS != linuxOS { if s, ok := t.(skipper); ok { s.Skip("NoFileDescriptorLeak requires Linux (/proc/self/fd)") } diff --git a/internal/assertions/safety_test.go b/internal/assertions/safety_test.go index 69fa52fc4..4f52c4fcf 100644 --- a/internal/assertions/safety_test.go +++ b/internal/assertions/safety_test.go @@ -70,7 +70,7 @@ func TestNoFileDescriptorLeak_Success(t *testing.T) { } func TestNoFileDescriptorLeak_Failure(t *testing.T) { - if runtime.GOOS != "linux" { + if runtime.GOOS != linuxOS { t.Skip("file descriptor leak detection requires Linux") } @@ -103,7 +103,7 @@ func TestNoFileDescriptorLeak_Failure(t *testing.T) { } func TestNoFileDescriptorLeak_SocketFiltered(t *testing.T) { - if runtime.GOOS != "linux" { + if runtime.GOOS != linuxOS { t.Skip("file descriptor leak detection requires Linux") } diff --git a/internal/fdleak/fdleak_test.go b/internal/fdleak/fdleak_test.go index ca1520c6a..fd2ce2a1c 100644 --- a/internal/fdleak/fdleak_test.go +++ b/internal/fdleak/fdleak_test.go @@ -67,7 +67,7 @@ func TestLeaked_WithLeak(t *testing.T) { t.Cleanup(func() { if leakedFile != nil { leakedFile.Close() - os.Remove(leakedFile.Name()) //nolint:gosec // G703 path traversal is ok: this is a test. + os.Remove(leakedFile.Name()) } }) diff --git a/internal/leak/leak_test.go b/internal/leak/leak_test.go index 9d939bdd0..733f7f870 100644 --- a/internal/leak/leak_test.go +++ b/internal/leak/leak_test.go @@ -154,11 +154,9 @@ func TestLeaked_PreExistingGoroutineNotLabeled(t *testing.T) { }) // Start a goroutine before the instrumented call. - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { <-blocker - }() + }) tested := func() { // Does nothing — the pre-existing goroutine is not ours. @@ -178,10 +176,7 @@ func TestLeaked_ParallelIsolation(t *testing.T) { results := make([]string, workers) for i := range workers { - wg.Add(1) - go func() { - defer wg.Done() - + wg.Go(func() { blocker := make(chan struct{}) tested := func() { go func() { @@ -191,7 +186,7 @@ func TestLeaked_ParallelIsolation(t *testing.T) { results[i] = Leaked(t.Context(), tested) close(blocker) // clean up - }() + }) } wg.Wait() diff --git a/internal/spew/spew_test.go b/internal/spew/spew_test.go index 59d6452d7..2b8ba318a 100644 --- a/internal/spew/spew_test.go +++ b/internal/spew/spew_test.go @@ -382,5 +382,5 @@ func redirStdout(f func()) ([]byte, error) { os.Stdout = origStdout tempFile.Close() - return os.ReadFile(fileName) //nolint:gosec // false positive: G703: Path traversal via taint analysis + return os.ReadFile(fileName) } diff --git a/internal/testintegration/spew/generator.go b/internal/testintegration/spew/generator.go index 921b9b605..015eaa2f3 100644 --- a/internal/testintegration/spew/generator.go +++ b/internal/testintegration/spew/generator.go @@ -43,9 +43,7 @@ func NoPanicProp(ctx context.Context, g *rapid.Generator[any]) func(*rapid.T) { var w sync.WaitGroup done := make(chan struct{}) - w.Add(1) - go func() { - defer w.Done() + w.Go(func() { select { case <-done: cancel() @@ -56,7 +54,7 @@ func NoPanicProp(ctx context.Context, g *rapid.Generator[any]) func(*rapid.T) { return } - }() + }) go func() { // this go routine may leak if timeout kicks // Sdump should never panic