Skip to content

Commit

Permalink
add NotWithin assertion support
Browse files Browse the repository at this point in the history
  • Loading branch information
adamluzsi committed Feb 1, 2023
1 parent 2e5db10 commit b1dd3c4
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 15 deletions.
48 changes: 34 additions & 14 deletions assert/Asserter.go
Original file line number Diff line number Diff line change
Expand Up @@ -968,8 +968,40 @@ func (a Asserter) ReadAll(r io.Reader, msg ...any) []byte {
}

func (a Asserter) Within(timeout time.Duration, blk func(context.Context), msg ...any) {
const FnMethod = "Within"
a.TB.Helper()
if !a.within(timeout, blk) {
a.fn(fmterror.Message{
Method: "Within",
Cause: "Expected to finish within the timeout duration.",
Message: msg,
Values: []fmterror.Value{
{
Label: "timeout",
Value: timeout,
},
},
}.String())
}
}

func (a Asserter) NotWithin(timeout time.Duration, blk func(context.Context), msg ...any) {
a.TB.Helper()
if a.within(timeout, blk) {
a.fn(fmterror.Message{
Method: "NotWithin",
Cause: `Expected to not finish within the timeout duration.`,
Message: msg,
Values: []fmterror.Value{
{
Label: "timeout",
Value: timeout,
},
},
}.String())
}
}

func (a Asserter) within(timeout time.Duration, blk func(context.Context)) bool {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var done, isFailNow uint32
Expand All @@ -985,20 +1017,8 @@ func (a Asserter) Within(timeout time.Duration, blk func(context.Context), msg .
Waiter{Timeout: timeout}.While(func() bool {
return atomic.LoadUint32(&done) == 0 && atomic.LoadUint32(&isFailNow) == 0
})
if atomic.LoadUint32(&done) == 0 {
a.fn(fmterror.Message{
Method: FnMethod,
Cause: `Timeout reached`,
Message: msg,
Values: []fmterror.Value{
{
Label: "timeout",
Value: timeout,
},
},
}.String())
}
if atomic.LoadUint32(&isFailNow) != 0 {
a.TB.FailNow()
}
return atomic.LoadUint32(&done) == 1
}
60 changes: 60 additions & 0 deletions assert/Asserter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,66 @@ func TestAsserter_Within(t *testing.T) {
})
}

func TestAsserter_NotWithin(t *testing.T) {
t.Run("when block finish within time, then assert fails", func(t *testing.T) {
dtb := &doubles.TB{}
a := asserter(dtb)
a.NotWithin(time.Second, func(ctx context.Context) {
select {
case <-time.After(time.Microsecond):
case <-ctx.Done():
}
})
assert.True(t, dtb.IsFailed)
})
t.Run("when block takes more time than the timeout, assertion succeed", func(t *testing.T) {
dtb := &doubles.TB{}
a := asserter(dtb)
a.NotWithin(time.Microsecond, func(ctx context.Context) {
timer := time.NewTimer(time.Hour)
select {
case <-timer.C:
case <-ctx.Done():
timer.Stop()
}
})
assert.False(t, dtb.IsFailed)
})
t.Run("when block takes more time than the timeout, the function's context is cancelled", func(t *testing.T) {
dtb := &doubles.TB{}
a := asserter(dtb)

var isCancelled int32
a.NotWithin(time.Microsecond, func(ctx context.Context) {
timer := time.NewTimer(time.Second)
select {
case <-timer.C:
case <-ctx.Done():
timer.Stop()
atomic.AddInt32(&isCancelled, 1)
}
})
assert.False(t, dtb.IsFailed)

assert.EventuallyWithin(3*time.Second).Assert(t, func(it assert.It) {
it.Must.True(atomic.LoadInt32(&isCancelled) == 1)
})
})
t.Run("when FailNow based failing as part of the Within block, it is propagated to the outside as well", func(t *testing.T) {
dtb := &doubles.TB{}
a := asserter(dtb)

ro := sandbox.Run(func() {
a.NotWithin(time.Second, func(ctx context.Context) {
dtb.FailNow()
})
})

assert.Must(t).True(dtb.IsFailed)
assert.Must(t).True(ro.Goexit)
})
}

func TestAsserter_ContainExactly_map(t *testing.T) {
type TestCase struct {
Desc string
Expand Down
27 changes: 26 additions & 1 deletion assert/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ func Example_configureDiffFunc() {

func ExampleWithin() {
var tb testing.TB

assert.Within(tb, time.Second, func(ctx context.Context) {
// OK
})
Expand All @@ -604,3 +604,28 @@ func ExampleAsserter_Within() {
// FAIL
})
}

func ExampleNotWithin() {
var tb testing.TB

assert.NotWithin(tb, time.Second, func(ctx context.Context) {
return // FAIL
})

assert.NotWithin(tb, time.Nanosecond, func(ctx context.Context) {
time.Sleep(time.Second) // OK
})
}

func ExampleAsserter_NotWithin() {
var tb testing.TB
a := assert.Must(tb)

a.NotWithin(time.Second, func(ctx context.Context) {
return // FAIL
})

a.NotWithin(time.Nanosecond, func(ctx context.Context) {
time.Sleep(time.Second) // OK
})
}
5 changes: 5 additions & 0 deletions assert/pkgfunc.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,8 @@ func Within(tb testing.TB, timeout time.Duration, blk func(context.Context), msg
tb.Helper()
Must(tb).Within(timeout, blk, msg...)
}

func NotWithin(tb testing.TB, timeout time.Duration, blk func(context.Context), msg ...any) {
tb.Helper()
Must(tb).NotWithin(timeout, blk, msg...)
}
15 changes: 15 additions & 0 deletions assert/pkgfunc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,21 @@ func TestPublicFunctions(t *testing.T) {
assert.Within(tb, time.Nanosecond, func(ctx context.Context) { time.Sleep(time.Second) })
},
},
// Not Within
{
Desc: ".NotWithin - happy",
Failed: false,
Assert: func(tb testing.TB) {
assert.NotWithin(tb, time.Nanosecond, func(ctx context.Context) { time.Sleep(time.Millisecond) })
},
},
{
Desc: ".NotWithin - rainy",
Failed: true,
Assert: func(tb testing.TB) {
assert.NotWithin(tb, time.Millisecond, func(ctx context.Context) {})
},
},
} {
t.Run(tc.Desc, func(t *testing.T) {
stub := &doubles.TB{}
Expand Down

0 comments on commit b1dd3c4

Please sign in to comment.