make Or() support MatchMayChangeInTheFuture()
- simplify tests by using empty And() or Or() instead of Receive() with a closed channel
diff --git a/matchers.go b/matchers.go
index 4bdc588..b6110c4 100644
--- a/matchers.go
+++ b/matchers.go
@@ -354,7 +354,7 @@
}
//SatisfyAll is an alias for And().
-// Ω(foo).Should(SatisfyAll(ContainElement("bar"), HaveLen(3)))
+// Ω("hi").Should(SatisfyAll(HaveLen(2), Equal("hi")))
func SatisfyAll(matchers ...types.GomegaMatcher) types.GomegaMatcher {
return And(matchers...)
}
@@ -369,7 +369,7 @@
}
//SatisfyAny is an alias for Or().
-// Expect(foo).To(SatisfyAny(ContainElement("bar"), HaveLen(3)))
+// Expect("hi").SatisfyAny(Or(HaveLen(3), HaveLen(2))
func SatisfyAny(matchers ...types.GomegaMatcher) types.GomegaMatcher {
return Or(matchers...)
}
diff --git a/matchers/and.go b/matchers/and.go
index 9acf470..5257b76 100644
--- a/matchers/and.go
+++ b/matchers/and.go
@@ -45,7 +45,7 @@
Match eval: T, T, T => T
So match is currently T, what should MatchMayChangeInTheFuture() return?
- Answer: Seems to depend on ANY of them being able to change to F.
+ Seems to depend on ANY of them being able to change to F.
*/
if m.firstFailedMatcher == nil {
diff --git a/matchers/and_test.go b/matchers/and_test.go
index 5e281ca..acf778c 100644
--- a/matchers/and_test.go
+++ b/matchers/and_test.go
@@ -64,23 +64,19 @@
})
Context("MatchMayChangeInTheFuture", func() {
- // setup a closed channel
- closedChannel := make(chan int)
- close(closedChannel)
- var i int
Context("Match returned false", func() {
Context("returns value of the failed matcher", func() {
It("false if failed matcher not going to change", func() {
// 3 matchers: 1st returns true, 2nd returns false and is not going to change, 3rd is never called
- m := And(Not(BeNil()), Receive(&i), Equal(1))
- Expect(m.Match(closedChannel)).To(BeFalse())
- Expect(m.(*AndMatcher).MatchMayChangeInTheFuture(closedChannel)).To(BeFalse()) // closed channel, so not going to change
+ m := And(Not(BeNil()), Or(), Equal(1))
+ Expect(m.Match("hi")).To(BeFalse())
+ Expect(m.(*AndMatcher).MatchMayChangeInTheFuture("hi")).To(BeFalse()) // empty Or() indicates not going to change
})
It("true if failed matcher indicates it might change", func() {
// 3 matchers: 1st returns true, 2nd returns false and "might" change, 3rd is never called
m := And(Not(BeNil()), Equal(5), Equal(1))
- Expect(m.Match(closedChannel)).To(BeFalse())
- Expect(m.(*AndMatcher).MatchMayChangeInTheFuture(closedChannel)).To(BeTrue()) // Equal(5) indicates it might change
+ Expect(m.Match("hi")).To(BeFalse())
+ Expect(m.(*AndMatcher).MatchMayChangeInTheFuture("hi")).To(BeTrue()) // Equal(5) indicates it might change
})
})
})
diff --git a/matchers/not.go b/matchers/not.go
index 0cacc94..6aed858 100644
--- a/matchers/not.go
+++ b/matchers/not.go
@@ -1,8 +1,8 @@
package matchers
import (
- "github.com/onsi/gomega/types"
"github.com/onsi/gomega/internal/asyncassertion"
+ "github.com/onsi/gomega/types"
)
type NotMatcher struct {
diff --git a/matchers/not_test.go b/matchers/not_test.go
index e745651..b3c1fdb 100644
--- a/matchers/not_test.go
+++ b/matchers/not_test.go
@@ -43,14 +43,10 @@
})
Context("MatchMayChangeInTheFuture()", func() {
- It("Propogates value from wrapped matcher", func() {
- // wrap a Receive matcher, which does implement this method
- channel := make(chan int)
- close(channel)
- var i int
- m := Not(Receive(&i))
- Expect(m.Match(channel)).To(BeTrue())
- Expect(m.(*NotMatcher).MatchMayChangeInTheFuture(channel)).To(BeFalse())
+ It("Propagates value from wrapped matcher", func() {
+ m := Not(Or()) // an empty Or() always returns false, and indicates it cannot change
+ Expect(m.Match("anything")).To(BeTrue())
+ Expect(m.(*NotMatcher).MatchMayChangeInTheFuture("anything")).To(BeFalse())
})
It("Defaults to true", func() {
m := Not(Equal(1)) // Equal does not have this method
diff --git a/matchers/or.go b/matchers/or.go
index 228fce6..29ad5c6 100644
--- a/matchers/or.go
+++ b/matchers/or.go
@@ -3,6 +3,7 @@
import (
"fmt"
"github.com/onsi/gomega/format"
+ "github.com/onsi/gomega/internal/asyncassertion"
"github.com/onsi/gomega/types"
)
@@ -10,17 +11,18 @@
Matchers []types.GomegaMatcher
// state
- successfulMatcher types.GomegaMatcher
+ firstSuccessfulMatcher types.GomegaMatcher
}
func (m *OrMatcher) Match(actual interface{}) (success bool, err error) {
+ m.firstSuccessfulMatcher = nil
for _, matcher := range m.Matchers {
success, err := matcher.Match(actual)
if err != nil {
return false, err
}
if success {
- m.successfulMatcher = matcher
+ m.firstSuccessfulMatcher = matcher
return true, nil
}
}
@@ -33,5 +35,32 @@
}
func (m *OrMatcher) NegatedFailureMessage(actual interface{}) (message string) {
- return m.successfulMatcher.NegatedFailureMessage(actual)
+ return m.firstSuccessfulMatcher.NegatedFailureMessage(actual)
+}
+
+func (m *OrMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
+ /*
+ Example with 3 matchers: A, B, C
+
+ Match evaluates them: F, T, <?> => T
+ So match is currently T, what should MatchMayChangeInTheFuture() return?
+ Seems like it only depends on B, since currently B MUST change to allow the result to become F
+
+ Match eval: F, F, F => F
+ So match is currently F, what should MatchMayChangeInTheFuture() return?
+ Seems to depend on ANY of them being able to change to T.
+ */
+
+ if m.firstSuccessfulMatcher != nil {
+ // one of the matchers succeeded.. it must be able to change in order to affect the result
+ return asyncassertion.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual)
+ } else {
+ // so all matchers failed.. Any one of them changing would change the result.
+ for _, matcher := range m.Matchers {
+ if asyncassertion.MatchMayChangeInTheFuture(matcher, actual) {
+ return true
+ }
+ }
+ return false // none of were going to change
+ }
}
diff --git a/matchers/or_test.go b/matchers/or_test.go
index d9c7b82..9589a17 100644
--- a/matchers/or_test.go
+++ b/matchers/or_test.go
@@ -3,6 +3,7 @@
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
+ . "github.com/onsi/gomega/matchers"
)
var _ = Describe("OrMatcher", func() {
@@ -43,4 +44,42 @@
})
})
})
+
+ Context("MatchMayChangeInTheFuture", func() {
+ Context("Match returned false", func() {
+ It("returns true if any of the matchers could change", func() {
+ // 3 matchers, all return false, and all could change
+ m := Or(BeNil(), Equal("hip"), HaveLen(1))
+ Expect(m.Match("hi")).To(BeFalse())
+ Expect(m.(*OrMatcher).MatchMayChangeInTheFuture("hi")).To(BeTrue()) // all 3 of these matchers default to 'true'
+ })
+ It("returns false if none of the matchers could change", func() {
+ // empty Or() has the property of never matching, and never can change since there are no sub-matchers that could change
+ m := Or()
+ Expect(m.Match("anything")).To(BeFalse())
+ Expect(m.(*OrMatcher).MatchMayChangeInTheFuture("anything")).To(BeFalse())
+
+ // Or() with 3 sub-matchers that return false, and can't change
+ m = Or(Or(), Or(), Or())
+ Expect(m.Match("hi")).To(BeFalse())
+ Expect(m.(*OrMatcher).MatchMayChangeInTheFuture("hi")).To(BeFalse()) // the 3 empty Or()'s won't change
+ })
+ })
+ Context("Match returned true", func() {
+ Context("returns value of the successful matcher", func() {
+ It("false if successful matcher not going to change", func() {
+ // 3 matchers: 1st returns false, 2nd returns true and is not going to change, 3rd is never called
+ m := Or(BeNil(), And(), Equal(1))
+ Expect(m.Match("hi")).To(BeTrue())
+ Expect(m.(*OrMatcher).MatchMayChangeInTheFuture("hi")).To(BeFalse())
+ })
+ It("true if successful matcher indicates it might change", func() {
+ // 3 matchers: 1st returns false, 2nd returns true and "might" change, 3rd is never called
+ m := Or(Not(BeNil()), Equal("hi"), Equal(1))
+ Expect(m.Match("hi")).To(BeTrue())
+ Expect(m.(*OrMatcher).MatchMayChangeInTheFuture("hi")).To(BeTrue()) // Equal("hi") indicates it might change
+ })
+ })
+ })
+ })
})
diff --git a/matchers/with_transform_test.go b/matchers/with_transform_test.go
index 7ac6f1c..2824df9 100644
--- a/matchers/with_transform_test.go
+++ b/matchers/with_transform_test.go
@@ -87,17 +87,10 @@
})
Context("MatchMayChangeInTheFuture()", func() {
- It("Propogates value from wrapped matcher on the transformed value", func() {
- // dummy struct that holds a channel
- type S struct{ C chan int }
- getC := func(s S) chan int { return s.C } // extracts channel from struct
- // wrap a Receive matcher, which does implement this method
- var i int
- m := WithTransform(getC, Receive(&i))
- s := S{make(chan int)}
- close(s.C)
- Expect(m.Match(s)).To(BeFalse())
- Expect(m.(*WithTransformMatcher).MatchMayChangeInTheFuture(s)).To(BeFalse()) // channel closed so Receive return false
+ It("Propagates value from wrapped matcher on the transformed value", func() {
+ m := WithTransform(plus1, Or()) // empty Or() always returns false, and indicates it cannot change
+ Expect(m.Match(1)).To(BeFalse())
+ Expect(m.(*WithTransformMatcher).MatchMayChangeInTheFuture(1)).To(BeFalse()) // empty Or() indicates cannot change
})
It("Defaults to true", func() {
m := WithTransform(plus1, Equal(2)) // Equal does not have this method