arnaucube
arnaucube/mirror-to-giteaPublic Self-hosted repository
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}