Codebase list golang-gomega / e60e915
Fix support for assertions in functions passed to Eventually and Consistently (fixes #457) 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. Onsi Fakhouri 2 years ago
32 changed file(s) with 2063 addition(s) and 1745 deletion(s). Raw diff Collapse all Expand all
0 ## 1.15.0
1
2 ### Fixes
3 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.
4
5 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.
6
7 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.
8
09 ## 1.14.0
110
211 ### Features
+0
-40
env.go less more
0 package gomega
1
2 import (
3 "os"
4
5 "github.com/onsi/gomega/internal/defaults"
6 )
7
8 const (
9 ConsistentlyDurationEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_DURATION"
10 ConsistentlyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_POLLING_INTERVAL"
11 EventuallyTimeoutEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_TIMEOUT"
12 EventuallyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_POLLING_INTERVAL"
13 )
14
15 func init() {
16 defaults.SetDurationFromEnv(
17 os.Getenv,
18 SetDefaultConsistentlyDuration,
19 ConsistentlyDurationEnvVarName,
20 )
21
22 defaults.SetDurationFromEnv(
23 os.Getenv,
24 SetDefaultConsistentlyPollingInterval,
25 ConsistentlyPollingIntervalEnvVarName,
26 )
27
28 defaults.SetDurationFromEnv(
29 os.Getenv,
30 SetDefaultEventuallyTimeout,
31 EventuallyTimeoutEnvVarName,
32 )
33
34 defaults.SetDurationFromEnv(
35 os.Getenv,
36 SetDefaultEventuallyPollingInterval,
37 EventuallyPollingIntervalEnvVarName,
38 )
39 }
258258 s.AppendHandlers(func(w http.ResponseWriter, req *http.Request) {
259259 // Expect(true).Should(BeFalse()) <-- would be nice to do it this way, but the test just can't be written this way
260260
261 By("We're cheating a bit here -- we're throwing a GINKGO_PANIC which simulates a failed assertion")
262 panic(GINKGO_PANIC)
261 By("We're cheating a bit here -- we're pretending to throw a Ginkgo panic which simulates a failed assertion")
262 panic("defer GinkgoRecover()")
263263 })
264264 })
265265
1515 import (
1616 "errors"
1717 "fmt"
18 "reflect"
1918 "time"
2019
21 "github.com/onsi/gomega/internal/assertion"
22 "github.com/onsi/gomega/internal/asyncassertion"
23 "github.com/onsi/gomega/internal/testingtsupport"
20 "github.com/onsi/gomega/internal"
2421 "github.com/onsi/gomega/types"
2522 )
2623
27 const GOMEGA_VERSION = "1.14.0"
28
29 const nilFailHandlerPanic = `You are trying to make an assertion, but Gomega's fail handler is nil.
24 const GOMEGA_VERSION = "1.15.0"
25
26 const nilGomegaPanic = `You are trying to make an assertion, but haven't registered Gomega's fail handler.
3027 If you're using Ginkgo then you probably forgot to put your assertion in an It().
3128 Alternatively, you may have forgotten to register a fail handler with RegisterFailHandler() or RegisterTestingT().
3229 Depending on your vendoring solution you may be inadvertently importing gomega and subpackages (e.g. ghhtp, gexec,...) from different locations.
3330 `
3431
35 var globalFailWrapper *types.GomegaFailWrapper
36
37 var defaultEventuallyTimeout = time.Second
38 var defaultEventuallyPollingInterval = 10 * time.Millisecond
39 var defaultConsistentlyDuration = 100 * time.Millisecond
40 var defaultConsistentlyPollingInterval = 10 * time.Millisecond
32 // Gomega describes the essential Gomega DSL. This interface allows libraries
33 // to abstract between the standard package-level function implementations
34 // and alternatives like *WithT.
35 //
36 // The types in the top-level DSL have gotten a bit messy due to earlier depracations that avoid stuttering
37 // and due to an accidental use of a concrete type (*WithT) in an earlier release.
38 //
39 // As of 1.15 both the WithT and Ginkgo variants of Gomega are implemented by the same underlying object
40 // however one (the Ginkgo variant) is exported as an interface (types.Gomega) whereas the other (the withT variant)
41 // is shared as a concrete type (*WithT, which is aliased to *internal.Gomega). 1.15 did not clean this mess up to ensure
42 // that declarations of *WithT in existing code are not broken by the upgrade to 1.15.
43 type Gomega = types.Gomega
44
45 // DefaultGomega supplies the standard package-level implementation
46 var Default = Gomega(internal.NewGomega(internal.FetchDefaultDurationBundle()))
47
48 // NewGomega returns an instance of Gomega wired into the passed-in fail handler.
49 // You generally don't need to use this when using Ginkgo - RegisterFailHandler will wire up the global gomega
50 // However creating a NewGomega with a custom fail handler can be useful in contexts where you want to use Gomega's
51 // rich ecosystem of matchers without causing a test to fail. For example, to aggregate a series of potential failures
52 // or for use in a non-test setting.
53 func NewGomega(fail types.GomegaFailHandler) Gomega {
54 return internal.NewGomega(Default.(*internal.Gomega).DurationBundle).ConfigureWithFailHandler(fail)
55 }
56
57 // WithT wraps a *testing.T and provides `Expect`, `Eventually`, and `Consistently` methods. This allows you to leverage
58 // Gomega's rich ecosystem of matchers in standard `testing` test suites.
59 //
60 // Use `NewWithT` to instantiate a `WithT`
61 //
62 // As of 1.15 both the WithT and Ginkgo variants of Gomega are implemented by the same underlying object
63 // however one (the Ginkgo variant) is exported as an interface (types.Gomega) whereas the other (the withT variant)
64 // is shared as a concrete type (*WithT, which is aliased to *internal.Gomega). 1.15 did not clean this mess up to ensure
65 // that declarations of *WithT in existing code are not broken by the upgrade to 1.15.
66 type WithT = internal.Gomega
67
68 // GomegaWithT is deprecated in favor of gomega.WithT, which does not stutter.
69 type GomegaWithT = WithT
70
71 // NewWithT takes a *testing.T and returngs a `gomega.WithT` allowing you to use `Expect`, `Eventually`, and `Consistently` along with
72 // Gomega's rich ecosystem of matchers in standard `testing` test suits.
73 //
74 // func TestFarmHasCow(t *testing.T) {
75 // g := gomega.NewWithT(t)
76 //
77 // f := farm.New([]string{"Cow", "Horse"})
78 // g.Expect(f.HasCow()).To(BeTrue(), "Farm should have cow")
79 // }
80 func NewWithT(t types.GomegaTestingT) *WithT {
81 return internal.NewGomega(Default.(*internal.Gomega).DurationBundle).ConfigureWithT(t)
82 }
83
84 // NewGomegaWithT is deprecated in favor of gomega.NewWithT, which does not stutter.
85 var NewGomegaWithT = NewWithT
4186
4287 // RegisterFailHandler connects Ginkgo to Gomega. When a matcher fails
4388 // the fail handler passed into RegisterFailHandler is called.
44 func RegisterFailHandler(handler types.GomegaFailHandler) {
45 RegisterFailHandlerWithT(testingtsupport.EmptyTWithHelper{}, handler)
46 }
47
48 // RegisterFailHandlerWithT ensures that the given types.TWithHelper and fail handler
49 // are used globally.
50 func RegisterFailHandlerWithT(t types.TWithHelper, handler types.GomegaFailHandler) {
51 if handler == nil {
52 globalFailWrapper = nil
53 return
54 }
55
56 globalFailWrapper = &types.GomegaFailWrapper{
57 Fail: handler,
58 TWithHelper: t,
59 }
89 func RegisterFailHandler(fail types.GomegaFailHandler) {
90 Default.(*internal.Gomega).ConfigureWithFailHandler(fail)
91 }
92
93 // RegisterFailHandlerWithT is deprecated and will be removed in a future release.
94 // users should use RegisterFailHandler, or RegisterTestingT
95 func RegisterFailHandlerWithT(_ types.GomegaTestingT, fail types.GomegaFailHandler) {
96 fmt.Println("RegisterFailHandlerWithT is deprecated. Please use RegisterFailHandler or RegisterTestingT instead.")
97 Default.(*internal.Gomega).ConfigureWithFailHandler(fail)
6098 }
6199
62100 // RegisterTestingT connects Gomega to Golang's XUnit style
63 // Testing.T tests. It is now deprecated and you should use NewWithT() instead.
64 //
65 // Legacy Documentation:
66 //
67 // You'll need to call this at the top of each XUnit style test:
68 //
69 // func TestFarmHasCow(t *testing.T) {
70 // RegisterTestingT(t)
71 //
72 // f := farm.New([]string{"Cow", "Horse"})
73 // Expect(f.HasCow()).To(BeTrue(), "Farm should have cow")
74 // }
75 //
76 // Note that this *testing.T is registered *globally* by Gomega (this is why you don't have to
77 // pass `t` down to the matcher itself). This means that you cannot run the XUnit style tests
78 // in parallel as the global fail handler cannot point to more than one testing.T at a time.
79 //
80 // NewWithT() does not have this limitation
81 //
82 // (As an aside: Ginkgo gets around this limitation by running parallel tests in different *processes*).
101 // Testing.T tests. It is now deprecated and you should use NewWithT() instead to get a fresh instance of Gomega for each test.
83102 func RegisterTestingT(t types.GomegaTestingT) {
84 tWithHelper, hasHelper := t.(types.TWithHelper)
85 if !hasHelper {
86 RegisterFailHandler(testingtsupport.BuildTestingTGomegaFailWrapper(t).Fail)
87 return
88 }
89 RegisterFailHandlerWithT(tWithHelper, testingtsupport.BuildTestingTGomegaFailWrapper(t).Fail)
103 Default.(*internal.Gomega).ConfigureWithT(t)
90104 }
91105
92106 // InterceptGomegaFailures runs a given callback and returns an array of
97111 // This is most useful when testing custom matchers, but can also be used to check
98112 // on a value using a Gomega assertion without causing a test failure.
99113 func InterceptGomegaFailures(f func()) []string {
100 originalHandler := globalFailWrapper.Fail
114 originalHandler := Default.(*internal.Gomega).Fail
101115 failures := []string{}
102 RegisterFailHandler(func(message string, callerSkip ...int) {
116 Default.(*internal.Gomega).Fail = func(message string, callerSkip ...int) {
103117 failures = append(failures, message)
104 })
118 }
105119 defer func() {
106 RegisterFailHandler(originalHandler)
120 Default.(*internal.Gomega).Fail = originalHandler
107121 }()
108122 f()
109123 return failures
116130 // does not register a failure with the FailHandler registered via RegisterFailHandler - it is up
117131 // to the user to decide what to do with the returned error
118132 func InterceptGomegaFailure(f func()) (err error) {
119 originalHandler := globalFailWrapper.Fail
120 RegisterFailHandler(func(message string, callerSkip ...int) {
133 originalHandler := Default.(*internal.Gomega).Fail
134 Default.(*internal.Gomega).Fail = func(message string, callerSkip ...int) {
121135 err = errors.New(message)
122136 panic("stop execution")
123 })
137 }
124138
125139 defer func() {
126 RegisterFailHandler(originalHandler)
140 Default.(*internal.Gomega).Fail = originalHandler
127141 if e := recover(); e != nil {
128142 if err == nil {
129143 panic(e)
135149 return err
136150 }
137151
152 func ensureDefaultGomegaIsConfigured() {
153 if !Default.(*internal.Gomega).IsConfigured() {
154 panic(nilGomegaPanic)
155 }
156 }
157
138158 // Ω wraps an actual value allowing assertions to be made on it:
139159 // Ω("foo").Should(Equal("foo"))
140160 //
153173 //
154174 // Ω and Expect are identical
155175 func Ω(actual interface{}, extra ...interface{}) Assertion {
156 return ExpectWithOffset(0, actual, extra...)
176 ensureDefaultGomegaIsConfigured()
177 return Default.Ω(actual, extra...)
157178 }
158179
159180 // Expect wraps an actual value allowing assertions to be made on it:
174195 //
175196 // Expect and Ω are identical
176197 func Expect(actual interface{}, extra ...interface{}) Assertion {
177 return ExpectWithOffset(0, actual, extra...)
198 ensureDefaultGomegaIsConfigured()
199 return Default.Expect(actual, extra...)
178200 }
179201
180202 // ExpectWithOffset wraps an actual value allowing assertions to be made on it:
187209 // error message to refer to the calling line in the test (as opposed to the line in the helper function)
188210 // set the first argument of `ExpectWithOffset` appropriately.
189211 func ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion {
190 if globalFailWrapper == nil {
191 panic(nilFailHandlerPanic)
192 }
193 return assertion.New(actual, globalFailWrapper, offset, extra...)
212 ensureDefaultGomegaIsConfigured()
213 return Default.ExpectWithOffset(offset, actual, extra...)
194214 }
195215
196216 // Eventually wraps an actual value allowing assertions to be made on it.
258278 //
259279 // Eventually's default timeout is 1 second, and its default polling interval is 10ms
260280 func Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion {
261 return EventuallyWithOffset(0, actual, intervals...)
281 ensureDefaultGomegaIsConfigured()
282 return Default.Eventually(actual, intervals...)
262283 }
263284
264285 // EventuallyWithOffset operates like Eventually but takes an additional
265286 // initial argument to indicate an offset in the call stack. This is useful when building helper
266287 // functions that contain matchers. To learn more, read about `ExpectWithOffset`.
267288 func EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
268 if globalFailWrapper == nil {
269 panic(nilFailHandlerPanic)
270 }
271 timeoutInterval := defaultEventuallyTimeout
272 pollingInterval := defaultEventuallyPollingInterval
273 if len(intervals) > 0 {
274 timeoutInterval = toDuration(intervals[0])
275 }
276 if len(intervals) > 1 {
277 pollingInterval = toDuration(intervals[1])
278 }
279 return asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, actual, globalFailWrapper, timeoutInterval, pollingInterval, offset)
289 ensureDefaultGomegaIsConfigured()
290 return Default.EventuallyWithOffset(offset, actual, intervals...)
280291 }
281292
282293 // Consistently wraps an actual value allowing assertions to be made on it.
308319 //
309320 // Consistently's default duration is 100ms, and its default polling interval is 10ms
310321 func Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion {
311 return ConsistentlyWithOffset(0, actual, intervals...)
322 ensureDefaultGomegaIsConfigured()
323 return Default.Consistently(actual, intervals...)
312324 }
313325
314326 // ConsistentlyWithOffset operates like Consistently but takes an additional
315327 // initial argument to indicate an offset in the call stack. This is useful when building helper
316328 // functions that contain matchers. To learn more, read about `ExpectWithOffset`.
317329 func ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
318 if globalFailWrapper == nil {
319 panic(nilFailHandlerPanic)
320 }
321 timeoutInterval := defaultConsistentlyDuration
322 pollingInterval := defaultConsistentlyPollingInterval
323 if len(intervals) > 0 {
324 timeoutInterval = toDuration(intervals[0])
325 }
326 if len(intervals) > 1 {
327 pollingInterval = toDuration(intervals[1])
328 }
329 return asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, actual, globalFailWrapper, timeoutInterval, pollingInterval, offset)
330 ensureDefaultGomegaIsConfigured()
331 return Default.ConsistentlyWithOffset(offset, actual, intervals...)
330332 }
331333
332334 // SetDefaultEventuallyTimeout sets the default timeout duration for Eventually. Eventually will repeatedly poll your condition until it succeeds, or until this timeout elapses.
333335 func SetDefaultEventuallyTimeout(t time.Duration) {
334 defaultEventuallyTimeout = t
336 Default.SetDefaultEventuallyTimeout(t)
335337 }
336338
337339 // SetDefaultEventuallyPollingInterval sets the default polling interval for Eventually.
338340 func SetDefaultEventuallyPollingInterval(t time.Duration) {
339 defaultEventuallyPollingInterval = t
341 Default.SetDefaultEventuallyPollingInterval(t)
340342 }
341343
342344 // SetDefaultConsistentlyDuration sets the default duration for Consistently. Consistently will verify that your condition is satisfied for this long.
343345 func SetDefaultConsistentlyDuration(t time.Duration) {
344 defaultConsistentlyDuration = t
346 Default.SetDefaultConsistentlyDuration(t)
345347 }
346348
347349 // SetDefaultConsistentlyPollingInterval sets the default polling interval for Consistently.
348350 func SetDefaultConsistentlyPollingInterval(t time.Duration) {
349 defaultConsistentlyPollingInterval = t
351 Default.SetDefaultConsistentlyPollingInterval(t)
350352 }
351353
352354 // AsyncAssertion is returned by Eventually and Consistently and polls the actual value passed into Eventually against
364366 //
365367 // Eventually(myChannel).Should(Receive(), "Something should have come down the pipe.")
366368 // Consistently(myChannel).ShouldNot(Receive(), func() string { return "Nothing should have come down the pipe." })
367 type AsyncAssertion interface {
368 Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
369 ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
370 }
369 type AsyncAssertion = types.AsyncAssertion
371370
372371 // GomegaAsyncAssertion is deprecated in favor of AsyncAssertion, which does not stutter.
373 type GomegaAsyncAssertion = AsyncAssertion
372 type GomegaAsyncAssertion = types.AsyncAssertion
374373
375374 // Assertion is returned by Ω and Expect and compares the actual value to the matcher
376375 // passed to the Should/ShouldNot and To/ToNot/NotTo methods.
389388 // Example:
390389 //
391390 // Ω(farm.HasCow()).Should(BeTrue(), "Farm %v should have a cow", farm)
392 type Assertion interface {
393 Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
394 ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
395
396 To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
397 ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
398 NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
399 }
391 type Assertion = types.Assertion
400392
401393 // GomegaAssertion is deprecated in favor of Assertion, which does not stutter.
402 type GomegaAssertion = Assertion
394 type GomegaAssertion = types.Assertion
403395
404396 // OmegaMatcher is deprecated in favor of the better-named and better-organized types.GomegaMatcher but sticks around to support existing code that uses it
405 type OmegaMatcher types.GomegaMatcher
406
407 // WithT wraps a *testing.T and provides `Expect`, `Eventually`, and `Consistently` methods. This allows you to leverage
408 // Gomega's rich ecosystem of matchers in standard `testing` test suites.
409 //
410 // Use `NewWithT` to instantiate a `WithT`
411 type WithT struct {
412 failWrapper *types.GomegaFailWrapper
413 }
414
415 // GomegaWithT is deprecated in favor of gomega.WithT, which does not stutter.
416 type GomegaWithT = WithT
417
418 // NewWithT takes a *testing.T and returngs a `gomega.WithT` allowing you to use `Expect`, `Eventually`, and `Consistently` along with
419 // Gomega's rich ecosystem of matchers in standard `testing` test suits.
420 //
421 // func TestFarmHasCow(t *testing.T) {
422 // g := gomega.NewWithT(t)
423 //
424 // f := farm.New([]string{"Cow", "Horse"})
425 // g.Expect(f.HasCow()).To(BeTrue(), "Farm should have cow")
426 // }
427 func NewWithT(t types.GomegaTestingT) *WithT {
428 return &WithT{
429 failWrapper: testingtsupport.BuildTestingTGomegaFailWrapper(t),
430 }
431 }
432
433 // NewGomegaWithT is deprecated in favor of gomega.NewWithT, which does not stutter.
434 func NewGomegaWithT(t types.GomegaTestingT) *GomegaWithT {
435 return NewWithT(t)
436 }
437
438 // ExpectWithOffset is used to make assertions. See documentation for ExpectWithOffset.
439 func (g *WithT) ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion {
440 return assertion.New(actual, g.failWrapper, offset, extra...)
441 }
442
443 // EventuallyWithOffset is used to make asynchronous assertions. See documentation for EventuallyWithOffset.
444 func (g *WithT) EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
445 timeoutInterval := defaultEventuallyTimeout
446 pollingInterval := defaultEventuallyPollingInterval
447 if len(intervals) > 0 {
448 timeoutInterval = toDuration(intervals[0])
449 }
450 if len(intervals) > 1 {
451 pollingInterval = toDuration(intervals[1])
452 }
453 return asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, actual, g.failWrapper, timeoutInterval, pollingInterval, offset)
454 }
455
456 // ConsistentlyWithOffset is used to make asynchronous assertions. See documentation for ConsistentlyWithOffset.
457 func (g *WithT) ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
458 timeoutInterval := defaultConsistentlyDuration
459 pollingInterval := defaultConsistentlyPollingInterval
460 if len(intervals) > 0 {
461 timeoutInterval = toDuration(intervals[0])
462 }
463 if len(intervals) > 1 {
464 pollingInterval = toDuration(intervals[1])
465 }
466 return asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, actual, g.failWrapper, timeoutInterval, pollingInterval, offset)
467 }
468
469 // Expect is used to make assertions. See documentation for Expect.
470 func (g *WithT) Expect(actual interface{}, extra ...interface{}) Assertion {
471 return g.ExpectWithOffset(0, actual, extra...)
472 }
473
474 // Eventually is used to make asynchronous assertions. See documentation for Eventually.
475 func (g *WithT) Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion {
476 return g.EventuallyWithOffset(0, actual, intervals...)
477 }
478
479 // Consistently is used to make asynchronous assertions. See documentation for Consistently.
480 func (g *WithT) Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion {
481 return g.ConsistentlyWithOffset(0, actual, intervals...)
482 }
483
484 func toDuration(input interface{}) time.Duration {
485 duration, ok := input.(time.Duration)
486 if ok {
487 return duration
488 }
489
490 value := reflect.ValueOf(input)
491 kind := reflect.TypeOf(input).Kind()
492
493 if reflect.Int <= kind && kind <= reflect.Int64 {
494 return time.Duration(value.Int()) * time.Second
495 } else if reflect.Uint <= kind && kind <= reflect.Uint64 {
496 return time.Duration(value.Uint()) * time.Second
497 } else if reflect.Float32 <= kind && kind <= reflect.Float64 {
498 return time.Duration(value.Float() * float64(time.Second))
499 } else if reflect.String == kind {
500 duration, err := time.ParseDuration(value.String())
501 if err != nil {
502 panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input))
503 }
504 return duration
505 }
506
507 panic(fmt.Sprintf("%v is not a valid interval. Must be time.Duration, parsable duration string or a number.", input))
508 }
509
510 // Gomega describes the essential Gomega DSL. This interface allows libraries
511 // to abstract between the standard package-level function implementations
512 // and alternatives like *WithT.
513 type Gomega interface {
514 Expect(actual interface{}, extra ...interface{}) Assertion
515 Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion
516 Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion
517 }
518
519 type globalFailHandlerGomega struct{}
520
521 // DefaultGomega supplies the standard package-level implementation
522 var Default Gomega = globalFailHandlerGomega{}
523
524 // Expect is used to make assertions. See documentation for Expect.
525 func (globalFailHandlerGomega) Expect(actual interface{}, extra ...interface{}) Assertion {
526 return Expect(actual, extra...)
527 }
528
529 // Eventually is used to make asynchronous assertions. See documentation for Eventually.
530 func (globalFailHandlerGomega) Eventually(actual interface{}, extra ...interface{}) AsyncAssertion {
531 return Eventually(actual, extra...)
532 }
533
534 // Consistently is used to make asynchronous assertions. See documentation for Consistently.
535 func (globalFailHandlerGomega) Consistently(actual interface{}, extra ...interface{}) AsyncAssertion {
536 return Consistently(actual, extra...)
537 }
397 type OmegaMatcher = types.GomegaMatcher
+0
-109
internal/assertion/assertion.go less more
0 package assertion
1
2 import (
3 "fmt"
4 "reflect"
5
6 "github.com/onsi/gomega/types"
7 )
8
9 type Assertion struct {
10 actualInput interface{}
11 failWrapper *types.GomegaFailWrapper
12 offset int
13 extra []interface{}
14 }
15
16 func New(actualInput interface{}, failWrapper *types.GomegaFailWrapper, offset int, extra ...interface{}) *Assertion {
17 return &Assertion{
18 actualInput: actualInput,
19 failWrapper: failWrapper,
20 offset: offset,
21 extra: extra,
22 }
23 }
24
25 func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
26 assertion.failWrapper.TWithHelper.Helper()
27 return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
28 }
29
30 func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
31 assertion.failWrapper.TWithHelper.Helper()
32 return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
33 }
34
35 func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
36 assertion.failWrapper.TWithHelper.Helper()
37 return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
38 }
39
40 func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
41 assertion.failWrapper.TWithHelper.Helper()
42 return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
43 }
44
45 func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
46 assertion.failWrapper.TWithHelper.Helper()
47 return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
48 }
49
50 func (assertion *Assertion) buildDescription(optionalDescription ...interface{}) string {
51 switch len(optionalDescription) {
52 case 0:
53 return ""
54 case 1:
55 if describe, ok := optionalDescription[0].(func() string); ok {
56 return describe() + "\n"
57 }
58 }
59 return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
60 }
61
62 func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
63 matches, err := matcher.Match(assertion.actualInput)
64 assertion.failWrapper.TWithHelper.Helper()
65 if err != nil {
66 description := assertion.buildDescription(optionalDescription...)
67 assertion.failWrapper.Fail(description+err.Error(), 2+assertion.offset)
68 return false
69 }
70 if matches != desiredMatch {
71 var message string
72 if desiredMatch {
73 message = matcher.FailureMessage(assertion.actualInput)
74 } else {
75 message = matcher.NegatedFailureMessage(assertion.actualInput)
76 }
77 description := assertion.buildDescription(optionalDescription...)
78 assertion.failWrapper.Fail(description+message, 2+assertion.offset)
79 return false
80 }
81
82 return true
83 }
84
85 func (assertion *Assertion) vetExtras(optionalDescription ...interface{}) bool {
86 success, message := vetExtras(assertion.extra)
87 if success {
88 return true
89 }
90
91 description := assertion.buildDescription(optionalDescription...)
92 assertion.failWrapper.TWithHelper.Helper()
93 assertion.failWrapper.Fail(description+message, 2+assertion.offset)
94 return false
95 }
96
97 func vetExtras(extras []interface{}) (bool, string) {
98 for i, extra := range extras {
99 if extra != nil {
100 zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface()
101 if !reflect.DeepEqual(zeroValue, extra) {
102 message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra)
103 return false, message
104 }
105 }
106 }
107 return true, ""
108 }
+0
-13
internal/assertion/assertion_suite_test.go less more
0 package assertion_test
1
2 import (
3 . "github.com/onsi/ginkgo"
4 . "github.com/onsi/gomega"
5
6 "testing"
7 )
8
9 func TestAssertion(t *testing.T) {
10 RegisterFailHandler(Fail)
11 RunSpecs(t, "Assertion Suite")
12 }
+0
-277
internal/assertion/assertion_test.go less more
0 package assertion_test
1
2 import (
3 "errors"
4
5 "github.com/onsi/gomega/internal/testingtsupport"
6
7 . "github.com/onsi/ginkgo"
8 . "github.com/onsi/gomega"
9 "github.com/onsi/gomega/internal/assertion"
10 "github.com/onsi/gomega/internal/fakematcher"
11 "github.com/onsi/gomega/types"
12 )
13
14 var _ = Describe("Assertion", func() {
15 var (
16 a *assertion.Assertion
17 failureMessage string
18 failureCallerSkip int
19 matcher *fakematcher.FakeMatcher
20 )
21
22 input := "The thing I'm testing"
23
24 var fakeFailWrapper = &types.GomegaFailWrapper{
25 Fail: func(message string, callerSkip ...int) {
26 failureMessage = message
27 if len(callerSkip) == 1 {
28 failureCallerSkip = callerSkip[0]
29 }
30 },
31 TWithHelper: testingtsupport.EmptyTWithHelper{},
32 }
33
34 BeforeEach(func() {
35 matcher = &fakematcher.FakeMatcher{}
36 failureMessage = ""
37 failureCallerSkip = 0
38 a = assertion.New(input, fakeFailWrapper, 1)
39 })
40
41 When("called", func() {
42 It("should pass the provided input value to the matcher", func() {
43 a.Should(matcher)
44
45 Expect(matcher.ReceivedActual).Should(Equal(input))
46 matcher.ReceivedActual = ""
47
48 a.ShouldNot(matcher)
49
50 Expect(matcher.ReceivedActual).Should(Equal(input))
51 matcher.ReceivedActual = ""
52
53 a.To(matcher)
54
55 Expect(matcher.ReceivedActual).Should(Equal(input))
56 matcher.ReceivedActual = ""
57
58 a.ToNot(matcher)
59
60 Expect(matcher.ReceivedActual).Should(Equal(input))
61 matcher.ReceivedActual = ""
62
63 a.NotTo(matcher)
64
65 Expect(matcher.ReceivedActual).Should(Equal(input))
66 })
67 })
68
69 When("the matcher succeeds", func() {
70 BeforeEach(func() {
71 matcher.MatchesToReturn = true
72 matcher.ErrToReturn = nil
73 })
74
75 Context("and a positive assertion is being made", func() {
76 It("should not call the failure callback", func() {
77 a.Should(matcher)
78 Expect(failureMessage).Should(Equal(""))
79 })
80
81 It("should be true", func() {
82 Expect(a.Should(matcher)).Should(BeTrue())
83 })
84 })
85
86 Context("and a negative assertion is being made", func() {
87 It("should call the failure callback", func() {
88 a.ShouldNot(matcher)
89 Expect(failureMessage).Should(Equal("negative: The thing I'm testing"))
90 Expect(failureCallerSkip).Should(Equal(3))
91 })
92
93 It("should be false", func() {
94 Expect(a.ShouldNot(matcher)).Should(BeFalse())
95 })
96 })
97
98 Context("and the optional description is a function", func() {
99 It("should not evaluate that function", func() {
100 evaluated := false
101 a.Should(matcher, func() string {
102 evaluated = true
103 return "A description"
104 })
105 Expect(evaluated).Should(BeFalse())
106 })
107 })
108 })
109
110 When("the matcher fails", func() {
111 BeforeEach(func() {
112 matcher.MatchesToReturn = false
113 matcher.ErrToReturn = nil
114 })
115
116 Context("and a positive assertion is being made", func() {
117 It("should call the failure callback", func() {
118 a.Should(matcher)
119 Expect(failureMessage).Should(Equal("positive: The thing I'm testing"))
120 Expect(failureCallerSkip).Should(Equal(3))
121 })
122
123 It("should be false", func() {
124 Expect(a.Should(matcher)).Should(BeFalse())
125 })
126 })
127
128 Context("and a negative assertion is being made", func() {
129 It("should not call the failure callback", func() {
130 a.ShouldNot(matcher)
131 Expect(failureMessage).Should(Equal(""))
132 })
133
134 It("should be true", func() {
135 Expect(a.ShouldNot(matcher)).Should(BeTrue())
136 })
137 })
138 })
139
140 Context("When reporting a failure", func() {
141 BeforeEach(func() {
142 matcher.MatchesToReturn = false
143 matcher.ErrToReturn = nil
144 })
145
146 Context("and there is an optional description", func() {
147 It("should append the description to the failure message", func() {
148 a.Should(matcher, "A description")
149 Expect(failureMessage).Should(Equal("A description\npositive: The thing I'm testing"))
150 Expect(failureCallerSkip).Should(Equal(3))
151 })
152 })
153
154 Context("and there are multiple arguments to the optional description", func() {
155 It("should append the formatted description to the failure message", func() {
156 a.Should(matcher, "A description of [%d]", 3)
157 Expect(failureMessage).Should(Equal("A description of [3]\npositive: The thing I'm testing"))
158 Expect(failureCallerSkip).Should(Equal(3))
159 })
160 })
161
162 Context("and the optional description is a function", func() {
163 It("should append the description to the failure message", func() {
164 a.Should(matcher, func() string { return "A description" })
165 Expect(failureMessage).Should(Equal("A description\npositive: The thing I'm testing"))
166 Expect(failureCallerSkip).Should(Equal(3))
167 })
168 })
169 })
170
171 Context("When the matcher returns an error", func() {
172 BeforeEach(func() {
173 matcher.ErrToReturn = errors.New("Kaboom!")
174 })
175
176 Context("and a positive assertion is being made", func() {
177 It("should call the failure callback", func() {
178 matcher.MatchesToReturn = true
179 a.Should(matcher)
180 Expect(failureMessage).Should(Equal("Kaboom!"))
181 Expect(failureCallerSkip).Should(Equal(3))
182 })
183 })
184
185 Context("and a negative assertion is being made", func() {
186 It("should call the failure callback", func() {
187 matcher.MatchesToReturn = false
188 a.ShouldNot(matcher)
189 Expect(failureMessage).Should(Equal("Kaboom!"))
190 Expect(failureCallerSkip).Should(Equal(3))
191 })
192 })
193
194 It("should always be false", func() {
195 Expect(a.Should(matcher)).Should(BeFalse())
196 Expect(a.ShouldNot(matcher)).Should(BeFalse())
197 })
198 })
199
200 When("there are extra parameters", func() {
201 It("(a simple example)", func() {
202 Expect(func() (string, int, error) {
203 return "foo", 0, nil
204 }()).Should(Equal("foo"))
205 })
206
207 When("the parameters are all nil or zero", func() {
208 It("should invoke the matcher", func() {
209 matcher.MatchesToReturn = true
210 matcher.ErrToReturn = nil
211
212 var typedNil []string
213 a = assertion.New(input, fakeFailWrapper, 1, 0, nil, typedNil)
214
215 result := a.Should(matcher)
216 Expect(result).Should(BeTrue())
217 Expect(matcher.ReceivedActual).Should(Equal(input))
218
219 Expect(failureMessage).Should(BeZero())
220 })
221 })
222
223 When("any of the parameters are not nil or zero", func() {
224 It("should call the failure callback", func() {
225 matcher.MatchesToReturn = false
226 matcher.ErrToReturn = nil
227
228 a = assertion.New(input, fakeFailWrapper, 1, errors.New("foo"))
229 result := a.Should(matcher)
230 Expect(result).Should(BeFalse())
231 Expect(matcher.ReceivedActual).Should(BeZero(), "The matcher doesn't even get called")
232 Expect(failureMessage).Should(ContainSubstring("foo"))
233 failureMessage = ""
234
235 a = assertion.New(input, fakeFailWrapper, 1, nil, 1)
236 result = a.ShouldNot(matcher)
237 Expect(result).Should(BeFalse())
238 Expect(failureMessage).Should(ContainSubstring("1"))
239 failureMessage = ""
240
241 a = assertion.New(input, fakeFailWrapper, 1, nil, 0, []string{"foo"})
242 result = a.To(matcher)
243 Expect(result).Should(BeFalse())
244 Expect(failureMessage).Should(ContainSubstring("foo"))
245 failureMessage = ""
246
247 a = assertion.New(input, fakeFailWrapper, 1, nil, 0, []string{"foo"})
248 result = a.ToNot(matcher)
249 Expect(result).Should(BeFalse())
250 Expect(failureMessage).Should(ContainSubstring("foo"))
251 failureMessage = ""
252
253 a = assertion.New(input, fakeFailWrapper, 1, nil, 0, []string{"foo"})
254 result = a.NotTo(matcher)
255 Expect(result).Should(BeFalse())
256 Expect(failureMessage).Should(ContainSubstring("foo"))
257 Expect(failureCallerSkip).Should(Equal(3))
258 })
259 })
260 })
261
262 Context("Making an assertion without a registered fail handler", func() {
263 It("should panic", func() {
264 defer func() {
265 e := recover()
266 RegisterFailHandler(Fail)
267 if e == nil {
268 Fail("expected a panic to have occurred")
269 }
270 }()
271
272 RegisterFailHandler(nil)
273 Expect(true).Should(BeTrue())
274 })
275 })
276 })
0 package internal
1
2 import (
3 "fmt"
4 "reflect"
5
6 "github.com/onsi/gomega/types"
7 )
8
9 type Assertion struct {
10 actualInput interface{}
11 offset int
12 extra []interface{}
13 g *Gomega
14 }
15
16 func NewAssertion(actualInput interface{}, g *Gomega, offset int, extra ...interface{}) *Assertion {
17 return &Assertion{
18 actualInput: actualInput,
19 offset: offset,
20 extra: extra,
21 g: g,
22 }
23 }
24
25 func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
26 assertion.g.THelper()
27 return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
28 }
29
30 func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
31 assertion.g.THelper()
32 return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
33 }
34
35 func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
36 assertion.g.THelper()
37 return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
38 }
39
40 func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
41 assertion.g.THelper()
42 return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
43 }
44
45 func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
46 assertion.g.THelper()
47 return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
48 }
49
50 func (assertion *Assertion) buildDescription(optionalDescription ...interface{}) string {
51 switch len(optionalDescription) {
52 case 0:
53 return ""
54 case 1:
55 if describe, ok := optionalDescription[0].(func() string); ok {
56 return describe() + "\n"
57 }
58 }
59 return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
60 }
61
62 func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
63 matches, err := matcher.Match(assertion.actualInput)
64 assertion.g.THelper()
65 if err != nil {
66 description := assertion.buildDescription(optionalDescription...)
67 assertion.g.Fail(description+err.Error(), 2+assertion.offset)
68 return false
69 }
70 if matches != desiredMatch {
71 var message string
72 if desiredMatch {
73 message = matcher.FailureMessage(assertion.actualInput)
74 } else {
75 message = matcher.NegatedFailureMessage(assertion.actualInput)
76 }
77 description := assertion.buildDescription(optionalDescription...)
78 assertion.g.Fail(description+message, 2+assertion.offset)
79 return false
80 }
81
82 return true
83 }
84
85 func (assertion *Assertion) vetExtras(optionalDescription ...interface{}) bool {
86 success, message := vetExtras(assertion.extra)
87 if success {
88 return true
89 }
90
91 description := assertion.buildDescription(optionalDescription...)
92 assertion.g.THelper()
93 assertion.g.Fail(description+message, 2+assertion.offset)
94 return false
95 }
96
97 func vetExtras(extras []interface{}) (bool, string) {
98 for i, extra := range extras {
99 if extra != nil {
100 zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface()
101 if !reflect.DeepEqual(zeroValue, extra) {
102 message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra)
103 return false, message
104 }
105 }
106 }
107 return true, ""
108 }
0 package internal_test
1
2 import (
3 . "github.com/onsi/ginkgo"
4 . "github.com/onsi/ginkgo/extensions/table"
5 . "github.com/onsi/gomega"
6 )
7
8 var _ = Describe("Making Synchronous Assertions", func() {
9 var SHOULD_MATCH = true
10 var SHOULD_NOT_MATCH = false
11 var IT_PASSES = true
12 var IT_FAILS = false
13
14 Extras := func(extras ...interface{}) []interface{} {
15 return extras
16 }
17
18 OptionalDescription := func(optionalDescription ...interface{}) []interface{} {
19 return optionalDescription
20 }
21
22 DescribeTable(
23 "the various cases",
24 func(actual interface{}, extras []interface{}, optionalDescription []interface{}, isPositiveAssertion bool, expectedFailureMessage string, expectedReturnValue bool) {
25 if isPositiveAssertion {
26 ig := NewInstrumentedGomega()
27 returnValue := ig.G.Expect(actual, extras...).To(SpecMatch(), optionalDescription...)
28 Expect(returnValue).To(Equal(expectedReturnValue))
29 Expect(ig.FailureMessage).To(Equal(expectedFailureMessage))
30 if expectedFailureMessage != "" {
31 Expect(ig.FailureSkip).To(Equal([]int{2}))
32 }
33 Expect(ig.RegisteredHelpers).To(ContainElement("(*Assertion).To"))
34
35 ig = NewInstrumentedGomega()
36 returnValue = ig.G.ExpectWithOffset(3, actual, extras...).To(SpecMatch(), optionalDescription...)
37 Expect(returnValue).To(Equal(expectedReturnValue))
38 Expect(ig.FailureMessage).To(Equal(expectedFailureMessage))
39 if expectedFailureMessage != "" {
40 Expect(ig.FailureSkip).To(Equal([]int{5}))
41 }
42 Expect(ig.RegisteredHelpers).To(ContainElement("(*Assertion).To"))
43
44 ig = NewInstrumentedGomega()
45 returnValue = ig.G.Ω(actual, extras...).Should(SpecMatch(), optionalDescription...)
46 Expect(returnValue).To(Equal(expectedReturnValue))
47 Expect(ig.FailureMessage).To(Equal(expectedFailureMessage))
48 if expectedFailureMessage != "" {
49 Expect(ig.FailureSkip).To(Equal([]int{2}))
50 }
51 Expect(ig.RegisteredHelpers).To(ContainElement("(*Assertion).Should"))
52 } else {
53 ig := NewInstrumentedGomega()
54 returnValue := ig.G.Expect(actual, extras...).ToNot(SpecMatch(), optionalDescription...)
55 Expect(returnValue).To(Equal(expectedReturnValue))
56 Expect(ig.FailureMessage).To(Equal(expectedFailureMessage))
57 if expectedFailureMessage != "" {
58 Expect(ig.FailureSkip).To(Equal([]int{2}))
59 }
60 Expect(ig.RegisteredHelpers).To(ContainElement("(*Assertion).ToNot"))
61
62 ig = NewInstrumentedGomega()
63 returnValue = ig.G.Expect(actual, extras...).NotTo(SpecMatch(), optionalDescription...)
64 Expect(returnValue).To(Equal(expectedReturnValue))
65 Expect(ig.FailureMessage).To(Equal(expectedFailureMessage))
66 if expectedFailureMessage != "" {
67 Expect(ig.FailureSkip).To(Equal([]int{2}))
68 }
69 Expect(ig.RegisteredHelpers).To(ContainElement("(*Assertion).NotTo"))
70
71 ig = NewInstrumentedGomega()
72 returnValue = ig.G.ExpectWithOffset(3, actual, extras...).NotTo(SpecMatch(), optionalDescription...)
73 Expect(returnValue).To(Equal(expectedReturnValue))
74 Expect(ig.FailureMessage).To(Equal(expectedFailureMessage))
75 if expectedFailureMessage != "" {
76 Expect(ig.FailureSkip).To(Equal([]int{5}))
77 }
78 Expect(ig.RegisteredHelpers).To(ContainElement("(*Assertion).NotTo"))
79
80 ig = NewInstrumentedGomega()
81 returnValue = ig.G.Ω(actual, extras...).ShouldNot(SpecMatch(), optionalDescription...)
82 Expect(returnValue).To(Equal(expectedReturnValue))
83 Expect(ig.FailureMessage).To(Equal(expectedFailureMessage))
84 if expectedFailureMessage != "" {
85 Expect(ig.FailureSkip).To(Equal([]int{2}))
86 }
87 Expect(ig.RegisteredHelpers).To(ContainElement("(*Assertion).ShouldNot"))
88 }
89 },
90 Entry(
91 "when the matcher matches and a positive assertion is being made",
92 MATCH, Extras(), OptionalDescription(),
93 SHOULD_MATCH, "", IT_PASSES,
94 ),
95 Entry(
96 "when the matcher matches and a negative assertion is being made",
97 MATCH, Extras(), OptionalDescription(),
98 SHOULD_NOT_MATCH, "negative: match", IT_FAILS,
99 ),
100 Entry(
101 "when the matcher does not match and a positive assertion is being made",
102 NO_MATCH, Extras(), OptionalDescription(),
103 SHOULD_MATCH, "positive: no match", IT_FAILS,
104 ),
105 Entry(
106 "when the matcher does not match and a negative assertion is being made",
107 NO_MATCH, Extras(), OptionalDescription(),
108 SHOULD_NOT_MATCH, "", IT_PASSES,
109 ),
110 Entry(
111 "when the matcher returns an error and a positive assertion is being made",
112 ERR_MATCH, Extras(), OptionalDescription(),
113 SHOULD_MATCH, "spec matcher error", IT_FAILS,
114 ),
115 Entry(
116 "when the matcher returns an error and a negative assertion is being made",
117 ERR_MATCH, Extras(), OptionalDescription(),
118 SHOULD_NOT_MATCH, "spec matcher error", IT_FAILS,
119 ),
120 Entry(
121 "when a failure occurs and there is a single optional description",
122 NO_MATCH, Extras(), OptionalDescription("a description"),
123 SHOULD_MATCH, "a description\npositive: no match", IT_FAILS,
124 ),
125 Entry(
126 "when a failure occurs and there are multiple optional descriptions",
127 NO_MATCH, Extras(), OptionalDescription("a description of [%d]", 3),
128 SHOULD_MATCH, "a description of [3]\npositive: no match", IT_FAILS,
129 ),
130 Entry(
131 "when a failure occurs and the optional description is a function",
132 NO_MATCH, Extras(), OptionalDescription(func() string { return "a description" }),
133 SHOULD_MATCH, "a description\npositive: no match", IT_FAILS,
134 ),
135 Entry(
136 "when the matcher matches and zero-valued extra parameters are included, it passes",
137 MATCH, Extras(0, "", struct{ Foo string }{}, nil), OptionalDescription(),
138 SHOULD_MATCH, "", IT_PASSES,
139 ),
140 Entry(
141 "when the matcher matches but a non-zero-valued extra parameter is included, it fails",
142 MATCH, Extras(1, "bam", struct{ Foo string }{Foo: "foo"}, nil), OptionalDescription(),
143 SHOULD_MATCH, "Unexpected non-nil/non-zero extra argument at index 1:\n\t<int>: 1", IT_FAILS,
144 ),
145 )
146 })
0 package internal
1
2 import (
3 "errors"
4 "fmt"
5 "reflect"
6 "runtime"
7 "time"
8
9 "github.com/onsi/gomega/types"
10 )
11
12 type AsyncAssertionType uint
13
14 const (
15 AsyncAssertionTypeEventually AsyncAssertionType = iota
16 AsyncAssertionTypeConsistently
17 )
18
19 type AsyncAssertion struct {
20 asyncType AsyncAssertionType
21
22 actualIsFunc bool
23 actualValue interface{}
24 actualFunc func() ([]reflect.Value, error)
25
26 timeoutInterval time.Duration
27 pollingInterval time.Duration
28 offset int
29 g *Gomega
30 }
31
32 func NewAsyncAssertion(asyncType AsyncAssertionType, actualInput interface{}, g *Gomega, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion {
33 out := &AsyncAssertion{
34 asyncType: asyncType,
35 timeoutInterval: timeoutInterval,
36 pollingInterval: pollingInterval,
37 offset: offset,
38 g: g,
39 }
40
41 switch actualType := reflect.TypeOf(actualInput); {
42 case actualType.Kind() != reflect.Func:
43 out.actualValue = actualInput
44 case actualType.NumIn() == 0 && actualType.NumOut() > 0:
45 out.actualIsFunc = true
46 out.actualFunc = func() ([]reflect.Value, error) {
47 return reflect.ValueOf(actualInput).Call([]reflect.Value{}), nil
48 }
49 case actualType.NumIn() == 1 && actualType.In(0).Implements(reflect.TypeOf((*types.Gomega)(nil)).Elem()):
50 out.actualIsFunc = true
51 out.actualFunc = func() (values []reflect.Value, err error) {
52 var assertionFailure error
53 assertionCapturingGomega := NewGomega(g.DurationBundle).ConfigureWithFailHandler(func(message string, callerSkip ...int) {
54 skip := 0
55 if len(callerSkip) > 0 {
56 skip = callerSkip[0]
57 }
58 _, file, line, _ := runtime.Caller(skip + 1)
59 assertionFailure = fmt.Errorf("Assertion in callback at %s:%d failed:\n%s", file, line, message)
60 panic("stop execution")
61 })
62
63 defer func() {
64 if actualType.NumOut() == 0 {
65 if assertionFailure == nil {
66 values = []reflect.Value{reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())}
67 } else {
68 values = []reflect.Value{reflect.ValueOf(assertionFailure)}
69 }
70 } else {
71 err = assertionFailure
72 }
73 if e := recover(); e != nil && assertionFailure == nil {
74 panic(e)
75 }
76 }()
77
78 values = reflect.ValueOf(actualInput).Call([]reflect.Value{reflect.ValueOf(assertionCapturingGomega)})
79 return
80 }
81 default:
82 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())
83 g.Fail(msg, offset+4)
84 }
85
86 return out
87 }
88
89 func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
90 assertion.g.THelper()
91 return assertion.match(matcher, true, optionalDescription...)
92 }
93
94 func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
95 assertion.g.THelper()
96 return assertion.match(matcher, false, optionalDescription...)
97 }
98
99 func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interface{}) string {
100 switch len(optionalDescription) {
101 case 0:
102 return ""
103 case 1:
104 if describe, ok := optionalDescription[0].(func() string); ok {
105 return describe() + "\n"
106 }
107 }
108 return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
109 }
110
111 func (assertion *AsyncAssertion) pollActual() (interface{}, error) {
112 if !assertion.actualIsFunc {
113 return assertion.actualValue, nil
114 }
115
116 values, err := assertion.actualFunc()
117 if err != nil {
118 return nil, err
119 }
120 extras := []interface{}{}
121 for _, value := range values[1:] {
122 extras = append(extras, value.Interface())
123 }
124 success, message := vetExtras(extras)
125 if !success {
126 return nil, errors.New(message)
127 }
128
129 return values[0].Interface(), nil
130 }
131
132 func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool {
133 if assertion.actualIsFunc {
134 return true
135 }
136 return types.MatchMayChangeInTheFuture(matcher, value)
137 }
138
139 func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
140 timer := time.Now()
141 timeout := time.After(assertion.timeoutInterval)
142
143 var matches bool
144 var err error
145 mayChange := true
146 value, err := assertion.pollActual()
147 if err == nil {
148 mayChange = assertion.matcherMayChange(matcher, value)
149 matches, err = matcher.Match(value)
150 }
151
152 assertion.g.THelper()
153
154 fail := func(preamble string) {
155 errMsg := ""
156 message := ""
157 if err != nil {
158 errMsg = "Error: " + err.Error()
159 } else {
160 if desiredMatch {
161 message = matcher.FailureMessage(value)
162 } else {
163 message = matcher.NegatedFailureMessage(value)
164 }
165 }
166 assertion.g.THelper()
167 description := assertion.buildDescription(optionalDescription...)
168 assertion.g.Fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset)
169 }
170
171 if assertion.asyncType == AsyncAssertionTypeEventually {
172 for {
173 if err == nil && matches == desiredMatch {
174 return true
175 }
176
177 if !mayChange {
178 fail("No future change is possible. Bailing out early")
179 return false
180 }
181
182 select {
183 case <-time.After(assertion.pollingInterval):
184 value, err = assertion.pollActual()
185 if err == nil {
186 mayChange = assertion.matcherMayChange(matcher, value)
187 matches, err = matcher.Match(value)
188 }
189 case <-timeout:
190 fail("Timed out")
191 return false
192 }
193 }
194 } else if assertion.asyncType == AsyncAssertionTypeConsistently {
195 for {
196 if !(err == nil && matches == desiredMatch) {
197 fail("Failed")
198 return false
199 }
200
201 if !mayChange {
202 return true
203 }
204
205 select {
206 case <-time.After(assertion.pollingInterval):
207 value, err = assertion.pollActual()
208 if err == nil {
209 mayChange = assertion.matcherMayChange(matcher, value)
210 matches, err = matcher.Match(value)
211 }
212 case <-timeout:
213 return true
214 }
215 }
216 }
217
218 return false
219 }
0 package internal_test
1
2 import (
3 "errors"
4 "runtime"
5 "time"
6
7 . "github.com/onsi/ginkgo"
8 . "github.com/onsi/gomega"
9 )
10
11 var _ = Describe("Asynchronous Assertions", func() {
12 var ig *InstrumentedGomega
13 BeforeEach(func() {
14 ig = NewInstrumentedGomega()
15 })
16
17 Describe("Basic Eventually support", func() {
18 Context("the positive case", func() {
19 It("polls the function and matcher until a match occurs", func() {
20 counter := 0
21 ig.G.Eventually(func() string {
22 counter++
23 if counter > 5 {
24 return MATCH
25 }
26 return NO_MATCH
27 }).Should(SpecMatch())
28 Ω(counter).Should(Equal(6))
29 Ω(ig.FailureMessage).Should(BeZero())
30 })
31
32 It("continues polling even if the matcher errors", func() {
33 counter := 0
34 ig.G.Eventually(func() string {
35 counter++
36 if counter > 5 {
37 return MATCH
38 }
39 return ERR_MATCH
40 }).Should(SpecMatch())
41 Ω(counter).Should(Equal(6))
42 Ω(ig.FailureMessage).Should(BeZero())
43 })
44
45 It("times out eventually if the assertion doesn't match in time", func() {
46 counter := 0
47 ig.G.Eventually(func() string {
48 counter++
49 if counter > 100 {
50 return MATCH
51 }
52 return NO_MATCH
53 }, "200ms", "20ms").Should(SpecMatch())
54 Ω(counter).Should(BeNumerically(">", 2))
55 Ω(counter).Should(BeNumerically("<", 20))
56 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after"))
57 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match"))
58 Ω(ig.FailureSkip).Should(Equal([]int{3}))
59 })
60 })
61
62 Context("the negative case", func() {
63 It("polls the function and matcher until a match does not occur", func() {
64 counter := 0
65 ig.G.Eventually(func() string {
66 counter++
67 if counter > 5 {
68 return NO_MATCH
69 }
70 return MATCH
71 }).ShouldNot(SpecMatch())
72 Ω(counter).Should(Equal(6))
73 Ω(ig.FailureMessage).Should(BeZero())
74 })
75
76 It("continues polling when the matcher errors - an error does not count as a successful non-match", func() {
77 counter := 0
78 ig.G.Eventually(func() string {
79 counter++
80 if counter > 5 {
81 return NO_MATCH
82 }
83 return ERR_MATCH
84 }).ShouldNot(SpecMatch())
85 Ω(counter).Should(Equal(6))
86 Ω(ig.FailureMessage).Should(BeZero())
87 })
88
89 It("times out eventually if the assertion doesn't match in time", func() {
90 counter := 0
91 ig.G.Eventually(func() string {
92 counter++
93 if counter > 100 {
94 return NO_MATCH
95 }
96 return MATCH
97 }, "200ms", "20ms").ShouldNot(SpecMatch())
98 Ω(counter).Should(BeNumerically(">", 2))
99 Ω(counter).Should(BeNumerically("<", 20))
100 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after"))
101 Ω(ig.FailureMessage).Should(ContainSubstring("negative: match"))
102 Ω(ig.FailureSkip).Should(Equal([]int{3}))
103 })
104 })
105
106 Context("when a failure occurs", func() {
107 It("registers the appropriate helper functions", func() {
108 ig.G.Eventually(NO_MATCH, "50ms", "10ms").Should(SpecMatch())
109 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after"))
110 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match"))
111 Ω(ig.FailureSkip).Should(Equal([]int{3}))
112 Ω(ig.RegisteredHelpers).Should(ContainElement("(*AsyncAssertion).Should"))
113 Ω(ig.RegisteredHelpers).Should(ContainElement("(*AsyncAssertion).match"))
114 })
115
116 It("renders the matcher's error if an error occured", func() {
117 ig.G.Eventually(ERR_MATCH, "50ms", "10ms").Should(SpecMatch())
118 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after"))
119 Ω(ig.FailureMessage).Should(ContainSubstring("Error: spec matcher error"))
120 })
121
122 It("renders the optional description", func() {
123 ig.G.Eventually(NO_MATCH, "50ms", "10ms").Should(SpecMatch(), "boop")
124 Ω(ig.FailureMessage).Should(ContainSubstring("boop"))
125 })
126
127 It("formats and renders the optional description when there are multiple arguments", func() {
128 ig.G.Eventually(NO_MATCH, "50ms", "10ms").Should(SpecMatch(), "boop %d", 17)
129 Ω(ig.FailureMessage).Should(ContainSubstring("boop 17"))
130 })
131
132 It("calls the optional description if it is a function", func() {
133 ig.G.Eventually(NO_MATCH, "50ms", "10ms").Should(SpecMatch(), func() string { return "boop" })
134 Ω(ig.FailureMessage).Should(ContainSubstring("boop"))
135 })
136 })
137 })
138
139 Describe("Basic Consistently support", func() {
140 Context("the positive case", func() {
141 It("polls the function and matcher ensuring a match occurs consistently", func() {
142 counter := 0
143 ig.G.Consistently(func() string {
144 counter++
145 return MATCH
146 }, "50ms", "10ms").Should(SpecMatch())
147 Ω(counter).Should(BeNumerically(">", 1))
148 Ω(counter).Should(BeNumerically("<", 7))
149 Ω(ig.FailureMessage).Should(BeZero())
150 })
151
152 It("fails if the matcher ever errors", func() {
153 counter := 0
154 ig.G.Consistently(func() string {
155 counter++
156 if counter == 3 {
157 return ERR_MATCH
158 }
159 return MATCH
160 }, "50ms", "10ms").Should(SpecMatch())
161 Ω(counter).Should(Equal(3))
162 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after"))
163 Ω(ig.FailureMessage).Should(ContainSubstring("Error: spec matcher error"))
164 })
165
166 It("fails if the matcher doesn't match at any point", func() {
167 counter := 0
168 ig.G.Consistently(func() string {
169 counter++
170 if counter == 3 {
171 return NO_MATCH
172 }
173 return MATCH
174 }, "50ms", "10ms").Should(SpecMatch())
175 Ω(counter).Should(Equal(3))
176 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after"))
177 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match"))
178 })
179 })
180
181 Context("the negative case", func() {
182 It("polls the function and matcher ensuring a match never occurs", func() {
183 counter := 0
184 ig.G.Consistently(func() string {
185 counter++
186 return NO_MATCH
187 }, "50ms", "10ms").ShouldNot(SpecMatch())
188 Ω(counter).Should(BeNumerically(">", 1))
189 Ω(counter).Should(BeNumerically("<", 7))
190 Ω(ig.FailureMessage).Should(BeZero())
191 })
192
193 It("fails if the matcher ever errors", func() {
194 counter := 0
195 ig.G.Consistently(func() string {
196 counter++
197 if counter == 3 {
198 return ERR_MATCH
199 }
200 return NO_MATCH
201 }, "50ms", "10ms").ShouldNot(SpecMatch())
202 Ω(counter).Should(Equal(3))
203 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after"))
204 Ω(ig.FailureMessage).Should(ContainSubstring("Error: spec matcher error"))
205 })
206
207 It("fails if the matcher matches at any point", func() {
208 counter := 0
209 ig.G.Consistently(func() string {
210 counter++
211 if counter == 3 {
212 return MATCH
213 }
214 return NO_MATCH
215 }, "50ms", "10ms").ShouldNot(SpecMatch())
216 Ω(counter).Should(Equal(3))
217 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after"))
218 Ω(ig.FailureMessage).Should(ContainSubstring("negative: match"))
219 })
220 })
221
222 Context("when a failure occurs", func() {
223 It("registers the appropriate helper functions", func() {
224 ig.G.Consistently(NO_MATCH).Should(SpecMatch())
225 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after"))
226 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match"))
227 Ω(ig.FailureSkip).Should(Equal([]int{3}))
228 Ω(ig.RegisteredHelpers).Should(ContainElement("(*AsyncAssertion).Should"))
229 Ω(ig.RegisteredHelpers).Should(ContainElement("(*AsyncAssertion).match"))
230 })
231
232 It("renders the matcher's error if an error occured", func() {
233 ig.G.Consistently(ERR_MATCH).Should(SpecMatch())
234 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after"))
235 Ω(ig.FailureMessage).Should(ContainSubstring("Error: spec matcher error"))
236 })
237
238 It("renders the optional description", func() {
239 ig.G.Consistently(NO_MATCH).Should(SpecMatch(), "boop")
240 Ω(ig.FailureMessage).Should(ContainSubstring("boop"))
241 })
242
243 It("formats and renders the optional description when there are multiple arguments", func() {
244 ig.G.Consistently(NO_MATCH).Should(SpecMatch(), "boop %d", 17)
245 Ω(ig.FailureMessage).Should(ContainSubstring("boop 17"))
246 })
247
248 It("calls the optional description if it is a function", func() {
249 ig.G.Consistently(NO_MATCH).Should(SpecMatch(), func() string { return "boop" })
250 Ω(ig.FailureMessage).Should(ContainSubstring("boop"))
251 })
252 })
253 })
254
255 Describe("the passed-in actual", func() {
256 type Foo struct{ Bar string }
257
258 Context("when passed a value", func() {
259 It("(eventually) continuously checks on the value until a match occurs", func() {
260 c := make(chan bool)
261 go func() {
262 time.Sleep(100 * time.Millisecond)
263 close(c)
264 }()
265 ig.G.Eventually(c, "1s", "10ms").Should(BeClosed())
266 Ω(ig.FailureMessage).Should(BeZero())
267 })
268
269 It("(consistently) continuously checks on the value ensuring a match always occurs", func() {
270 c := make(chan bool)
271 close(c)
272 ig.G.Consistently(c, "50ms", "10ms").Should(BeClosed())
273 Ω(ig.FailureMessage).Should(BeZero())
274 })
275 })
276
277 Context("when passed a function that takes no arguments and returns one value", func() {
278 It("(eventually) polls the function until the returned value satisfies the matcher", func() {
279 counter := 0
280 ig.G.Eventually(func() int {
281 counter += 1
282 return counter
283 }, "1s", "10ms").Should(BeNumerically(">", 5))
284 Ω(ig.FailureMessage).Should(BeZero())
285 })
286
287 It("(consistently) polls the function ensuring the returned value satisfies the matcher", func() {
288 counter := 0
289 ig.G.Consistently(func() int {
290 counter += 1
291 return counter
292 }, "50ms", "10ms").Should(BeNumerically("<", 20))
293 Ω(counter).Should(BeNumerically(">", 2))
294 Ω(ig.FailureMessage).Should(BeZero())
295 })
296
297 It("works when the function returns nil", func() {
298 counter := 0
299 ig.G.Eventually(func() error {
300 counter += 1
301 if counter > 5 {
302 return nil
303 }
304 return errors.New("oops")
305 }, "1s", "10ms").Should(BeNil())
306 Ω(ig.FailureMessage).Should(BeZero())
307 })
308 })
309
310 Context("when passed a function that takes no arguments and returns mutliple values", func() {
311 Context("with Eventually", func() {
312 It("polls the function until the first returned value satisfies the matcher _and_ all additional values are zero", func() {
313 counter, s, f, err := 0, "hi", Foo{Bar: "hi"}, errors.New("hi")
314 ig.G.Eventually(func() (int, string, Foo, error) {
315 switch counter += 1; counter {
316 case 2:
317 s = ""
318 case 3:
319 f = Foo{}
320 case 4:
321 err = nil
322 }
323 return counter, s, f, err
324 }, "1s", "10ms").Should(BeNumerically("<", 100))
325 Ω(ig.FailureMessage).Should(BeZero())
326 Ω(counter).Should(Equal(4))
327 })
328
329 It("reports on the non-zero value if it times out", func() {
330 ig.G.Eventually(func() (int, string, Foo, error) {
331 return 1, "", Foo{Bar: "hi"}, nil
332 }, "30ms", "10ms").Should(BeNumerically("<", 100))
333 Ω(ig.FailureMessage).Should(ContainSubstring("Error: Unexpected non-nil/non-zero extra argument at index 2:"))
334 Ω(ig.FailureMessage).Should(ContainSubstring(`Foo{Bar:"hi"}`))
335 })
336
337 Context("when making a ShouldNot assertion", func() {
338 It("doesn't succeed until the matcher is (not) satisfied with the first returned value _and_ all additional values are zero", func() {
339 counter, s, f, err := 0, "hi", Foo{Bar: "hi"}, errors.New("hi")
340 ig.G.Eventually(func() (int, string, Foo, error) {
341 switch counter += 1; counter {
342 case 2:
343 s = ""
344 case 3:
345 f = Foo{}
346 case 4:
347 err = nil
348 }
349 return counter, s, f, err
350 }, "1s", "10ms").ShouldNot(BeNumerically("<", 0))
351 Ω(ig.FailureMessage).Should(BeZero())
352 Ω(counter).Should(Equal(4))
353 })
354 })
355 })
356
357 Context("with Consistently", func() {
358 It("polls the function and succeeds if all the values are zero and the matcher is consistently satisfied", func() {
359 var err error
360 counter, s, f := 0, "", Foo{}
361 ig.G.Consistently(func() (int, string, Foo, error) {
362 counter += 1
363 return counter, s, f, err
364 }, "50ms", "10ms").Should(BeNumerically("<", 100))
365 Ω(ig.FailureMessage).Should(BeZero())
366 Ω(counter).Should(BeNumerically(">", 2))
367 })
368
369 It("polls the function and fails any of the values are non-zero", func() {
370 var err error
371 counter, s, f := 0, "", Foo{}
372 ig.G.Consistently(func() (int, string, Foo, error) {
373 counter += 1
374 if counter == 3 {
375 f = Foo{Bar: "welp"}
376 }
377 return counter, s, f, err
378 }, "50ms", "10ms").Should(BeNumerically("<", 100))
379 Ω(ig.FailureMessage).Should(ContainSubstring("Error: Unexpected non-nil/non-zero extra argument at index 2:"))
380 Ω(ig.FailureMessage).Should(ContainSubstring(`Foo{Bar:"welp"}`))
381 Ω(counter).Should(Equal(3))
382 })
383
384 Context("when making a ShouldNot assertion", func() {
385 It("succeeds if all additional values are zero", func() {
386 var err error
387 counter, s, f := 0, "", Foo{}
388 ig.G.Consistently(func() (int, string, Foo, error) {
389 counter += 1
390 return counter, s, f, err
391 }, "50ms", "10ms").ShouldNot(BeNumerically(">", 100))
392 Ω(ig.FailureMessage).Should(BeZero())
393 Ω(counter).Should(BeNumerically(">", 2))
394 })
395
396 It("fails if any additional values are ever non-zero", func() {
397 var err error
398 counter, s, f := 0, "", Foo{}
399 ig.G.Consistently(func() (int, string, Foo, error) {
400 counter += 1
401 if counter == 3 {
402 s = "welp"
403 }
404 return counter, s, f, err
405 }, "50ms", "10ms").ShouldNot(BeNumerically(">", 100))
406 Ω(ig.FailureMessage).Should(ContainSubstring("Error: Unexpected non-nil/non-zero extra argument at index 1:"))
407 Ω(ig.FailureMessage).Should(ContainSubstring(`<string>: "welp"`))
408 Ω(counter).Should(Equal(3))
409 })
410 })
411 })
412 })
413
414 Context("when passed a function that takes a Gomega argument and returns values", func() {
415 Context("with Eventually", func() {
416 It("passes in a Gomega and passes if the matcher matches, all extra values are zero, and there are no failed assertions", func() {
417 counter, s, f, err := 0, "hi", Foo{Bar: "hi"}, errors.New("hi")
418 ig.G.Eventually(func(g Gomega) (int, string, Foo, error) {
419 switch counter += 1; counter {
420 case 2:
421 s = ""
422 case 3:
423 f = Foo{}
424 case 4:
425 err = nil
426 }
427 if counter == 5 {
428 g.Expect(true).To(BeTrue())
429 } else {
430 g.Expect(false).To(BeTrue())
431 panic("boom") //never see since the expectation stops execution
432 }
433 return counter, s, f, err
434 }, "1s", "10ms").Should(BeNumerically("<", 100))
435 Ω(ig.FailureMessage).Should(BeZero())
436 Ω(counter).Should(Equal(5))
437 })
438
439 It("times out if assertions in the function never succeed and reports on the error", func() {
440 _, file, line, _ := runtime.Caller(0)
441 ig.G.Eventually(func(g Gomega) int {
442 g.Expect(false).To(BeTrue())
443 return 10
444 }, "30ms", "10ms").Should(Equal(10))
445 Ω(ig.FailureMessage).Should(ContainSubstring("Error: Assertion in callback at %s:%d failed:", file, line+2))
446 Ω(ig.FailureMessage).Should(ContainSubstring("Expected\n <bool>: false\nto be true"))
447 })
448
449 It("forwards panics", func() {
450 Ω(func() {
451 ig.G.Eventually(func(g Gomega) int {
452 g.Expect(true).To(BeTrue())
453 panic("boom")
454 return 10
455 }, "30ms", "10ms").Should(Equal(10))
456 }).Should(PanicWith("boom"))
457 Ω(ig.FailureMessage).Should(BeEmpty())
458 })
459
460 Context("when making a ShouldNot assertion", func() {
461 It("doesn't succeed until all extra values are zero, there are no failed assertions, and the matcher is (not) satisfied", func() {
462 counter, s, f, err := 0, "hi", Foo{Bar: "hi"}, errors.New("hi")
463 ig.G.Eventually(func(g Gomega) (int, string, Foo, error) {
464 switch counter += 1; counter {
465 case 2:
466 s = ""
467 case 3:
468 f = Foo{}
469 case 4:
470 err = nil
471 }
472 if counter == 5 {
473 g.Expect(true).To(BeTrue())
474 } else {
475 g.Expect(false).To(BeTrue())
476 panic("boom") //never see since the expectation stops execution
477 }
478 return counter, s, f, err
479 }, "1s", "10ms").ShouldNot(BeNumerically("<", 0))
480 Ω(ig.FailureMessage).Should(BeZero())
481 Ω(counter).Should(Equal(5))
482 })
483 })
484
485 It("fails if an assertion is never satisfied", func() {
486 _, file, line, _ := runtime.Caller(0)
487 ig.G.Eventually(func(g Gomega) int {
488 g.Expect(false).To(BeTrue())
489 return 9
490 }, "30ms", "10ms").ShouldNot(Equal(10))
491 Ω(ig.FailureMessage).Should(ContainSubstring("Error: Assertion in callback at %s:%d failed:", file, line+2))
492 Ω(ig.FailureMessage).Should(ContainSubstring("Expected\n <bool>: false\nto be true"))
493 })
494 })
495
496 Context("with Consistently", func() {
497 It("passes in a Gomega and passes if the matcher matches, all extra values are zero, and there are no failed assertions", func() {
498 var err error
499 counter, s, f := 0, "", Foo{}
500 ig.G.Consistently(func(g Gomega) (int, string, Foo, error) {
501 counter += 1
502 g.Expect(true).To(BeTrue())
503 return counter, s, f, err
504 }, "50ms", "10ms").Should(BeNumerically("<", 100))
505 Ω(ig.FailureMessage).Should(BeZero())
506 Ω(counter).Should(BeNumerically(">", 2))
507 })
508
509 It("fails if the passed-in gomega ever hits a failure", func() {
510 var err error
511 counter, s, f := 0, "", Foo{}
512 _, file, line, _ := runtime.Caller(0)
513 ig.G.Consistently(func(g Gomega) (int, string, Foo, error) {
514 counter += 1
515 g.Expect(true).To(BeTrue())
516 if counter == 3 {
517 g.Expect(false).To(BeTrue())
518 panic("boom") //never see this
519 }
520 return counter, s, f, err
521 }, "50ms", "10ms").Should(BeNumerically("<", 100))
522 Ω(ig.FailureMessage).Should(ContainSubstring("Error: Assertion in callback at %s:%d failed:", file, line+5))
523 Ω(ig.FailureMessage).Should(ContainSubstring("Expected\n <bool>: false\nto be true"))
524 Ω(counter).Should(Equal(3))
525 })
526
527 It("forwards panics", func() {
528 Ω(func() {
529 ig.G.Consistently(func(g Gomega) int {
530 g.Expect(true).To(BeTrue())
531 panic("boom")
532 return 10
533 }, "50ms", "10ms").Should(Equal(10))
534 }).Should(PanicWith("boom"))
535 Ω(ig.FailureMessage).Should(BeEmpty())
536 })
537
538 Context("when making a ShouldNot assertion", func() {
539 It("succeeds if any interior assertions always pass", func() {
540 ig.G.Consistently(func(g Gomega) int {
541 g.Expect(true).To(BeTrue())
542 return 9
543 }, "50ms", "10ms").ShouldNot(Equal(10))
544 Ω(ig.FailureMessage).Should(BeEmpty())
545 })
546
547 It("fails if any interior assertions ever fail", func() {
548 counter := 0
549 _, file, line, _ := runtime.Caller(0)
550 ig.G.Consistently(func(g Gomega) int {
551 g.Expect(true).To(BeTrue())
552 counter += 1
553 if counter == 3 {
554 g.Expect(false).To(BeTrue())
555 panic("boom") //never see this
556 }
557 return 9
558 }, "50ms", "10ms").ShouldNot(Equal(10))
559 Ω(ig.FailureMessage).Should(ContainSubstring("Error: Assertion in callback at %s:%d failed:", file, line+5))
560 Ω(ig.FailureMessage).Should(ContainSubstring("Expected\n <bool>: false\nto be true"))
561 })
562 })
563 })
564 })
565
566 Context("when passed a function that takes a Gomega argument and returns nothing", func() {
567 Context("with Eventually", func() {
568 It("returns the first failed assertion as an error and so should Succeed() if the callback ever runs without issue", func() {
569 counter := 0
570 ig.G.Eventually(func(g Gomega) {
571 counter += 1
572 if counter < 5 {
573 g.Expect(false).To(BeTrue())
574 g.Expect("bloop").To(Equal("blarp"))
575 }
576 }, "1s", "10ms").Should(Succeed())
577 Ω(counter).Should(Equal(5))
578 Ω(ig.FailureMessage).Should(BeZero())
579 })
580
581 It("returns the first failed assertion as an error and so should timeout if the callback always fails", func() {
582 counter := 0
583 ig.G.Eventually(func(g Gomega) {
584 counter += 1
585 if counter < 5000 {
586 g.Expect(false).To(BeTrue())
587 g.Expect("bloop").To(Equal("blarp"))
588 }
589 }, "100ms", "10ms").Should(Succeed())
590 Ω(counter).Should(BeNumerically(">", 1))
591 Ω(ig.FailureMessage).Should(ContainSubstring("Expected success, but got an error"))
592 Ω(ig.FailureMessage).Should(ContainSubstring("<bool>: false"))
593 Ω(ig.FailureMessage).Should(ContainSubstring("to be true"))
594 Ω(ig.FailureMessage).ShouldNot(ContainSubstring("bloop"))
595 })
596
597 It("returns the first failed assertion as an error and should satisy ShouldNot(Succeed) eventually", func() {
598 counter := 0
599 ig.G.Eventually(func(g Gomega) {
600 counter += 1
601 if counter > 5 {
602 g.Expect(false).To(BeTrue())
603 g.Expect("bloop").To(Equal("blarp"))
604 }
605 }, "100ms", "10ms").ShouldNot(Succeed())
606 Ω(counter).Should(Equal(6))
607 Ω(ig.FailureMessage).Should(BeZero())
608 })
609
610 It("should fail to ShouldNot(Succeed) eventually if an error never occurs", func() {
611 ig.G.Eventually(func(g Gomega) {
612 g.Expect(true).To(BeTrue())
613 }, "50ms", "10ms").ShouldNot(Succeed())
614 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after"))
615 Ω(ig.FailureMessage).Should(ContainSubstring("Expected failure, but got no error."))
616 })
617 })
618
619 Context("with Consistently", func() {
620 It("returns the first failed assertion as an error and so should Succeed() if the callback always runs without issue", func() {
621 counter := 0
622 ig.G.Consistently(func(g Gomega) {
623 counter += 1
624 g.Expect(true).To(BeTrue())
625 }, "50ms", "10ms").Should(Succeed())
626 Ω(counter).Should(BeNumerically(">", 2))
627 Ω(ig.FailureMessage).Should(BeZero())
628 })
629
630 It("returns the first failed assertion as an error and so should fail if the callback ever fails", func() {
631 counter := 0
632 ig.G.Consistently(func(g Gomega) {
633 counter += 1
634 g.Expect(true).To(BeTrue())
635 if counter == 3 {
636 g.Expect(false).To(BeTrue())
637 g.Expect("bloop").To(Equal("blarp"))
638 }
639 }, "50ms", "10ms").Should(Succeed())
640 Ω(ig.FailureMessage).Should(ContainSubstring("Expected success, but got an error"))
641 Ω(ig.FailureMessage).Should(ContainSubstring("<bool>: false"))
642 Ω(ig.FailureMessage).Should(ContainSubstring("to be true"))
643 Ω(ig.FailureMessage).ShouldNot(ContainSubstring("bloop"))
644 Ω(counter).Should(Equal(3))
645 })
646
647 It("returns the first failed assertion as an error and should satisy ShouldNot(Succeed) consistently if an error always occur", func() {
648 counter := 0
649 ig.G.Consistently(func(g Gomega) {
650 counter += 1
651 g.Expect(true).To(BeFalse())
652 }, "50ms", "10ms").ShouldNot(Succeed())
653 Ω(counter).Should(BeNumerically(">", 2))
654 Ω(ig.FailureMessage).Should(BeZero())
655 })
656
657 It("should fail to satisfy ShouldNot(Succeed) consistently if an error ever does not occur", func() {
658 counter := 0
659 ig.G.Consistently(func(g Gomega) {
660 counter += 1
661 if counter == 3 {
662 g.Expect(true).To(BeTrue())
663 } else {
664 g.Expect(false).To(BeTrue())
665 }
666 }, "50ms", "10ms").ShouldNot(Succeed())
667 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after"))
668 Ω(ig.FailureMessage).Should(ContainSubstring("Expected failure, but got no error."))
669 Ω(counter).Should(Equal(3))
670 })
671 })
672 })
673
674 Describe("when passed an invalid function", func() {
675 It("errors immediately", func() {
676 ig.G.Eventually(func() {})
677 Ω(ig.FailureMessage).Should(Equal("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 0 arguments and returns 0 values."))
678 Ω(ig.FailureSkip).Should(Equal([]int{4}))
679
680 ig = NewInstrumentedGomega()
681 ig.G.Eventually(func(g Gomega, foo string) {})
682 Ω(ig.FailureMessage).Should(Equal("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 2 arguments and returns 0 values."))
683 Ω(ig.FailureSkip).Should(Equal([]int{4}))
684
685 ig = NewInstrumentedGomega()
686 ig.G.Eventually(func(foo string) {})
687 Ω(ig.FailureMessage).Should(Equal("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 1 arguments and returns 0 values."))
688 Ω(ig.FailureSkip).Should(Equal([]int{4}))
689 })
690 })
691 })
692
693 Describe("when using OracleMatchers", func() {
694 It("stops and gives up with an appropriate failure message if the OracleMatcher says things can't change", func() {
695 c := make(chan bool)
696 close(c)
697
698 t := time.Now()
699 ig.G.Eventually(c, "100ms", "10ms").Should(Receive(), "Receive is an OracleMatcher that gives up if the channel is closed")
700 Ω(time.Since(t)).Should(BeNumerically("<", 90*time.Millisecond))
701 Ω(ig.FailureMessage).Should(ContainSubstring("No future change is possible."))
702 Ω(ig.FailureMessage).Should(ContainSubstring("The channel is closed."))
703 })
704
705 It("never gives up if actual is a function", func() {
706 c := make(chan bool)
707 close(c)
708
709 t := time.Now()
710 ig.G.Eventually(func() chan bool { return c }, "100ms", "10ms").Should(Receive(), "Receive is an OracleMatcher that gives up if the channel is closed")
711 Ω(time.Since(t)).Should(BeNumerically(">=", 90*time.Millisecond))
712 Ω(ig.FailureMessage).ShouldNot(ContainSubstring("No future change is possible."))
713 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after"))
714 })
715 })
716 })
+0
-235
internal/asyncassertion/async_assertion.go less more
0 // untested sections: 2
1
2 package asyncassertion
3
4 import (
5 "errors"
6 "fmt"
7 "reflect"
8 "runtime"
9 "time"
10
11 "github.com/onsi/gomega/internal/oraclematcher"
12 "github.com/onsi/gomega/types"
13 )
14
15 type AsyncAssertionType uint
16
17 const (
18 AsyncAssertionTypeEventually AsyncAssertionType = iota
19 AsyncAssertionTypeConsistently
20 )
21
22 type 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
31 func 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 {
35 panic("Expected a function with no arguments and zero 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
49 func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
50 assertion.failWrapper.TWithHelper.Helper()
51 return assertion.match(matcher, true, optionalDescription...)
52 }
53
54 func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
55 assertion.failWrapper.TWithHelper.Helper()
56 return assertion.match(matcher, false, optionalDescription...)
57 }
58
59 func (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
71 func (assertion *AsyncAssertion) actualInputIsAFunction() bool {
72 actualType := reflect.TypeOf(assertion.actualInput)
73 return actualType.Kind() == reflect.Func && actualType.NumIn() == 0
74 }
75
76 func (assertion *AsyncAssertion) pollActual() (interface{}, error) {
77 if !assertion.actualInputIsAFunction() {
78 return assertion.actualInput, nil
79 }
80 var capturedAssertionFailure string
81 var values []reflect.Value
82
83 numOut := reflect.TypeOf(assertion.actualInput).NumOut()
84
85 func() {
86 originalHandler := assertion.failWrapper.Fail
87 assertion.failWrapper.Fail = func(message string, callerSkip ...int) {
88 skip := 0
89 if len(callerSkip) > 0 {
90 skip = callerSkip[0]
91 }
92 _, file, line, _ := runtime.Caller(skip + 1)
93 capturedAssertionFailure = fmt.Sprintf("Assertion in callback at %s:%d failed:\n%s", file, line, message)
94 panic("stop execution")
95 }
96
97 defer func() {
98 assertion.failWrapper.Fail = originalHandler
99 if e := recover(); e != nil && capturedAssertionFailure == "" {
100 panic(e)
101 }
102 }()
103
104 values = reflect.ValueOf(assertion.actualInput).Call([]reflect.Value{})
105 }()
106
107 if capturedAssertionFailure != "" {
108 if numOut == 0 {
109 return errors.New(capturedAssertionFailure), nil
110 } else {
111 return nil, errors.New(capturedAssertionFailure)
112 }
113 }
114
115 if numOut > 0 {
116 extras := []interface{}{}
117 for _, value := range values[1:] {
118 extras = append(extras, value.Interface())
119 }
120
121 success, message := vetExtras(extras)
122
123 if !success {
124 return nil, errors.New(message)
125 }
126
127 return values[0].Interface(), nil
128 }
129
130 return nil, nil
131 }
132
133 func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool {
134 if assertion.actualInputIsAFunction() {
135 return true
136 }
137
138 return oraclematcher.MatchMayChangeInTheFuture(matcher, value)
139 }
140
141 func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
142 timer := time.Now()
143 timeout := time.After(assertion.timeoutInterval)
144
145 var matches bool
146 var err error
147 mayChange := true
148 value, err := assertion.pollActual()
149 if err == nil {
150 mayChange = assertion.matcherMayChange(matcher, value)
151 matches, err = matcher.Match(value)
152 }
153
154 assertion.failWrapper.TWithHelper.Helper()
155
156 fail := func(preamble string) {
157 errMsg := ""
158 message := ""
159 if err != nil {
160 errMsg = "Error: " + err.Error()
161 } else {
162 if desiredMatch {
163 message = matcher.FailureMessage(value)
164 } else {
165 message = matcher.NegatedFailureMessage(value)
166 }
167 }
168 assertion.failWrapper.TWithHelper.Helper()
169 description := assertion.buildDescription(optionalDescription...)
170 assertion.failWrapper.Fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset)
171 }
172
173 if assertion.asyncType == AsyncAssertionTypeEventually {
174 for {
175 if err == nil && matches == desiredMatch {
176 return true
177 }
178
179 if !mayChange {
180 fail("No future change is possible. Bailing out early")
181 return false
182 }
183
184 select {
185 case <-time.After(assertion.pollingInterval):
186 value, err = assertion.pollActual()
187 if err == nil {
188 mayChange = assertion.matcherMayChange(matcher, value)
189 matches, err = matcher.Match(value)
190 }
191 case <-timeout:
192 fail("Timed out")
193 return false
194 }
195 }
196 } else if assertion.asyncType == AsyncAssertionTypeConsistently {
197 for {
198 if !(err == nil && matches == desiredMatch) {
199 fail("Failed")
200 return false
201 }
202
203 if !mayChange {
204 return true
205 }
206
207 select {
208 case <-time.After(assertion.pollingInterval):
209 value, err = assertion.pollActual()
210 if err == nil {
211 mayChange = assertion.matcherMayChange(matcher, value)
212 matches, err = matcher.Match(value)
213 }
214 case <-timeout:
215 return true
216 }
217 }
218 }
219
220 return false
221 }
222
223 func vetExtras(extras []interface{}) (bool, string) {
224 for i, extra := range extras {
225 if extra != nil {
226 zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface()
227 if !reflect.DeepEqual(zeroValue, extra) {
228 message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra)
229 return false, message
230 }
231 }
232 }
233 return true, ""
234 }
+0
-13
internal/asyncassertion/async_assertion_suite_test.go less more
0 package asyncassertion_test
1
2 import (
3 . "github.com/onsi/ginkgo"
4 . "github.com/onsi/gomega"
5
6 "testing"
7 )
8
9 func TestAsyncAssertion(t *testing.T) {
10 RegisterFailHandler(Fail)
11 RunSpecs(t, "AsyncAssertion Suite")
12 }
+0
-489
internal/asyncassertion/async_assertion_test.go less more
0 package asyncassertion_test
1
2 import (
3 "errors"
4 "runtime"
5 "time"
6
7 "github.com/onsi/gomega/internal/testingtsupport"
8
9 . "github.com/onsi/ginkgo"
10 . "github.com/onsi/gomega"
11 "github.com/onsi/gomega/internal/asyncassertion"
12 "github.com/onsi/gomega/types"
13 )
14
15 var _ = Describe("Async Assertion", func() {
16 var (
17 failureMessage string
18 callerSkip int
19 )
20
21 var fakeFailWrapper = &types.GomegaFailWrapper{
22 Fail: func(message string, skip ...int) {
23 failureMessage = message
24 callerSkip = skip[0]
25 },
26 TWithHelper: testingtsupport.EmptyTWithHelper{},
27 }
28
29 BeforeEach(func() {
30 failureMessage = ""
31 callerSkip = 0
32 })
33
34 Describe("Eventually", func() {
35 Context("the positive case", func() {
36 It("should poll the function and matcher", func() {
37 counter := 0
38 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() int {
39 counter++
40 return counter
41 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
42
43 a.Should(BeNumerically("==", 5))
44 Expect(failureMessage).Should(BeZero())
45 })
46
47 It("should continue when the matcher errors", func() {
48 counter := 0
49 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() interface{} {
50 counter++
51 if counter == 5 {
52 return "not-a-number" //this should cause the matcher to error
53 }
54 return counter
55 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
56
57 a.Should(BeNumerically("==", 5), "My description %d", 2)
58
59 Expect(failureMessage).Should(ContainSubstring("Timed out after"))
60 Expect(failureMessage).Should(ContainSubstring("My description 2"))
61 Expect(callerSkip).Should(Equal(4))
62 })
63
64 It("should be able to timeout", func() {
65 counter := 0
66 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() int {
67 counter++
68 return counter
69 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
70
71 a.Should(BeNumerically(">", 100), "My description %d", 2)
72
73 Expect(counter).Should(BeNumerically(">", 8))
74 Expect(counter).Should(BeNumerically("<=", 10))
75 Expect(failureMessage).Should(ContainSubstring("Timed out after"))
76 Expect(failureMessage).Should(MatchRegexp(`\<int\>: \d`), "Should pass the correct value to the matcher message formatter.")
77 Expect(failureMessage).Should(ContainSubstring("My description 2"))
78 Expect(callerSkip).Should(Equal(4))
79 })
80
81 When("the optional description is a function", func() {
82 It("should append the description to the failure message", func() {
83 counter := 0
84 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() interface{} {
85 counter++
86 if counter == 5 {
87 return "not-a-number" //this should cause the matcher to error
88 }
89 return counter
90 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
91
92 a.Should(BeNumerically("==", 5), func() string { return "My description" })
93
94 Expect(failureMessage).Should(ContainSubstring("Timed out after"))
95 Expect(failureMessage).Should(ContainSubstring("My description"))
96 Expect(callerSkip).Should(Equal(4))
97 })
98
99 Context("and there is no failure", func() {
100 It("should not evaluate that function", func() {
101 counter := 0
102 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() int {
103 counter++
104 return counter
105 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
106
107 evaluated := false
108 a.Should(BeNumerically("==", 5), func() string {
109 evaluated = true
110 return "A description"
111 })
112
113 Expect(failureMessage).Should(BeZero())
114 Expect(evaluated).Should(BeFalse())
115 })
116 })
117 })
118 })
119
120 Context("the negative case", func() {
121 It("should poll the function and matcher", func() {
122 counter := 0
123 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() int {
124 counter += 1
125 return counter
126 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
127
128 a.ShouldNot(BeNumerically("<", 3))
129
130 Expect(counter).Should(Equal(3))
131 Expect(failureMessage).Should(BeZero())
132 })
133
134 It("should timeout when the matcher errors", func() {
135 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() interface{} {
136 return 0 //this should cause the matcher to error
137 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
138
139 a.ShouldNot(HaveLen(0), "My description %d", 2)
140
141 Expect(failureMessage).Should(ContainSubstring("Timed out after"))
142 Expect(failureMessage).Should(ContainSubstring("Error:"))
143 Expect(failureMessage).Should(ContainSubstring("My description 2"))
144 Expect(callerSkip).Should(Equal(4))
145 })
146
147 It("should be able to timeout", func() {
148 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() int {
149 return 0
150 }, fakeFailWrapper, time.Duration(0.1*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
151
152 a.ShouldNot(Equal(0), "My description %d", 2)
153
154 Expect(failureMessage).Should(ContainSubstring("Timed out after"))
155 Expect(failureMessage).Should(ContainSubstring("<int>: 0"), "Should pass the correct value to the matcher message formatter.")
156 Expect(failureMessage).Should(ContainSubstring("My description 2"))
157 Expect(callerSkip).Should(Equal(4))
158 })
159 })
160
161 Context("with a function that returns multiple values", func() {
162 It("should eventually succeed if the additional arguments are nil", func() {
163 i := 0
164 Eventually(func() (int, error) {
165 i++
166 return i, nil
167 }).Should(Equal(10))
168 })
169
170 It("should eventually timeout if the additional arguments are not nil", func() {
171 i := 0
172 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() (int, error) {
173 i++
174 return i, errors.New("bam")
175 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
176 a.Should(Equal(2))
177
178 Expect(failureMessage).Should(ContainSubstring("Timed out after"))
179 Expect(failureMessage).Should(ContainSubstring("Error:"))
180 Expect(failureMessage).Should(ContainSubstring("bam"))
181 Expect(callerSkip).Should(Equal(4))
182 })
183 })
184
185 Context("when the polled function makes assertions", func() {
186 It("fails if those assertions never succeed", func() {
187 var file string
188 var line int
189 err := InterceptGomegaFailure(func() {
190 i := 0
191 Eventually(func() int {
192 _, file, line, _ = runtime.Caller(0)
193 Expect(i).To(BeNumerically(">", 5))
194 return 2
195 }, 200*time.Millisecond, 20*time.Millisecond).Should(Equal(2))
196 })
197 Ω(err.Error()).Should(ContainSubstring("Timed out after"))
198 Ω(err.Error()).Should(ContainSubstring("Assertion in callback at %s:%d failed:", file, line+1))
199 Ω(err.Error()).Should(ContainSubstring("to be >"))
200 })
201
202 It("eventually succeeds if the assertions succeed", func() {
203 err := InterceptGomegaFailure(func() {
204 i := 0
205 Eventually(func() int {
206 i++
207 Expect(i).To(BeNumerically(">", 5))
208 return 2
209 }, 200*time.Millisecond, 20*time.Millisecond).Should(Equal(2))
210 })
211 Ω(err).ShouldNot(HaveOccurred())
212 })
213
214 It("succeeds if the assertions succeed even if the function doesn't return anything", func() {
215 i := 0
216 Eventually(func() {
217 i++
218 Expect(i).To(BeNumerically(">", 5))
219 }, 200*time.Millisecond, 20*time.Millisecond).Should(Succeed())
220 })
221
222 It("succeeds if the function returns nothing, the assertions eventually fail and the Eventually is assertion that it ShouldNot(Succeed()) ", func() {
223 i := 0
224 Eventually(func() {
225 i++
226 Expect(i).To(BeNumerically("<", 5))
227 }, 200*time.Millisecond, 20*time.Millisecond).ShouldNot(Succeed())
228 })
229 })
230
231 Context("Making an assertion without a registered fail handler", func() {
232 It("should panic", func() {
233 defer func() {
234 e := recover()
235 RegisterFailHandler(Fail)
236 if e == nil {
237 Fail("expected a panic to have occurred")
238 }
239 }()
240
241 RegisterFailHandler(nil)
242 c := make(chan bool, 1)
243 c <- true
244 Eventually(c).Should(Receive())
245 })
246 })
247 })
248
249 Describe("Consistently", func() {
250 Describe("The positive case", func() {
251 When("the matcher consistently passes for the duration", func() {
252 It("should pass", func() {
253 calls := 0
254 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, func() string {
255 calls++
256 return "foo"
257 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
258
259 a.Should(Equal("foo"))
260 Expect(calls).Should(BeNumerically(">", 8))
261 Expect(calls).Should(BeNumerically("<=", 10))
262 Expect(failureMessage).Should(BeZero())
263 })
264 })
265
266 When("the matcher fails at some point", func() {
267 It("should fail", func() {
268 calls := 0
269 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, func() interface{} {
270 calls++
271 if calls > 5 {
272 return "bar"
273 }
274 return "foo"
275 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
276
277 a.Should(Equal("foo"))
278 Expect(failureMessage).Should(ContainSubstring("to equal"))
279 Expect(callerSkip).Should(Equal(4))
280 })
281 })
282
283 When("the matcher errors at some point", func() {
284 It("should fail", func() {
285 calls := 0
286 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, func() interface{} {
287 calls++
288 if calls > 5 {
289 return 3
290 }
291 return []int{1, 2, 3}
292 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
293
294 a.Should(HaveLen(3))
295 Expect(failureMessage).Should(ContainSubstring("HaveLen matcher expects"))
296 Expect(callerSkip).Should(Equal(4))
297 })
298 })
299 })
300
301 Describe("The negative case", func() {
302 When("the matcher consistently passes for the duration", func() {
303 It("should pass", func() {
304 c := make(chan bool)
305 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, c, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
306
307 a.ShouldNot(Receive())
308 Expect(failureMessage).Should(BeZero())
309 })
310 })
311
312 When("the matcher fails at some point", func() {
313 It("should fail", func() {
314 c := make(chan bool)
315 go func() {
316 time.Sleep(time.Duration(100 * time.Millisecond))
317 c <- true
318 }()
319
320 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, c, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
321
322 a.ShouldNot(Receive())
323 Expect(failureMessage).Should(ContainSubstring("not to receive anything"))
324 })
325 })
326
327 When("the matcher errors at some point", func() {
328 It("should fail", func() {
329 calls := 0
330 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, func() interface{} {
331 calls++
332 return calls
333 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
334
335 a.ShouldNot(BeNumerically(">", 5))
336 Expect(failureMessage).Should(ContainSubstring("not to be >"))
337 Expect(callerSkip).Should(Equal(4))
338 })
339 })
340 })
341
342 Context("with a function that returns multiple values", func() {
343 It("should consistently succeed if the additional arguments are nil", func() {
344 i := 2
345 Consistently(func() (int, error) {
346 i++
347 return i, nil
348 }).Should(BeNumerically(">=", 2))
349 })
350
351 It("should eventually timeout if the additional arguments are not nil", func() {
352 i := 2
353 a := asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() (int, error) {
354 i++
355 return i, errors.New("bam")
356 }, fakeFailWrapper, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1)
357 a.Should(BeNumerically(">=", 2))
358
359 Expect(failureMessage).Should(ContainSubstring("Error:"))
360 Expect(failureMessage).Should(ContainSubstring("bam"))
361 Expect(callerSkip).Should(Equal(4))
362 })
363 })
364
365 Context("when the polled function makes assertions", func() {
366 It("fails if those assertions ever fail", func() {
367 var file string
368 var line int
369
370 err := InterceptGomegaFailure(func() {
371 i := 0
372 Consistently(func() int {
373 _, file, line, _ = runtime.Caller(0)
374 Expect(i).To(BeNumerically("<", 5))
375 i++
376 return 2
377 }, 200*time.Millisecond, 20*time.Millisecond).Should(Equal(2))
378 })
379 Ω(err.Error()).Should(ContainSubstring("Failed after"))
380 Ω(err.Error()).Should(ContainSubstring("Assertion in callback at %s:%d failed:", file, line+1))
381 Ω(err.Error()).Should(ContainSubstring("to be <"))
382 })
383
384 It("succeeds if the assertion consistently succeeds", func() {
385 err := InterceptGomegaFailure(func() {
386 i := 0
387 Consistently(func() int {
388 i++
389 Expect(i).To(BeNumerically("<", 1000))
390 return 2
391 }, 200*time.Millisecond, 20*time.Millisecond).Should(Equal(2))
392 })
393 Ω(err).ShouldNot(HaveOccurred())
394 })
395
396 It("succeeds if the assertions succeed even if the function doesn't return anything", func() {
397 i := 0
398 Consistently(func() {
399 i++
400 Expect(i).To(BeNumerically("<", 1000))
401 }, 200*time.Millisecond, 20*time.Millisecond).Should(Succeed())
402 })
403
404 It("succeeds if the assertions fail even if the function doesn't return anything and Consistently is checking for ShouldNot(Succeed())", func() {
405 i := 0
406 Consistently(func() {
407 i++
408 Expect(i).To(BeNumerically(">", 1000))
409 }, 200*time.Millisecond, 20*time.Millisecond).ShouldNot(Succeed())
410 })
411
412 })
413
414 Context("Making an assertion without a registered fail handler", func() {
415 It("should panic", func() {
416 defer func() {
417 e := recover()
418 RegisterFailHandler(Fail)
419 if e == nil {
420 Fail("expected a panic to have occurred")
421 }
422 }()
423
424 RegisterFailHandler(nil)
425 c := make(chan bool)
426 Consistently(c).ShouldNot(Receive())
427 })
428 })
429 })
430
431 When("passed a function with the wrong # or arguments", func() {
432 It("should panic", func() {
433 Expect(func() {
434 asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() {}, fakeFailWrapper, 0, 0, 1)
435 }).ShouldNot(Panic())
436
437 Expect(func() {
438 asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func(a string) int { return 0 }, fakeFailWrapper, 0, 0, 1)
439 }).Should(Panic())
440
441 Expect(func() {
442 asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func(a string) {}, fakeFailWrapper, 0, 0, 1)
443 }).Should(Panic())
444
445 Expect(func() {
446 asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() int { return 0 }, fakeFailWrapper, 0, 0, 1)
447 }).ShouldNot(Panic())
448
449 Expect(func() {
450 asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, func() (int, error) { return 0, nil }, fakeFailWrapper, 0, 0, 1)
451 }).ShouldNot(Panic())
452 })
453 })
454
455 Describe("bailing early", func() {
456 When("actual is a value", func() {
457 It("Eventually should bail out and fail early if the matcher says to", func() {
458 c := make(chan bool)
459 close(c)
460
461 t := time.Now()
462 failures := InterceptGomegaFailures(func() {
463 Eventually(c, 0.1).Should(Receive())
464 })
465 Expect(time.Since(t)).Should(BeNumerically("<", 90*time.Millisecond))
466
467 Expect(failures).Should(HaveLen(1))
468 })
469 })
470
471 When("actual is a function", func() {
472 It("should never bail early", func() {
473 c := make(chan bool)
474 close(c)
475
476 t := time.Now()
477 failures := InterceptGomegaFailures(func() {
478 Eventually(func() chan bool {
479 return c
480 }, 0.1).Should(Receive())
481 })
482 Expect(time.Since(t)).Should(BeNumerically(">=", 90*time.Millisecond))
483
484 Expect(failures).Should(HaveLen(1))
485 })
486 })
487 })
488 })
+0
-13
internal/defaults/defaults_suite_test.go less more
0 package defaults_test
1
2 import (
3 "testing"
4
5 . "github.com/onsi/ginkgo"
6 . "github.com/onsi/gomega"
7 )
8
9 func TestDefaults(t *testing.T) {
10 RegisterFailHandler(Fail)
11 RunSpecs(t, "Gomega Defaults Suite")
12 }
+0
-22
internal/defaults/env.go less more
0 package defaults
1
2 import (
3 "fmt"
4 "time"
5 )
6
7 func SetDurationFromEnv(getDurationFromEnv func(string) string, varSetter func(time.Duration), name string) {
8 durationFromEnv := getDurationFromEnv(name)
9
10 if len(durationFromEnv) == 0 {
11 return
12 }
13
14 duration, err := time.ParseDuration(durationFromEnv)
15
16 if err != nil {
17 panic(fmt.Sprintf("Expected a duration when using %s! Parse error %v", name, err))
18 }
19
20 varSetter(duration)
21 }
+0
-81
internal/defaults/env_test.go less more
0 package defaults_test
1
2 import (
3 "time"
4
5 . "github.com/onsi/ginkgo"
6 . "github.com/onsi/gomega"
7
8 d "github.com/onsi/gomega/internal/defaults"
9 )
10
11 var _ = Describe("Durations", func() {
12 var (
13 duration *time.Duration
14 envVarGot string
15 envVarToReturn string
16
17 getDurationFromEnv = func(name string) string {
18 envVarGot = name
19 return envVarToReturn
20 }
21
22 setDuration = func(t time.Duration) {
23 duration = &t
24 }
25
26 setDurationCalled = func() bool {
27 return duration != nil
28 }
29
30 resetDuration = func() {
31 duration = nil
32 }
33 )
34
35 BeforeEach(func() {
36 resetDuration()
37 })
38
39 Context("When the environment has a duration", func() {
40 Context("When the duration is valid", func() {
41 BeforeEach(func() {
42 envVarToReturn = "10m"
43
44 d.SetDurationFromEnv(getDurationFromEnv, setDuration, "MY_ENV_VAR")
45 })
46
47 It("sets the duration", func() {
48 Expect(envVarGot).To(Equal("MY_ENV_VAR"))
49 Expect(setDurationCalled()).To(Equal(true))
50 Expect(*duration).To(Equal(10 * time.Minute))
51 })
52 })
53
54 Context("When the duration is not valid", func() {
55 BeforeEach(func() {
56 envVarToReturn = "10"
57 })
58
59 It("panics with a helpful error message", func() {
60 Expect(func() {
61 d.SetDurationFromEnv(getDurationFromEnv, setDuration, "MY_ENV_VAR")
62 }).To(PanicWith(MatchRegexp("Expected a duration when using MY_ENV_VAR")))
63 })
64 })
65 })
66
67 Context("When the environment does not have a duration", func() {
68 BeforeEach(func() {
69 envVarToReturn = ""
70
71 d.SetDurationFromEnv(getDurationFromEnv, setDuration, "MY_ENV_VAR")
72 })
73
74 It("does not set the duration", func() {
75 Expect(envVarGot).To(Equal("MY_ENV_VAR"))
76 Expect(setDurationCalled()).To(Equal(false))
77 Expect(duration).To(BeNil())
78 })
79 })
80 })
11
22 import (
33 "errors"
4 "runtime"
5 "time"
46
57 . "github.com/onsi/ginkgo"
68 . "github.com/onsi/gomega"
9 "github.com/onsi/gomega/internal"
710 )
811
12 func getGlobalDurationBundle() internal.DurationBundle {
13 return Default.(*internal.Gomega).DurationBundle
14 }
15
16 func setGlobalDurationBundle(bundle internal.DurationBundle) {
17 SetDefaultEventuallyTimeout(bundle.EventuallyTimeout)
18 SetDefaultEventuallyPollingInterval(bundle.EventuallyPollingInterval)
19 SetDefaultConsistentlyDuration(bundle.ConsistentlyDuration)
20 SetDefaultConsistentlyPollingInterval(bundle.ConsistentlyPollingInterval)
21 }
22
923 var _ = Describe("Gomega DSL", func() {
24 var globalDurationBundle internal.DurationBundle
25
26 BeforeEach(func() {
27 globalDurationBundle = getGlobalDurationBundle()
28 })
29
30 AfterEach(func() {
31 RegisterFailHandler(Fail)
32 setGlobalDurationBundle(globalDurationBundle)
33 })
34
35 Describe("The Default, global, Gomega", func() {
36 It("exists", func() {
37 Ω(Default).ShouldNot(BeNil())
38 })
39
40 It("is wired up via the global DSL", func() {
41 counter := 0
42 Eventually(func() int {
43 counter += 1
44 return counter
45 }).Should(Equal(5))
46 Ω(counter).Should(Equal(5))
47 })
48 })
49
50 Describe("NewGomega", func() {
51 It("creates and configures a new Gomega, using the global duration bundle", func() {
52 bundle := internal.DurationBundle{
53 EventuallyTimeout: time.Minute,
54 EventuallyPollingInterval: 2 * time.Minute,
55 ConsistentlyDuration: 3 * time.Minute,
56 ConsistentlyPollingInterval: 4 * time.Minute,
57 }
58 setGlobalDurationBundle(bundle)
59
60 var calledWith string
61 g := NewGomega(func(message string, skip ...int) {
62 calledWith = message
63 })
64
65 gAsStruct := g.(*internal.Gomega)
66 Ω(gAsStruct.DurationBundle).Should(Equal(bundle))
67
68 g.Ω(true).Should(BeFalse())
69 Ω(calledWith).Should(Equal("Expected\n <bool>: true\nto be false"))
70 })
71 })
72
73 Describe("NewWithT", func() {
74 It("creates and configure a new Gomega with the passed-in T, using the global duration bundle", func() {
75 bundle := internal.DurationBundle{
76 EventuallyTimeout: time.Minute,
77 EventuallyPollingInterval: 2 * time.Minute,
78 ConsistentlyDuration: 3 * time.Minute,
79 ConsistentlyPollingInterval: 4 * time.Minute,
80 }
81 setGlobalDurationBundle(bundle)
82
83 fakeT := &FakeGomegaTestingT{}
84 g := NewWithT(fakeT)
85
86 Ω(g.DurationBundle).Should(Equal(bundle))
87
88 g.Ω(true).Should(BeFalse())
89 Ω(fakeT.CalledFatalf).Should(Equal("\nExpected\n <bool>: true\nto be false"))
90 Ω(fakeT.CalledHelper).Should(BeTrue())
91 })
92 })
93
94 Describe("RegisterFailHandler", func() {
95 It("overrides the global fail handler", func() {
96 var calledWith string
97 RegisterFailHandler(func(message string, skip ...int) {
98 calledWith = message
99 })
100
101 Ω(true).Should(BeFalse())
102
103 RegisterFailHandler(Fail)
104 Ω(calledWith).Should(Equal("Expected\n <bool>: true\nto be false"))
105 })
106 })
107
108 Describe("RegisterTestingT", func() {
109 It("overrides the global fail handler", func() {
110 fakeT := &FakeGomegaTestingT{}
111 RegisterTestingT(fakeT)
112
113 Ω(true).Should(BeFalse())
114 RegisterFailHandler(Fail)
115 Ω(fakeT.CalledFatalf).Should(Equal("\nExpected\n <bool>: true\nto be false"))
116 Ω(fakeT.CalledHelper).Should(BeTrue())
117 })
118 })
119
10120 Describe("InterceptGomegaFailures", func() {
11121 Context("when no failures occur", func() {
12122 It("returns an empty array", func() {
61171 })
62172 })
63173 })
174
175 Context("Making an assertion without a registered fail handler", func() {
176 It("should panic", func() {
177 defer func() {
178 e := recover()
179 RegisterFailHandler(Fail)
180 if e == nil {
181 Fail("expected a panic to have occurred")
182 }
183 }()
184
185 RegisterFailHandler(nil)
186 Expect(true).Should(BeTrue())
187 })
188 })
189
190 Describe("specifying default durations globally", func() {
191 It("should update the durations on the Default gomega", func() {
192 bundle := internal.DurationBundle{
193 EventuallyTimeout: time.Minute,
194 EventuallyPollingInterval: 2 * time.Minute,
195 ConsistentlyDuration: 3 * time.Minute,
196 ConsistentlyPollingInterval: 4 * time.Minute,
197 }
198
199 SetDefaultEventuallyTimeout(bundle.EventuallyTimeout)
200 SetDefaultEventuallyPollingInterval(bundle.EventuallyPollingInterval)
201 SetDefaultConsistentlyDuration(bundle.ConsistentlyDuration)
202 SetDefaultConsistentlyPollingInterval(bundle.ConsistentlyPollingInterval)
203
204 Ω(Default.(*internal.Gomega).DurationBundle).Should(Equal(bundle))
205 })
206 })
207
208 Describe("Offsets", func() {
209 AfterEach(func() {
210 RegisterFailHandler(Fail)
211 })
212
213 It("computes the correct offsets", func() {
214 doubleNested := func(eventually bool) {
215 func() {
216 if eventually {
217 EventuallyWithOffset(2, true, "10ms", "5ms").Should(BeFalse())
218 } else {
219 ExpectWithOffset(2, true).To(BeFalse())
220 }
221 }()
222 }
223
224 reportedFile, reportedLine := "", 0
225 captureLocation := func(message string, skip ...int) {
226 _, reportedFile, reportedLine, _ = runtime.Caller(skip[0] + 1)
227 }
228
229 _, thisFile, anchorLine, _ := runtime.Caller(0) // 0
230 RegisterFailHandler(captureLocation) // 1
231 Expect(true).To(BeFalse()) // *2*
232 RegisterFailHandler(Fail) // 3
233 Ω(reportedFile).Should(Equal(thisFile)) // 4
234 Ω(reportedLine - anchorLine).Should(Equal(2)) // 5
235 RegisterFailHandler(captureLocation) // 6
236 doubleNested(false) // *7*
237 RegisterFailHandler(Fail) // 8
238 Ω(reportedFile).Should(Equal(thisFile)) // 9
239 Ω(reportedLine - anchorLine).Should(Equal(7)) // 10
240 RegisterFailHandler(captureLocation) // 11
241 Eventually(true, "10ms", "5ms").Should(BeFalse()) // *12*
242 RegisterFailHandler(Fail) // 13
243 Ω(reportedFile).Should(Equal(thisFile)) // 14
244 Ω(reportedLine - anchorLine).Should(Equal(12)) // 15
245 RegisterFailHandler(captureLocation) // 16
246 doubleNested(true) // *17*
247 RegisterFailHandler(Fail) // 18
248 Ω(reportedFile).Should(Equal(thisFile)) // 19
249 Ω(reportedLine - anchorLine).Should(Equal(17)) // 20
250 })
251 })
64252 })
0 package internal
1
2 import (
3 "fmt"
4 "os"
5 "reflect"
6 "time"
7 )
8
9 type DurationBundle struct {
10 EventuallyTimeout time.Duration
11 EventuallyPollingInterval time.Duration
12 ConsistentlyDuration time.Duration
13 ConsistentlyPollingInterval time.Duration
14 }
15
16 const (
17 EventuallyTimeoutEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_TIMEOUT"
18 EventuallyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_POLLING_INTERVAL"
19
20 ConsistentlyDurationEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_DURATION"
21 ConsistentlyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_POLLING_INTERVAL"
22 )
23
24 func FetchDefaultDurationBundle() DurationBundle {
25 return DurationBundle{
26 EventuallyTimeout: durationFromEnv(EventuallyTimeoutEnvVarName, time.Second),
27 EventuallyPollingInterval: durationFromEnv(EventuallyPollingIntervalEnvVarName, 10*time.Millisecond),
28
29 ConsistentlyDuration: durationFromEnv(ConsistentlyDurationEnvVarName, 100*time.Millisecond),
30 ConsistentlyPollingInterval: durationFromEnv(ConsistentlyPollingIntervalEnvVarName, 10*time.Millisecond),
31 }
32 }
33
34 func durationFromEnv(key string, defaultDuration time.Duration) time.Duration {
35 value := os.Getenv(key)
36 if value == "" {
37 return defaultDuration
38 }
39 duration, err := time.ParseDuration(value)
40 if err != nil {
41 panic(fmt.Sprintf("Expected a duration when using %s! Parse error %v", key, err))
42 }
43 return duration
44 }
45
46 func toDuration(input interface{}) time.Duration {
47 duration, ok := input.(time.Duration)
48 if ok {
49 return duration
50 }
51
52 value := reflect.ValueOf(input)
53 kind := reflect.TypeOf(input).Kind()
54
55 if reflect.Int <= kind && kind <= reflect.Int64 {
56 return time.Duration(value.Int()) * time.Second
57 } else if reflect.Uint <= kind && kind <= reflect.Uint64 {
58 return time.Duration(value.Uint()) * time.Second
59 } else if reflect.Float32 <= kind && kind <= reflect.Float64 {
60 return time.Duration(value.Float() * float64(time.Second))
61 } else if reflect.String == kind {
62 duration, err := time.ParseDuration(value.String())
63 if err != nil {
64 panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input))
65 }
66 return duration
67 }
68
69 panic(fmt.Sprintf("%v is not a valid interval. Must be time.Duration, parsable duration string or a number.", input))
70 }
0 package internal_test
1
2 import (
3 "os"
4 "time"
5
6 . "github.com/onsi/ginkgo"
7 . "github.com/onsi/gomega"
8
9 "github.com/onsi/gomega/internal"
10 )
11
12 var _ = Describe("DurationBundle and Duration Support", func() {
13 Describe("fetching default durations from the environment", func() {
14 var envVars []string
15 var originalValues map[string]string
16
17 BeforeEach(func() {
18 envVars = []string{internal.EventuallyTimeoutEnvVarName, internal.EventuallyPollingIntervalEnvVarName, internal.ConsistentlyDurationEnvVarName, internal.ConsistentlyPollingIntervalEnvVarName}
19 originalValues = map[string]string{}
20
21 for _, envVar := range envVars {
22 originalValues[envVar] = os.Getenv(envVar)
23 }
24 })
25
26 AfterEach(func() {
27 for _, envVar := range envVars {
28 Ω(os.Setenv(envVar, originalValues[envVar])).Should(Succeed())
29 }
30 })
31
32 Context("with no environment set", func() {
33 BeforeEach(func() {
34 for _, envVar := range envVars {
35 os.Unsetenv(envVar)
36 }
37 })
38
39 It("returns the default bundle", func() {
40 bundle := internal.FetchDefaultDurationBundle()
41 Ω(bundle.EventuallyTimeout).Should(Equal(time.Second))
42 Ω(bundle.EventuallyPollingInterval).Should(Equal(10 * time.Millisecond))
43 Ω(bundle.ConsistentlyDuration).Should(Equal(100 * time.Millisecond))
44 Ω(bundle.ConsistentlyPollingInterval).Should(Equal(10 * time.Millisecond))
45 })
46 })
47
48 Context("with a valid environment set", func() {
49 BeforeEach(func() {
50 os.Setenv(internal.EventuallyTimeoutEnvVarName, "1m")
51 os.Setenv(internal.EventuallyPollingIntervalEnvVarName, "2s")
52 os.Setenv(internal.ConsistentlyDurationEnvVarName, "1h")
53 os.Setenv(internal.ConsistentlyPollingIntervalEnvVarName, "3ms")
54 })
55
56 It("returns an appropriate bundle", func() {
57 bundle := internal.FetchDefaultDurationBundle()
58 Ω(bundle.EventuallyTimeout).Should(Equal(time.Minute))
59 Ω(bundle.EventuallyPollingInterval).Should(Equal(2 * time.Second))
60 Ω(bundle.ConsistentlyDuration).Should(Equal(time.Hour))
61 Ω(bundle.ConsistentlyPollingInterval).Should(Equal(3 * time.Millisecond))
62 })
63 })
64
65 Context("with an invalid environment set", func() {
66 BeforeEach(func() {
67 os.Setenv(internal.EventuallyTimeoutEnvVarName, "chicken nuggets")
68 })
69
70 It("panics", func() {
71 Ω(func() {
72 internal.FetchDefaultDurationBundle()
73 }).Should(PanicWith(`Expected a duration when using GOMEGA_DEFAULT_EVENTUALLY_TIMEOUT! Parse error time: invalid duration "chicken nuggets"`))
74 })
75 })
76 })
77
78 Describe("specifying default durations on a Gomega instance", func() {
79 It("is supported", func() {
80 ig := NewInstrumentedGomega()
81 ig.G.SetDefaultConsistentlyDuration(50 * time.Millisecond)
82 ig.G.SetDefaultConsistentlyPollingInterval(5 * time.Millisecond)
83 ig.G.SetDefaultEventuallyTimeout(200 * time.Millisecond)
84 ig.G.SetDefaultEventuallyPollingInterval(20 * time.Millisecond)
85
86 counter := 0
87 t := time.Now()
88 ig.G.Consistently(func() bool {
89 counter += 1
90 return true
91 }).Should(BeTrue())
92 dt := time.Since(t)
93 Ω(dt).Should(BeNumerically("~", 50*time.Millisecond, 25*time.Millisecond))
94 Ω(counter).Should(BeNumerically("~", 10, 5))
95
96 t = time.Now()
97 counter = 0
98 ig.G.Eventually(func() bool {
99 counter += 1
100 if counter >= 6 {
101 return true
102 }
103 return false
104 }).Should(BeTrue())
105 dt = time.Since(t)
106 Ω(dt).Should(BeNumerically("~", 120*time.Millisecond, 20*time.Millisecond))
107 })
108 })
109
110 Describe("specifying durations", func() {
111 It("supports passing in a duration", func() {
112 t := time.Now()
113 Consistently(true, 50*time.Millisecond).Should(BeTrue())
114 Ω(time.Since(t)).Should(BeNumerically("~", 50*time.Millisecond, 30*time.Millisecond))
115 })
116
117 It("supports passing in a raw integer # of seconds", func() {
118 t := time.Now()
119 Consistently(true, 1).Should(BeTrue())
120 Ω(time.Since(t)).Should(BeNumerically("~", time.Second, 100*time.Millisecond))
121 })
122
123 It("supports passing in an unsigned integer # of seconds", func() {
124 t := time.Now()
125 Consistently(true, uint(1)).Should(BeTrue())
126 Ω(time.Since(t)).Should(BeNumerically("~", time.Second, 100*time.Millisecond))
127 })
128
129 It("supports passing in a float number of seconds", func() {
130 t := time.Now()
131 Consistently(true, 0.05).Should(BeTrue())
132 Ω(time.Since(t)).Should(BeNumerically("~", 50*time.Millisecond, 30*time.Millisecond))
133 })
134
135 It("supports passing in a duration string", func() {
136 t := time.Now()
137 Consistently(true, "50ms").Should(BeTrue())
138 Ω(time.Since(t)).Should(BeNumerically("~", 50*time.Millisecond, 30*time.Millisecond))
139 })
140
141 It("panics when the duration string can't be parsed", func() {
142 Ω(func() {
143 Consistently(true, "fries").Should(BeTrue())
144 }).Should(PanicWith(`"fries" is not a valid parsable duration string.`))
145 })
146
147 It("panics if anything else is passed in", func() {
148 Ω(func() {
149 Consistently(true, true).Should(BeTrue())
150 }).Should(PanicWith("true is not a valid interval. Must be time.Duration, parsable duration string or a number."))
151 })
152 })
153 })
+0
-23
internal/fakematcher/fake_matcher.go less more
0 package fakematcher
1
2 import "fmt"
3
4 type FakeMatcher struct {
5 ReceivedActual interface{}
6 MatchesToReturn bool
7 ErrToReturn error
8 }
9
10 func (matcher *FakeMatcher) Match(actual interface{}) (bool, error) {
11 matcher.ReceivedActual = actual
12
13 return matcher.MatchesToReturn, matcher.ErrToReturn
14 }
15
16 func (matcher *FakeMatcher) FailureMessage(actual interface{}) string {
17 return fmt.Sprintf("positive: %v", actual)
18 }
19
20 func (matcher *FakeMatcher) NegatedFailureMessage(actual interface{}) string {
21 return fmt.Sprintf("negative: %v", actual)
22 }
0 package internal
1
2 import (
3 "time"
4
5 "github.com/onsi/gomega/types"
6 )
7
8 type Gomega struct {
9 Fail types.GomegaFailHandler
10 THelper func()
11 DurationBundle DurationBundle
12 }
13
14 func NewGomega(bundle DurationBundle) *Gomega {
15 return &Gomega{
16 Fail: nil,
17 THelper: nil,
18 DurationBundle: bundle,
19 }
20 }
21
22 func (g *Gomega) IsConfigured() bool {
23 return g.Fail != nil && g.THelper != nil
24 }
25
26 func (g *Gomega) ConfigureWithFailHandler(fail types.GomegaFailHandler) *Gomega {
27 g.Fail = fail
28 g.THelper = func() {}
29 return g
30 }
31
32 func (g *Gomega) ConfigureWithT(t types.GomegaTestingT) *Gomega {
33 g.Fail = func(message string, _ ...int) {
34 t.Helper()
35 t.Fatalf("\n%s", message)
36 }
37 g.THelper = t.Helper
38 return g
39 }
40
41 func (g *Gomega) Ω(atual interface{}, extra ...interface{}) types.Assertion {
42 return g.ExpectWithOffset(0, atual, extra...)
43 }
44
45 func (g *Gomega) Expect(atual interface{}, extra ...interface{}) types.Assertion {
46 return g.ExpectWithOffset(0, atual, extra...)
47 }
48
49 func (g *Gomega) ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) types.Assertion {
50 return NewAssertion(actual, g, offset, extra...)
51 }
52
53 func (g *Gomega) Eventually(actual interface{}, intervals ...interface{}) types.AsyncAssertion {
54 return g.EventuallyWithOffset(0, actual, intervals...)
55 }
56
57 func (g *Gomega) EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) types.AsyncAssertion {
58 timeoutInterval := g.DurationBundle.EventuallyTimeout
59 pollingInterval := g.DurationBundle.EventuallyPollingInterval
60 if len(intervals) > 0 {
61 timeoutInterval = toDuration(intervals[0])
62 }
63 if len(intervals) > 1 {
64 pollingInterval = toDuration(intervals[1])
65 }
66
67 return NewAsyncAssertion(AsyncAssertionTypeEventually, actual, g, timeoutInterval, pollingInterval, offset)
68 }
69
70 func (g *Gomega) Consistently(actual interface{}, intervals ...interface{}) types.AsyncAssertion {
71 return g.ConsistentlyWithOffset(0, actual, intervals...)
72 }
73
74 func (g *Gomega) ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) types.AsyncAssertion {
75 timeoutInterval := g.DurationBundle.ConsistentlyDuration
76 pollingInterval := g.DurationBundle.ConsistentlyPollingInterval
77 if len(intervals) > 0 {
78 timeoutInterval = toDuration(intervals[0])
79 }
80 if len(intervals) > 1 {
81 pollingInterval = toDuration(intervals[1])
82 }
83
84 return NewAsyncAssertion(AsyncAssertionTypeConsistently, actual, g, timeoutInterval, pollingInterval, offset)
85 }
86
87 func (g *Gomega) SetDefaultEventuallyTimeout(t time.Duration) {
88 g.DurationBundle.EventuallyTimeout = t
89 }
90
91 func (g *Gomega) SetDefaultEventuallyPollingInterval(t time.Duration) {
92 g.DurationBundle.EventuallyPollingInterval = t
93 }
94
95 func (g *Gomega) SetDefaultConsistentlyDuration(t time.Duration) {
96 g.DurationBundle.ConsistentlyDuration = t
97 }
98
99 func (g *Gomega) SetDefaultConsistentlyPollingInterval(t time.Duration) {
100 g.DurationBundle.ConsistentlyPollingInterval = t
101 }
0 package internal_test
1
2 import (
3 "runtime"
4
5 . "github.com/onsi/ginkgo"
6 . "github.com/onsi/gomega"
7 "github.com/onsi/gomega/internal"
8 )
9
10 var _ = Describe("Gomega", func() {
11 It("is mostly tested in assertion_test and async_assertion_test", func() {
12
13 })
14 Describe("when initialized", func() {
15 var g *internal.Gomega
16
17 BeforeEach(func() {
18 g = internal.NewGomega(internal.DurationBundle{})
19 Ω(g.Fail).Should(BeNil())
20 Ω(g.THelper).Should(BeNil())
21 })
22
23 It("should be registered as unconfigured", func() {
24 Ω(g.IsConfigured()).Should(BeFalse())
25 })
26
27 Context("when configured with a fail handler", func() {
28 It("registers the fail handler and a no-op helper", func() {
29 var capturedMessage string
30 g.ConfigureWithFailHandler(func(message string, skip ...int) {
31 capturedMessage = message
32 })
33 Ω(g.IsConfigured()).Should(BeTrue())
34
35 g.Fail("hi bob")
36 Ω(capturedMessage).Should(Equal("hi bob"))
37 Ω(g.THelper).ShouldNot(Panic())
38 })
39 })
40
41 Context("when configured with a T", func() {
42 It("registers a fail handler an the T's helper", func() {
43 fake := &FakeGomegaTestingT{}
44 g.ConfigureWithT(fake)
45 Ω(g.IsConfigured()).Should(BeTrue())
46
47 g.Fail("hi bob")
48 Ω(fake.CalledHelper).Should(BeTrue())
49 Ω(fake.CalledFatalf).Should(Equal("\nhi bob"))
50
51 fake.CalledHelper = false
52 g.THelper()
53 Ω(fake.CalledHelper).Should(BeTrue())
54 })
55 })
56 })
57
58 Describe("Offset", func() {
59 It("computes the correct offsets", func() {
60 doubleNested := func(g Gomega, eventually bool) {
61 func() {
62 if eventually {
63 g.EventuallyWithOffset(2, true, "10ms", "5ms").Should(BeFalse())
64 } else {
65 g.ExpectWithOffset(2, true).To(BeFalse())
66 }
67 }()
68 }
69
70 reportedFile, reportedLine := "", 0
71 _, thisFile, anchorLine, _ := runtime.Caller(0) // 0
72 g := NewGomega(func(message string, skip ...int) { // 1
73 _, reportedFile, reportedLine, _ = runtime.Caller(skip[0] + 1) // 2
74 }) // 3
75 g.Expect(true).To(BeFalse()) // *4*
76 Ω(reportedFile).Should(Equal(thisFile)) // 5
77 Ω(reportedLine - anchorLine).Should(Equal(4)) // 6
78 doubleNested(g, false) // *7*
79 Ω(reportedFile).Should(Equal(thisFile)) // 8
80 Ω(reportedLine - anchorLine).Should(Equal(7)) // 9
81 g.Eventually(true, "10ms", "5ms").Should(BeFalse()) // *10*
82 Ω(reportedFile).Should(Equal(thisFile)) // 11
83 Ω(reportedLine - anchorLine).Should(Equal(10)) // 12
84 doubleNested(g, true) // *13*
85 Ω(reportedFile).Should(Equal(thisFile)) // 14
86 Ω(reportedLine - anchorLine).Should(Equal(13)) // 15
87 })
88 })
89 })
00 package internal_test
11
22 import (
3 "errors"
4 "fmt"
5 "runtime"
6 "strings"
37 "testing"
48
59 . "github.com/onsi/ginkgo"
610 . "github.com/onsi/gomega"
11 "github.com/onsi/gomega/internal"
712 )
813
914 func TestInternal(t *testing.T) {
1015 RegisterFailHandler(Fail)
1116 RunSpecs(t, "Internal Suite")
1217 }
18
19 // InstrumentedGomega
20 type InstrumentedGomega struct {
21 G *internal.Gomega
22 FailureMessage string
23 FailureSkip []int
24 RegisteredHelpers []string
25 }
26
27 func NewInstrumentedGomega() *InstrumentedGomega {
28 out := &InstrumentedGomega{}
29
30 out.G = internal.NewGomega(internal.FetchDefaultDurationBundle())
31 out.G.Fail = func(message string, skip ...int) {
32 out.FailureMessage = message
33 out.FailureSkip = skip
34 }
35 out.G.THelper = func() {
36 pc, _, _, _ := runtime.Caller(1)
37 f := runtime.FuncForPC(pc)
38 funcName := strings.TrimPrefix(f.Name(), "github.com/onsi/gomega/internal.")
39 out.RegisteredHelpers = append(out.RegisteredHelpers, funcName)
40 }
41
42 return out
43 }
44
45 // TestMatcher
46 var MATCH = "match"
47 var NO_MATCH = "no match"
48 var ERR_MATCH = "err match"
49 var TEST_MATCHER_ERR = errors.New("spec matcher error")
50
51 type SpecMatcher struct{}
52
53 func (matcher SpecMatcher) Match(actual interface{}) (bool, error) {
54 switch actual {
55 case MATCH:
56 return true, nil
57 case NO_MATCH:
58 return false, nil
59 case ERR_MATCH:
60 return false, TEST_MATCHER_ERR
61 }
62 return false, fmt.Errorf("unkown actual %v", actual)
63 }
64
65 func (matcher SpecMatcher) FailureMessage(actual interface{}) string {
66 return fmt.Sprintf("positive: %s", actual)
67 }
68
69 func (matcher SpecMatcher) NegatedFailureMessage(actual interface{}) string {
70 return fmt.Sprintf("negative: %s", actual)
71 }
72
73 func SpecMatch() SpecMatcher {
74 return SpecMatcher{}
75 }
76
77 //FakeGomegaTestingT
78 type FakeGomegaTestingT struct {
79 CalledHelper bool
80 CalledFatalf string
81 }
82
83 func (f *FakeGomegaTestingT) Helper() {
84 f.CalledHelper = true
85 }
86
87 func (f *FakeGomegaTestingT) Fatalf(s string, args ...interface{}) {
88 f.CalledFatalf = fmt.Sprintf(s, args...)
89 }
+0
-25
internal/oraclematcher/oracle_matcher.go less more
0 package oraclematcher
1
2 import "github.com/onsi/gomega/types"
3
4 /*
5 GomegaMatchers that also match the OracleMatcher interface can convey information about
6 whether or not their result will change upon future attempts.
7
8 This allows `Eventually` and `Consistently` to short circuit if success becomes impossible.
9
10 For example, a process' exit code can never change. So, gexec's Exit matcher returns `true`
11 for `MatchMayChangeInTheFuture` until the process exits, at which point it returns `false` forevermore.
12 */
13 type OracleMatcher interface {
14 MatchMayChangeInTheFuture(actual interface{}) bool
15 }
16
17 func MatchMayChangeInTheFuture(matcher types.GomegaMatcher, value interface{}) bool {
18 oracleMatcher, ok := matcher.(OracleMatcher)
19 if !ok {
20 return true
21 }
22
23 return oracleMatcher.MatchMayChangeInTheFuture(value)
24 }
+0
-60
internal/testingtsupport/testing_t_support.go less more
0 package testingtsupport
1
2 import (
3 "regexp"
4 "runtime/debug"
5 "strings"
6
7 "github.com/onsi/gomega/types"
8 )
9
10 var StackTracePruneRE = regexp.MustCompile(`\/gomega\/|\/ginkgo\/|\/pkg\/testing\/|\/pkg\/runtime\/`)
11
12 type EmptyTWithHelper struct{}
13
14 func (e EmptyTWithHelper) Helper() {}
15
16 type gomegaTestingT interface {
17 Fatalf(format string, args ...interface{})
18 }
19
20 func BuildTestingTGomegaFailWrapper(t gomegaTestingT) *types.GomegaFailWrapper {
21 tWithHelper, hasHelper := t.(types.TWithHelper)
22 if !hasHelper {
23 tWithHelper = EmptyTWithHelper{}
24 }
25
26 fail := func(message string, callerSkip ...int) {
27 if hasHelper {
28 tWithHelper.Helper()
29 t.Fatalf("\n%s", message)
30 } else {
31 skip := 2
32 if len(callerSkip) > 0 {
33 skip += callerSkip[0]
34 }
35 stackTrace := pruneStack(string(debug.Stack()), skip)
36 t.Fatalf("\n%s\n%s\n", stackTrace, message)
37 }
38 }
39
40 return &types.GomegaFailWrapper{
41 Fail: fail,
42 TWithHelper: tWithHelper,
43 }
44 }
45
46 func pruneStack(fullStackTrace string, skip int) string {
47 stack := strings.Split(fullStackTrace, "\n")[1:]
48 if len(stack) > 2*skip {
49 stack = stack[2*skip:]
50 }
51 prunedStack := []string{}
52 for i := 0; i < len(stack)/2; i++ {
53 if !StackTracePruneRE.Match([]byte(stack[i*2])) {
54 prunedStack = append(prunedStack, stack[i*2])
55 prunedStack = append(prunedStack, stack[i*2+1])
56 }
57 }
58 return strings.Join(prunedStack, "\n")
59 }
00 package testingtsupport_test
11
22 import (
3 "regexp"
4 "time"
5
6 "github.com/onsi/gomega/internal/testingtsupport"
7
83 . "github.com/onsi/gomega"
94
10 "fmt"
115 "testing"
126 )
137
1610 Ω(true).Should(BeTrue())
1711 }
1812
19 type FakeTWithHelper struct {
20 LastFatal string
13 func TestNewGomegaWithT(t *testing.T) {
14 g := NewWithT(t)
15 g.Expect(true).To(BeTrue())
2116 }
22
23 func (f *FakeTWithHelper) Fatalf(format string, args ...interface{}) {
24 f.LastFatal = fmt.Sprintf(format, args...)
25 }
26
27 func TestGomegaWithTWithoutHelper(t *testing.T) {
28 g := NewGomegaWithT(t)
29
30 testingtsupport.StackTracePruneRE = regexp.MustCompile(`\/ginkgo\/`)
31
32 f := &FakeTWithHelper{}
33 testG := NewGomegaWithT(f)
34
35 testG.Expect("foo").To(Equal("foo"))
36 g.Expect(f.LastFatal).To(BeZero())
37
38 testG.Expect("foo").To(Equal("bar"))
39 g.Expect(f.LastFatal).To(ContainSubstring("<string>: foo"))
40 g.Expect(f.LastFatal).To(ContainSubstring("testingtsupport_test"), "It should include a stacktrace")
41
42 testG.Eventually("foo2", time.Millisecond).Should(Equal("bar"))
43 g.Expect(f.LastFatal).To(ContainSubstring("<string>: foo2"))
44
45 testG.Consistently("foo3", time.Millisecond).Should(Equal("bar"))
46 g.Expect(f.LastFatal).To(ContainSubstring("<string>: foo3"))
47 }
48
49 type FakeTWithoutHelper struct {
50 LastFatal string
51 HelperCount int
52 }
53
54 func (f *FakeTWithoutHelper) Fatalf(format string, args ...interface{}) {
55 f.LastFatal = fmt.Sprintf(format, args...)
56 }
57
58 func (f *FakeTWithoutHelper) Helper() {
59 f.HelperCount += 1
60 }
61
62 func (f *FakeTWithoutHelper) ResetHelper() {
63 f.HelperCount = 0
64 }
65
66 func TestGomegaWithTWithHelper(t *testing.T) {
67 g := NewGomegaWithT(t)
68
69 f := &FakeTWithoutHelper{}
70 testG := NewGomegaWithT(f)
71
72 testG.Expect("foo").To(Equal("foo"))
73 g.Expect(f.LastFatal).To(BeZero())
74 g.Expect(f.HelperCount).To(BeNumerically(">", 0))
75 f.ResetHelper()
76
77 testG.Expect("foo").To(Equal("bar"))
78 g.Expect(f.LastFatal).To(ContainSubstring("<string>: foo"))
79 g.Expect(f.LastFatal).NotTo(ContainSubstring("testingtsupport_test"), "It should _not_ include a stacktrace")
80 g.Expect(f.HelperCount).To(BeNumerically(">", 0))
81 f.ResetHelper()
82
83 testG.Eventually("foo2", time.Millisecond).Should(Equal("bar"))
84 g.Expect(f.LastFatal).To(ContainSubstring("<string>: foo2"))
85 g.Expect(f.HelperCount).To(BeNumerically(">", 0))
86 f.ResetHelper()
87
88 testG.Consistently("foo3", time.Millisecond).Should(Equal("bar"))
89 g.Expect(f.LastFatal).To(ContainSubstring("<string>: foo3"))
90 g.Expect(f.HelperCount).To(BeNumerically(">", 0))
91 }
33 "fmt"
44
55 "github.com/onsi/gomega/format"
6 "github.com/onsi/gomega/internal/oraclematcher"
76 "github.com/onsi/gomega/types"
87 )
98
5150 if m.firstFailedMatcher == nil {
5251 // so all matchers succeeded.. Any one of them changing would change the result.
5352 for _, matcher := range m.Matchers {
54 if oraclematcher.MatchMayChangeInTheFuture(matcher, actual) {
53 if types.MatchMayChangeInTheFuture(matcher, actual) {
5554 return true
5655 }
5756 }
5857 return false // none of were going to change
5958 }
6059 // one of the matchers failed.. it must be able to change in order to affect the result
61 return oraclematcher.MatchMayChangeInTheFuture(m.firstFailedMatcher, actual)
60 return types.MatchMayChangeInTheFuture(m.firstFailedMatcher, actual)
6261 }
00 package matchers
11
22 import (
3 "github.com/onsi/gomega/internal/oraclematcher"
43 "github.com/onsi/gomega/types"
54 )
65
2524 }
2625
2726 func (m *NotMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
28 return oraclematcher.MatchMayChangeInTheFuture(m.Matcher, actual) // just return m.Matcher's value
27 return types.MatchMayChangeInTheFuture(m.Matcher, actual) // just return m.Matcher's value
2928 }
33 "fmt"
44
55 "github.com/onsi/gomega/format"
6 "github.com/onsi/gomega/internal/oraclematcher"
76 "github.com/onsi/gomega/types"
87 )
98
5352
5453 if m.firstSuccessfulMatcher != nil {
5554 // one of the matchers succeeded.. it must be able to change in order to affect the result
56 return oraclematcher.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual)
55 return types.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual)
5756 } else {
5857 // so all matchers failed.. Any one of them changing would change the result.
5958 for _, matcher := range m.Matchers {
60 if oraclematcher.MatchMayChangeInTheFuture(matcher, actual) {
59 if types.MatchMayChangeInTheFuture(matcher, actual) {
6160 return true
6261 }
6362 }
33 "fmt"
44 "reflect"
55
6 "github.com/onsi/gomega/internal/oraclematcher"
76 "github.com/onsi/gomega/types"
87 )
98
7675 // Querying the next matcher is fine if the transformer always will return the same value.
7776 // But if the transformer is non-deterministic and returns a different value each time, then there
7877 // is no point in querying the next matcher, since it can only comment on the last transformed value.
79 return oraclematcher.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue)
78 return types.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue)
8079 }
00 package types
11
2 type TWithHelper interface {
3 Helper()
4 }
2 import (
3 "time"
4 )
55
66 type GomegaFailHandler func(message string, callerSkip ...int)
77
8 type GomegaFailWrapper struct {
9 Fail GomegaFailHandler
10 TWithHelper TWithHelper
8 //A simple *testing.T interface wrapper
9 type GomegaTestingT interface {
10 Helper()
11 Fatalf(format string, args ...interface{})
1112 }
1213
13 //A simple *testing.T interface wrapper
14 type GomegaTestingT interface {
15 Fatalf(format string, args ...interface{})
14 // Gomega represents an object that can perform synchronous and assynchronous assertions with Gomega matchers
15 type Gomega interface {
16 Ω(actual interface{}, extra ...interface{}) Assertion
17 Expect(actual interface{}, extra ...interface{}) Assertion
18 ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion
19
20 Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion
21 EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion
22
23 Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion
24 ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion
25
26 SetDefaultEventuallyTimeout(time.Duration)
27 SetDefaultEventuallyPollingInterval(time.Duration)
28 SetDefaultConsistentlyDuration(time.Duration)
29 SetDefaultConsistentlyPollingInterval(time.Duration)
1630 }
1731
1832 //All Gomega matchers must implement the GomegaMatcher interface
2337 FailureMessage(actual interface{}) (message string)
2438 NegatedFailureMessage(actual interface{}) (message string)
2539 }
40
41 /*
42 GomegaMatchers that also match the OracleMatcher interface can convey information about
43 whether or not their result will change upon future attempts.
44
45 This allows `Eventually` and `Consistently` to short circuit if success becomes impossible.
46
47 For example, a process' exit code can never change. So, gexec's Exit matcher returns `true`
48 for `MatchMayChangeInTheFuture` until the process exits, at which point it returns `false` forevermore.
49 */
50 type OracleMatcher interface {
51 MatchMayChangeInTheFuture(actual interface{}) bool
52 }
53
54 func MatchMayChangeInTheFuture(matcher GomegaMatcher, value interface{}) bool {
55 oracleMatcher, ok := matcher.(OracleMatcher)
56 if !ok {
57 return true
58 }
59
60 return oracleMatcher.MatchMayChangeInTheFuture(value)
61 }
62
63 // AsyncAssertions are returned by Eventually and Consistently and enable matchers to be polled repeatedly to ensure
64 // they are eventually satisfied
65 type AsyncAssertion interface {
66 Should(matcher GomegaMatcher, optionalDescription ...interface{}) bool
67 ShouldNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool
68 }
69
70 // Assertions are returned by Ω and Expect and enable assertions against Gomega matchers
71 type Assertion interface {
72 Should(matcher GomegaMatcher, optionalDescription ...interface{}) bool
73 ShouldNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool
74
75 To(matcher GomegaMatcher, optionalDescription ...interface{}) bool
76 ToNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool
77 NotTo(matcher GomegaMatcher, optionalDescription ...interface{}) bool
78 }