Skip to content

Commit 6cb41b9

Browse files
committed
Make NSPredicate expectation constructor API accept nil object
- Change the object parameter of `XCTestCase.expectation(for:evaluatedWith:handler:)` from type `AnyObject` to `Any?` with a default value of `nil`. This more closely matches the type of the object parameter from the NSPredicate initializer. - Modify the internally-formatted `description` string to match ObjC XCTest implementation. - Update and add new unit tests
1 parent 11b22e5 commit 6cb41b9

File tree

4 files changed

+67
-34
lines changed

4 files changed

+67
-34
lines changed

Sources/XCTest/Private/XCPredicateExpectation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@
1313

1414
internal class XCPredicateExpectation: XCTestExpectation {
1515
internal let predicate: NSPredicate
16-
internal let object: AnyObject
16+
internal let object: Any?
1717
internal var timer: Timer?
1818
internal let handler: XCPredicateExpectationHandler?
1919
private let evaluationInterval = 0.01
2020

21-
internal init(predicate: NSPredicate, object: AnyObject, description: String, file: StaticString, line: Int, testCase: XCTestCase, handler: XCPredicateExpectationHandler? = nil) {
21+
internal init(predicate: NSPredicate, object: Any? = nil, file: StaticString, line: Int, testCase: XCTestCase, handler: XCPredicateExpectationHandler? = nil) {
2222
self.predicate = predicate
2323
self.object = object
2424
self.handler = handler
25-
self.timer = nil
25+
let description = "Expect predicate `\(predicate)`" + (object.map { " for object \($0)" } ?? "")
2626
super.init(description: description, file: file, line: line, testCase: testCase)
2727
}
2828

Sources/XCTest/Public/Asynchronous/XCTestCase+PredicateExpectation.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public extension XCTestCase {
1919
/// - Parameter predicate: The predicate that will be used to evaluate the
2020
/// object.
2121
/// - Parameter object: The object that is evaluated against the conditions
22-
/// specified by the predicate.
22+
/// specified by the predicate, if any. Default is nil.
2323
/// - Parameter file: The file name to use in the error message if
2424
/// this expectation is not waited for. Default is the file
2525
/// containing the call to this method. It is rare to provide this
@@ -33,11 +33,10 @@ public extension XCTestCase {
3333
/// first successful evaluation will fulfill the expectation. If provided,
3434
/// the handler can override that behavior which leaves the caller
3535
/// responsible for fulfilling the expectation.
36-
@discardableResult func expectation(for predicate: NSPredicate, evaluatedWith object: AnyObject, file: StaticString = #file, line: Int = #line, handler: XCPredicateExpectationHandler? = nil) -> XCTestExpectation {
36+
@discardableResult func expectation(for predicate: NSPredicate, evaluatedWith object: Any? = nil, file: StaticString = #file, line: Int = #line, handler: XCPredicateExpectationHandler? = nil) -> XCTestExpectation {
3737
let expectation = XCPredicateExpectation(
3838
predicate: predicate,
3939
object: object,
40-
description: "Expect `\(predicate)` for object \(object)",
4140
file: file,
4241
line: line,
4342
testCase: self,

Tests/Functional/Asynchronous/Predicates/Expectations/main.swift

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,66 +13,100 @@
1313

1414
// CHECK: Test Suite 'PredicateExpectationsTestCase' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
1515
class PredicateExpectationsTestCase: XCTestCase {
16-
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyTruePredicateAndObject_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
17-
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyTruePredicateAndObject_passes' passed \(\d+\.\d+ seconds\)
18-
func test_immediatelyTruePredicateAndObject_passes() {
16+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyTruePredicate_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
17+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyTruePredicate_passes' passed \(\d+\.\d+ seconds\)
18+
func test_immediatelyTruePredicate_passes() {
1919
let predicate = NSPredicate(value: true)
20-
let object = NSObject()
21-
expectation(for: predicate, evaluatedWith: object)
20+
expectation(for: predicate)
2221
waitForExpectations(timeout: 0.1)
2322
}
2423

25-
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyFalsePredicateAndObject_fails' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
26-
// CHECK: .*/Tests/Functional/Asynchronous/Predicates/Expectations/main.swift:[[@LINE+6]]: error: PredicateExpectationsTestCase.test_immediatelyFalsePredicateAndObject_fails : Asynchronous wait failed - Exceeded timeout of 0.1 seconds, with unfulfilled expectations: Expect `<NSPredicate: 0x[0-9A-Fa-f]{1,16}>` for object <NSObject: 0x[0-9A-Fa-f]{1,16}>
27-
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyFalsePredicateAndObject_fails' failed \(\d+\.\d+ seconds\)
28-
func test_immediatelyFalsePredicateAndObject_fails() {
24+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyFalsePredicate_fails' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
25+
// CHECK: .*/Tests/Functional/Asynchronous/Predicates/Expectations/main.swift:[[@LINE+5]]: error: PredicateExpectationsTestCase.test_immediatelyFalsePredicate_fails : Asynchronous wait failed - Exceeded timeout of 0.1 seconds, with unfulfilled expectations: Expect predicate `<NSPredicate: 0x[0-9A-Fa-f]{1,16}>`
26+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyFalsePredicate_fails' failed \(\d+\.\d+ seconds\)
27+
func test_immediatelyFalsePredicate_fails() {
2928
let predicate = NSPredicate(value: false)
30-
let object = NSObject()
31-
expectation(for: predicate, evaluatedWith: object)
29+
expectation(for: predicate)
3230
waitForExpectations(timeout: 0.1)
3331
}
3432

35-
// CHECK: Test Case 'PredicateExpectationsTestCase.test_delayedTruePredicateAndObject_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
36-
// CHECK: Test Case 'PredicateExpectationsTestCase.test_delayedTruePredicateAndObject_passes' passed \(\d+\.\d+ seconds\)
37-
func test_delayedTruePredicateAndObject_passes() {
33+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_delayedTruePredicate_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
34+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_delayedTruePredicate_passes' passed \(\d+\.\d+ seconds\)
35+
func test_delayedTruePredicate_passes() {
3836
var didEvaluate = false
3937
let predicate = NSPredicate(block: { evaluatedObject, bindings in
38+
XCTAssertNil(evaluatedObject)
4039
defer { didEvaluate = true }
4140
return didEvaluate
4241
})
43-
expectation(for: predicate, evaluatedWith: NSObject())
42+
expectation(for: predicate)
4443
waitForExpectations(timeout: 0.1)
4544
}
4645

47-
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyTrueDelayedFalsePredicateAndObject_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
48-
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyTrueDelayedFalsePredicateAndObject_passes' passed \(\d+\.\d+ seconds\)
49-
func test_immediatelyTrueDelayedFalsePredicateAndObject_passes() {
46+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyTrueDelayedFalsePredicate_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
47+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_immediatelyTrueDelayedFalsePredicate_passes' passed \(\d+\.\d+ seconds\)
48+
func test_immediatelyTrueDelayedFalsePredicate_passes() {
5049
var didEvaluate = false
5150
let predicate = NSPredicate(block: { evaluatedObject, bindings in
51+
XCTAssertNil(evaluatedObject)
5252
defer { didEvaluate = true }
5353
return !didEvaluate
5454
})
55-
expectation(for: predicate, evaluatedWith: NSObject())
55+
expectation(for: predicate)
5656
XCTAssertTrue(didEvaluate)
5757

5858
waitForExpectations(timeout: 0.1)
5959
}
60+
61+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_blockPredicateWithNilObject_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
62+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_blockPredicateWithNilObject_passes' passed \(\d+\.\d+ seconds\)
63+
func test_blockPredicateWithNilObject_passes() {
64+
var flag = false
65+
let predicate = NSPredicate(block: { _, _ in
66+
return flag
67+
})
68+
expectation(for: predicate, evaluatedWith: nil)
69+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
70+
flag = true
71+
}
72+
waitForExpectations(timeout: 1)
73+
XCTAssertTrue(flag)
74+
}
75+
76+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_blockPredicateWithObject_passes' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
77+
// CHECK: Test Case 'PredicateExpectationsTestCase.test_blockPredicateWithObject_passes' passed \(\d+\.\d+ seconds\)
78+
func test_blockPredicateWithObject_passes() {
79+
class Foo { var x = false }
80+
let foo = Foo()
81+
let predicate = NSPredicate(block: { evaluatedObject, _ in
82+
guard let object = evaluatedObject as? Foo else { return false }
83+
return object.x
84+
})
85+
expectation(for: predicate, evaluatedWith: foo)
86+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
87+
foo.x = true
88+
}
89+
waitForExpectations(timeout: 1)
90+
XCTAssertTrue(foo.x)
91+
}
6092

6193
static var allTests = {
6294
return [
63-
("test_immediatelyTruePredicateAndObject_passes", test_immediatelyTruePredicateAndObject_passes),
64-
("test_immediatelyFalsePredicateAndObject_fails", test_immediatelyFalsePredicateAndObject_fails),
65-
("test_delayedTruePredicateAndObject_passes", test_delayedTruePredicateAndObject_passes),
66-
("test_immediatelyTrueDelayedFalsePredicateAndObject_passes", test_immediatelyTrueDelayedFalsePredicateAndObject_passes),
95+
("test_immediatelyTruePredicate_passes", test_immediatelyTruePredicate_passes),
96+
("test_immediatelyFalsePredicate_fails", test_immediatelyFalsePredicate_fails),
97+
("test_delayedTruePredicate_passes", test_delayedTruePredicate_passes),
98+
("test_immediatelyTrueDelayedFalsePredicate_passes", test_immediatelyTrueDelayedFalsePredicate_passes),
99+
("test_blockPredicateWithNilObject_passes", test_blockPredicateWithNilObject_passes),
100+
("test_blockPredicateWithObject_passes", test_blockPredicateWithObject_passes),
67101
]
68102
}()
69103
}
70104

71105
// CHECK: Test Suite 'PredicateExpectationsTestCase' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
72-
// CHECK: \t Executed 4 tests, with 1 failure \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
106+
// CHECK: \t Executed 6 tests, with 1 failure \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
73107
XCTMain([testCase(PredicateExpectationsTestCase.allTests)])
74108

75109
// CHECK: Test Suite '.*\.xctest' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
76-
// CHECK: \t Executed 4 tests, with 1 failure \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
110+
// CHECK: \t Executed 6 tests, with 1 failure \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
77111
// CHECK: Test Suite 'All tests' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
78-
// CHECK: \t Executed 4 tests, with 1 failure \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
112+
// CHECK: \t Executed 6 tests, with 1 failure \(0 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds

Tests/Functional/Asynchronous/Predicates/Handler/main.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class PredicateHandlerTestCase: XCTestCase {
2424
waitForExpectations(timeout: 0.1)
2525
}
2626
// CHECK: Test Case 'PredicateHandlerTestCase.test_predicateIsTrue_handlerReturnsFalse_fails' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
27-
// CHECK: .*/Tests/Functional/Asynchronous/Predicates/Handler/main.swift:[[@LINE+8]]: error: PredicateHandlerTestCase.test_predicateIsTrue_handlerReturnsFalse_fails : Asynchronous wait failed - Exceeded timeout of 0.1 seconds, with unfulfilled expectations: Expect `<NSPredicate: 0x[0-9a-fA-F]{1,16}>` for object <NSObject: 0x[0-9a-fA-F]{1,16}>
27+
// CHECK: .*/Tests/Functional/Asynchronous/Predicates/Handler/main.swift:[[@LINE+8]]: error: PredicateHandlerTestCase.test_predicateIsTrue_handlerReturnsFalse_fails : Asynchronous wait failed - Exceeded timeout of 0.1 seconds, with unfulfilled expectations: Expect predicate `<NSPredicate: 0x[0-9a-fA-F]{1,16}>` for object <NSObject: 0x[0-9a-fA-F]{1,16}>
2828
// CHECK: Test Case 'PredicateHandlerTestCase.test_predicateIsTrue_handlerReturnsFalse_fails' failed \(\d+\.\d+ seconds\)
2929
func test_predicateIsTrue_handlerReturnsFalse_fails() {
3030
let predicate = NSPredicate(value: true)
@@ -36,7 +36,7 @@ class PredicateHandlerTestCase: XCTestCase {
3636
}
3737

3838
// CHECK: Test Case 'PredicateHandlerTestCase.test_predicateIsTrueAfterTimeout_handlerIsNotCalled_fails' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
39-
// CHECK: .*/Tests/Functional/Asynchronous/Predicates/Handler/main.swift:[[@LINE+14]]: error: PredicateHandlerTestCase.test_predicateIsTrueAfterTimeout_handlerIsNotCalled_fails : Asynchronous wait failed - Exceeded timeout of 0.1 seconds, with unfulfilled expectations: Expect `<NSPredicate: 0x[0-9a-fA-F]{1,16}>` for object \d{4}-\d{2}-\d{2} \d+:\d+:\d+ \+\d+
39+
// CHECK: .*/Tests/Functional/Asynchronous/Predicates/Handler/main.swift:[[@LINE+14]]: error: PredicateHandlerTestCase.test_predicateIsTrueAfterTimeout_handlerIsNotCalled_fails : Asynchronous wait failed - Exceeded timeout of 0.1 seconds, with unfulfilled expectations: Expect predicate `<NSPredicate: 0x[0-9a-fA-F]{1,16}>` for object \d{4}-\d{2}-\d{2} \d+:\d+:\d+ \+\d+
4040
// CHECK: Test Case 'PredicateHandlerTestCase.test_predicateIsTrueAfterTimeout_handlerIsNotCalled_fails' failed \(\d+\.\d+ seconds\)
4141
func test_predicateIsTrueAfterTimeout_handlerIsNotCalled_fails() {
4242
let halfSecLaterDate = NSDate(timeIntervalSinceNow: 0.2)

0 commit comments

Comments
 (0)