Skip to content

Commit

Permalink
add EventuallyWithT assertion
Browse files Browse the repository at this point in the history
  • Loading branch information
tobikris committed Sep 13, 2022
1 parent 181cea6 commit 13548f5
Show file tree
Hide file tree
Showing 6 changed files with 362 additions and 0 deletions.
34 changes: 34 additions & 0 deletions assert/assertion_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,40 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick
return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
}

// EventuallyWithTf asserts that given condition will be met in waitFor time,
// periodically checking target function each tick. In contrast to Eventually,
// it supplies a CollectT to the condition function, so that the condition
// function can use the CollectT to call other assertions.
// The supplied CollectT collects all errors from one tick (if there are any).
// If the condition is not met before waitFor, the collected errors of
// the last tick are copied to t.
//
// func falseThenTrue(falses int) func() bool {
// count := 0
// return func() bool {
// if count < falses {
// count++
// return false
// }
// return true
// }
// }
// f := falseThenTrue(5)
// assert.EventuallyWithTf(t, func(mockT assert.TestingT) (success bool, "error message %s", "formatted") {
// defer func() {
// r := recover()
// success = (r == nil)
// }()
// assert.True(mockT, f())
// return
// }, 50*time.Millisecond, 10*time.Millisecond)
func EventuallyWithTf(t TestingT, condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
}

// Exactlyf asserts that two objects are equal in value and type.
//
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
Expand Down
68 changes: 68 additions & 0 deletions assert/assertion_forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,74 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti
return Eventually(a.t, condition, waitFor, tick, msgAndArgs...)
}

// EventuallyWithT asserts that given condition will be met in waitFor time,
// periodically checking target function each tick. In contrast to Eventually,
// it supplies a CollectT to the condition function, so that the condition
// function can use the CollectT to call other assertions.
// The supplied CollectT collects all errors from one tick (if there are any).
// If the condition is not met before waitFor, the collected errors of
// the last tick are copied to t.
//
// func falseThenTrue(falses int) func() bool {
// count := 0
// return func() bool {
// if count < falses {
// count++
// return false
// }
// return true
// }
// }
// f := falseThenTrue(5)
// a.EventuallyWithT(func(mockT assert.TestingT) (success bool) {
// defer func() {
// r := recover()
// success = (r == nil)
// }()
// assert.True(mockT, f())
// return
// }, 50*time.Millisecond, 10*time.Millisecond)
func (a *Assertions) EventuallyWithT(condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...)
}

// EventuallyWithTf asserts that given condition will be met in waitFor time,
// periodically checking target function each tick. In contrast to Eventually,
// it supplies a CollectT to the condition function, so that the condition
// function can use the CollectT to call other assertions.
// The supplied CollectT collects all errors from one tick (if there are any).
// If the condition is not met before waitFor, the collected errors of
// the last tick are copied to t.
//
// func falseThenTrue(falses int) func() bool {
// count := 0
// return func() bool {
// if count < falses {
// count++
// return false
// }
// return true
// }
// }
// f := falseThenTrue(5)
// a.EventuallyWithTf(func(mockT assert.TestingT) (success bool, "error message %s", "formatted") {
// defer func() {
// r := recover()
// success = (r == nil)
// }()
// assert.True(mockT, f())
// return
// }, 50*time.Millisecond, 10*time.Millisecond)
func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...)
}

// Eventuallyf asserts that given condition will be met in waitFor time,
// periodically checking target function each tick.
//
Expand Down
86 changes: 86 additions & 0 deletions assert/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1756,6 +1756,92 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
}
}

// CollectT implements the TestingT interface and collects all errors.
type CollectT struct {
errors []error
}

// Errorf collects the error.
func (c *CollectT) Errorf(format string, args ...interface{}) {
c.errors = append(c.errors, fmt.Errorf(format, args...))
}

// FailNow panics.
func (c *CollectT) FailNow() {
panic("Assertion failed")
}

// Reset clears the collected errors.
func (c *CollectT) Reset() {
c.errors = nil
}

// Copy copies the collected errors to the supplied t.
func (c *CollectT) Copy(t TestingT) {
for _, err := range c.errors {
t.Errorf("%v", err)
}
}

// EventuallyWithT asserts that given condition will be met in waitFor time,
// periodically checking target function each tick. In contrast to Eventually,
// it supplies a CollectT to the condition function, so that the condition
// function can use the CollectT to call other assertions.
// The supplied CollectT collects all errors from one tick (if there are any).
// If the condition is not met before waitFor, the collected errors of
// the last tick are copied to t.
//
// func falseThenTrue(falses int) func() bool {
// count := 0
// return func() bool {
// if count < falses {
// count++
// return false
// }
// return true
// }
// }
// f := falseThenTrue(5)
// assert.EventuallyWithT(t, func(mockT assert.TestingT) (success bool) {
// defer func() {
// r := recover()
// success = (r == nil)
// }()
// assert.True(mockT, f())
// return
// }, 50*time.Millisecond, 10*time.Millisecond)
func EventuallyWithT(t TestingT, condition func(collect *CollectT) bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}

collect := new(CollectT)
ch := make(chan bool, 1)

timer := time.NewTimer(waitFor)
defer timer.Stop()

ticker := time.NewTicker(tick)
defer ticker.Stop()

for tick := ticker.C; ; {
select {
case <-timer.C:
collect.Copy(t)
return Fail(t, "Condition never satisfied", msgAndArgs...)
case <-tick:
tick = nil
collect.Reset()
go func() { ch <- condition(collect) }()
case v := <-ch:
if v {
return true
}
tick = ticker.C
}
}
}

// Never asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick.
//
Expand Down
32 changes: 32 additions & 0 deletions assert/assertions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2428,6 +2428,38 @@ func TestEventuallyTrue(t *testing.T) {
True(t, Eventually(t, condition, 100*time.Millisecond, 20*time.Millisecond))
}

func TestEventuallyWithTFalse(t *testing.T) {
mockT := new(CollectT)

condition := func(collect *CollectT) bool {
return True(collect, false)
}

False(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
Len(t, mockT.errors, 2)
}

func TestEventuallyWithTTrue(t *testing.T) {
mockT := new(CollectT)

state := 0
condition := func(collect *CollectT) bool {
defer func() {
state += 1
}()

if state == 2 {
True(collect, true)
return true
}

return True(collect, false)
}

True(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
Len(t, mockT.errors, 0)
}

func TestNeverFalse(t *testing.T) {
condition := func() bool {
return false
Expand Down
74 changes: 74 additions & 0 deletions require/require.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,80 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
t.FailNow()
}

// EventuallyWithT asserts that given condition will be met in waitFor time,
// periodically checking target function each tick. In contrast to Eventually,
// it supplies a CollectT to the condition function, so that the condition
// function can use the CollectT to call other assertions.
// The supplied CollectT collects all errors from one tick (if there are any).
// If the condition is not met before waitFor, the collected errors of
// the last tick are copied to t.
//
// func falseThenTrue(falses int) func() bool {
// count := 0
// return func() bool {
// if count < falses {
// count++
// return false
// }
// return true
// }
// }
// f := falseThenTrue(5)
// assert.EventuallyWithT(t, func(mockT assert.TestingT) (success bool) {
// defer func() {
// r := recover()
// success = (r == nil)
// }()
// assert.True(mockT, f())
// return
// }, 50*time.Millisecond, 10*time.Millisecond)
func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT) bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) {
return
}
t.FailNow()
}

// EventuallyWithTf asserts that given condition will be met in waitFor time,
// periodically checking target function each tick. In contrast to Eventually,
// it supplies a CollectT to the condition function, so that the condition
// function can use the CollectT to call other assertions.
// The supplied CollectT collects all errors from one tick (if there are any).
// If the condition is not met before waitFor, the collected errors of
// the last tick are copied to t.
//
// func falseThenTrue(falses int) func() bool {
// count := 0
// return func() bool {
// if count < falses {
// count++
// return false
// }
// return true
// }
// }
// f := falseThenTrue(5)
// assert.EventuallyWithTf(t, func(mockT assert.TestingT) (success bool, "error message %s", "formatted") {
// defer func() {
// r := recover()
// success = (r == nil)
// }()
// assert.True(mockT, f())
// return
// }, 50*time.Millisecond, 10*time.Millisecond)
func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT) bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.EventuallyWithTf(t, condition, waitFor, tick, msg, args...) {
return
}
t.FailNow()
}

// Eventuallyf asserts that given condition will be met in waitFor time,
// periodically checking target function each tick.
//
Expand Down
Loading

0 comments on commit 13548f5

Please sign in to comment.