Skip to content

Commit 1ee0178

Browse files
bnickelyounata
andauthored
Do not fail on timeout for toAlways and toNever (#1192)
* Prevent failures on heavy load when doing continuous tests * Add tests * Fix typo --------- Co-authored-by: Rachel Brindle <[email protected]>
1 parent 15106f1 commit 1ee0178

File tree

5 files changed

+39
-10
lines changed

5 files changed

+39
-10
lines changed

Sources/Nimble/DSL+Wait.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public class NMBWait: NSObject {
6969
}
7070
}
7171
}
72-
}.timeout(timeout, forcefullyAbortTimeout: leeway).wait(
72+
}.timeout(timeout, forcefullyAbortTimeout: leeway, isContinuous: false).wait(
7373
"waitUntil(...)",
7474
sourceLocation: SourceLocation(fileID: fileID, filePath: file, line: line, column: column)
7575
)

Sources/Nimble/Polling+AsyncAwait.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ internal actor Poller<T> {
4242
fnName: fnName) {
4343
if self.updateMatcherResult(result: try await matcherRunner())
4444
.toBoolean(expectation: style) {
45-
if matchStyle.isContinous {
45+
if matchStyle.isContinuous {
4646
return .incomplete
4747
}
4848
return .finished(true)
4949
} else {
50-
if matchStyle.isContinous {
50+
if matchStyle.isContinuous {
5151
return .finished(false)
5252
} else {
5353
return .incomplete

Sources/Nimble/Polling.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public struct PollingDefaults: @unchecked Sendable {
6969
internal enum AsyncMatchStyle {
7070
case eventually, never, always
7171

72-
var isContinous: Bool {
72+
var isContinuous: Bool {
7373
switch self {
7474
case .eventually:
7575
return false
@@ -96,15 +96,16 @@ internal func poll<T>(
9696
pollInterval: poll,
9797
timeoutInterval: timeout,
9898
sourceLocation: actualExpression.location,
99-
fnName: fnName) {
99+
fnName: fnName,
100+
isContinuous: matchStyle.isContinuous) {
100101
lastMatcherResult = try matcher.satisfies(uncachedExpression)
101102
if lastMatcherResult!.toBoolean(expectation: style) {
102-
if matchStyle.isContinous {
103+
if matchStyle.isContinuous {
103104
return .incomplete
104105
}
105106
return .finished(true)
106107
} else {
107-
if matchStyle.isContinous {
108+
if matchStyle.isContinuous {
108109
return .finished(false)
109110
} else {
110111
return .incomplete

Sources/Nimble/Utils/PollAwait.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ internal class AwaitPromiseBuilder<T> {
157157
self.trigger = trigger
158158
}
159159

160-
func timeout(_ timeoutInterval: NimbleTimeInterval, forcefullyAbortTimeout: NimbleTimeInterval) -> Self {
160+
func timeout(_ timeoutInterval: NimbleTimeInterval, forcefullyAbortTimeout: NimbleTimeInterval, isContinuous: Bool) -> Self {
161161
/// = Discussion =
162162
///
163163
/// There's a lot of technical decisions here that is useful to elaborate on. This is
@@ -217,7 +217,7 @@ internal class AwaitPromiseBuilder<T> {
217217
let didNotTimeOut = timedOutSem.wait(timeout: now) != .success
218218
let timeoutWasNotTriggered = semTimedOutOrBlocked.wait(timeout: .now()) == .success
219219
if didNotTimeOut && timeoutWasNotTriggered {
220-
if self.promise.resolveResult(.blockedRunLoop) {
220+
if self.promise.resolveResult(isContinuous ? .timedOut : .blockedRunLoop) {
221221
#if canImport(CoreFoundation)
222222
CFRunLoopStop(CFRunLoopGetMain())
223223
#else
@@ -385,6 +385,7 @@ internal func pollBlock(
385385
timeoutInterval: NimbleTimeInterval,
386386
sourceLocation: SourceLocation,
387387
fnName: String = #function,
388+
isContinuous: Bool,
388389
expression: @escaping () throws -> PollStatus) -> PollResult<Bool> {
389390
let awaiter = NimbleEnvironment.activeInstance.awaiter
390391
let result = awaiter.poll(pollInterval) { () throws -> Bool? in
@@ -393,7 +394,7 @@ internal func pollBlock(
393394
}
394395
return nil
395396
}
396-
.timeout(timeoutInterval, forcefullyAbortTimeout: timeoutInterval.divided)
397+
.timeout(timeoutInterval, forcefullyAbortTimeout: timeoutInterval.divided, isContinuous: isContinuous)
397398
.wait(fnName, sourceLocation: sourceLocation)
398399

399400
return result

Tests/NimbleTests/PollingTest.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,33 @@ final class PollingTest: XCTestCase {
137137
}
138138
}
139139
}
140+
141+
func testToEventuallyDetectsStalledMainThreadActivity() {
142+
func spinAndReturnTrue() -> Bool {
143+
Thread.sleep(forTimeInterval: 0.5)
144+
return true
145+
}
146+
let msg = "expected to eventually be true, got <true> (timed out, but main run loop was unresponsive)."
147+
failsWithErrorMessage(msg) {
148+
expect(spinAndReturnTrue()).toEventually(beTrue())
149+
}
150+
}
151+
152+
func testToNeverDoesNotFailStalledMainThreadActivity() {
153+
func spinAndReturnTrue() -> Bool {
154+
Thread.sleep(forTimeInterval: 0.5)
155+
return true
156+
}
157+
expect(spinAndReturnTrue()).toNever(beFalse())
158+
}
159+
160+
func testToAlwaysDetectsStalledMainThreadActivity() {
161+
func spinAndReturnTrue() -> Bool {
162+
Thread.sleep(forTimeInterval: 0.5)
163+
return true
164+
}
165+
expect(spinAndReturnTrue()).toAlways(beTrue())
166+
}
140167

141168
func testCombiningAsyncWaitUntilAndToEventuallyIsNotAllowed() {
142169
// Currently we are unable to catch Objective-C exceptions when built by the Swift Package Manager

0 commit comments

Comments
 (0)