Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add assert.Consistently #1606

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
36 changes: 36 additions & 0 deletions assert/assertion_format.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions assert/assertion_forward.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 87 additions & 0 deletions assert/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2038,6 +2038,93 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
}
}

// Consistently asserts that given condition will be met for the entire
// duration of waitFor time, periodically checking target function each tick.
//
// assert.Consistently(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
func Consistently(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}

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:
return true
case <-tick:
tick = nil
go func() { ch <- condition() }()
case v := <-ch:
if !v {
return Fail(t, "Condition never satisfied", msgAndArgs...)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: not sure if this is the right error message. To me, "Condition never satisfied" makes sense in the case of Eventually. The term never is not applicable in the context of Consistently.

}
tick = ticker.C
}
}
}

// ConsistentlyWithT asserts that given condition will be met for the entire
// waitFor time, periodically checking target function each tick. In contrast
// to Consistently, it supplies a CollectT to the condition function, so that
// the condition function can use the CollectT to call other assertions. The
// condition is considered "met" if no errors are raised across all ticks. The
// supplied CollectT collects all errors from one tick (if there are any). If
// the condition is not met once before waitFor, the collected error of the
// failing tick are copied to t.
//
// externalValue := false
// go func() {
// time.Sleep(8*time.Second)
// externalValue = true
// }()
// assert.ConsistentlyWithT(t, func(c *assert.CollectT) {
// // add assertions as needed; any assertion failure will fail the current tick
// assert.True(c, externalValue, "expected 'externalValue' to be true")
// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false")
func ConsistentlyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}

ch := make(chan []error, 1)

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

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

for tick := ticker.C; ; {
select {
case <-timer.C:
return true
case <-tick:
tick = nil
go func() {
collect := new(CollectT)
defer func() {
ch <- collect.errors
}()
condition(collect)
}()
case errs := <-ch:
if len(errs) > 0 {
return Fail(t, "Condition never satisfied", msgAndArgs...)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add the errors to t first before calling Fail. Reference: https://github.com/stretchr/testify/blob/master/assert/assertions.go#L2017

}

tick = ticker.C
}
}
}

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

func TestConsistentlyTrue(t *testing.T) {
condition := func() bool {
return true
}

True(t, Consistently(t, condition, 100*time.Millisecond, 20*time.Millisecond))
}

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

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

return state != 2
}

False(t, Consistently(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
}

// errorsCapturingT is a mock implementation of TestingT that captures errors reported with Errorf.
type errorsCapturingT struct {
errors []error
Expand Down Expand Up @@ -2970,6 +2993,32 @@ func TestEventuallyWithT_ReturnsTheLatestFinishedConditionErrors(t *testing.T) {
Len(t, mockT.errors, 2)
}

func TestConsistentlyWithTTrue(t *testing.T) {
mockT := new(errorsCapturingT)

condition := func(collect *CollectT) {
True(collect, true)
}

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

func TestConsistentlyWithTFalse(t *testing.T) {
mockT := new(errorsCapturingT)

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

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

func TestNeverFalse(t *testing.T) {
condition := func() bool {
return false
Expand Down
84 changes: 84 additions & 0 deletions require/require.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading