package asyncassertion_test import ( "errors" "time" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/internal/asyncassertion" ) var _ = Describe("Async Assertion", func() { var ( failureMessage string callerSkip int ) var fakeFailHandler = func(message string, skip ...int) { failureMessage = message callerSkip = skip[0] } BeforeEach(func() { failureMessage = "" callerSkip = 0 }) Describe("Eventually", func() { Context("the positive case", func() { It("should poll the function and matcher", func() { counter := 0 a := New(AsyncAssertionTypeEventually, func() int { counter++ return counter }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.Should(BeNumerically("==", 5)) Ω(failureMessage).Should(BeZero()) }) It("should continue when the matcher errors", func() { counter := 0 a := New(AsyncAssertionTypeEventually, func() interface{} { counter++ if counter == 5 { return "not-a-number" //this should cause the matcher to error } return counter }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.Should(BeNumerically("==", 5), "My description %d", 2) Ω(failureMessage).Should(ContainSubstring("Timed out after")) Ω(failureMessage).Should(ContainSubstring("My description 2")) Ω(callerSkip).Should(Equal(4)) }) It("should be able to timeout", func() { counter := 0 a := New(AsyncAssertionTypeEventually, func() int { counter++ return counter }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.Should(BeNumerically(">", 100), "My description %d", 2) Ω(counter).Should(BeNumerically(">", 8)) Ω(counter).Should(BeNumerically("<=", 10)) Ω(failureMessage).Should(ContainSubstring("Timed out after")) Ω(failureMessage).Should(MatchRegexp(`\: \d`), "Should pass the correct value to the matcher message formatter.") Ω(failureMessage).Should(ContainSubstring("My description 2")) Ω(callerSkip).Should(Equal(4)) }) }) Context("the negative case", func() { It("should poll the function and matcher", func() { counter := 0 a := New(AsyncAssertionTypeEventually, func() int { counter += 1 return counter }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.ShouldNot(BeNumerically("<", 3)) Ω(counter).Should(Equal(3)) Ω(failureMessage).Should(BeZero()) }) It("should timeout when the matcher errors", func() { a := New(AsyncAssertionTypeEventually, func() interface{} { return 0 //this should cause the matcher to error }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.ShouldNot(HaveLen(0), "My description %d", 2) Ω(failureMessage).Should(ContainSubstring("Timed out after")) Ω(failureMessage).Should(ContainSubstring("Error:")) Ω(failureMessage).Should(ContainSubstring("My description 2")) Ω(callerSkip).Should(Equal(4)) }) It("should be able to timeout", func() { a := New(AsyncAssertionTypeEventually, func() int { return 0 }, fakeFailHandler, time.Duration(0.1*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.ShouldNot(Equal(0), "My description %d", 2) Ω(failureMessage).Should(ContainSubstring("Timed out after")) Ω(failureMessage).Should(ContainSubstring(": 0"), "Should pass the correct value to the matcher message formatter.") Ω(failureMessage).Should(ContainSubstring("My description 2")) Ω(callerSkip).Should(Equal(4)) }) }) Context("with a function that returns multiple values", func() { It("should eventually succeed if the additional arguments are nil", func() { i := 0 Eventually(func() (int, error) { i++ return i, nil }).Should(Equal(10)) }) It("should eventually timeout if the additional arguments are not nil", func() { i := 0 a := New(AsyncAssertionTypeEventually, func() (int, error) { i++ return i, errors.New("bam") }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.Should(Equal(2)) Ω(failureMessage).Should(ContainSubstring("Timed out after")) Ω(failureMessage).Should(ContainSubstring("Error:")) Ω(failureMessage).Should(ContainSubstring("bam")) Ω(callerSkip).Should(Equal(4)) }) }) Context("Making an assertion without a registered fail handler", func() { It("should panic", func() { defer func() { e := recover() RegisterFailHandler(Fail) if e == nil { Fail("expected a panic to have occurred") } }() RegisterFailHandler(nil) c := make(chan bool, 1) c <- true Eventually(c).Should(Receive()) }) }) }) Describe("Consistently", func() { Describe("The positive case", func() { Context("when the matcher consistently passes for the duration", func() { It("should pass", func() { calls := 0 a := New(AsyncAssertionTypeConsistently, func() string { calls++ return "foo" }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.Should(Equal("foo")) Ω(calls).Should(BeNumerically(">", 8)) Ω(calls).Should(BeNumerically("<=", 10)) Ω(failureMessage).Should(BeZero()) }) }) Context("when the matcher fails at some point", func() { It("should fail", func() { calls := 0 a := New(AsyncAssertionTypeConsistently, func() interface{} { calls++ if calls > 5 { return "bar" } return "foo" }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.Should(Equal("foo")) Ω(failureMessage).Should(ContainSubstring("to equal")) Ω(callerSkip).Should(Equal(4)) }) }) Context("when the matcher errors at some point", func() { It("should fail", func() { calls := 0 a := New(AsyncAssertionTypeConsistently, func() interface{} { calls++ if calls > 5 { return 3 } return []int{1, 2, 3} }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.Should(HaveLen(3)) Ω(failureMessage).Should(ContainSubstring("HaveLen matcher expects")) Ω(callerSkip).Should(Equal(4)) }) }) }) Describe("The negative case", func() { Context("when the matcher consistently passes for the duration", func() { It("should pass", func() { c := make(chan bool) a := New(AsyncAssertionTypeConsistently, c, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.ShouldNot(Receive()) Ω(failureMessage).Should(BeZero()) }) }) Context("when the matcher fails at some point", func() { It("should fail", func() { c := make(chan bool) go func() { time.Sleep(time.Duration(100 * time.Millisecond)) c <- true }() a := New(AsyncAssertionTypeConsistently, c, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.ShouldNot(Receive()) Ω(failureMessage).Should(ContainSubstring("not to receive anything")) }) }) Context("when the matcher errors at some point", func() { It("should fail", func() { calls := 0 a := New(AsyncAssertionTypeConsistently, func() interface{} { calls++ return calls }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.ShouldNot(BeNumerically(">", 5)) Ω(failureMessage).Should(ContainSubstring("not to be >")) Ω(callerSkip).Should(Equal(4)) }) }) }) Context("with a function that returns multiple values", func() { It("should consistently succeed if the additional arguments are nil", func() { i := 2 Consistently(func() (int, error) { i++ return i, nil }).Should(BeNumerically(">=", 2)) }) It("should eventually timeout if the additional arguments are not nil", func() { i := 2 a := New(AsyncAssertionTypeEventually, func() (int, error) { i++ return i, errors.New("bam") }, fakeFailHandler, time.Duration(0.2*float64(time.Second)), time.Duration(0.02*float64(time.Second)), 1) a.Should(BeNumerically(">=", 2)) Ω(failureMessage).Should(ContainSubstring("Error:")) Ω(failureMessage).Should(ContainSubstring("bam")) Ω(callerSkip).Should(Equal(4)) }) }) Context("Making an assertion without a registered fail handler", func() { It("should panic", func() { defer func() { e := recover() RegisterFailHandler(Fail) if e == nil { Fail("expected a panic to have occurred") } }() RegisterFailHandler(nil) c := make(chan bool) Consistently(c).ShouldNot(Receive()) }) }) }) Context("when passed a function with the wrong # or arguments & returns", func() { It("should panic", func() { Ω(func() { New(AsyncAssertionTypeEventually, func() {}, fakeFailHandler, 0, 0, 1) }).Should(Panic()) Ω(func() { New(AsyncAssertionTypeEventually, func(a string) int { return 0 }, fakeFailHandler, 0, 0, 1) }).Should(Panic()) Ω(func() { New(AsyncAssertionTypeEventually, func() int { return 0 }, fakeFailHandler, 0, 0, 1) }).ShouldNot(Panic()) Ω(func() { New(AsyncAssertionTypeEventually, func() (int, error) { return 0, nil }, fakeFailHandler, 0, 0, 1) }).ShouldNot(Panic()) }) }) Describe("bailing early", func() { Context("when actual is a value", func() { It("Eventually should bail out and fail early if the matcher says to", func() { c := make(chan bool) close(c) t := time.Now() failures := InterceptGomegaFailures(func() { Eventually(c, 0.1).Should(Receive()) }) Ω(time.Since(t)).Should(BeNumerically("<", 90*time.Millisecond)) Ω(failures).Should(HaveLen(1)) }) }) Context("when actual is a function", func() { It("should never bail early", func() { c := make(chan bool) close(c) t := time.Now() failures := InterceptGomegaFailures(func() { Eventually(func() chan bool { return c }, 0.1).Should(Receive()) }) Ω(time.Since(t)).Should(BeNumerically(">=", 90*time.Millisecond)) Ω(failures).Should(HaveLen(1)) }) }) }) })