Skip to content

Commit 825739c

Browse files
committed
Polling: Take in configuration arguments, add polling interval
1 parent edfe9d4 commit 825739c

File tree

2 files changed

+34
-26
lines changed

2 files changed

+34
-26
lines changed

Sources/Testing/Polling/Polling.swift

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@
2626
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
2727
public func confirmPassesEventually(
2828
_ comment: Comment? = nil,
29+
maxPollingIterations: Int = 1000,
30+
pollingInterval: Duration = .milliseconds(1),
2931
isolation: isolated (any Actor)? = #isolation,
3032
sourceLocation: SourceLocation = #_sourceLocation,
3133
_ body: @escaping () async throws -> Bool
3234
) async {
3335
let poller = Poller(
3436
pollingBehavior: .passesOnce,
37+
pollingIterations: maxPollingIterations,
38+
pollingInterval: pollingInterval,
3539
comment: comment,
3640
sourceLocation: sourceLocation
3741
)
@@ -70,15 +74,20 @@ public struct PollingFailedError: Error {}
7074
/// through other forms of `confirmation`.
7175
@_spi(Experimental)
7276
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
77+
@discardableResult
7378
public func confirmPassesEventually<R>(
7479
_ comment: Comment? = nil,
80+
maxPollingIterations: Int = 1000,
81+
pollingInterval: Duration = .milliseconds(1),
7582
isolation: isolated (any Actor)? = #isolation,
7683
sourceLocation: SourceLocation = #_sourceLocation,
7784
_ body: @escaping () async throws -> R?
7885
) async throws -> R where R: Sendable {
7986
let recorder = PollingRecorder<R>()
8087
let poller = Poller(
8188
pollingBehavior: .passesOnce,
89+
pollingIterations: maxPollingIterations,
90+
pollingInterval: pollingInterval,
8291
comment: comment,
8392
sourceLocation: sourceLocation
8493
)
@@ -113,12 +122,16 @@ public func confirmPassesEventually<R>(
113122
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
114123
public func confirmAlwaysPasses(
115124
_ comment: Comment? = nil,
125+
maxPollingIterations: Int = 1000,
126+
pollingInterval: Duration = .milliseconds(1),
116127
isolation: isolated (any Actor)? = #isolation,
117128
sourceLocation: SourceLocation = #_sourceLocation,
118129
_ body: @escaping () async throws -> Bool
119130
) async {
120131
let poller = Poller(
121132
pollingBehavior: .passesAlways,
133+
pollingIterations: maxPollingIterations,
134+
pollingInterval: pollingInterval,
122135
comment: comment,
123136
sourceLocation: sourceLocation
124137
)
@@ -153,28 +166,26 @@ public func confirmAlwaysPasses(
153166
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
154167
public func confirmAlwaysPasses<R>(
155168
_ comment: Comment? = nil,
169+
maxPollingIterations: Int = 1000,
170+
pollingInterval: Duration = .milliseconds(1),
156171
isolation: isolated (any Actor)? = #isolation,
157172
sourceLocation: SourceLocation = #_sourceLocation,
158173
_ body: @escaping () async throws -> R?
159-
) async throws -> R where R: Sendable {
160-
let recorder = PollingRecorder<R>()
174+
) async {
161175
let poller = Poller(
162176
pollingBehavior: .passesAlways,
177+
pollingIterations: maxPollingIterations,
178+
pollingInterval: pollingInterval,
163179
comment: comment,
164180
sourceLocation: sourceLocation
165181
)
166182
await poller.evaluate(isolation: isolation) {
167183
do {
168-
return try await recorder.record(value: body())
184+
return try await body() != nil
169185
} catch {
170186
return false
171187
}
172188
}
173-
174-
if let value = await recorder.lastValue {
175-
return value
176-
}
177-
throw PollingFailedError()
178189
}
179190

180191
/// A type to record the last value returned by a closure returning an optional
@@ -289,6 +300,11 @@ private struct Poller {
289300
/// while the expression continues to pass)
290301
let pollingBehavior: PollingBehavior
291302

303+
// How many times to poll
304+
let pollingIterations: Int
305+
// Minimum waiting period between polling
306+
let pollingInterval: Duration
307+
292308
/// A comment from the test author associated with the polling
293309
let comment: Comment?
294310

@@ -306,7 +322,6 @@ private struct Poller {
306322
_ body: @escaping () async -> Bool
307323
) async {
308324
let result = await poll(
309-
runAmount: 1_000_000,
310325
expression: body
311326
)
312327
result.issue(
@@ -325,20 +340,22 @@ private struct Poller {
325340
/// - timeout: How long to poll for unitl the timeout triggers.
326341
/// - Returns: The result of this polling.
327342
private func poll(
328-
runAmount: Int,
329343
isolation: isolated (any Actor)? = #isolation,
330344
expression: @escaping () async -> Bool
331345
) async -> PollResult {
332-
for _ in 0..<runAmount {
333-
if Task.isCancelled {
334-
return .cancelled
335-
}
346+
for _ in 0..<pollingIterations {
336347
if let result = await pollingBehavior.processFinishedExpression(
337348
expressionResult: expression()
338349
) {
339350
return result
340351
}
341-
await Task.yield()
352+
do {
353+
try await Task.sleep(for: pollingInterval)
354+
} catch {
355+
// `Task.sleep` should only throw an error if it's cancelled
356+
// during the sleep period.
357+
return .cancelled
358+
}
342359
}
343360
return .ranToCompletion
344361
}

Tests/TestingTests/PollingTests.swift

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,12 @@ struct PollingTests {
6565
await confirmAlwaysPasses { true }
6666
}
6767

68-
@Test("Returning value returns the last value from the expression")
69-
func returnsLastValueReturned() async throws {
70-
let incrementor = Incrementor()
71-
let value = try await confirmAlwaysPasses {
72-
await incrementor.increment()
73-
}
74-
#expect(value > 1)
75-
}
76-
7768
@Test("Simple failing expressions") func trivialSadPath() async {
7869
let issues = await runTest {
7970
await confirmAlwaysPasses { false }
80-
_ = try await confirmAlwaysPasses { Optional<Int>.none }
71+
await confirmAlwaysPasses { Optional<Int>.none }
8172
}
82-
#expect(issues.count == 3)
73+
#expect(issues.count == 2)
8374
}
8475

8576
@Test("if the closures starts off as true, but becomes false")

0 commit comments

Comments
 (0)