Skip to content

Commit

Permalink
feat: add poll functions
Browse files Browse the repository at this point in the history
  • Loading branch information
nrwiersma committed Dec 10, 2023
1 parent 0923ac6 commit 76e7cd8
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 15 deletions.
25 changes: 18 additions & 7 deletions wait/wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,23 @@ type ConditionFunc func(context.Context) (done bool, err error)

// PollUntil tries a condition until stopped by the context.
func PollUntil(ctx context.Context, fn ConditionFunc, interval time.Duration) error {
done, err := fn(ctx)
if err != nil {
return err
}
if done {
return nil
return poll(ctx, false, fn, interval)
}

// PollImmediateUntil tries a condition until stopped by the context.
func PollImmediateUntil(ctx context.Context, fn ConditionFunc, interval time.Duration) error {
return poll(ctx, true, fn, interval)
}

func poll(ctx context.Context, immediate bool, fn ConditionFunc, interval time.Duration) error {
if immediate {
done, err := fn(ctx)
if err != nil {
return err
}
if done {
return nil
}
}

tick := time.NewTicker(interval)
Expand All @@ -28,7 +39,7 @@ func PollUntil(ctx context.Context, fn ConditionFunc, interval time.Duration) er
case <-ctx.Done():
return ctx.Err()
case <-tick.C:
done, err = fn(ctx)
done, err := fn(ctx)
if err != nil {
return err
}
Expand Down
47 changes: 39 additions & 8 deletions wait/wait_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,17 @@ func TestPollUntil(t *testing.T) {
go func() {
defer close(called)

err := wait.PollUntil(ctx, func(context.Context) (done bool, err error) {
err := wait.PollImmediateUntil(ctx, func(context.Context) (done bool, err error) {
called <- struct{}{}
return false, nil
}, time.Microsecond)
}, 100*time.Microsecond)

assert.ErrorIs(t, err, context.Canceled)
}()

// Wait for the initial condition call, and the first tick
// Wait for the first tick
// condition call.
<-called
<-called

// Stop waiting.
cancel()
Expand Down Expand Up @@ -75,22 +74,54 @@ func TestPollUntil_HandlesDone(t *testing.T) {
require.NoError(t, err)
}

func TestPollUntil_HandlesImmediateError(t *testing.T) {
func TestPollImmediateUntil(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())

called := make(chan struct{})
go func() {
defer close(called)

err := wait.PollImmediateUntil(ctx, func(context.Context) (done bool, err error) {
called <- struct{}{}
return false, nil
}, 100*time.Microsecond)

assert.ErrorIs(t, err, context.Canceled)
}()

// Wait for the initial condition call, and the first tick
// condition call.
<-called
<-called

// Stop waiting.
cancel()

// Assert that the condition is not called more than once after
// canceling the context.
var calledCount int
for range called {
calledCount++
}
assert.LessOrEqual(t, calledCount, 1)
}

func TestPollImmediateUntil_HandlesImmediateError(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := wait.PollUntil(ctx, func(context.Context) (done bool, err error) {
err := wait.PollImmediateUntil(ctx, func(context.Context) (done bool, err error) {
return false, errors.New("test")
}, time.Microsecond)

require.Error(t, err)
}

func TestPollUntil_HandlesImmediateDone(t *testing.T) {
func TestPollImmediateUntil_HandlesImmediateDone(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := wait.PollUntil(ctx, func(context.Context) (done bool, err error) {
err := wait.PollImmediateUntil(ctx, func(context.Context) (done bool, err error) {
return true, nil
}, time.Microsecond)

Expand Down

0 comments on commit 76e7cd8

Please sign in to comment.