-
-
Notifications
You must be signed in to change notification settings - Fork 595
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
All pass matcher #95
All pass matcher #95
Conversation
Hey @rbeeger, I like the overloaded allPass matcher that also accepts a nimble matcher. For the objc part, is there something that prevents the use of NSFastEnumeration/NSEnumeration? It would help generalize it for more than NSArray and NSSet. |
NSFastEnumeration doesn't work in swift's for in. It only works in the ObjC for in. But here http://stackoverflow.com/questions/25788290/nsfastenumeration-in-swift someone mentioned NSFastGenerator which allows to handle all NSFastEnumerations quite well. It works for the tests I have written for it. I've added a new commit with this change to this pull request. |
Oops, while changing to NSFastEnumeration, I removed rejecting objects it cannot handle. And the error message also looked wrong. |
The failure message for the ObjC variante of allPassMatcher now makes it clear that it needs a NSFastEnumeration and doesn't contain the "expected to" prefix which was out of place in that situation.
failureMessage.expected = "" | ||
failureMessage.to = "" | ||
return false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this if statement be inlined to where collectionIsUsable = false
? I think we can remove that variable entirely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately there are two situations at which the collection becomes unusable:
- it contains at least one object that isn't a NSObject (line 58)
- the collection isn't a NSFastEnumeration
Inlining would double the same code.
It could be moved into a closure that would be called at both places, but that would add a variable for the closure and would make the whole thing more confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, my bad, I didn't see that, thanks for the pointing that out.
After looking at it again, there seems to be some more complex details with nested matchers. Should matching nils in collections be allowed? expect([nil, nil] as [Int?]).to(allPass(beNil())) What's the error message for matching nils against non-nil accepting matchers? expect([nil, nil] as [Int?]).to(allPass(beLessThan(5))) Thoughts @Quick/contributors & @rbeeger? @rbeeger, a minor quibble: func testAllPassFailsAgainstNils() {
// should it be "pass" instead of "match"?
// expected error message: "expected to all match (use beNil() to match nils)"
failsWithErrorMessageForNil("expected to all match") {
expect(nil as [Int]?).to(allPass(beLessThan(5)))
}
} Also, I put one other comment in the commit. |
I fixed the handling of nil epected values. The other one is harder. Each attempt at allowing matching nils in collections I made failed. We have two layers of generics here. One is in the allPass matcher and the other in the used matcher. Currently the passed collection cannot have an optional generator element type. Passing '[1,2,3,4] as [Int?]' in any of the tests of the allPass matcher breaks it. I haven't found the correct generic types constellation that would make it work. The problem is in getting the optional from the test through allPass into the used matcher without having it wrapped in yet another optional. |
I investigated sequences of optional values as expected values a bit further and still haven't found a solution that works well. I can change the element type of the sequence to public func allPass<T,U where U: SequenceType, U.Generator.Element == T?>
(matcher: NonNilMatcherFunc<T>) -> NonNilMatcherFunc<U> {
return createAllPassMatcher() {matcher.matches($0, failureMessage: $1)}
} Of course I need to change Error messages now contain But now we get to the part that doesn't work anymore expect(Set([1,2,3,4])).to(allPass(beLessThan(5))) That's because we now need a sequence of I cannot duplicate the functions with BTW: If we were to have those sequences of optional values, we would need to wrap the usage of public func allPass<T,U where U: SequenceType, U.Generator.Element == T>
(matcher: NonNilMatcherFunc<T>) -> NonNilMatcherFunc<U> {
let wrapper = NonNilMatcherWrapper(NonNilBasicMatcherWrapper(matcher))
return createAllPassMatcher() {wrapper.matches($0, failureMessage: $1)}
} because some of them like |
That compiler crashing seems like a radar to file 🐛. Although I think having a more restrictive matcher is better than nothing. So if non-nil sequence types can only be matched, I think that's an acceptable compromise until Apple addresses that compiler bug. @rbeeger, thanks again for the exploring the problem space. |
The biggest problem is that Anyways, always interesting to work on Nimble. |
Unfortunately replacing the usage of public func allPass<T,U,V where U: SequenceType, V: NonNilBasicMatcher, U.Generator.Element == T, V.ValueType == T>
(matcher: V) -> NonNilMatcherFunc<U> {
let wrapper = NonNilMatcherWrapper(NonNilBasicMatcherWrapper(matcher))
return createAllPassMatcher() {wrapper.matches($0, failureMessage: $1)}
} The static code analyzer seems to like it and compiling Nimble also seems to work, but the compiler crashes with a segmentation fault while compiling AllPassTest.swift for testing. Hopefully XCode 6.3 beta 3 will be better at it. |
OK, I found the problem. |
I added support for At first sight allPass behaves strangely when used with collections of optionals like The tests The problem is that we loose the information about having a collection of optionals here https://github.com/rbeeger/Nimble/blob/allPassMatcher/Nimble/Matchers/AllPass.swift#L34 and end up with optionals that are again wrapped. So instead of As the second of the two tests shows, this special Optional layer can be removed in the variant that takes a simple function by force unwrapping the element to be tested. |
Hey @rbeeger, I ran this against my Xcode 6.3 beta 2 and it seemed to run just fine (test passing and all). Maybe this is just another stale derived data issue that you've been experiencing? |
@jeffh All the checked in tests also pass here. I wouldn't commit red tests 😀 . In the first one two statements are wrapped in The second test shows how you can work around the current strange behavior. The current situation is the best I can come up with for this matcher at this time. The two tests are just a warning to those using it. It shows that I am aware of this not fully satisfying situation with collections of optionals. |
@jeffh I'm wondering if there is some serious problem with this pull request that keeps it from being merged and if there is something I could do to fix it. It's not perfect, but the best I could come up with. And its little quirks don't look overly problematic to me. |
return createAllPassMatcher() {wrapper.matches($0, failureMessage: $1)} | ||
} | ||
|
||
public func allPass<U,V where U: SequenceType, V: Matcher, U.Generator.Element == V.ValueType> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is tangential to this pull request, but I recently created a matcher similar to this one: https://github.com/modocache/Guanaco/blob/18126c0c8ba9a7cb3cc5d43aec83c81047410ab6/Guanaco/HaveSucceeded.swift#L21-L49
I also had to write several matcher functions in order to handle Matcher
, BasicMatcher
, and NonNilBasicMatcher
. Architecturally, this seems like a shortcoming. Any thoughts on improvements here, @jeffh?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, there needs to be some changes to simplify the internals. I think the removal a BasicMatcher and NonNilBasicMatcher would be preferred in some way. There also needs a reinvestigation if the swift compiler still gives an obscure message when a generic does match optionals. having the matcher be able to specify that while Nimble's Expectation
and matcher protocols be ignorant of that detail would be best.
All that stuff are just floating in my head right now. For various personal reasons, I don't have the available free time yet.
I think this looks great. I'll merge tonight unless any @Quick/contributors want to chime in. 🌸 |
Looks 🎉 |
Thanks again, @rbeeger! 👍 |
This implements #52