Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 49 additions & 19 deletions internal/assertions/condition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion internal/assertions/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 3 additions & 1 deletion internal/assertions/safety.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)")
}
Expand Down
4 changes: 2 additions & 2 deletions internal/assertions/safety_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}

Expand Down Expand Up @@ -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")
}

Expand Down
2 changes: 1 addition & 1 deletion internal/fdleak/fdleak_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
})

Expand Down
13 changes: 4 additions & 9 deletions internal/leak/leak_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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() {
Expand All @@ -191,7 +186,7 @@ func TestLeaked_ParallelIsolation(t *testing.T) {

results[i] = Leaked(t.Context(), tested)
close(blocker) // clean up
}()
})
}

wg.Wait()
Expand Down
2 changes: 1 addition & 1 deletion internal/spew/spew_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
6 changes: 2 additions & 4 deletions internal/testintegration/spew/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down
Loading