Skip to content

Commit fa6c58f

Browse files
authored
Merge pull request #800 from rkreutz/feature/to-never
Adding toNever expectation
2 parents 17a1f9c + 07d6799 commit fa6c58f

File tree

2 files changed

+110
-5
lines changed

2 files changed

+110
-5
lines changed

Sources/Nimble/Matchers/Async.swift

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,19 @@ extension AsyncDefaults {
1515
public static var PollInterval: TimeInterval = 0.01
1616
}
1717

18-
private func async<T>(style: ExpectationStyle, predicate: Predicate<T>, timeout: DispatchTimeInterval, poll: DispatchTimeInterval, fnName: String) -> Predicate<T> {
18+
private enum AsyncMatchStyle {
19+
case eventually, never
20+
}
21+
22+
// swiftlint:disable:next function_parameter_count
23+
private func async<T>(
24+
style: ExpectationStyle,
25+
matchStyle: AsyncMatchStyle,
26+
predicate: Predicate<T>,
27+
timeout: DispatchTimeInterval,
28+
poll: DispatchTimeInterval,
29+
fnName: String
30+
) -> Predicate<T> {
1931
return Predicate { actualExpression in
2032
let uncachedExpression = actualExpression.withoutCaching()
2133
let fnName = "expect(...).\(fnName)(...)"
@@ -30,10 +42,24 @@ private func async<T>(style: ExpectationStyle, predicate: Predicate<T>, timeout:
3042
return lastPredicateResult!.toBoolean(expectation: style)
3143
}
3244
switch result {
33-
case .completed: return lastPredicateResult!
45+
case .completed:
46+
switch matchStyle {
47+
case .eventually:
48+
return lastPredicateResult!
49+
case .never:
50+
return PredicateResult(
51+
status: .fail,
52+
message: lastPredicateResult?.message ?? .fail("matched the predicate when it shouldn't have")
53+
)
54+
}
3455
case .timedOut:
35-
let message = lastPredicateResult?.message ?? .fail("timed out before returning a value")
36-
return PredicateResult(status: .fail, message: message)
56+
switch matchStyle {
57+
case .eventually:
58+
let message = lastPredicateResult?.message ?? .fail("timed out before returning a value")
59+
return PredicateResult(status: .fail, message: message)
60+
case .never:
61+
return PredicateResult(status: .doesNotMatch, message: .expectedTo("never match the predicate"))
62+
}
3763
case let .errorThrown(error):
3864
return PredicateResult(status: .fail, message: .fail("unexpected error thrown: <\(error)>"))
3965
case let .raisedException(exception):
@@ -68,7 +94,14 @@ extension Expectation {
6894
let (pass, msg) = execute(
6995
expression,
7096
.toMatch,
71-
async(style: .toMatch, predicate: predicate, timeout: timeout, poll: pollInterval, fnName: "toEventually"),
97+
async(
98+
style: .toMatch,
99+
matchStyle: .eventually,
100+
predicate: predicate,
101+
timeout: timeout,
102+
poll: pollInterval,
103+
fnName: "toEventually"
104+
),
72105
to: "to eventually",
73106
description: description,
74107
captureExceptions: false
@@ -90,6 +123,7 @@ extension Expectation {
90123
.toNotMatch,
91124
async(
92125
style: .toNotMatch,
126+
matchStyle: .eventually,
93127
predicate: predicate,
94128
timeout: timeout,
95129
poll: pollInterval,
@@ -113,4 +147,43 @@ extension Expectation {
113147
public func toNotEventually(_ predicate: Predicate<T>, timeout: DispatchTimeInterval = AsyncDefaults.timeout, pollInterval: DispatchTimeInterval = AsyncDefaults.pollInterval, description: String? = nil) {
114148
return toEventuallyNot(predicate, timeout: timeout, pollInterval: pollInterval, description: description)
115149
}
150+
151+
/// Tests the actual value using a matcher to never match by checking
152+
/// continuously at each pollInterval until the timeout is reached.
153+
///
154+
/// @discussion
155+
/// This function manages the main run loop (`NSRunLoop.mainRunLoop()`) while this function
156+
/// is executing. Any attempts to touch the run loop may cause non-deterministic behavior.
157+
public func toNever(_ predicate: Predicate<T>, until: DispatchTimeInterval = AsyncDefaults.timeout, pollInterval: DispatchTimeInterval = AsyncDefaults.pollInterval, description: String? = nil) {
158+
nimblePrecondition(expression.isClosure, "NimbleInternalError", toEventuallyRequiresClosureError.stringValue)
159+
160+
let (pass, msg) = execute(
161+
expression,
162+
.toNotMatch,
163+
async(
164+
style: .toMatch,
165+
matchStyle: .never,
166+
predicate: predicate,
167+
timeout: until,
168+
poll: pollInterval,
169+
fnName: "toNever"
170+
),
171+
to: "to never",
172+
description: description,
173+
captureExceptions: false
174+
)
175+
verify(pass, msg)
176+
}
177+
178+
/// Tests the actual value using a matcher to never match by checking
179+
/// continuously at each pollInterval until the timeout is reached.
180+
///
181+
/// Alias of toNever()
182+
///
183+
/// @discussion
184+
/// This function manages the main run loop (`NSRunLoop.mainRunLoop()`) while this function
185+
/// is executing. Any attempts to touch the run loop may cause non-deterministic behavior.
186+
public func neverTo(_ predicate: Predicate<T>, until: DispatchTimeInterval = AsyncDefaults.timeout, pollInterval: DispatchTimeInterval = AsyncDefaults.pollInterval, description: String? = nil) {
187+
return toNever(predicate, until: until, pollInterval: pollInterval, description: description)
188+
}
116189
}

Tests/NimbleTests/AsynchronousTest.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,4 +250,36 @@ final class AsyncTest: XCTestCase {
250250
}
251251
}
252252

253+
func testToNeverPositiveMatches() {
254+
var value = 0
255+
deferToMainQueue { value = 1 }
256+
expect { value }.toNever(beGreaterThan(1))
257+
258+
deferToMainQueue { value = 0 }
259+
expect { value }.neverTo(beGreaterThan(1))
260+
}
261+
262+
func testToNeverNegativeMatches() {
263+
var value = 0
264+
failsWithErrorMessage("expected to never equal <0>, got <0>") {
265+
expect { value }.toNever(equal(0))
266+
}
267+
failsWithErrorMessage("expected to never equal <0>, got <0>") {
268+
expect { value }.neverTo(equal(0))
269+
}
270+
failsWithErrorMessage("expected to never equal <1>, got <1>") {
271+
deferToMainQueue { value = 1 }
272+
expect { value }.toNever(equal(1))
273+
}
274+
failsWithErrorMessage("expected to never equal <1>, got <1>") {
275+
deferToMainQueue { value = 1 }
276+
expect { value }.neverTo(equal(1))
277+
}
278+
failsWithErrorMessage("unexpected error thrown: <\(errorToThrow)>") {
279+
expect { try self.doThrowError() }.toNever(equal(0))
280+
}
281+
failsWithErrorMessage("unexpected error thrown: <\(errorToThrow)>") {
282+
expect { try self.doThrowError() }.neverTo(equal(0))
283+
}
284+
}
253285
}

0 commit comments

Comments
 (0)