9 "github.com/onsi/gomega/internal/oraclematcher"
10 "github.com/onsi/gomega/types"
13 type AsyncAssertionType uint
16 AsyncAssertionTypeEventually AsyncAssertionType = iota
17 AsyncAssertionTypeConsistently
20 type AsyncAssertion struct {
21 asyncType AsyncAssertionType
22 actualInput interface{}
23 timeoutInterval time.Duration
24 pollingInterval time.Duration
25 fail types.GomegaFailHandler
29 func New(asyncType AsyncAssertionType, actualInput interface{}, fail types.GomegaFailHandler, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion {
30 actualType := reflect.TypeOf(actualInput)
31 if actualType.Kind() == reflect.Func {
32 if actualType.NumIn() != 0 || actualType.NumOut() == 0 {
33 panic("Expected a function with no arguments and one or more return values.")
37 return &AsyncAssertion{
39 actualInput: actualInput,
41 timeoutInterval: timeoutInterval,
42 pollingInterval: pollingInterval,
47 func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
48 return assertion.match(matcher, true, optionalDescription...)
51 func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
52 return assertion.match(matcher, false, optionalDescription...)
55 func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interface{}) string {
56 switch len(optionalDescription) {
60 return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
64 func (assertion *AsyncAssertion) actualInputIsAFunction() bool {
65 actualType := reflect.TypeOf(assertion.actualInput)
66 return actualType.Kind() == reflect.Func && actualType.NumIn() == 0 && actualType.NumOut() > 0
69 func (assertion *AsyncAssertion) pollActual() (interface{}, error) {
70 if assertion.actualInputIsAFunction() {
71 values := reflect.ValueOf(assertion.actualInput).Call([]reflect.Value{})
73 extras := []interface{}{}
74 for _, value := range values[1:] {
75 extras = append(extras, value.Interface())
78 success, message := vetExtras(extras)
81 return nil, errors.New(message)
84 return values[0].Interface(), nil
87 return assertion.actualInput, nil
90 func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool {
91 if assertion.actualInputIsAFunction() {
95 return oraclematcher.MatchMayChangeInTheFuture(matcher, value)
98 func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
100 timeout := time.After(assertion.timeoutInterval)
102 description := assertion.buildDescription(optionalDescription...)
107 value, err := assertion.pollActual()
109 mayChange = assertion.matcherMayChange(matcher, value)
110 matches, err = matcher.Match(value)
113 fail := func(preamble string) {
117 errMsg = "Error: " + err.Error()
120 message = matcher.FailureMessage(value)
122 message = matcher.NegatedFailureMessage(value)
125 assertion.fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset)
128 if assertion.asyncType == AsyncAssertionTypeEventually {
130 if err == nil && matches == desiredMatch {
135 fail("No future change is possible. Bailing out early")
140 case <-time.After(assertion.pollingInterval):
141 value, err = assertion.pollActual()
143 mayChange = assertion.matcherMayChange(matcher, value)
144 matches, err = matcher.Match(value)
151 } else if assertion.asyncType == AsyncAssertionTypeConsistently {
153 if !(err == nil && matches == desiredMatch) {
163 case <-time.After(assertion.pollingInterval):
164 value, err = assertion.pollActual()
166 mayChange = assertion.matcherMayChange(matcher, value)
167 matches, err = matcher.Match(value)
178 func vetExtras(extras []interface{}) (bool, string) {
179 for i, extra := range extras {
181 zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface()
182 if !reflect.DeepEqual(zeroValue, extra) {
183 message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra)
184 return false, message