mirror-to-gitea/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go
vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion.goRaw
1// untested sections: 2
2
3package asyncassertion
4
5import (
6 "errors"
7 "fmt"
8 "reflect"
9 "time"
10
11 "github.com/onsi/gomega/internal/oraclematcher"
12 "github.com/onsi/gomega/types"
13)
14
15type AsyncAssertionType uint
16
17const (
18 AsyncAssertionTypeEventually AsyncAssertionType = iota
19 AsyncAssertionTypeConsistently
20)
21
22type AsyncAssertion struct {
23 asyncType AsyncAssertionType
24 actualInput interface{}
25 timeoutInterval time.Duration
26 pollingInterval time.Duration
27 failWrapper *types.GomegaFailWrapper
28 offset int
29}
30
31func New(asyncType AsyncAssertionType, actualInput interface{}, failWrapper *types.GomegaFailWrapper, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion {
32 actualType := reflect.TypeOf(actualInput)
33 if actualType.Kind() == reflect.Func {
34 if actualType.NumIn() != 0 || actualType.NumOut() == 0 {
35 panic("Expected a function with no arguments and one or more return values.")
36 }
37 }
38
39 return &AsyncAssertion{
40 asyncType: asyncType,
41 actualInput: actualInput,
42 failWrapper: failWrapper,
43 timeoutInterval: timeoutInterval,
44 pollingInterval: pollingInterval,
45 offset: offset,
46 }
47}
48
49func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
50 assertion.failWrapper.TWithHelper.Helper()
51 return assertion.match(matcher, true, optionalDescription...)
52}
53
54func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
55 assertion.failWrapper.TWithHelper.Helper()
56 return assertion.match(matcher, false, optionalDescription...)
57}
58
59func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interface{}) string {
60 switch len(optionalDescription) {
61 case 0:
62 return ""
63 case 1:
64 if describe, ok := optionalDescription[0].(func() string); ok {
65 return describe() + "\n"
66 }
67 }
68 return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
69}
70
71func (assertion *AsyncAssertion) actualInputIsAFunction() bool {
72 actualType := reflect.TypeOf(assertion.actualInput)
73 return actualType.Kind() == reflect.Func && actualType.NumIn() == 0 && actualType.NumOut() > 0
74}
75
76func (assertion *AsyncAssertion) pollActual() (interface{}, error) {
77 if assertion.actualInputIsAFunction() {
78 values := reflect.ValueOf(assertion.actualInput).Call([]reflect.Value{})
79
80 extras := []interface{}{}
81 for _, value := range values[1:] {
82 extras = append(extras, value.Interface())
83 }
84
85 success, message := vetExtras(extras)
86
87 if !success {
88 return nil, errors.New(message)
89 }
90
91 return values[0].Interface(), nil
92 }
93
94 return assertion.actualInput, nil
95}
96
97func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool {
98 if assertion.actualInputIsAFunction() {
99 return true
100 }
101
102 return oraclematcher.MatchMayChangeInTheFuture(matcher, value)
103}
104
105func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
106 timer := time.Now()
107 timeout := time.After(assertion.timeoutInterval)
108
109 var matches bool
110 var err error
111 mayChange := true
112 value, err := assertion.pollActual()
113 if err == nil {
114 mayChange = assertion.matcherMayChange(matcher, value)
115 matches, err = matcher.Match(value)
116 }
117
118 assertion.failWrapper.TWithHelper.Helper()
119
120 fail := func(preamble string) {
121 errMsg := ""
122 message := ""
123 if err != nil {
124 errMsg = "Error: " + err.Error()
125 } else {
126 if desiredMatch {
127 message = matcher.FailureMessage(value)
128 } else {
129 message = matcher.NegatedFailureMessage(value)
130 }
131 }
132 assertion.failWrapper.TWithHelper.Helper()
133 description := assertion.buildDescription(optionalDescription...)
134 assertion.failWrapper.Fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset)
135 }
136
137 if assertion.asyncType == AsyncAssertionTypeEventually {
138 for {
139 if err == nil && matches == desiredMatch {
140 return true
141 }
142
143 if !mayChange {
144 fail("No future change is possible. Bailing out early")
145 return false
146 }
147
148 select {
149 case <-time.After(assertion.pollingInterval):
150 value, err = assertion.pollActual()
151 if err == nil {
152 mayChange = assertion.matcherMayChange(matcher, value)
153 matches, err = matcher.Match(value)
154 }
155 case <-timeout:
156 fail("Timed out")
157 return false
158 }
159 }
160 } else if assertion.asyncType == AsyncAssertionTypeConsistently {
161 for {
162 if !(err == nil && matches == desiredMatch) {
163 fail("Failed")
164 return false
165 }
166
167 if !mayChange {
168 return true
169 }
170
171 select {
172 case <-time.After(assertion.pollingInterval):
173 value, err = assertion.pollActual()
174 if err == nil {
175 mayChange = assertion.matcherMayChange(matcher, value)
176 matches, err = matcher.Match(value)
177 }
178 case <-timeout:
179 return true
180 }
181 }
182 }
183
184 return false
185}
186
187func vetExtras(extras []interface{}) (bool, string) {
188 for i, extra := range extras {
189 if extra != nil {
190 zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface()
191 if !reflect.DeepEqual(zeroValue, extra) {
192 message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra)
193 return false, message
194 }
195 }
196 }
197 return true, ""
198}