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

Reduce NMBObjCMatcher usages #600

Merged
merged 1 commit into from
Sep 18, 2018
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
11 changes: 7 additions & 4 deletions Sources/Nimble/Matchers/BeLogical.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,13 @@ extension NMBObjCMatcher {
}
}

@objc public class func beFalseMatcher() -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
let expr = actualExpression.cast { ($0 as? NSNumber)?.boolValue ?? false }
return try beFalse().matches(expr, failureMessage: failureMessage)
@objc public class func beFalseMatcher() -> NMBMatcher {
return NMBPredicate { actualExpression in
let expr = actualExpression.cast { value -> Bool? in
guard let value = value else { return nil }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nil should not be converted to false here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good catch. Should we add a test that covers this difference between beFalse() and beFalsy()? We're also accepting values that fail to cast to NSNumber, should we also return nil if the cast fails?

Copy link
Member Author

@ikesyo ikesyo Sep 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is already covered here:

Previously the behavior is ensured by NMBObjCMatcher(canMatchNil: false):

if !canMatchNil {
if try actualExpression.evaluate() == nil {
failureMessage.postfixActual = " (use beNil() to match nils)"
return false
}
}

Copy link
Member Author

@ikesyo ikesyo Sep 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I wanted to say is that the message

nil should not be converted to false here.

is only applicable to the new NMBPredicate impl in this PR, but not to the previous NMBObjCMatcher impl.

return (value as? NSNumber)?.boolValue ?? false
}
return try beFalse().satisfies(expr).toObjectiveC()
}
}
}
Expand Down
25 changes: 16 additions & 9 deletions Sources/Nimble/Matchers/Contain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,34 @@ public func contain(_ items: [Any?]) -> Predicate<NMBContainer> {

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func containMatcher(_ expected: [NSObject]) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
@objc public class func containMatcher(_ expected: [NSObject]) -> NMBMatcher {
return NMBPredicate { actualExpression in
let location = actualExpression.location
let actualValue = try actualExpression.evaluate()
if let value = actualValue as? NMBContainer {
let expr = Expression(expression: ({ value as NMBContainer }), location: location)

// A straightforward cast on the array causes this to crash, so we have to cast the individual items
let expectedOptionals: [Any?] = expected.map({ $0 as Any? })
return try contain(expectedOptionals).matches(expr, failureMessage: failureMessage)
return try contain(expectedOptionals).satisfies(expr).toObjectiveC()
} else if let value = actualValue as? NSString {
let expr = Expression(expression: ({ value as String }), location: location)
// swiftlint:disable:next force_cast
return try contain(expected as! [String]).matches(expr, failureMessage: failureMessage)
} else if actualValue != nil {
// swiftlint:disable:next line_length
failureMessage.postfixMessage = "contain <\(arrayAsString(expected))> (only works for NSArrays, NSSets, NSHashTables, and NSStrings)"
return try contain(expected as! [String]).satisfies(expr).toObjectiveC()
}

let message: ExpectationMessage
if actualValue != nil {
message = ExpectationMessage.expectedActualValueTo(
// swiftlint:disable:next line_length
"contain <\(arrayAsString(expected))> (only works for NSArrays, NSSets, NSHashTables, and NSStrings)"
)
} else {
failureMessage.postfixMessage = "contain <\(arrayAsString(expected))>"
message = ExpectationMessage
.expectedActualValueTo("contain <\(arrayAsString(expected))>")
.appendedBeNilHint()
}
return false
return NMBPredicateResult(status: .fail, message: message.toObjectiveC())
}
}
}
Expand Down
24 changes: 12 additions & 12 deletions Sources/Nimble/Matchers/ContainElementSatisfying.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,32 @@ public func containElementSatisfying<S: Sequence, T>(_ predicate: @escaping ((T)

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func containElementSatisfyingMatcher(_ predicate: @escaping ((NSObject) -> Bool)) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
@objc public class func containElementSatisfyingMatcher(_ predicate: @escaping ((NSObject) -> Bool)) -> NMBMatcher {
return NMBPredicate { actualExpression in
let value = try actualExpression.evaluate()
guard let enumeration = value as? NSFastEnumeration else {
// swiftlint:disable:next line_length
failureMessage.postfixMessage = "containElementSatisfying must be provided an NSFastEnumeration object"
failureMessage.actualValue = nil
failureMessage.expected = ""
failureMessage.to = ""
return false
let message = ExpectationMessage.fail(
"containElementSatisfying must be provided an NSFastEnumeration object"
)
return NMBPredicateResult(status: .fail, message: message.toObjectiveC())
}

let message = ExpectationMessage
.expectedTo("find object in collection that satisfies predicate")
.toObjectiveC()

var iterator = NSFastEnumerationIterator(enumeration)
while let item = iterator.next() {
guard let object = item as? NSObject else {
continue
}

if predicate(object) {
return true
return NMBPredicateResult(status: .matches, message: message)
}
}

failureMessage.actualValue = nil
failureMessage.postfixMessage = "find object in collection that satisfies predicate"
return false
return NMBPredicateResult(status: .doesNotMatch, message: message)
}
}
}
Expand Down
23 changes: 16 additions & 7 deletions Sources/Nimble/Matchers/HaveCount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,27 @@ public func haveCount(_ expectedValue: Int) -> Predicate<NMBCollection> {

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
extension NMBObjCMatcher {
@objc public class func haveCountMatcher(_ expected: NSNumber) -> NMBObjCMatcher {
return NMBObjCMatcher(canMatchNil: false) { actualExpression, failureMessage in
@objc public class func haveCountMatcher(_ expected: NSNumber) -> NMBMatcher {
return NMBPredicate { actualExpression in
let location = actualExpression.location
let actualValue = try actualExpression.evaluate()
if let value = actualValue as? NMBCollection {
let expr = Expression(expression: ({ value as NMBCollection}), location: location)
return try haveCount(expected.intValue).matches(expr, failureMessage: failureMessage)
} else if let actualValue = actualValue {
failureMessage.postfixMessage = "get type of NSArray, NSSet, NSDictionary, or NSHashTable"
failureMessage.actualValue = "\(String(describing: type(of: actualValue)))"
return try haveCount(expected.intValue).satisfies(expr).toObjectiveC()
}
return false

let message: ExpectationMessage
if let actualValue = actualValue {
message = ExpectationMessage.expectedCustomValueTo(
"get type of NSArray, NSSet, NSDictionary, or NSHashTable",
"\(String(describing: type(of: actualValue)))"
)
} else {
message = ExpectationMessage
.expectedActualValueTo("have a collection with count \(stringify(expected.intValue))")
.appendedBeNilHint()
}
return NMBPredicateResult(status: .fail, message: message.toObjectiveC())
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions Tests/NimbleTests/objc/ObjCContainTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,13 @@ - (void)testVariadicArguments {
});
}

- (void)testUnsupportedTypes {
expectFailureMessage(@"expected to contain <foo> (only works for NSArrays, NSSets, NSHashTables, and NSStrings), got <1>", ^{
expect(@1).to(contain(@"foo"));
});
expectFailureMessage(@"expected to not contain <foo> (only works for NSArrays, NSSets, NSHashTables, and NSStrings), got <1>", ^{
expect(@1).toNot(contain(@"foo"));
});
}

@end
1 change: 0 additions & 1 deletion Tests/NimbleTests/objc/ObjCEndWithTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ - (void)testPositiveMatches {
expect(@"hello world!").toNot(endWith(@"hello"));
expect(array).to(endWith(@2));
expect(array).toNot(endWith(@1));
expect(@1).toNot(contain(@"foo"));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing contain matcher in ObjCEndWithTest.m is odd (and the fact the test is passing is odd as well) 😛

}

- (void)testNegativeMatches {
Expand Down
9 changes: 9 additions & 0 deletions Tests/NimbleTests/objc/ObjCHaveCountTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,13 @@ - (void)testHaveCountForUnsupportedTypes {
});
}

- (void)testNilMatches {
expectNilFailureMessage(@"expected to have a collection with count 3, got <nil>", ^{
expect(nil).to(haveCount(3));
});
expectNilFailureMessage(@"expected to not have a collection with count 3, got <nil>", ^{
expect(nil).toNot(haveCount(3));
});
}

@end