Skip to content
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

Merged
merged 8 commits into from
Mar 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Nimble.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,18 @@
1FD8CD771968AB07008ED995 /* ObjCMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FD8CD2D1968AB07008ED995 /* ObjCMatcher.swift */; };
DA9E8C821A414BB9002633C2 /* DSL+Wait.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9E8C811A414BB9002633C2 /* DSL+Wait.swift */; };
DA9E8C831A414BB9002633C2 /* DSL+Wait.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9E8C811A414BB9002633C2 /* DSL+Wait.swift */; };
DD72EC641A93874A002F7651 /* AllPassTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD72EC631A93874A002F7651 /* AllPassTest.swift */; };
DD72EC651A93874A002F7651 /* AllPassTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD72EC631A93874A002F7651 /* AllPassTest.swift */; };
DD9A9A8F19CF439B00706F49 /* BeIdenticalToObjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD9A9A8D19CF413800706F49 /* BeIdenticalToObjectTest.swift */; };
DD9A9A9019CF43AD00706F49 /* BeIdenticalToObjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD9A9A8D19CF413800706F49 /* BeIdenticalToObjectTest.swift */; };
DDB1BC791A92235600F743C3 /* AllPass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB1BC781A92235600F743C3 /* AllPass.swift */; };
DDB1BC7A1A92235600F743C3 /* AllPass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB1BC781A92235600F743C3 /* AllPass.swift */; };
DDB4D5ED19FE43C200E9D9FE /* Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB4D5EC19FE43C200E9D9FE /* Match.swift */; };
DDB4D5EE19FE43C200E9D9FE /* Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB4D5EC19FE43C200E9D9FE /* Match.swift */; };
DDB4D5F019FE442800E9D9FE /* MatchTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB4D5EF19FE442800E9D9FE /* MatchTest.swift */; };
DDB4D5F119FE442800E9D9FE /* MatchTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB4D5EF19FE442800E9D9FE /* MatchTest.swift */; };
DDEFAEB41A93CBE6005CA37A /* ObjCAllPassTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DDEFAEB31A93CBE6005CA37A /* ObjCAllPassTest.m */; };
DDEFAEB51A93CBE6005CA37A /* ObjCAllPassTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DDEFAEB31A93CBE6005CA37A /* ObjCAllPassTest.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -330,9 +336,12 @@
1FFD729B1963FCAB00CD29A2 /* NimbleTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NimbleTests-Bridging-Header.h"; sourceTree = "<group>"; };
1FFD729C1963FCAB00CD29A2 /* Nimble-OSXTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Nimble-OSXTests-Bridging-Header.h"; sourceTree = "<group>"; };
DA9E8C811A414BB9002633C2 /* DSL+Wait.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DSL+Wait.swift"; sourceTree = "<group>"; };
DD72EC631A93874A002F7651 /* AllPassTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllPassTest.swift; sourceTree = "<group>"; };
DD9A9A8D19CF413800706F49 /* BeIdenticalToObjectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BeIdenticalToObjectTest.swift; sourceTree = "<group>"; };
DDB1BC781A92235600F743C3 /* AllPass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllPass.swift; sourceTree = "<group>"; };
DDB4D5EC19FE43C200E9D9FE /* Match.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Match.swift; sourceTree = "<group>"; };
DDB4D5EF19FE442800E9D9FE /* MatchTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatchTest.swift; sourceTree = "<group>"; };
DDEFAEB31A93CBE6005CA37A /* ObjCAllPassTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjCAllPassTest.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -468,6 +477,7 @@
1F299EAA19627B2D002641AF /* BeEmptyTest.swift */,
1F1B5AD31963E13900CA8BF9 /* BeAKindOfTest.swift */,
DDB4D5EF19FE442800E9D9FE /* MatchTest.swift */,
DD72EC631A93874A002F7651 /* AllPassTest.swift */,
);
path = Matchers;
sourceTree = "<group>";
Expand Down Expand Up @@ -503,6 +513,7 @@
DDB4D5EC19FE43C200E9D9FE /* Match.swift */,
1FD8CD1D1968AB07008ED995 /* MatcherProtocols.swift */,
1FD8CD1E1968AB07008ED995 /* RaisesException.swift */,
DDB1BC781A92235600F743C3 /* AllPass.swift */,
);
path = Matchers;
sourceTree = "<group>";
Expand Down Expand Up @@ -569,6 +580,7 @@
1F4A56991A3B3539009E1637 /* ObjCEqualTest.m */,
1F4A569C1A3B3565009E1637 /* ObjCMatchTest.m */,
1F4A569F1A3B359E009E1637 /* ObjCRaiseExceptionTest.m */,
DDEFAEB31A93CBE6005CA37A /* ObjCAllPassTest.m */,
);
path = objc;
sourceTree = "<group>";
Expand Down Expand Up @@ -771,6 +783,7 @@
1FD8CD501968AB07008ED995 /* BeLogical.swift in Sources */,
1FD8CD661968AB07008ED995 /* NMBExceptionCapture.m in Sources */,
DA9E8C821A414BB9002633C2 /* DSL+Wait.swift in Sources */,
DDB1BC791A92235600F743C3 /* AllPass.swift in Sources */,
1FD8CD3E1968AB07008ED995 /* BeAKindOf.swift in Sources */,
DDB4D5ED19FE43C200E9D9FE /* Match.swift in Sources */,
1FD8CD2E1968AB07008ED995 /* AssertionRecorder.swift in Sources */,
Expand Down Expand Up @@ -834,6 +847,7 @@
1F925EF6195C147800ED456B /* BeCloseToTest.swift in Sources */,
1F4A56791A3B32E3009E1637 /* ObjCBeGreaterThanOrEqualToTest.m in Sources */,
1F4A568B1A3B3407009E1637 /* ObjCBeTrueTest.m in Sources */,
DDEFAEB41A93CBE6005CA37A /* ObjCAllPassTest.m in Sources */,
1F4A567F1A3B333F009E1637 /* ObjCBeLessThanTest.m in Sources */,
1F925EE6195C121200ED456B /* AsynchronousTest.swift in Sources */,
1F0648CC19639F5A001F9C46 /* ObjectWithLazyProperty.swift in Sources */,
Expand All @@ -843,6 +857,7 @@
1F925F08195C18CF00ED456B /* BeGreaterThanTest.swift in Sources */,
1F925F05195C18B700ED456B /* EqualTest.swift in Sources */,
1F4A566D1A3B3159009E1637 /* ObjCBeKindOfTest.m in Sources */,
DD72EC641A93874A002F7651 /* AllPassTest.swift in Sources */,
1F4A569D1A3B3565009E1637 /* ObjCMatchTest.m in Sources */,
1F925EE9195C124400ED456B /* BeAnInstanceOfTest.swift in Sources */,
1F4A566A1A3B3108009E1637 /* ObjCBeAnInstanceOfTest.m in Sources */,
Expand All @@ -866,6 +881,7 @@
1FD8CD511968AB07008ED995 /* BeLogical.swift in Sources */,
1FD8CD671968AB07008ED995 /* NMBExceptionCapture.m in Sources */,
DA9E8C831A414BB9002633C2 /* DSL+Wait.swift in Sources */,
DDB1BC7A1A92235600F743C3 /* AllPass.swift in Sources */,
1FD8CD3F1968AB07008ED995 /* BeAKindOf.swift in Sources */,
1FD8CD2F1968AB07008ED995 /* AssertionRecorder.swift in Sources */,
DDB4D5EE19FE43C200E9D9FE /* Match.swift in Sources */,
Expand Down Expand Up @@ -929,6 +945,7 @@
1F925EF7195C147800ED456B /* BeCloseToTest.swift in Sources */,
1F4A567A1A3B32E3009E1637 /* ObjCBeGreaterThanOrEqualToTest.m in Sources */,
1F4A568C1A3B3407009E1637 /* ObjCBeTrueTest.m in Sources */,
DDEFAEB51A93CBE6005CA37A /* ObjCAllPassTest.m in Sources */,
1F4A56801A3B333F009E1637 /* ObjCBeLessThanTest.m in Sources */,
1F925EE7195C121200ED456B /* AsynchronousTest.swift in Sources */,
1F0648CD19639F5A001F9C46 /* ObjectWithLazyProperty.swift in Sources */,
Expand All @@ -938,6 +955,7 @@
1F925F09195C18CF00ED456B /* BeGreaterThanTest.swift in Sources */,
1F925F06195C18B700ED456B /* EqualTest.swift in Sources */,
1F4A566E1A3B3159009E1637 /* ObjCBeKindOfTest.m in Sources */,
DD72EC651A93874A002F7651 /* AllPassTest.swift in Sources */,
1F4A569E1A3B3565009E1637 /* ObjCMatchTest.m in Sources */,
1F925EEA195C124400ED456B /* BeAnInstanceOfTest.swift in Sources */,
1F4A566B1A3B3108009E1637 /* ObjCBeAnInstanceOfTest.m in Sources */,
Expand Down
99 changes: 99 additions & 0 deletions Nimble/Matchers/AllPass.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import Foundation

public func allPass<T,U where U: SequenceType, U.Generator.Element == T>
(passFunc: (T?) -> Bool) -> NonNilMatcherFunc<U> {
return allPass("pass a condition", passFunc)
}

public func allPass<T,U where U: SequenceType, U.Generator.Element == T>
(passName:String, passFunc: (T?) -> Bool) -> NonNilMatcherFunc<U> {
return createAllPassMatcher() {
expression, failureMessage in
failureMessage.postfixMessage = passName
return passFunc(expression.evaluate())
}
}

public func allPass<U,V where U: SequenceType, V: NonNilBasicMatcher, U.Generator.Element == V.ValueType>
(matcher: V) -> NonNilMatcherFunc<U> {
let wrapper = NonNilMatcherWrapper(NonNilBasicMatcherWrapper(matcher))
return createAllPassMatcher() {wrapper.matches($0, failureMessage: $1)}
}

public func allPass<U,V where U: SequenceType, V: BasicMatcher, U.Generator.Element == V.ValueType>
(matcher: V) -> NonNilMatcherFunc<U> {
let wrapper = BasicMatcherWrapper(matcher: matcher)
return createAllPassMatcher() {wrapper.matches($0, failureMessage: $1)}
}

public func allPass<U,V where U: SequenceType, V: Matcher, U.Generator.Element == V.ValueType>
Copy link
Member

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?

Copy link
Member

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.

(matcher: V) -> NonNilMatcherFunc<U> {
return createAllPassMatcher() {matcher.matches($0, failureMessage: $1)}
}

private func createAllPassMatcher<T,U where U: SequenceType, U.Generator.Element == T>
(elementEvaluator:(Expression<T>, FailureMessage) -> Bool) -> NonNilMatcherFunc<U> {
return NonNilMatcherFunc { actualExpression, failureMessage in
failureMessage.actualValue = nil
if let actualValue = actualExpression.evaluate() {
for currentElement in actualValue {
let exp = Expression(
expression: {currentElement}, location: actualExpression.location)
if !elementEvaluator(exp, failureMessage) {
failureMessage.postfixMessage =
"all \(failureMessage.postfixMessage),"
+ " but failed first at element <\(stringify(currentElement))>"
+ " in <\(stringify(actualValue))>"
return false
}
}
failureMessage.postfixMessage = "all \(failureMessage.postfixMessage)"
} else {
failureMessage.postfixMessage = "all pass (use beNil() to match nils)"
return false
}

return true
}
}

extension NMBObjCMatcher {
public class func allPassMatcher(matcher: NMBObjCMatcher) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage, location in
let actualValue = actualExpression.evaluate()
var nsObjects = [NSObject]()

var collectionIsUsable = true
if let value = actualValue as? NSFastEnumeration {
let generator = NSFastGenerator(value)
while let obj:AnyObject = generator.next() {
if let nsObject = obj as? NSObject {
nsObjects.append(nsObject)
} else {
collectionIsUsable = false
break
}
}
} else {
collectionIsUsable = false
}

if !collectionIsUsable {
failureMessage.postfixMessage =
"allPass only works with NSFastEnumeration (NSArray, NSSet, ...) of NSObjects"
failureMessage.expected = ""
failureMessage.to = ""
return false
}
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.


let expr = Expression(expression: ({ nsObjects }), location: location)
let elementEvaluator: (Expression<NSObject>, FailureMessage) -> Bool = {
expression, failureMessage in
return matcher.matches(
{expression.evaluate()}, failureMessage: failureMessage, location: expr.location)
}
return createAllPassMatcher(elementEvaluator).matches(
expr, failureMessage: failureMessage)
}
}
}
4 changes: 4 additions & 0 deletions Nimble/objc/DSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ NIMBLE_EXPORT id<NMBMatcher> NMB_match(id expectedValue);
NIMBLE_SHORT(id<NMBMatcher> match(id expectedValue),
NMB_match(expectedValue));

NIMBLE_EXPORT id<NMBMatcher> NMB_allPass(id matcher);
NIMBLE_SHORT(id<NMBMatcher> allPass(id matcher),
NMB_allPass(matcher));

// In order to preserve breakpoint behavior despite using macros to fill in __FILE__ and __LINE__,
// define a builder that populates __FILE__ and __LINE__, and returns a block that takes timeout
// and action arguments. See https://github.com/Quick/Quick/pull/185 for details.
Expand Down
4 changes: 4 additions & 0 deletions Nimble/objc/DSL.m
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@
return [NMBObjCMatcher matchMatcher:expectedValue];
}

NIMBLE_EXPORT id<NMBMatcher> NMB_allPass(id expectedValue) {
return [NMBObjCMatcher allPassMatcher:expectedValue];
}

NIMBLE_EXPORT NMBObjCRaiseExceptionMatcher *NMB_raiseException() {
return [NMBObjCMatcher raiseExceptionMatcher];
}
Expand Down
74 changes: 74 additions & 0 deletions NimbleTests/Matchers/AllPassTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import XCTest
import Nimble

class AllPassTest: XCTestCase {
func testAllPassArray() {
expect([1,2,3,4]).to(allPass({$0 < 5}))
expect([1,2,3,4]).toNot(allPass({$0 > 5}))

failsWithErrorMessage(
"expected to all pass a condition, but failed first at element <3> in <[1, 2, 3, 4]>") {
expect([1,2,3,4]).to(allPass({$0 < 3}))
}
failsWithErrorMessage("expected to not all pass a condition") {
expect([1,2,3,4]).toNot(allPass({$0 < 5}))
}
failsWithErrorMessage(
"expected to all be something, but failed first at element <3> in <[1, 2, 3, 4]>") {
expect([1,2,3,4]).to(allPass("be something", {$0 < 3}))
}
failsWithErrorMessage("expected to not all be something") {
expect([1,2,3,4]).toNot(allPass("be something", {$0 < 5}))
}
}

func testAllPassMatcher() {
expect([1,2,3,4]).to(allPass(beLessThan(5)))
expect([1,2,3,4]).toNot(allPass(beGreaterThan(5)))

failsWithErrorMessage(
"expected to all be less than <3>, but failed first at element <3> in <[1, 2, 3, 4]>") {
expect([1,2,3,4]).to(allPass(beLessThan(3)))
}
failsWithErrorMessage("expected to not all be less than <5>") {
expect([1,2,3,4]).toNot(allPass(beLessThan(5)))
}
}

func testAllPassCollectionsWithOptionalsDontWork() {
failsWithErrorMessage("expected to all be nil, but failed first at element <nil> in <[nil, nil, nil]>") {
expect([nil, nil, nil] as [Int?]).to(allPass(beNil()))
}
failsWithErrorMessage("expected to all pass a condition, but failed first at element <nil> in <[nil, nil, nil]>") {
expect([nil, nil, nil] as [Int?]).to(allPass({$0 == nil}))
}
}

func testAllPassCollectionsWithOptionalsUnwrappingOneOptionalLayer() {
expect([nil, nil, nil] as [Int?]).to(allPass({$0! == nil}))
expect([nil, 1, nil] as [Int?]).toNot(allPass({$0! == nil}))
expect([1, 1, 1] as [Int?]).to(allPass({$0! == 1}))
expect([1, 1, nil] as [Int?]).toNot(allPass({$0! == 1}))
expect([1, 2, 3] as [Int?]).to(allPass({$0! < 4}))
expect([1, 2, 3] as [Int?]).toNot(allPass({$0! < 3}))
expect([1, 2, nil] as [Int?]).to(allPass({$0! < 3}))
}

func testAllPassSet() {
expect(Set([1,2,3,4])).to(allPass({$0 < 5}))
expect(Set([1,2,3,4])).toNot(allPass({$0 > 5}))

failsWithErrorMessage("expected to not all pass a condition") {
expect(Set([1,2,3,4])).toNot(allPass({$0 < 5}))
}
failsWithErrorMessage("expected to not all be something") {
expect(Set([1,2,3,4])).toNot(allPass("be something", {$0 < 5}))
}
}

func testAllPassWithNilAsExpectedValue() {
failsWithErrorMessageForNil("expected to all pass") {
expect(nil as [Int]?).to(allPass(beLessThan(5)))
}
}
}
38 changes: 38 additions & 0 deletions NimbleTests/objc/ObjCAllPassTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#import <XCTest/XCTest.h>
#import "NimbleSpecHelper.h"

@interface ObjCAllPassTest : XCTestCase

@end

@implementation ObjCAllPassTest

- (void)testPositiveMatches {
expect(@[@1, @2, @3,@4]).to(allPass(beLessThan(@5)));
expect(@[@1, @2, @3,@4]).toNot(allPass(beGreaterThan(@5)));

expect([NSSet setWithArray:@[@1, @2, @3,@4]]).to(allPass(beLessThan(@5)));
expect([NSSet setWithArray:@[@1, @2, @3,@4]]).toNot(allPass(beGreaterThan(@5)));
}

- (void)testNegativeMatches {
expectFailureMessage(@"expected to all be less than <3.0000>, but failed first at element"
" <3.0000> in <[1.0000, 2.0000, 3.0000, 4.0000]>", ^{
expect(@[@1, @2, @3,@4]).to(allPass(beLessThan(@3)));
});
expectFailureMessage(@"expected to not all be less than <5.0000>", ^{
expect(@[@1, @2, @3,@4]).toNot(allPass(beLessThan(@5)));
});
expectFailureMessage(@"expected to not all be less than <5.0000>", ^{
expect([NSSet setWithArray:@[@1, @2, @3,@4]]).toNot(allPass(beLessThan(@5)));
});
expectFailureMessage(@"allPass only works with NSFastEnumeration"
" (NSArray, NSSet, ...) of NSObjects, got <3.0000>", ^{
expect(@3).to(allPass(beLessThan(@5)));
});
expectFailureMessage(@"allPass only works with NSFastEnumeration"
" (NSArray, NSSet, ...) of NSObjects, got <3.0000>", ^{
expect(@3).toNot(allPass(beLessThan(@5)));
});
}
@end
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ expect(ocean.isClean).toEventually(beTruthy())
- [Exceptions](#exceptions)
- [Collection Membership](#collection-membership)
- [Strings](#strings)
- [Checking if all elements of a collection pass a condition](#checking-if-all-elements-of-a-collection-pass-a-condition)
- [Writing Your Own Matchers](#writing-your-own-matchers)
- [Lazy Evaluation](#lazy-evaluation)
- [Type Checking via Swift Generics](#type-checking-via-swift-generics)
Expand Down Expand Up @@ -758,6 +759,29 @@ expect(actual).to(beEmpty());
expect(actual).to(match(expected))
```

## Checking if all elements of a collection pass a condition

```swift
// Swift

// with a custom function:
expect([1,2,3,4]).to(allPass({$0 < 5}))

// with another matcher:
expect([1,2,3,4]).to(allPass(beLessThan(5)))
```

```objc
// Objective-C

expect(@[@1, @2, @3,@4]).to(allPass(beLessThan(@5)));
```

For Swift the actual value has to be a SequenceType, e.g. an array, a set or a custom seqence type.

For Objective-C the actual value has to be a NSFastEnumeration, e.g. NSArray and NSSet, of NSObjects and only the variant which
uses another matcher is available here.

# Writing Your Own Matchers

In Nimble, matchers are Swift functions that take an expected
Expand Down