Bump github.com/onsi/gomega from 1.14.0 to 1.15.0

Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.14.0 to 1.15.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.14.0...v1.15.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2021-08-06 12:21:04 +00:00
committed by GitHub
parent a82ceafb73
commit 79e4baf4ca
18 changed files with 551 additions and 615 deletions

2
go.mod
View File

@ -44,7 +44,7 @@ require (
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 github.com/moby/term v0.0.0-20201216013528-df9cb8a40635
github.com/mrunalp/fileutils v0.5.0 github.com/mrunalp/fileutils v0.5.0
github.com/onsi/ginkgo v1.16.4 github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.14.0 github.com/onsi/gomega v1.15.0
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
github.com/opencontainers/runc v1.0.1 github.com/opencontainers/runc v1.0.1

3
go.sum
View File

@ -711,8 +711,9 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI=
github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU=
github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=

View File

@ -1,3 +1,12 @@
## 1.15.0
### Fixes
The previous version (1.14.0) introduced a change to allow `Eventually` and `Consistently` to support functions that make assertions. This was accomplished by overriding the global fail handler when running the callbacks passed to `Eventually/Consistently` in order to capture any resulting errors. Issue #457 uncovered a flaw with this approach: when multiple `Eventually`s are running concurrently they race when overriding the singleton global fail handler.
1.15.0 resolves this by requiring users who want to make assertions in `Eventually/Consistently` call backs to explicitly pass in a function that takes a `Gomega` as an argument. The passed-in `Gomega` instance can be used to make assertions. Any failures will cause `Eventually` to retry the callback. This cleaner interface avoids the issue of swapping out globals but comes at the cost of changing the contract introduced in v1.14.0. As such 1.15.0 introduces a breaking change with respect to 1.14.0 - however we expect that adoption of this feature in 1.14.0 remains limited.
In addition, 1.15.0 cleans up some of Gomega's internals. Most users shouldn't notice any differences stemming from the refactoring that was made.
## 1.14.0 ## 1.14.0
### Features ### Features

40
vendor/github.com/onsi/gomega/env.go generated vendored
View File

@ -1,40 +0,0 @@
package gomega
import (
"os"
"github.com/onsi/gomega/internal/defaults"
)
const (
ConsistentlyDurationEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_DURATION"
ConsistentlyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_POLLING_INTERVAL"
EventuallyTimeoutEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_TIMEOUT"
EventuallyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_POLLING_INTERVAL"
)
func init() {
defaults.SetDurationFromEnv(
os.Getenv,
SetDefaultConsistentlyDuration,
ConsistentlyDurationEnvVarName,
)
defaults.SetDurationFromEnv(
os.Getenv,
SetDefaultConsistentlyPollingInterval,
ConsistentlyPollingIntervalEnvVarName,
)
defaults.SetDurationFromEnv(
os.Getenv,
SetDefaultEventuallyTimeout,
EventuallyTimeoutEnvVarName,
)
defaults.SetDurationFromEnv(
os.Getenv,
SetDefaultEventuallyPollingInterval,
EventuallyPollingIntervalEnvVarName,
)
}

View File

@ -16,78 +16,92 @@ package gomega
import ( import (
"errors" "errors"
"fmt" "fmt"
"reflect"
"time" "time"
"github.com/onsi/gomega/internal/assertion" "github.com/onsi/gomega/internal"
"github.com/onsi/gomega/internal/asyncassertion"
"github.com/onsi/gomega/internal/testingtsupport"
"github.com/onsi/gomega/types" "github.com/onsi/gomega/types"
) )
const GOMEGA_VERSION = "1.14.0" const GOMEGA_VERSION = "1.15.0"
const nilFailHandlerPanic = `You are trying to make an assertion, but Gomega's fail handler is nil. const nilGomegaPanic = `You are trying to make an assertion, but haven't registered Gomega's fail handler.
If you're using Ginkgo then you probably forgot to put your assertion in an It(). If you're using Ginkgo then you probably forgot to put your assertion in an It().
Alternatively, you may have forgotten to register a fail handler with RegisterFailHandler() or RegisterTestingT(). Alternatively, you may have forgotten to register a fail handler with RegisterFailHandler() or RegisterTestingT().
Depending on your vendoring solution you may be inadvertently importing gomega and subpackages (e.g. ghhtp, gexec,...) from different locations. Depending on your vendoring solution you may be inadvertently importing gomega and subpackages (e.g. ghhtp, gexec,...) from different locations.
` `
var globalFailWrapper *types.GomegaFailWrapper // Gomega describes the essential Gomega DSL. This interface allows libraries
// to abstract between the standard package-level function implementations
// and alternatives like *WithT.
//
// The types in the top-level DSL have gotten a bit messy due to earlier depracations that avoid stuttering
// and due to an accidental use of a concrete type (*WithT) in an earlier release.
//
// As of 1.15 both the WithT and Ginkgo variants of Gomega are implemented by the same underlying object
// however one (the Ginkgo variant) is exported as an interface (types.Gomega) whereas the other (the withT variant)
// is shared as a concrete type (*WithT, which is aliased to *internal.Gomega). 1.15 did not clean this mess up to ensure
// that declarations of *WithT in existing code are not broken by the upgrade to 1.15.
type Gomega = types.Gomega
var defaultEventuallyTimeout = time.Second // DefaultGomega supplies the standard package-level implementation
var defaultEventuallyPollingInterval = 10 * time.Millisecond var Default = Gomega(internal.NewGomega(internal.FetchDefaultDurationBundle()))
var defaultConsistentlyDuration = 100 * time.Millisecond
var defaultConsistentlyPollingInterval = 10 * time.Millisecond // NewGomega returns an instance of Gomega wired into the passed-in fail handler.
// You generally don't need to use this when using Ginkgo - RegisterFailHandler will wire up the global gomega
// However creating a NewGomega with a custom fail handler can be useful in contexts where you want to use Gomega's
// rich ecosystem of matchers without causing a test to fail. For example, to aggregate a series of potential failures
// or for use in a non-test setting.
func NewGomega(fail types.GomegaFailHandler) Gomega {
return internal.NewGomega(Default.(*internal.Gomega).DurationBundle).ConfigureWithFailHandler(fail)
}
// WithT wraps a *testing.T and provides `Expect`, `Eventually`, and `Consistently` methods. This allows you to leverage
// Gomega's rich ecosystem of matchers in standard `testing` test suites.
//
// Use `NewWithT` to instantiate a `WithT`
//
// As of 1.15 both the WithT and Ginkgo variants of Gomega are implemented by the same underlying object
// however one (the Ginkgo variant) is exported as an interface (types.Gomega) whereas the other (the withT variant)
// is shared as a concrete type (*WithT, which is aliased to *internal.Gomega). 1.15 did not clean this mess up to ensure
// that declarations of *WithT in existing code are not broken by the upgrade to 1.15.
type WithT = internal.Gomega
// GomegaWithT is deprecated in favor of gomega.WithT, which does not stutter.
type GomegaWithT = WithT
// NewWithT takes a *testing.T and returngs a `gomega.WithT` allowing you to use `Expect`, `Eventually`, and `Consistently` along with
// Gomega's rich ecosystem of matchers in standard `testing` test suits.
//
// func TestFarmHasCow(t *testing.T) {
// g := gomega.NewWithT(t)
//
// f := farm.New([]string{"Cow", "Horse"})
// g.Expect(f.HasCow()).To(BeTrue(), "Farm should have cow")
// }
func NewWithT(t types.GomegaTestingT) *WithT {
return internal.NewGomega(Default.(*internal.Gomega).DurationBundle).ConfigureWithT(t)
}
// NewGomegaWithT is deprecated in favor of gomega.NewWithT, which does not stutter.
var NewGomegaWithT = NewWithT
// RegisterFailHandler connects Ginkgo to Gomega. When a matcher fails // RegisterFailHandler connects Ginkgo to Gomega. When a matcher fails
// the fail handler passed into RegisterFailHandler is called. // the fail handler passed into RegisterFailHandler is called.
func RegisterFailHandler(handler types.GomegaFailHandler) { func RegisterFailHandler(fail types.GomegaFailHandler) {
RegisterFailHandlerWithT(testingtsupport.EmptyTWithHelper{}, handler) Default.(*internal.Gomega).ConfigureWithFailHandler(fail)
} }
// RegisterFailHandlerWithT ensures that the given types.TWithHelper and fail handler // RegisterFailHandlerWithT is deprecated and will be removed in a future release.
// are used globally. // users should use RegisterFailHandler, or RegisterTestingT
func RegisterFailHandlerWithT(t types.TWithHelper, handler types.GomegaFailHandler) { func RegisterFailHandlerWithT(_ types.GomegaTestingT, fail types.GomegaFailHandler) {
if handler == nil { fmt.Println("RegisterFailHandlerWithT is deprecated. Please use RegisterFailHandler or RegisterTestingT instead.")
globalFailWrapper = nil Default.(*internal.Gomega).ConfigureWithFailHandler(fail)
return
}
globalFailWrapper = &types.GomegaFailWrapper{
Fail: handler,
TWithHelper: t,
}
} }
// RegisterTestingT connects Gomega to Golang's XUnit style // RegisterTestingT connects Gomega to Golang's XUnit style
// Testing.T tests. It is now deprecated and you should use NewWithT() instead. // Testing.T tests. It is now deprecated and you should use NewWithT() instead to get a fresh instance of Gomega for each test.
//
// Legacy Documentation:
//
// You'll need to call this at the top of each XUnit style test:
//
// func TestFarmHasCow(t *testing.T) {
// RegisterTestingT(t)
//
// f := farm.New([]string{"Cow", "Horse"})
// Expect(f.HasCow()).To(BeTrue(), "Farm should have cow")
// }
//
// Note that this *testing.T is registered *globally* by Gomega (this is why you don't have to
// pass `t` down to the matcher itself). This means that you cannot run the XUnit style tests
// in parallel as the global fail handler cannot point to more than one testing.T at a time.
//
// NewWithT() does not have this limitation
//
// (As an aside: Ginkgo gets around this limitation by running parallel tests in different *processes*).
func RegisterTestingT(t types.GomegaTestingT) { func RegisterTestingT(t types.GomegaTestingT) {
tWithHelper, hasHelper := t.(types.TWithHelper) Default.(*internal.Gomega).ConfigureWithT(t)
if !hasHelper {
RegisterFailHandler(testingtsupport.BuildTestingTGomegaFailWrapper(t).Fail)
return
}
RegisterFailHandlerWithT(tWithHelper, testingtsupport.BuildTestingTGomegaFailWrapper(t).Fail)
} }
// InterceptGomegaFailures runs a given callback and returns an array of // InterceptGomegaFailures runs a given callback and returns an array of
@ -98,13 +112,13 @@ func RegisterTestingT(t types.GomegaTestingT) {
// This is most useful when testing custom matchers, but can also be used to check // This is most useful when testing custom matchers, but can also be used to check
// on a value using a Gomega assertion without causing a test failure. // on a value using a Gomega assertion without causing a test failure.
func InterceptGomegaFailures(f func()) []string { func InterceptGomegaFailures(f func()) []string {
originalHandler := globalFailWrapper.Fail originalHandler := Default.(*internal.Gomega).Fail
failures := []string{} failures := []string{}
RegisterFailHandler(func(message string, callerSkip ...int) { Default.(*internal.Gomega).Fail = func(message string, callerSkip ...int) {
failures = append(failures, message) failures = append(failures, message)
}) }
defer func() { defer func() {
RegisterFailHandler(originalHandler) Default.(*internal.Gomega).Fail = originalHandler
}() }()
f() f()
return failures return failures
@ -117,14 +131,14 @@ func InterceptGomegaFailures(f func()) []string {
// does not register a failure with the FailHandler registered via RegisterFailHandler - it is up // does not register a failure with the FailHandler registered via RegisterFailHandler - it is up
// to the user to decide what to do with the returned error // to the user to decide what to do with the returned error
func InterceptGomegaFailure(f func()) (err error) { func InterceptGomegaFailure(f func()) (err error) {
originalHandler := globalFailWrapper.Fail originalHandler := Default.(*internal.Gomega).Fail
RegisterFailHandler(func(message string, callerSkip ...int) { Default.(*internal.Gomega).Fail = func(message string, callerSkip ...int) {
err = errors.New(message) err = errors.New(message)
panic("stop execution") panic("stop execution")
}) }
defer func() { defer func() {
RegisterFailHandler(originalHandler) Default.(*internal.Gomega).Fail = originalHandler
if e := recover(); e != nil { if e := recover(); e != nil {
if err == nil { if err == nil {
panic(e) panic(e)
@ -136,6 +150,12 @@ func InterceptGomegaFailure(f func()) (err error) {
return err return err
} }
func ensureDefaultGomegaIsConfigured() {
if !Default.(*internal.Gomega).IsConfigured() {
panic(nilGomegaPanic)
}
}
// Ω wraps an actual value allowing assertions to be made on it: // Ω wraps an actual value allowing assertions to be made on it:
// Ω("foo").Should(Equal("foo")) // Ω("foo").Should(Equal("foo"))
// //
@ -154,7 +174,8 @@ func InterceptGomegaFailure(f func()) (err error) {
// //
// Ω and Expect are identical // Ω and Expect are identical
func Ω(actual interface{}, extra ...interface{}) Assertion { func Ω(actual interface{}, extra ...interface{}) Assertion {
return ExpectWithOffset(0, actual, extra...) ensureDefaultGomegaIsConfigured()
return Default.Ω(actual, extra...)
} }
// Expect wraps an actual value allowing assertions to be made on it: // Expect wraps an actual value allowing assertions to be made on it:
@ -175,7 +196,8 @@ func Ω(actual interface{}, extra ...interface{}) Assertion {
// //
// Expect and Ω are identical // Expect and Ω are identical
func Expect(actual interface{}, extra ...interface{}) Assertion { func Expect(actual interface{}, extra ...interface{}) Assertion {
return ExpectWithOffset(0, actual, extra...) ensureDefaultGomegaIsConfigured()
return Default.Expect(actual, extra...)
} }
// ExpectWithOffset wraps an actual value allowing assertions to be made on it: // ExpectWithOffset wraps an actual value allowing assertions to be made on it:
@ -188,166 +210,156 @@ func Expect(actual interface{}, extra ...interface{}) Assertion {
// error message to refer to the calling line in the test (as opposed to the line in the helper function) // error message to refer to the calling line in the test (as opposed to the line in the helper function)
// set the first argument of `ExpectWithOffset` appropriately. // set the first argument of `ExpectWithOffset` appropriately.
func ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion { func ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion {
if globalFailWrapper == nil { ensureDefaultGomegaIsConfigured()
panic(nilFailHandlerPanic) return Default.ExpectWithOffset(offset, actual, extra...)
}
return assertion.New(actual, globalFailWrapper, offset, extra...)
} }
// Eventually wraps an actual value allowing assertions to be made on it. /*
// The assertion is tried periodically until it passes or a timeout occurs. Eventually enables making assertions on asynchronous behavior.
//
// Both the timeout and polling interval are configurable as optional arguments: Eventually checks that an assertion *eventually* passes. Eventually blocks when called and attempts an assertion periodically until it passes or a timeout occurs. Both the timeout and polling interval are configurable as optional arguments.
// The first optional argument is the timeout The first optional argument is the timeout (which defaults to 1s), the second is the polling interval (which defaults to 10ms). Both intervals can be specified as time.Duration, parsable duration strings or floats/integers (in which case they are interpreted as seconds).
// The second optional argument is the polling interval
// Eventually works with any Gomega compatible matcher and supports making assertions against three categories of actual value:
// Both intervals can either be specified as time.Duration, parsable duration strings or as floats/integers. In the
// last case they are interpreted as seconds. **Category 1: Making Eventually assertions on values**
//
// If Eventually is passed an actual that is a function taking no arguments, There are several examples of values that can change over time. These can be passed in to Eventually and will be passed to the matcher repeatedly until a match occurs. For example:
// then Eventually will call the function periodically and try the matcher against the function's first return value.
// c := make(chan bool)
// Example: go DoStuff(c)
// Eventually(c, "50ms").Should(BeClosed())
// Eventually(func() int {
// return thingImPolling.Count() will poll the channel repeatedly until it is closed. In this example `Eventually` will block until either the specified timeout of 50ms has elapsed or the channel is closed, whichever comes first.
// }).Should(BeNumerically(">=", 17))
// Several Gomega libraries allow you to use Eventually in this way. For example, the gomega/gexec package allows you to block until a *gexec.Session exits successfuly via:
// Note that this example could be rewritten:
// Eventually(session).Should(gexec.Exit(0))
// Eventually(thingImPolling.Count).Should(BeNumerically(">=", 17))
// And the gomega/gbytes package allows you to monitor a streaming *gbytes.Buffer until a given string is seen:
// If the function returns more than one value, then Eventually will pass the first value to the matcher and
// assert that all other values are nil/zero. Eventually(buffer).Should(gbytes.Say("hello there"))
// This allows you to pass Eventually a function that returns a value and an error - a common pattern in Go.
// In these examples, both `session` and `buffer` are designed to be thread-safe when polled by the `Exit` and `Say` matchers. This is not true in general of most raw values, so while it is tempting to do something like:
// For example, consider a method that returns a value and an error:
// func FetchFromDB() (string, error) // THIS IS NOT THREAD-SAFE
// var s *string
// Then go mutateStringEventually(s)
// Eventually(FetchFromDB).Should(Equal("hasselhoff")) Eventually(s).Should(Equal("I've changed"))
//
// Will pass only if the the returned error is nil and the returned string passes the matcher. this will trigger Go's race detector as the goroutine polling via Eventually will race over the value of s with the goroutine mutating the string. For cases like this you can use channels or introduce your own locking around s by passing Eventually a function.
//
// Eventually allows you to make assertions in the pased-in function. The function is assumed to have failed and will be retried if any assertion in the function fails. **Category 2: Make Eventually assertions on functions**
// For example:
// Eventually can be passed functions that **take no arguments** and **return at least one value**. When configured this way, Eventually will poll the function repeatedly and pass the first returned value to the matcher.
// Eventually(func() Widget {
// resp, err := http.Get(url) For example:
// Expect(err).NotTo(HaveOccurred())
// defer resp.Body.Close() Eventually(func() int {
// Expect(resp.SatusCode).To(Equal(http.StatusOK)) return client.FetchCount()
// var widget Widget }).Should(BeNumerically(">=", 17))
// Expect(json.NewDecoder(resp.Body).Decode(&widget)).To(Succeed())
// return widget will repeatedly poll client.FetchCount until the BeNumerically matcher is satisfied. (Note that this example could have been written as Eventually(client.FetchCount).Should(BeNumerically(">=", 17)))
// }).Should(Equal(expectedWidget))
// If multple values are returned by the function, Eventually will pass the first value to the matcher and require that all others are zero-valued. This allows you to pass Eventually a function that returns a value and an error - a common patternin Go.
// will keep trying the passed-in function until all its assertsions pass (i.e. the http request succeeds) _and_ the returned object satisfies the passed-in matcher.
// For example, consider a method that returns a value and an error:
// Functions passed to Eventually typically have a return value. However you are allowed to pass in a function with no return value. Eventually assumes such a function func FetchFromDB() (string, error)
// is making assertions and will turn it into a function that returns an error if any assertion fails, or nil if no assertion fails. This allows you to use the Succeed() matcher
// to express that a complex operation should eventually succeed. For example: Then
// Eventually(FetchFromDB).Should(Equal("got it"))
// Eventually(func() {
// model, err := db.Find("foo") will pass only if and when the returned error is nil *and* the returned string satisfies the matcher.
// Expect(err).NotTo(HaveOccurred())
// Expect(model.Reticulated()).To(BeTrue()) It is important to note that the function passed into Eventually is invoked *synchronously* when polled. Eventually does not (in fact, it cannot) kill the function if it takes longer to return than Eventually's configured timeout. You should design your functions with this in mind.
// Expect(model.Save()).To(Succeed())
// }).Should(Succeed()) **Category 3: Making assertions _in_ the function passed into Eventually**
//
// will rerun the function until all its assertions pass. When testing complex systems it can be valuable to assert that a _set_ of assertions passes Eventually. Eventually supports this by accepting functions that take a single Gomega argument and return zero or more values.
//
// Eventually's default timeout is 1 second, and its default polling interval is 10ms Here's an example that makes some asssertions and returns a value and error:
Eventually(func(g Gomega) (Widget, error) {
ids, err := client.FetchIDs()
g.Expect(err).NotTo(HaveOccurred())
g.Expect(ids).To(ContainElement(1138))
return client.FetchWidget(1138)
}).Should(Equal(expectedWidget))
will pass only if all the assertions in the polled function pass and the return value satisfied the matcher.
Eventually also supports a special case polling function that takes a single Gomega argument and returns no values. Eventually assumes such a function is making assertions and is designed to work with the Succeed matcher to validate that all assertions have passed.
For example:
Eventually(func(g Gomega) {
model, err := client.Find(1138)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(model.Reticulate()).To(Succeed())
g.Expect(model.IsReticulated()).To(BeTrue())
g.Expect(model.Save()).To(Succeed())
}).Should(Succeed())
will rerun the function until all assertions pass.
*/
func Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion { func Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion {
return EventuallyWithOffset(0, actual, intervals...) ensureDefaultGomegaIsConfigured()
return Default.Eventually(actual, intervals...)
} }
// EventuallyWithOffset operates like Eventually but takes an additional // EventuallyWithOffset operates like Eventually but takes an additional
// initial argument to indicate an offset in the call stack. This is useful when building helper // initial argument to indicate an offset in the call stack. This is useful when building helper
// functions that contain matchers. To learn more, read about `ExpectWithOffset`. // functions that contain matchers. To learn more, read about `ExpectWithOffset`.
func EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion { func EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
if globalFailWrapper == nil { ensureDefaultGomegaIsConfigured()
panic(nilFailHandlerPanic) return Default.EventuallyWithOffset(offset, actual, intervals...)
}
timeoutInterval := defaultEventuallyTimeout
pollingInterval := defaultEventuallyPollingInterval
if len(intervals) > 0 {
timeoutInterval = toDuration(intervals[0])
}
if len(intervals) > 1 {
pollingInterval = toDuration(intervals[1])
}
return asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, actual, globalFailWrapper, timeoutInterval, pollingInterval, offset)
} }
// Consistently wraps an actual value allowing assertions to be made on it. /*
// The assertion is tried periodically and is required to pass for a period of time. Consistently, like Eventually, enables making assertions on asynchronous behavior.
//
// Both the total time and polling interval are configurable as optional arguments: Consistently blocks when called for a specified duration. During that duration Consistently repeatedly polls its matcher and ensures that it is satisfied. If the matcher is consistently satisfied, then Consistently will pass. Otherwise Consistently will fail.
// The first optional argument is the duration that Consistently will run for
// The second optional argument is the polling interval Both the total waiting duration and the polling interval are configurable as optional arguments. The first optional arugment is the duration that Consistently will run for (defaults to 100ms), and the second argument is the polling interval (defaults to 10ms). As with Eventually, these intervals can be passed in as time.Duration, parsable duration strings or an integer or float number of seconds.
//
// Both intervals can either be specified as time.Duration, parsable duration strings or as floats/integers. In the Consistently accepts the same three categories of actual as Eventually, check the Eventually docs to learn more.
// last case they are interpreted as seconds.
// Consistently is useful in cases where you want to assert that something *does not happen* for a period of time. For example, you may want to assert that a goroutine does *not* send data down a channel. In this case you could write:
// If Consistently is passed an actual that is a function taking no arguments.
// Consistently(channel, "200ms").ShouldNot(Receive())
// If the function returns one value, then Consistently will call the function periodically and try the matcher against the function's first return value.
// This will block for 200 milliseconds and repeatedly check the channel and ensure nothing has been received.
// If the function returns more than one value, then Consistently will pass the first value to the matcher and */
// assert that all other values are nil/zero.
// This allows you to pass Consistently a function that returns a value and an error - a common pattern in Go.
//
// Like Eventually, Consistently allows you to make assertions in the function. If any assertion fails Consistently will fail. In addition,
// Consistently also allows you to pass in a function with no return value. In this case Consistently can be paired with the Succeed() matcher to assert
// that no assertions in the function fail.
//
// Consistently is useful in cases where you want to assert that something *does not happen* over a period of time.
// For example, you want to assert that a goroutine does *not* send data down a channel. In this case, you could:
//
// Consistently(channel).ShouldNot(Receive())
//
// Consistently's default duration is 100ms, and its default polling interval is 10ms
func Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion { func Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion {
return ConsistentlyWithOffset(0, actual, intervals...) ensureDefaultGomegaIsConfigured()
return Default.Consistently(actual, intervals...)
} }
// ConsistentlyWithOffset operates like Consistently but takes an additional // ConsistentlyWithOffset operates like Consistently but takes an additional
// initial argument to indicate an offset in the call stack. This is useful when building helper // initial argument to indicate an offset in the call stack. This is useful when building helper
// functions that contain matchers. To learn more, read about `ExpectWithOffset`. // functions that contain matchers. To learn more, read about `ExpectWithOffset`.
func ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion { func ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
if globalFailWrapper == nil { ensureDefaultGomegaIsConfigured()
panic(nilFailHandlerPanic) return Default.ConsistentlyWithOffset(offset, actual, intervals...)
}
timeoutInterval := defaultConsistentlyDuration
pollingInterval := defaultConsistentlyPollingInterval
if len(intervals) > 0 {
timeoutInterval = toDuration(intervals[0])
}
if len(intervals) > 1 {
pollingInterval = toDuration(intervals[1])
}
return asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, actual, globalFailWrapper, timeoutInterval, pollingInterval, offset)
} }
// SetDefaultEventuallyTimeout sets the default timeout duration for Eventually. Eventually will repeatedly poll your condition until it succeeds, or until this timeout elapses. // SetDefaultEventuallyTimeout sets the default timeout duration for Eventually. Eventually will repeatedly poll your condition until it succeeds, or until this timeout elapses.
func SetDefaultEventuallyTimeout(t time.Duration) { func SetDefaultEventuallyTimeout(t time.Duration) {
defaultEventuallyTimeout = t Default.SetDefaultEventuallyTimeout(t)
} }
// SetDefaultEventuallyPollingInterval sets the default polling interval for Eventually. // SetDefaultEventuallyPollingInterval sets the default polling interval for Eventually.
func SetDefaultEventuallyPollingInterval(t time.Duration) { func SetDefaultEventuallyPollingInterval(t time.Duration) {
defaultEventuallyPollingInterval = t Default.SetDefaultEventuallyPollingInterval(t)
} }
// SetDefaultConsistentlyDuration sets the default duration for Consistently. Consistently will verify that your condition is satisfied for this long. // SetDefaultConsistentlyDuration sets the default duration for Consistently. Consistently will verify that your condition is satisfied for this long.
func SetDefaultConsistentlyDuration(t time.Duration) { func SetDefaultConsistentlyDuration(t time.Duration) {
defaultConsistentlyDuration = t Default.SetDefaultConsistentlyDuration(t)
} }
// SetDefaultConsistentlyPollingInterval sets the default polling interval for Consistently. // SetDefaultConsistentlyPollingInterval sets the default polling interval for Consistently.
func SetDefaultConsistentlyPollingInterval(t time.Duration) { func SetDefaultConsistentlyPollingInterval(t time.Duration) {
defaultConsistentlyPollingInterval = t Default.SetDefaultConsistentlyPollingInterval(t)
} }
// AsyncAssertion is returned by Eventually and Consistently and polls the actual value passed into Eventually against // AsyncAssertion is returned by Eventually and Consistently and polls the actual value passed into Eventually against
@ -365,13 +377,10 @@ func SetDefaultConsistentlyPollingInterval(t time.Duration) {
// //
// Eventually(myChannel).Should(Receive(), "Something should have come down the pipe.") // Eventually(myChannel).Should(Receive(), "Something should have come down the pipe.")
// Consistently(myChannel).ShouldNot(Receive(), func() string { return "Nothing should have come down the pipe." }) // Consistently(myChannel).ShouldNot(Receive(), func() string { return "Nothing should have come down the pipe." })
type AsyncAssertion interface { type AsyncAssertion = types.AsyncAssertion
Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
}
// GomegaAsyncAssertion is deprecated in favor of AsyncAssertion, which does not stutter. // GomegaAsyncAssertion is deprecated in favor of AsyncAssertion, which does not stutter.
type GomegaAsyncAssertion = AsyncAssertion type GomegaAsyncAssertion = types.AsyncAssertion
// Assertion is returned by Ω and Expect and compares the actual value to the matcher // Assertion is returned by Ω and Expect and compares the actual value to the matcher
// passed to the Should/ShouldNot and To/ToNot/NotTo methods. // passed to the Should/ShouldNot and To/ToNot/NotTo methods.
@ -390,149 +399,10 @@ type GomegaAsyncAssertion = AsyncAssertion
// Example: // Example:
// //
// Ω(farm.HasCow()).Should(BeTrue(), "Farm %v should have a cow", farm) // Ω(farm.HasCow()).Should(BeTrue(), "Farm %v should have a cow", farm)
type Assertion interface { type Assertion = types.Assertion
Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
}
// GomegaAssertion is deprecated in favor of Assertion, which does not stutter. // GomegaAssertion is deprecated in favor of Assertion, which does not stutter.
type GomegaAssertion = Assertion type GomegaAssertion = types.Assertion
// OmegaMatcher is deprecated in favor of the better-named and better-organized types.GomegaMatcher but sticks around to support existing code that uses it // OmegaMatcher is deprecated in favor of the better-named and better-organized types.GomegaMatcher but sticks around to support existing code that uses it
type OmegaMatcher types.GomegaMatcher type OmegaMatcher = types.GomegaMatcher
// WithT wraps a *testing.T and provides `Expect`, `Eventually`, and `Consistently` methods. This allows you to leverage
// Gomega's rich ecosystem of matchers in standard `testing` test suites.
//
// Use `NewWithT` to instantiate a `WithT`
type WithT struct {
failWrapper *types.GomegaFailWrapper
}
// GomegaWithT is deprecated in favor of gomega.WithT, which does not stutter.
type GomegaWithT = WithT
// NewWithT takes a *testing.T and returngs a `gomega.WithT` allowing you to use `Expect`, `Eventually`, and `Consistently` along with
// Gomega's rich ecosystem of matchers in standard `testing` test suits.
//
// func TestFarmHasCow(t *testing.T) {
// g := gomega.NewWithT(t)
//
// f := farm.New([]string{"Cow", "Horse"})
// g.Expect(f.HasCow()).To(BeTrue(), "Farm should have cow")
// }
func NewWithT(t types.GomegaTestingT) *WithT {
return &WithT{
failWrapper: testingtsupport.BuildTestingTGomegaFailWrapper(t),
}
}
// NewGomegaWithT is deprecated in favor of gomega.NewWithT, which does not stutter.
func NewGomegaWithT(t types.GomegaTestingT) *GomegaWithT {
return NewWithT(t)
}
// ExpectWithOffset is used to make assertions. See documentation for ExpectWithOffset.
func (g *WithT) ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion {
return assertion.New(actual, g.failWrapper, offset, extra...)
}
// EventuallyWithOffset is used to make asynchronous assertions. See documentation for EventuallyWithOffset.
func (g *WithT) EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
timeoutInterval := defaultEventuallyTimeout
pollingInterval := defaultEventuallyPollingInterval
if len(intervals) > 0 {
timeoutInterval = toDuration(intervals[0])
}
if len(intervals) > 1 {
pollingInterval = toDuration(intervals[1])
}
return asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, actual, g.failWrapper, timeoutInterval, pollingInterval, offset)
}
// ConsistentlyWithOffset is used to make asynchronous assertions. See documentation for ConsistentlyWithOffset.
func (g *WithT) ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
timeoutInterval := defaultConsistentlyDuration
pollingInterval := defaultConsistentlyPollingInterval
if len(intervals) > 0 {
timeoutInterval = toDuration(intervals[0])
}
if len(intervals) > 1 {
pollingInterval = toDuration(intervals[1])
}
return asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, actual, g.failWrapper, timeoutInterval, pollingInterval, offset)
}
// Expect is used to make assertions. See documentation for Expect.
func (g *WithT) Expect(actual interface{}, extra ...interface{}) Assertion {
return g.ExpectWithOffset(0, actual, extra...)
}
// Eventually is used to make asynchronous assertions. See documentation for Eventually.
func (g *WithT) Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion {
return g.EventuallyWithOffset(0, actual, intervals...)
}
// Consistently is used to make asynchronous assertions. See documentation for Consistently.
func (g *WithT) Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion {
return g.ConsistentlyWithOffset(0, actual, intervals...)
}
func toDuration(input interface{}) time.Duration {
duration, ok := input.(time.Duration)
if ok {
return duration
}
value := reflect.ValueOf(input)
kind := reflect.TypeOf(input).Kind()
if reflect.Int <= kind && kind <= reflect.Int64 {
return time.Duration(value.Int()) * time.Second
} else if reflect.Uint <= kind && kind <= reflect.Uint64 {
return time.Duration(value.Uint()) * time.Second
} else if reflect.Float32 <= kind && kind <= reflect.Float64 {
return time.Duration(value.Float() * float64(time.Second))
} else if reflect.String == kind {
duration, err := time.ParseDuration(value.String())
if err != nil {
panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input))
}
return duration
}
panic(fmt.Sprintf("%v is not a valid interval. Must be time.Duration, parsable duration string or a number.", input))
}
// Gomega describes the essential Gomega DSL. This interface allows libraries
// to abstract between the standard package-level function implementations
// and alternatives like *WithT.
type Gomega interface {
Expect(actual interface{}, extra ...interface{}) Assertion
Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion
Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion
}
type globalFailHandlerGomega struct{}
// DefaultGomega supplies the standard package-level implementation
var Default Gomega = globalFailHandlerGomega{}
// Expect is used to make assertions. See documentation for Expect.
func (globalFailHandlerGomega) Expect(actual interface{}, extra ...interface{}) Assertion {
return Expect(actual, extra...)
}
// Eventually is used to make asynchronous assertions. See documentation for Eventually.
func (globalFailHandlerGomega) Eventually(actual interface{}, extra ...interface{}) AsyncAssertion {
return Eventually(actual, extra...)
}
// Consistently is used to make asynchronous assertions. See documentation for Consistently.
func (globalFailHandlerGomega) Consistently(actual interface{}, extra ...interface{}) AsyncAssertion {
return Consistently(actual, extra...)
}

View File

@ -1,4 +1,4 @@
package assertion package internal
import ( import (
"fmt" "fmt"
@ -9,42 +9,42 @@ import (
type Assertion struct { type Assertion struct {
actualInput interface{} actualInput interface{}
failWrapper *types.GomegaFailWrapper
offset int offset int
extra []interface{} extra []interface{}
g *Gomega
} }
func New(actualInput interface{}, failWrapper *types.GomegaFailWrapper, offset int, extra ...interface{}) *Assertion { func NewAssertion(actualInput interface{}, g *Gomega, offset int, extra ...interface{}) *Assertion {
return &Assertion{ return &Assertion{
actualInput: actualInput, actualInput: actualInput,
failWrapper: failWrapper,
offset: offset, offset: offset,
extra: extra, extra: extra,
g: g,
} }
} }
func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.failWrapper.TWithHelper.Helper() assertion.g.THelper()
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...) return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
} }
func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.failWrapper.TWithHelper.Helper() assertion.g.THelper()
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
} }
func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.failWrapper.TWithHelper.Helper() assertion.g.THelper()
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...) return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
} }
func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.failWrapper.TWithHelper.Helper() assertion.g.THelper()
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
} }
func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.failWrapper.TWithHelper.Helper() assertion.g.THelper()
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
} }
@ -62,10 +62,10 @@ func (assertion *Assertion) buildDescription(optionalDescription ...interface{})
func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
matches, err := matcher.Match(assertion.actualInput) matches, err := matcher.Match(assertion.actualInput)
assertion.failWrapper.TWithHelper.Helper() assertion.g.THelper()
if err != nil { if err != nil {
description := assertion.buildDescription(optionalDescription...) description := assertion.buildDescription(optionalDescription...)
assertion.failWrapper.Fail(description+err.Error(), 2+assertion.offset) assertion.g.Fail(description+err.Error(), 2+assertion.offset)
return false return false
} }
if matches != desiredMatch { if matches != desiredMatch {
@ -76,7 +76,7 @@ func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool
message = matcher.NegatedFailureMessage(assertion.actualInput) message = matcher.NegatedFailureMessage(assertion.actualInput)
} }
description := assertion.buildDescription(optionalDescription...) description := assertion.buildDescription(optionalDescription...)
assertion.failWrapper.Fail(description+message, 2+assertion.offset) assertion.g.Fail(description+message, 2+assertion.offset)
return false return false
} }
@ -90,8 +90,8 @@ func (assertion *Assertion) vetExtras(optionalDescription ...interface{}) bool {
} }
description := assertion.buildDescription(optionalDescription...) description := assertion.buildDescription(optionalDescription...)
assertion.failWrapper.TWithHelper.Helper() assertion.g.THelper()
assertion.failWrapper.Fail(description+message, 2+assertion.offset) assertion.g.Fail(description+message, 2+assertion.offset)
return false return false
} }

View File

@ -1,6 +1,4 @@
// untested sections: 2 package internal
package asyncassertion
import ( import (
"errors" "errors"
@ -9,7 +7,6 @@ import (
"runtime" "runtime"
"time" "time"
"github.com/onsi/gomega/internal/oraclematcher"
"github.com/onsi/gomega/types" "github.com/onsi/gomega/types"
) )
@ -22,38 +19,81 @@ const (
type AsyncAssertion struct { type AsyncAssertion struct {
asyncType AsyncAssertionType asyncType AsyncAssertionType
actualInput interface{}
actualIsFunc bool
actualValue interface{}
actualFunc func() ([]reflect.Value, error)
timeoutInterval time.Duration timeoutInterval time.Duration
pollingInterval time.Duration pollingInterval time.Duration
failWrapper *types.GomegaFailWrapper
offset int offset int
g *Gomega
} }
func New(asyncType AsyncAssertionType, actualInput interface{}, failWrapper *types.GomegaFailWrapper, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion { func NewAsyncAssertion(asyncType AsyncAssertionType, actualInput interface{}, g *Gomega, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion {
actualType := reflect.TypeOf(actualInput) out := &AsyncAssertion{
if actualType.Kind() == reflect.Func {
if actualType.NumIn() != 0 {
panic("Expected a function with no arguments and zero or more return values.")
}
}
return &AsyncAssertion{
asyncType: asyncType, asyncType: asyncType,
actualInput: actualInput,
failWrapper: failWrapper,
timeoutInterval: timeoutInterval, timeoutInterval: timeoutInterval,
pollingInterval: pollingInterval, pollingInterval: pollingInterval,
offset: offset, offset: offset,
g: g,
} }
switch actualType := reflect.TypeOf(actualInput); {
case actualType.Kind() != reflect.Func:
out.actualValue = actualInput
case actualType.NumIn() == 0 && actualType.NumOut() > 0:
out.actualIsFunc = true
out.actualFunc = func() ([]reflect.Value, error) {
return reflect.ValueOf(actualInput).Call([]reflect.Value{}), nil
}
case actualType.NumIn() == 1 && actualType.In(0).Implements(reflect.TypeOf((*types.Gomega)(nil)).Elem()):
out.actualIsFunc = true
out.actualFunc = func() (values []reflect.Value, err error) {
var assertionFailure error
assertionCapturingGomega := NewGomega(g.DurationBundle).ConfigureWithFailHandler(func(message string, callerSkip ...int) {
skip := 0
if len(callerSkip) > 0 {
skip = callerSkip[0]
}
_, file, line, _ := runtime.Caller(skip + 1)
assertionFailure = fmt.Errorf("Assertion in callback at %s:%d failed:\n%s", file, line, message)
panic("stop execution")
})
defer func() {
if actualType.NumOut() == 0 {
if assertionFailure == nil {
values = []reflect.Value{reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())}
} else {
values = []reflect.Value{reflect.ValueOf(assertionFailure)}
}
} else {
err = assertionFailure
}
if e := recover(); e != nil && assertionFailure == nil {
panic(e)
}
}()
values = reflect.ValueOf(actualInput).Call([]reflect.Value{reflect.ValueOf(assertionCapturingGomega)})
return
}
default:
msg := fmt.Sprintf("The function passed to Gomega's async assertions should either take no arguments and return values, or take a single Gomega interface that it can use to make assertions within the body of the function. When taking a Gomega interface the function can optionally return values or return nothing. The function you passed takes %d arguments and returns %d values.", actualType.NumIn(), actualType.NumOut())
g.Fail(msg, offset+4)
}
return out
} }
func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.failWrapper.TWithHelper.Helper() assertion.g.THelper()
return assertion.match(matcher, true, optionalDescription...) return assertion.match(matcher, true, optionalDescription...)
} }
func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.failWrapper.TWithHelper.Helper() assertion.g.THelper()
return assertion.match(matcher, false, optionalDescription...) return assertion.match(matcher, false, optionalDescription...)
} }
@ -69,58 +109,20 @@ func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interfa
return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
} }
func (assertion *AsyncAssertion) actualInputIsAFunction() bool {
actualType := reflect.TypeOf(assertion.actualInput)
return actualType.Kind() == reflect.Func && actualType.NumIn() == 0
}
func (assertion *AsyncAssertion) pollActual() (interface{}, error) { func (assertion *AsyncAssertion) pollActual() (interface{}, error) {
if !assertion.actualInputIsAFunction() { if !assertion.actualIsFunc {
return assertion.actualInput, nil return assertion.actualValue, nil
}
var capturedAssertionFailure string
var values []reflect.Value
numOut := reflect.TypeOf(assertion.actualInput).NumOut()
func() {
originalHandler := assertion.failWrapper.Fail
assertion.failWrapper.Fail = func(message string, callerSkip ...int) {
skip := 0
if len(callerSkip) > 0 {
skip = callerSkip[0]
}
_, file, line, _ := runtime.Caller(skip + 1)
capturedAssertionFailure = fmt.Sprintf("Assertion in callback at %s:%d failed:\n%s", file, line, message)
panic("stop execution")
} }
defer func() { values, err := assertion.actualFunc()
assertion.failWrapper.Fail = originalHandler if err != nil {
if e := recover(); e != nil && capturedAssertionFailure == "" { return nil, err
panic(e)
} }
}()
values = reflect.ValueOf(assertion.actualInput).Call([]reflect.Value{})
}()
if capturedAssertionFailure != "" {
if numOut == 0 {
return errors.New(capturedAssertionFailure), nil
} else {
return nil, errors.New(capturedAssertionFailure)
}
}
if numOut > 0 {
extras := []interface{}{} extras := []interface{}{}
for _, value := range values[1:] { for _, value := range values[1:] {
extras = append(extras, value.Interface()) extras = append(extras, value.Interface())
} }
success, message := vetExtras(extras) success, message := vetExtras(extras)
if !success { if !success {
return nil, errors.New(message) return nil, errors.New(message)
} }
@ -128,15 +130,11 @@ func (assertion *AsyncAssertion) pollActual() (interface{}, error) {
return values[0].Interface(), nil return values[0].Interface(), nil
} }
return nil, nil
}
func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool { func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool {
if assertion.actualInputIsAFunction() { if assertion.actualIsFunc {
return true return true
} }
return types.MatchMayChangeInTheFuture(matcher, value)
return oraclematcher.MatchMayChangeInTheFuture(matcher, value)
} }
func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
@ -152,7 +150,7 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch
matches, err = matcher.Match(value) matches, err = matcher.Match(value)
} }
assertion.failWrapper.TWithHelper.Helper() assertion.g.THelper()
fail := func(preamble string) { fail := func(preamble string) {
errMsg := "" errMsg := ""
@ -166,9 +164,9 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch
message = matcher.NegatedFailureMessage(value) message = matcher.NegatedFailureMessage(value)
} }
} }
assertion.failWrapper.TWithHelper.Helper() assertion.g.THelper()
description := assertion.buildDescription(optionalDescription...) description := assertion.buildDescription(optionalDescription...)
assertion.failWrapper.Fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset) assertion.g.Fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset)
} }
if assertion.asyncType == AsyncAssertionTypeEventually { if assertion.asyncType == AsyncAssertionTypeEventually {
@ -220,16 +218,3 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch
return false return false
} }
func vetExtras(extras []interface{}) (bool, string) {
for i, extra := range extras {
if extra != nil {
zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface()
if !reflect.DeepEqual(zeroValue, extra) {
message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra)
return false, message
}
}
}
return true, ""
}

View File

@ -1,22 +0,0 @@
package defaults
import (
"fmt"
"time"
)
func SetDurationFromEnv(getDurationFromEnv func(string) string, varSetter func(time.Duration), name string) {
durationFromEnv := getDurationFromEnv(name)
if len(durationFromEnv) == 0 {
return
}
duration, err := time.ParseDuration(durationFromEnv)
if err != nil {
panic(fmt.Sprintf("Expected a duration when using %s! Parse error %v", name, err))
}
varSetter(duration)
}

View File

@ -0,0 +1,71 @@
package internal
import (
"fmt"
"os"
"reflect"
"time"
)
type DurationBundle struct {
EventuallyTimeout time.Duration
EventuallyPollingInterval time.Duration
ConsistentlyDuration time.Duration
ConsistentlyPollingInterval time.Duration
}
const (
EventuallyTimeoutEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_TIMEOUT"
EventuallyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_POLLING_INTERVAL"
ConsistentlyDurationEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_DURATION"
ConsistentlyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_POLLING_INTERVAL"
)
func FetchDefaultDurationBundle() DurationBundle {
return DurationBundle{
EventuallyTimeout: durationFromEnv(EventuallyTimeoutEnvVarName, time.Second),
EventuallyPollingInterval: durationFromEnv(EventuallyPollingIntervalEnvVarName, 10*time.Millisecond),
ConsistentlyDuration: durationFromEnv(ConsistentlyDurationEnvVarName, 100*time.Millisecond),
ConsistentlyPollingInterval: durationFromEnv(ConsistentlyPollingIntervalEnvVarName, 10*time.Millisecond),
}
}
func durationFromEnv(key string, defaultDuration time.Duration) time.Duration {
value := os.Getenv(key)
if value == "" {
return defaultDuration
}
duration, err := time.ParseDuration(value)
if err != nil {
panic(fmt.Sprintf("Expected a duration when using %s! Parse error %v", key, err))
}
return duration
}
func toDuration(input interface{}) time.Duration {
duration, ok := input.(time.Duration)
if ok {
return duration
}
value := reflect.ValueOf(input)
kind := reflect.TypeOf(input).Kind()
if reflect.Int <= kind && kind <= reflect.Int64 {
return time.Duration(value.Int()) * time.Second
} else if reflect.Uint <= kind && kind <= reflect.Uint64 {
return time.Duration(value.Uint()) * time.Second
} else if reflect.Float32 <= kind && kind <= reflect.Float64 {
return time.Duration(value.Float() * float64(time.Second))
} else if reflect.String == kind {
duration, err := time.ParseDuration(value.String())
if err != nil {
panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input))
}
return duration
}
panic(fmt.Sprintf("%v is not a valid interval. Must be time.Duration, parsable duration string or a number.", input))
}

102
vendor/github.com/onsi/gomega/internal/gomega.go generated vendored Normal file
View File

@ -0,0 +1,102 @@
package internal
import (
"time"
"github.com/onsi/gomega/types"
)
type Gomega struct {
Fail types.GomegaFailHandler
THelper func()
DurationBundle DurationBundle
}
func NewGomega(bundle DurationBundle) *Gomega {
return &Gomega{
Fail: nil,
THelper: nil,
DurationBundle: bundle,
}
}
func (g *Gomega) IsConfigured() bool {
return g.Fail != nil && g.THelper != nil
}
func (g *Gomega) ConfigureWithFailHandler(fail types.GomegaFailHandler) *Gomega {
g.Fail = fail
g.THelper = func() {}
return g
}
func (g *Gomega) ConfigureWithT(t types.GomegaTestingT) *Gomega {
g.Fail = func(message string, _ ...int) {
t.Helper()
t.Fatalf("\n%s", message)
}
g.THelper = t.Helper
return g
}
func (g *Gomega) Ω(atual interface{}, extra ...interface{}) types.Assertion {
return g.ExpectWithOffset(0, atual, extra...)
}
func (g *Gomega) Expect(atual interface{}, extra ...interface{}) types.Assertion {
return g.ExpectWithOffset(0, atual, extra...)
}
func (g *Gomega) ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) types.Assertion {
return NewAssertion(actual, g, offset, extra...)
}
func (g *Gomega) Eventually(actual interface{}, intervals ...interface{}) types.AsyncAssertion {
return g.EventuallyWithOffset(0, actual, intervals...)
}
func (g *Gomega) EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) types.AsyncAssertion {
timeoutInterval := g.DurationBundle.EventuallyTimeout
pollingInterval := g.DurationBundle.EventuallyPollingInterval
if len(intervals) > 0 {
timeoutInterval = toDuration(intervals[0])
}
if len(intervals) > 1 {
pollingInterval = toDuration(intervals[1])
}
return NewAsyncAssertion(AsyncAssertionTypeEventually, actual, g, timeoutInterval, pollingInterval, offset)
}
func (g *Gomega) Consistently(actual interface{}, intervals ...interface{}) types.AsyncAssertion {
return g.ConsistentlyWithOffset(0, actual, intervals...)
}
func (g *Gomega) ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) types.AsyncAssertion {
timeoutInterval := g.DurationBundle.ConsistentlyDuration
pollingInterval := g.DurationBundle.ConsistentlyPollingInterval
if len(intervals) > 0 {
timeoutInterval = toDuration(intervals[0])
}
if len(intervals) > 1 {
pollingInterval = toDuration(intervals[1])
}
return NewAsyncAssertion(AsyncAssertionTypeConsistently, actual, g, timeoutInterval, pollingInterval, offset)
}
func (g *Gomega) SetDefaultEventuallyTimeout(t time.Duration) {
g.DurationBundle.EventuallyTimeout = t
}
func (g *Gomega) SetDefaultEventuallyPollingInterval(t time.Duration) {
g.DurationBundle.EventuallyPollingInterval = t
}
func (g *Gomega) SetDefaultConsistentlyDuration(t time.Duration) {
g.DurationBundle.ConsistentlyDuration = t
}
func (g *Gomega) SetDefaultConsistentlyPollingInterval(t time.Duration) {
g.DurationBundle.ConsistentlyPollingInterval = t
}

View File

@ -1,25 +0,0 @@
package oraclematcher
import "github.com/onsi/gomega/types"
/*
GomegaMatchers that also match the OracleMatcher interface can convey information about
whether or not their result will change upon future attempts.
This allows `Eventually` and `Consistently` to short circuit if success becomes impossible.
For example, a process' exit code can never change. So, gexec's Exit matcher returns `true`
for `MatchMayChangeInTheFuture` until the process exits, at which point it returns `false` forevermore.
*/
type OracleMatcher interface {
MatchMayChangeInTheFuture(actual interface{}) bool
}
func MatchMayChangeInTheFuture(matcher types.GomegaMatcher, value interface{}) bool {
oracleMatcher, ok := matcher.(OracleMatcher)
if !ok {
return true
}
return oracleMatcher.MatchMayChangeInTheFuture(value)
}

View File

@ -1,60 +0,0 @@
package testingtsupport
import (
"regexp"
"runtime/debug"
"strings"
"github.com/onsi/gomega/types"
)
var StackTracePruneRE = regexp.MustCompile(`\/gomega\/|\/ginkgo\/|\/pkg\/testing\/|\/pkg\/runtime\/`)
type EmptyTWithHelper struct{}
func (e EmptyTWithHelper) Helper() {}
type gomegaTestingT interface {
Fatalf(format string, args ...interface{})
}
func BuildTestingTGomegaFailWrapper(t gomegaTestingT) *types.GomegaFailWrapper {
tWithHelper, hasHelper := t.(types.TWithHelper)
if !hasHelper {
tWithHelper = EmptyTWithHelper{}
}
fail := func(message string, callerSkip ...int) {
if hasHelper {
tWithHelper.Helper()
t.Fatalf("\n%s", message)
} else {
skip := 2
if len(callerSkip) > 0 {
skip += callerSkip[0]
}
stackTrace := pruneStack(string(debug.Stack()), skip)
t.Fatalf("\n%s\n%s\n", stackTrace, message)
}
}
return &types.GomegaFailWrapper{
Fail: fail,
TWithHelper: tWithHelper,
}
}
func pruneStack(fullStackTrace string, skip int) string {
stack := strings.Split(fullStackTrace, "\n")[1:]
if len(stack) > 2*skip {
stack = stack[2*skip:]
}
prunedStack := []string{}
for i := 0; i < len(stack)/2; i++ {
if !StackTracePruneRE.Match([]byte(stack[i*2])) {
prunedStack = append(prunedStack, stack[i*2])
prunedStack = append(prunedStack, stack[i*2+1])
}
}
return strings.Join(prunedStack, "\n")
}

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"github.com/onsi/gomega/format" "github.com/onsi/gomega/format"
"github.com/onsi/gomega/internal/oraclematcher"
"github.com/onsi/gomega/types" "github.com/onsi/gomega/types"
) )
@ -52,12 +51,12 @@ func (m *AndMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
if m.firstFailedMatcher == nil { if m.firstFailedMatcher == nil {
// so all matchers succeeded.. Any one of them changing would change the result. // so all matchers succeeded.. Any one of them changing would change the result.
for _, matcher := range m.Matchers { for _, matcher := range m.Matchers {
if oraclematcher.MatchMayChangeInTheFuture(matcher, actual) { if types.MatchMayChangeInTheFuture(matcher, actual) {
return true return true
} }
} }
return false // none of were going to change return false // none of were going to change
} }
// one of the matchers failed.. it must be able to change in order to affect the result // one of the matchers failed.. it must be able to change in order to affect the result
return oraclematcher.MatchMayChangeInTheFuture(m.firstFailedMatcher, actual) return types.MatchMayChangeInTheFuture(m.firstFailedMatcher, actual)
} }

View File

@ -1,7 +1,6 @@
package matchers package matchers
import ( import (
"github.com/onsi/gomega/internal/oraclematcher"
"github.com/onsi/gomega/types" "github.com/onsi/gomega/types"
) )
@ -26,5 +25,5 @@ func (m *NotMatcher) NegatedFailureMessage(actual interface{}) (message string)
} }
func (m *NotMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { func (m *NotMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
return oraclematcher.MatchMayChangeInTheFuture(m.Matcher, actual) // just return m.Matcher's value return types.MatchMayChangeInTheFuture(m.Matcher, actual) // just return m.Matcher's value
} }

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"github.com/onsi/gomega/format" "github.com/onsi/gomega/format"
"github.com/onsi/gomega/internal/oraclematcher"
"github.com/onsi/gomega/types" "github.com/onsi/gomega/types"
) )
@ -54,11 +53,11 @@ func (m *OrMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
if m.firstSuccessfulMatcher != nil { if m.firstSuccessfulMatcher != nil {
// one of the matchers succeeded.. it must be able to change in order to affect the result // one of the matchers succeeded.. it must be able to change in order to affect the result
return oraclematcher.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual) return types.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual)
} else { } else {
// so all matchers failed.. Any one of them changing would change the result. // so all matchers failed.. Any one of them changing would change the result.
for _, matcher := range m.Matchers { for _, matcher := range m.Matchers {
if oraclematcher.MatchMayChangeInTheFuture(matcher, actual) { if types.MatchMayChangeInTheFuture(matcher, actual) {
return true return true
} }
} }

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"github.com/onsi/gomega/internal/oraclematcher"
"github.com/onsi/gomega/types" "github.com/onsi/gomega/types"
) )
@ -77,5 +76,5 @@ func (m *WithTransformMatcher) MatchMayChangeInTheFuture(_ interface{}) bool {
// Querying the next matcher is fine if the transformer always will return the same value. // Querying the next matcher is fine if the transformer always will return the same value.
// But if the transformer is non-deterministic and returns a different value each time, then there // But if the transformer is non-deterministic and returns a different value each time, then there
// is no point in querying the next matcher, since it can only comment on the last transformed value. // is no point in querying the next matcher, since it can only comment on the last transformed value.
return oraclematcher.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue) return types.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue)
} }

View File

@ -1,21 +1,35 @@
package types package types
type TWithHelper interface { import (
Helper() "time"
} )
type GomegaFailHandler func(message string, callerSkip ...int) type GomegaFailHandler func(message string, callerSkip ...int)
type GomegaFailWrapper struct {
Fail GomegaFailHandler
TWithHelper TWithHelper
}
//A simple *testing.T interface wrapper //A simple *testing.T interface wrapper
type GomegaTestingT interface { type GomegaTestingT interface {
Helper()
Fatalf(format string, args ...interface{}) Fatalf(format string, args ...interface{})
} }
// Gomega represents an object that can perform synchronous and assynchronous assertions with Gomega matchers
type Gomega interface {
Ω(actual interface{}, extra ...interface{}) Assertion
Expect(actual interface{}, extra ...interface{}) Assertion
ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion
Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion
EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion
Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion
ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion
SetDefaultEventuallyTimeout(time.Duration)
SetDefaultEventuallyPollingInterval(time.Duration)
SetDefaultConsistentlyDuration(time.Duration)
SetDefaultConsistentlyPollingInterval(time.Duration)
}
//All Gomega matchers must implement the GomegaMatcher interface //All Gomega matchers must implement the GomegaMatcher interface
// //
//For details on writing custom matchers, check out: http://onsi.github.io/gomega/#adding-your-own-matchers //For details on writing custom matchers, check out: http://onsi.github.io/gomega/#adding-your-own-matchers
@ -24,3 +38,42 @@ type GomegaMatcher interface {
FailureMessage(actual interface{}) (message string) FailureMessage(actual interface{}) (message string)
NegatedFailureMessage(actual interface{}) (message string) NegatedFailureMessage(actual interface{}) (message string)
} }
/*
GomegaMatchers that also match the OracleMatcher interface can convey information about
whether or not their result will change upon future attempts.
This allows `Eventually` and `Consistently` to short circuit if success becomes impossible.
For example, a process' exit code can never change. So, gexec's Exit matcher returns `true`
for `MatchMayChangeInTheFuture` until the process exits, at which point it returns `false` forevermore.
*/
type OracleMatcher interface {
MatchMayChangeInTheFuture(actual interface{}) bool
}
func MatchMayChangeInTheFuture(matcher GomegaMatcher, value interface{}) bool {
oracleMatcher, ok := matcher.(OracleMatcher)
if !ok {
return true
}
return oracleMatcher.MatchMayChangeInTheFuture(value)
}
// AsyncAssertions are returned by Eventually and Consistently and enable matchers to be polled repeatedly to ensure
// they are eventually satisfied
type AsyncAssertion interface {
Should(matcher GomegaMatcher, optionalDescription ...interface{}) bool
ShouldNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool
}
// Assertions are returned by Ω and Expect and enable assertions against Gomega matchers
type Assertion interface {
Should(matcher GomegaMatcher, optionalDescription ...interface{}) bool
ShouldNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool
To(matcher GomegaMatcher, optionalDescription ...interface{}) bool
ToNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool
NotTo(matcher GomegaMatcher, optionalDescription ...interface{}) bool
}

8
vendor/modules.txt vendored
View File

@ -488,16 +488,12 @@ github.com/onsi/ginkgo/reporters/stenographer
github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable
github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty
github.com/onsi/ginkgo/types github.com/onsi/ginkgo/types
# github.com/onsi/gomega v1.14.0 # github.com/onsi/gomega v1.15.0
github.com/onsi/gomega github.com/onsi/gomega
github.com/onsi/gomega/format github.com/onsi/gomega/format
github.com/onsi/gomega/gbytes github.com/onsi/gomega/gbytes
github.com/onsi/gomega/gexec github.com/onsi/gomega/gexec
github.com/onsi/gomega/internal/assertion github.com/onsi/gomega/internal
github.com/onsi/gomega/internal/asyncassertion
github.com/onsi/gomega/internal/defaults
github.com/onsi/gomega/internal/oraclematcher
github.com/onsi/gomega/internal/testingtsupport
github.com/onsi/gomega/matchers github.com/onsi/gomega/matchers
github.com/onsi/gomega/matchers/support/goraph/bipartitegraph github.com/onsi/gomega/matchers/support/goraph/bipartitegraph
github.com/onsi/gomega/matchers/support/goraph/edge github.com/onsi/gomega/matchers/support/goraph/edge