Skip to content

Commit 0a14466

Browse files
authored
Fix bug where expect(nil).toAlways(equal(0)) would erroneously pass (#1121)
* Fix bug where expect(nil).toAlways(equal(0)) would erroneously pass * Work with earlier versions of swift
1 parent b7c23fe commit 0a14466

File tree

9 files changed

+88
-31
lines changed

9 files changed

+88
-31
lines changed

Sources/Nimble/Polling+AsyncAwait.swift

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,19 @@ internal actor Poller<T> {
4141
file: expression.location.file,
4242
line: expression.location.line,
4343
fnName: fnName) {
44-
self.updateMatcherResult(result: try await matcherRunner())
45-
.toBoolean(expectation: style)
44+
if self.updateMatcherResult(result: try await matcherRunner())
45+
.toBoolean(expectation: style) {
46+
if matchStyle.isContinous {
47+
return .incomplete
48+
}
49+
return .finished(true)
50+
} else {
51+
if matchStyle.isContinous {
52+
return .finished(false)
53+
} else {
54+
return .incomplete
55+
}
56+
}
4657
}
4758
return processPollResult(result.toPollResult(), matchStyle: matchStyle, lastMatcherResult: lastMatcherResult, fnName: fnName)
4859
}
@@ -152,7 +163,7 @@ extension SyncExpectation {
152163
description: description) {
153164
await poll(
154165
expression: asyncExpression,
155-
style: .toMatch,
166+
style: .toNotMatch,
156167
matchStyle: .never,
157168
timeout: until,
158169
poll: pollInterval,
@@ -186,7 +197,7 @@ extension SyncExpectation {
186197
description: description) {
187198
await poll(
188199
expression: asyncExpression,
189-
style: .toNotMatch,
200+
style: .toMatch,
190201
matchStyle: .always,
191202
timeout: until,
192203
poll: pollInterval,
@@ -282,7 +293,7 @@ extension SyncExpectation {
282293
description: description) {
283294
await poll(
284295
expression: asyncExpression,
285-
style: .toMatch,
296+
style: .toNotMatch,
286297
matchStyle: .never,
287298
timeout: until,
288299
poll: pollInterval,
@@ -316,7 +327,7 @@ extension SyncExpectation {
316327
description: description) {
317328
await poll(
318329
expression: asyncExpression,
319-
style: .toNotMatch,
330+
style: .toMatch,
320331
matchStyle: .always,
321332
timeout: until,
322333
poll: pollInterval,
@@ -409,7 +420,7 @@ extension AsyncExpectation {
409420
description: description) {
410421
await poll(
411422
expression: expression,
412-
style: .toMatch,
423+
style: .toNotMatch,
413424
matchStyle: .never,
414425
timeout: until,
415426
poll: pollInterval,
@@ -442,7 +453,7 @@ extension AsyncExpectation {
442453
description: description) {
443454
await poll(
444455
expression: expression,
445-
style: .toNotMatch,
456+
style: .toMatch,
446457
matchStyle: .always,
447458
timeout: until,
448459
poll: pollInterval,
@@ -533,7 +544,7 @@ extension AsyncExpectation {
533544
description: description) {
534545
await poll(
535546
expression: expression,
536-
style: .toMatch,
547+
style: .toNotMatch,
537548
matchStyle: .never,
538549
timeout: until,
539550
poll: pollInterval,
@@ -566,7 +577,7 @@ extension AsyncExpectation {
566577
description: description) {
567578
await poll(
568579
expression: expression,
569-
style: .toNotMatch,
580+
style: .toMatch,
570581
matchStyle: .always,
571582
timeout: until,
572583
poll: pollInterval,

Sources/Nimble/Polling+Require.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ extension SyncRequirement {
107107
expression,
108108
.toNotMatch,
109109
poll(
110-
style: .toMatch,
110+
style: .toNotMatch,
111111
matchStyle: .never,
112112
matcher: matcher,
113113
timeout: until,
@@ -158,7 +158,7 @@ extension SyncRequirement {
158158
expression,
159159
.toMatch,
160160
poll(
161-
style: .toNotMatch,
161+
style: .toMatch,
162162
matchStyle: .always,
163163
matcher: matcher,
164164
timeout: until,
@@ -266,7 +266,7 @@ extension SyncRequirement {
266266
description: description) {
267267
await poll(
268268
expression: asyncExpression,
269-
style: .toMatch,
269+
style: .toNotMatch,
270270
matchStyle: .never,
271271
timeout: until,
272272
poll: pollInterval,
@@ -300,7 +300,7 @@ extension SyncRequirement {
300300
description: description) {
301301
await poll(
302302
expression: asyncExpression,
303-
style: .toNotMatch,
303+
style: .toMatch,
304304
matchStyle: .always,
305305
timeout: until,
306306
poll: pollInterval,
@@ -396,7 +396,7 @@ extension SyncRequirement {
396396
description: description) {
397397
await poll(
398398
expression: asyncExpression,
399-
style: .toMatch,
399+
style: .toNotMatch,
400400
matchStyle: .never,
401401
timeout: until,
402402
poll: pollInterval,
@@ -430,7 +430,7 @@ extension SyncRequirement {
430430
description: description) {
431431
await poll(
432432
expression: asyncExpression,
433-
style: .toNotMatch,
433+
style: .toMatch,
434434
matchStyle: .always,
435435
timeout: until,
436436
poll: pollInterval,
@@ -523,7 +523,7 @@ extension AsyncRequirement {
523523
description: description) {
524524
await poll(
525525
expression: expression,
526-
style: .toMatch,
526+
style: .toNotMatch,
527527
matchStyle: .never,
528528
timeout: until,
529529
poll: pollInterval,
@@ -556,7 +556,7 @@ extension AsyncRequirement {
556556
description: description) {
557557
await poll(
558558
expression: expression,
559-
style: .toNotMatch,
559+
style: .toMatch,
560560
matchStyle: .always,
561561
timeout: until,
562562
poll: pollInterval,
@@ -647,7 +647,7 @@ extension AsyncRequirement {
647647
description: description) {
648648
await poll(
649649
expression: expression,
650-
style: .toMatch,
650+
style: .toNotMatch,
651651
matchStyle: .never,
652652
timeout: until,
653653
poll: pollInterval,
@@ -680,7 +680,7 @@ extension AsyncRequirement {
680680
description: description) {
681681
await poll(
682682
expression: expression,
683-
style: .toNotMatch,
683+
style: .toMatch,
684684
matchStyle: .always,
685685
timeout: until,
686686
poll: pollInterval,

Sources/Nimble/Polling.swift

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ public struct PollingDefaults {
4141

4242
internal enum AsyncMatchStyle {
4343
case eventually, never, always
44+
45+
var isContinous: Bool {
46+
switch self {
47+
case .eventually:
48+
return false
49+
case .never, .always:
50+
return true
51+
}
52+
}
4453
}
4554

4655
// swiftlint:disable:next function_parameter_count
@@ -63,7 +72,18 @@ internal func poll<T>(
6372
line: actualExpression.location.line,
6473
fnName: fnName) {
6574
lastMatcherResult = try matcher.satisfies(uncachedExpression)
66-
return lastMatcherResult!.toBoolean(expectation: style)
75+
if lastMatcherResult!.toBoolean(expectation: style) {
76+
if matchStyle.isContinous {
77+
return .incomplete
78+
}
79+
return .finished(true)
80+
} else {
81+
if matchStyle.isContinous {
82+
return .finished(false)
83+
} else {
84+
return .incomplete
85+
}
86+
}
6787
}
6888
return processPollResult(result, matchStyle: matchStyle, lastMatcherResult: lastMatcherResult, fnName: fnName)
6989
}
@@ -220,7 +240,7 @@ extension SyncExpectation {
220240
expression,
221241
.toNotMatch,
222242
poll(
223-
style: .toMatch,
243+
style: .toNotMatch,
224244
matchStyle: .never,
225245
matcher: matcher,
226246
timeout: until,
@@ -271,7 +291,7 @@ extension SyncExpectation {
271291
expression,
272292
.toMatch,
273293
poll(
274-
style: .toNotMatch,
294+
style: .toMatch,
275295
matchStyle: .always,
276296
matcher: matcher,
277297
timeout: until,

Sources/Nimble/Utils/AsyncAwait.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,11 @@ private func timeout<T>(timeoutQueue: DispatchQueue, timeoutInterval: NimbleTime
165165
return await promise.value
166166
}
167167

168-
private func poll(_ pollInterval: NimbleTimeInterval, expression: @escaping () async throws -> Bool) async -> AsyncPollResult<Bool> {
168+
private func poll(_ pollInterval: NimbleTimeInterval, expression: @escaping () async throws -> PollStatus) async -> AsyncPollResult<Bool> {
169169
for try await _ in AsyncTimerSequence(interval: pollInterval) {
170170
do {
171-
if try await expression() {
172-
return .completed(true)
171+
if case .finished(let result) = try await expression() {
172+
return .completed(result)
173173
}
174174
} catch {
175175
return .errorThrown(error)
@@ -199,7 +199,7 @@ private func runPoller(
199199
pollInterval: NimbleTimeInterval,
200200
awaiter: Awaiter,
201201
fnName: String = #function, file: FileString = #file, line: UInt = #line,
202-
expression: @escaping () async throws -> Bool
202+
expression: @escaping () async throws -> PollStatus
203203
) async -> AsyncPollResult<Bool> {
204204
awaiter.waitLock.acquireWaitingLock(
205205
fnName,
@@ -324,7 +324,7 @@ internal func pollBlock(
324324
file: FileString,
325325
line: UInt,
326326
fnName: String = #function,
327-
expression: @escaping () async throws -> Bool) async -> AsyncPollResult<Bool> {
327+
expression: @escaping () async throws -> PollStatus) async -> AsyncPollResult<Bool> {
328328
await runPoller(
329329
timeoutInterval: timeoutInterval,
330330
pollInterval: pollInterval,

Sources/Nimble/Utils/PollAwait.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ internal enum PollResult<T> {
9898
}
9999
}
100100

101+
internal enum PollStatus {
102+
case finished(Bool)
103+
case incomplete
104+
}
105+
101106
/// Holds the resulting value from an asynchronous expectation.
102107
/// This class is thread-safe at receiving a "response" to this promise.
103108
internal final class AwaitPromise<T> {
@@ -399,11 +404,11 @@ internal func pollBlock(
399404
file: FileString,
400405
line: UInt,
401406
fnName: String = #function,
402-
expression: @escaping () throws -> Bool) -> PollResult<Bool> {
407+
expression: @escaping () throws -> PollStatus) -> PollResult<Bool> {
403408
let awaiter = NimbleEnvironment.activeInstance.awaiter
404409
let result = awaiter.poll(pollInterval) { () throws -> Bool? in
405-
if try expression() {
406-
return true
410+
if case .finished(let result) = try expression() {
411+
return result
407412
}
408413
return nil
409414
}

Tests/NimbleTests/AsyncAwaitTest+Require.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ final class AsyncAwaitRequireTest: XCTestCase { // swiftlint:disable:this type_b
243243
await failsWithErrorMessage("unexpected error thrown: <\(errorToThrow)>") {
244244
try await require { try self.doThrowError() }.neverTo(equal(0))
245245
}
246+
await failsWithErrorMessage("expected to never equal <1>, got <1>") {
247+
try await require(1).toNever(equal(1))
248+
}
246249
}
247250

248251
func testToAlwaysPositiveMatches() async throws {
@@ -276,6 +279,9 @@ final class AsyncAwaitRequireTest: XCTestCase { // swiftlint:disable:this type_b
276279
await failsWithErrorMessage("unexpected error thrown: <\(errorToThrow)>") {
277280
try await require { try self.doThrowError() }.alwaysTo(equal(0))
278281
}
282+
await failsWithErrorMessage("expected to always equal <0>, got <nil> (use beNil() to match nils)") {
283+
try await require(nil).toAlways(equal(0))
284+
}
279285
}
280286
}
281287

Tests/NimbleTests/AsyncAwaitTest.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,9 @@ final class AsyncAwaitTest: XCTestCase { // swiftlint:disable:this type_body_len
372372
await failsWithErrorMessage("unexpected error thrown: <\(errorToThrow)>") {
373373
await expect { try self.doThrowError() }.alwaysTo(equal(0))
374374
}
375+
await failsWithErrorMessage("expected to always equal <0>, got <nil> (use beNil() to match nils)") {
376+
await expect(nil).toAlways(equal(0))
377+
}
375378
}
376379
}
377380

Tests/NimbleTests/PollingTest+Require.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ final class PollingRequireTest: XCTestCase {
174174
failsWithErrorMessage("unexpected error thrown: <\(errorToThrow)>") {
175175
try require { try self.doThrowError() }.neverTo(equal(0))
176176
}
177+
failsWithErrorMessage("expected to never equal <1>, got <1>") {
178+
try require(1).toNever(equal(1))
179+
}
177180
}
178181

179182
func testToAlwaysPositiveMatches() throws {
@@ -207,6 +210,9 @@ final class PollingRequireTest: XCTestCase {
207210
failsWithErrorMessage("unexpected error thrown: <\(errorToThrow)>") {
208211
try require { try self.doThrowError() }.alwaysTo(equal(0))
209212
}
213+
failsWithErrorMessage("expected to always equal <1>, got <nil> (use beNil() to match nils)") {
214+
try require(nil).toAlways(equal(1))
215+
}
210216
}
211217
}
212218

Tests/NimbleTests/PollingTest.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,9 @@ final class PollingTest: XCTestCase {
295295
failsWithErrorMessage("unexpected error thrown: <\(errorToThrow)>") {
296296
expect { try self.doThrowError() }.neverTo(equal(0))
297297
}
298+
failsWithErrorMessage("expected to never equal <0>, got <0>") {
299+
expect(0).toNever(equal(0))
300+
}
298301
}
299302

300303
func testToAlwaysPositiveMatches() {
@@ -328,6 +331,9 @@ final class PollingTest: XCTestCase {
328331
failsWithErrorMessage("unexpected error thrown: <\(errorToThrow)>") {
329332
expect { try self.doThrowError() }.alwaysTo(equal(0))
330333
}
334+
failsWithErrorMessage("expected to always equal <0>, got <nil> (use beNil() to match nils)") {
335+
expect(nil).toAlways(equal(0))
336+
}
331337
}
332338
}
333339

0 commit comments

Comments
 (0)