| 
 | 1 | +package helpers  | 
 | 2 | + | 
 | 3 | +import (  | 
 | 4 | +	"fmt"  | 
 | 5 | +	"time"  | 
 | 6 | + | 
 | 7 | +	"github.com/stretchr/testify/assert"  | 
 | 8 | +	"github.com/stretchr/testify/require"  | 
 | 9 | +)  | 
 | 10 | + | 
 | 11 | +// TryReceive waits for a value from the channel and returns (value, true, false) if  | 
 | 12 | +// successful; (<empty>, false, false) if the timeout expired first; or  | 
 | 13 | +// (<empty>, false, true) if the channel was closed.  | 
 | 14 | +func TryReceive[V any](ch <-chan V, timeout time.Duration) (V, bool, bool) {  | 
 | 15 | +	deadline := time.NewTimer(timeout)  | 
 | 16 | +	defer deadline.Stop()  | 
 | 17 | +	select {  | 
 | 18 | +	case v, ok := <-ch:  | 
 | 19 | +		if ok {  | 
 | 20 | +			return v, true, false  | 
 | 21 | +		}  | 
 | 22 | +		return v, false, true  | 
 | 23 | +	case <-deadline.C:  | 
 | 24 | +		var empty V  | 
 | 25 | +		return empty, false, false  | 
 | 26 | +	}  | 
 | 27 | +}  | 
 | 28 | + | 
 | 29 | +// RequireValue returns the next value from the channel, or forces an immediate test failure  | 
 | 30 | +// and exit if the timeout expires first.  | 
 | 31 | +func RequireValue[V any](t require.TestingT, ch <-chan V, timeout time.Duration, customMessageAndArgs ...any) V {  | 
 | 32 | +	v, ok, closed := TryReceive(ch, timeout)  | 
 | 33 | +	if ok {  | 
 | 34 | +		return v  | 
 | 35 | +	}  | 
 | 36 | +	var empty V  | 
 | 37 | +	if closed {  | 
 | 38 | +		failWithMessageAndArgs(t, customMessageAndArgs,  | 
 | 39 | +			"expected a %T value from channel but the channel was closed", empty)  | 
 | 40 | +	} else {  | 
 | 41 | +		failWithMessageAndArgs(t, customMessageAndArgs,  | 
 | 42 | +			"expected a %T value from channel but did not receive one in %s", empty, timeout)  | 
 | 43 | +	}  | 
 | 44 | +	t.FailNow()  | 
 | 45 | +	return empty // never reached  | 
 | 46 | +}  | 
 | 47 | + | 
 | 48 | +// AssertNoMoreValues asserts that no value is available from the channel within the timeout,  | 
 | 49 | +// but that the channel was not closed.  | 
 | 50 | +func AssertNoMoreValues[V any](  | 
 | 51 | +	t assert.TestingT,  | 
 | 52 | +	ch <-chan V,  | 
 | 53 | +	timeout time.Duration,  | 
 | 54 | +	customMessageAndArgs ...any,  | 
 | 55 | +) bool {  | 
 | 56 | +	v, ok, closed := TryReceive(ch, timeout)  | 
 | 57 | +	if ok {  | 
 | 58 | +		failWithMessageAndArgs(t, customMessageAndArgs,  | 
 | 59 | +			"expected no more %T values from channel but got one: %+v", v, v)  | 
 | 60 | +		return false  | 
 | 61 | +	}  | 
 | 62 | +	if closed {  | 
 | 63 | +		failWithMessageAndArgs(t, customMessageAndArgs, "channel was unexpectedly closed")  | 
 | 64 | +		return false  | 
 | 65 | +	}  | 
 | 66 | +	return true  | 
 | 67 | +}  | 
 | 68 | + | 
 | 69 | +// AssertChannelClosed asserts that the channel is closed within the timeout, sending no values.  | 
 | 70 | +func AssertChannelClosed[V any](  | 
 | 71 | +	t assert.TestingT,  | 
 | 72 | +	ch <-chan V,  | 
 | 73 | +	timeout time.Duration,  | 
 | 74 | +	customMessageAndArgs ...any,  | 
 | 75 | +) bool {  | 
 | 76 | +	v, ok, closed := TryReceive(ch, timeout)  | 
 | 77 | +	if ok {  | 
 | 78 | +		failWithMessageAndArgs(t, customMessageAndArgs,  | 
 | 79 | +			"expected no more %T values from channel but got one: %+v", v, v)  | 
 | 80 | +		return false  | 
 | 81 | +	}  | 
 | 82 | +	if !closed {  | 
 | 83 | +		failWithMessageAndArgs(t, customMessageAndArgs,  | 
 | 84 | +			"expected channel to be closed within %s but it was not", timeout)  | 
 | 85 | +		return false  | 
 | 86 | +	}  | 
 | 87 | +	return true  | 
 | 88 | +}  | 
 | 89 | + | 
 | 90 | +// AssertChannelNotClosed asserts that the channel is not closed within the timeout, consuming  | 
 | 91 | +// any values that may be sent during that time.  | 
 | 92 | +func AssertChannelNotClosed[V any](  | 
 | 93 | +	t assert.TestingT,  | 
 | 94 | +	ch <-chan V,  | 
 | 95 | +	timeout time.Duration,  | 
 | 96 | +	customMessageAndArgs ...any,  | 
 | 97 | +) bool {  | 
 | 98 | +	deadline := time.NewTimer(timeout)  | 
 | 99 | +	defer deadline.Stop()  | 
 | 100 | +	for {  | 
 | 101 | +		select {  | 
 | 102 | +		case _, ok := <-ch:  | 
 | 103 | +			if !ok {  | 
 | 104 | +				failWithMessageAndArgs(t, customMessageAndArgs, "channel was unexpectedly closed")  | 
 | 105 | +				return false  | 
 | 106 | +			}  | 
 | 107 | +		case <-deadline.C:  | 
 | 108 | +			return true  | 
 | 109 | +		}  | 
 | 110 | +	}  | 
 | 111 | +}  | 
 | 112 | + | 
 | 113 | +func failWithMessageAndArgs(t assert.TestingT, customMessageAndArgs []any, defaultMsg string, defaultArgs ...any) {  | 
 | 114 | +	t.Errorf(defaultMsg, defaultArgs...)  | 
 | 115 | +	if len(customMessageAndArgs) != 0 {  | 
 | 116 | +		t.Errorf(fmt.Sprintf("%s", customMessageAndArgs[0]), customMessageAndArgs[1:]...)  | 
 | 117 | +	}  | 
 | 118 | +}  | 
0 commit comments