From 6b0178d8cb86a2d90bece7e649283378601db0f3 Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Fri, 23 Dec 2022 21:43:26 +0530 Subject: [PATCH 01/22] test: improve async tests correctness --- .../NonThrowingFutureTests.swift | 8 - Tests/AsyncObjectsTests/TaskQueueTests.swift | 268 ++++++++++-------- .../ThrowingFutureTests.swift | 18 -- 3 files changed, 142 insertions(+), 152 deletions(-) diff --git a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift index d2a33063..ef3462cb 100644 --- a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift @@ -99,8 +99,6 @@ class NonThrowingFutureCombiningTests: XCTestCase { let value = await allFuture.get() XCTAssertEqual(value, [1, 3, 2]) } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(producing: 1) @@ -136,8 +134,6 @@ class NonThrowingFutureCombiningTests: XCTestCase { } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(producing: 1) @@ -168,8 +164,6 @@ class NonThrowingFutureCombiningTests: XCTestCase { XCTAssertEqual(value, 1) } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(producing: 1) @@ -200,8 +194,6 @@ class NonThrowingFutureCombiningTests: XCTestCase { XCTAssertEqual(value, 1) } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(producing: 1) diff --git a/Tests/AsyncObjectsTests/TaskQueueTests.swift b/Tests/AsyncObjectsTests/TaskQueueTests.swift index 8d211b05..4d0cfced 100644 --- a/Tests/AsyncObjectsTests/TaskQueueTests.swift +++ b/Tests/AsyncObjectsTests/TaskQueueTests.swift @@ -40,7 +40,7 @@ class TaskQueueTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in options.forEach { option in group.addTask { - try await Self.checkWaitOnQueue(option: option) + try await self.checkWaitOnQueue(option: option) } } try await group.waitForAll() @@ -79,7 +79,7 @@ class TaskQueueTests: XCTestCase { @MainActor class TaskQueueTimeoutTests: XCTestCase { - private static func checkWaitTimeoutOnQueue( + private func checkWaitTimeoutOnQueue( option: TaskOption, file: StaticString = #filePath, function: StaticString = #function, @@ -94,16 +94,17 @@ class TaskQueueTimeoutTests: XCTestCase { file: file, function: function, line: line ) { try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await queue.exec( - priority: option.task, - flags: [option.flags, .block] - ) { - try await Self.sleep(seconds: 2) + await withUnsafeContinuation { continuation in + group.addTask { + try await queue.exec( + priority: option.task, + flags: [option.flags, .block] + ) { + continuation.resume() + try await Self.sleep(seconds: 2) + } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { do { try await queue.wait(forSeconds: 1) @@ -141,7 +142,7 @@ class TaskQueueTimeoutTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in options.forEach { option in group.addTask { - try await Self.checkWaitTimeoutOnQueue(option: option) + try await self.checkWaitTimeoutOnQueue(option: option) } } try await group.waitForAll() @@ -150,7 +151,7 @@ class TaskQueueTimeoutTests: XCTestCase { #if swift(>=5.7) @available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) - private static func checkWaitTimeoutOnQueue( + private func checkWaitTimeoutOnQueue( option: TaskOption, clock: C, file: StaticString = #filePath, @@ -166,16 +167,17 @@ class TaskQueueTimeoutTests: XCTestCase { file: file, function: function, line: line ) { try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await queue.exec( - priority: option.task, - flags: [option.flags, .block] - ) { - try await Self.sleep(seconds: 2, clock: clock) + await withUnsafeContinuation { continuation in + group.addTask { + try await queue.exec( + priority: option.task, + flags: [option.flags, .block] + ) { + continuation.resume() + try await Self.sleep(seconds: 2, clock: clock) + } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01, clock: clock) group.addTask { do { try await queue.wait(forSeconds: 1, clock: clock) @@ -220,7 +222,7 @@ class TaskQueueTimeoutTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in options.forEach { option in group.addTask { - try await Self.checkWaitTimeoutOnQueue( + try await self.checkWaitTimeoutOnQueue( option: option, clock: clock ) @@ -258,13 +260,14 @@ class TaskQueueBlockOperationTests: XCTestCase { let queue = TaskQueue() try await Self.checkExecInterval(durationInSeconds: 1) { try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await queue.exec { - try await Self.sleep(seconds: 1) + await withUnsafeContinuation { continuation in + group.addTask { + try await queue.exec { + continuation.resume() + try await Self.sleep(seconds: 1) + } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec(flags: .block) { try await Self.sleep(seconds: 1) @@ -279,13 +282,14 @@ class TaskQueueBlockOperationTests: XCTestCase { let queue = TaskQueue() try await Self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 2) + await withUnsafeContinuation { continuation in + group.addTask { + try await queue.exec(flags: .block) { + continuation.resume() + try await Self.sleep(seconds: 2) + } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec { try await Self.sleep(seconds: 1) @@ -350,8 +354,6 @@ class TaskQueueBlockOperationTests: XCTestCase { // Throws error for waiting method throw CancellationError() } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec(flags: .block) { try await Self.sleep(seconds: 2) @@ -379,8 +381,6 @@ class TaskQueueBlockOperationTests: XCTestCase { // Throws error for waiting method throw CancellationError() } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec(flags: .block) { try await Self.sleep(seconds: 2) @@ -413,8 +413,6 @@ class TaskQueueBlockOperationTests: XCTestCase { // Throws error for waiting method throw CancellationError() } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec(flags: .block) { try await Self.sleep(seconds: 2) @@ -470,13 +468,14 @@ class TaskQueueBarrierOperationTests: XCTestCase { let queue = TaskQueue() try await Self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await queue.exec { - try await Self.sleep(seconds: 2) + await withUnsafeContinuation { continuation in + group.addTask { + try await queue.exec { + continuation.resume() + try await Self.sleep(seconds: 2) + } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec(flags: .barrier) { try await Self.sleep(seconds: 1) @@ -491,13 +490,14 @@ class TaskQueueBarrierOperationTests: XCTestCase { let queue = TaskQueue() try await Self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 2) + await withUnsafeContinuation { continuation in + group.addTask { + try await queue.exec(flags: .barrier) { + continuation.resume() + try await Self.sleep(seconds: 2) + } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec { try await Self.sleep(seconds: 1) @@ -562,8 +562,6 @@ class TaskQueueBarrierOperationTests: XCTestCase { // Throws error for waiting method throw CancellationError() } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec(flags: .barrier) { try await Self.sleep(seconds: 2) @@ -591,8 +589,6 @@ class TaskQueueBarrierOperationTests: XCTestCase { // Throws error for waiting method throw CancellationError() } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec(flags: .barrier) { try await Self.sleep(seconds: 3) @@ -625,8 +621,6 @@ class TaskQueueBarrierOperationTests: XCTestCase { // Throws error for waiting method throw CancellationError() } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec(flags: .barrier) { try await Self.sleep(seconds: 3) @@ -663,13 +657,14 @@ class TaskQueueMixedOperationTests: XCTestCase { let queue = TaskQueue() try await Self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 2) + await withUnsafeContinuation { continuation in + group.addTask { + try await queue.exec(flags: .block) { + continuation.resume() + try await Self.sleep(seconds: 2) + } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec(flags: .barrier) { try await Self.sleep(seconds: 1) @@ -684,13 +679,14 @@ class TaskQueueMixedOperationTests: XCTestCase { let queue = TaskQueue() try await Self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 2) + await withUnsafeContinuation { continuation in + group.addTask { + try await queue.exec(flags: .barrier) { + continuation.resume() + try await Self.sleep(seconds: 2) + } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.exec(flags: .block) { try await Self.sleep(seconds: 1) @@ -708,21 +704,27 @@ class TaskQueueMixedOperationTests: XCTestCase { // Concurrent + Barrier try await Self.checkExecInterval(durationInSeconds: 5) { try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await queue.exec { - try await Self.sleep(seconds: 2) + await withUnsafeContinuation { continuation in + group.addTask { + try await queue.exec { + continuation.resume() + try await Self.sleep(seconds: 2) + } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) - await group.addTaskAndStart { + group.addTask { try await queue.exec(flags: .block) { try await Self.sleep(seconds: 1) } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) - await group.addTaskAndStart { + while let (_, (_, flags)) = await Task(priority: .background, operation: { + let items = await queue.queue + return items.reversed().first + }).value { + guard flags.contains(.block) else { continue } + break + } + group.addTask { try await queue.exec(flags: .barrier) { try await Self.sleep(seconds: 3) } @@ -739,21 +741,27 @@ class TaskQueueMixedOperationTests: XCTestCase { // Concurrent + Barrier + Block await Self.checkExecInterval(durationInSeconds: 6) { await withTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - await queue.exec { - try! await Self.sleep(seconds: 3) + await withUnsafeContinuation { continuation in + group.addTask { + await queue.exec { + continuation.resume() + try! await Self.sleep(seconds: 3) + } } } - // Make sure previous tasks started - try! await Self.sleep(seconds: 0.01) - await group.addTaskAndStart { + group.addTask { await queue.exec(flags: .barrier) { try! await Self.sleep(seconds: 2) } } - // Make sure previous tasks started - try! await Self.sleep(seconds: 0.01) - await group.addTaskAndStart { + while let (_, (_, flags)) = await Task(priority: .background, operation: { + let items = await queue.queue + return items.reversed().first + }).value { + guard flags.contains(.barrier) else { continue } + break + } + group.addTask { await queue.exec(flags: .block) { try! await Self.sleep(seconds: 1) } @@ -769,30 +777,33 @@ class TaskQueueMixedOperationTests: XCTestCase { let queue = TaskQueue() await Self.checkExecInterval(durationInSeconds: 8) { await withTaskGroup(of: Void.self) { group in - group.addTask { - await queue.exec { - try! await Self.sleep(seconds: 1) - } - } - group.addTask { - await queue.exec { - try! await Self.sleep(seconds: 2) + await withTaskGroup(of: Void.self) { cgroup in + for i in 0..<3 { + cgroup.addTask { + await withUnsafeContinuation { continuation in + Task { + await queue.exec { + continuation.resume() + try! await Self.sleep(seconds: i + 1) + } + } + } + } } + await cgroup.waitForAll() } group.addTask { - await queue.exec { - try! await Self.sleep(seconds: 3) - } - } - // Make sure previous tasks started - try! await Self.sleep(seconds: 0.01) - await group.addTaskAndStart { await queue.exec(flags: .barrier) { try! await Self.sleep(seconds: 2) } } - // Make sure previous tasks started - try! await Self.sleep(seconds: 0.01) + while let (_, (_, flags)) = await Task(priority: .background, operation: { + let items = await queue.queue + return items.reversed().first + }).value { + guard flags.contains(.barrier) else { continue } + break + } group.addTask { await queue.exec { try! await Self.sleep(seconds: 1) @@ -816,30 +827,33 @@ class TaskQueueMixedOperationTests: XCTestCase { let queue = TaskQueue() try await Self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in - group.addTask { - try await queue.exec { - try await Self.sleep(seconds: 1) - } - } - group.addTask { - try await queue.exec { - try await Self.sleep(seconds: 2) + try await withThrowingTaskGroup(of: Void.self) { cgroup in + for i in 0..<3 { + cgroup.addTask { + await withUnsafeContinuation { continuation in + Task { + try await queue.exec { + continuation.resume() + try await Self.sleep(seconds: i + 1) + } + } + } + } } + try await cgroup.waitForAll() } group.addTask { - try await queue.exec { - try await Self.sleep(seconds: 3) - } - } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) - await group.addTaskAndStart { try await queue.exec(flags: .barrier) { try await Self.sleep(seconds: 2) } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) + while let (_, (_, flags)) = await Task(priority: .background, operation: { + let items = await queue.queue + return items.reversed().first + }).value { + guard flags.contains(.barrier) else { continue } + break + } group.addTask { try await queue.exec { try await Self.sleep(seconds: 2) @@ -869,8 +883,9 @@ class TaskQueueMixedOperationTests: XCTestCase { } } } - - for _ in 0..<3 { try await group.next() } + while await Task(priority: .background, operation: { + return !(await queue.blocked) + }).value { } group.cancelAll() } } @@ -960,7 +975,7 @@ fileprivate extension XCTestCase { queue: TaskPriority?, task: TaskPriority?, flags: TaskQueue.Flags ) - static func checkWaitOnQueue( + func checkWaitOnQueue( option: TaskOption, file: StaticString = #filePath, function: StaticString = #function, @@ -975,16 +990,17 @@ fileprivate extension XCTestCase { file: file, function: function, line: line ) { try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await queue.exec( - priority: option.task, - flags: option.flags - ) { - try await Self.sleep(seconds: 1) + await withUnsafeContinuation { continuation in + group.addTask { + try await queue.exec( + priority: option.task, + flags: option.flags + ) { + continuation.resume() + try await Self.sleep(seconds: 1) + } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await queue.wait() } try await group.waitForAll() } diff --git a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift index 0504665d..c7f232ac 100644 --- a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift @@ -159,8 +159,6 @@ class ThrowingFutureCombiningAllTests: XCTestCase { let value = try await allFuture.get() XCTAssertEqual(value, [1, 2, 3]) } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(producing: 1) @@ -197,8 +195,6 @@ class ThrowingFutureCombiningAllTests: XCTestCase { } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(producing: 1) @@ -244,8 +240,6 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(producing: 1) @@ -286,8 +280,6 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(producing: 1) @@ -328,8 +320,6 @@ class ThrowingFutureRacingTests: XCTestCase { XCTAssertEqual(value, 1) } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(producing: 1) @@ -366,8 +356,6 @@ class ThrowingFutureRacingTests: XCTestCase { } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(throwing: CancellationError()) @@ -402,8 +390,6 @@ class ThrowingFutureSelectAnyTests: XCTestCase { XCTAssertEqual(value, 1) } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(producing: 1) @@ -434,8 +420,6 @@ class ThrowingFutureSelectAnyTests: XCTestCase { XCTAssertEqual(value, 2) } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(throwing: CancellationError()) @@ -472,8 +456,6 @@ class ThrowingFutureSelectAnyTests: XCTestCase { } } } - // Make sure previous tasks started - try await Self.sleep(seconds: 0.01) group.addTask { try await Self.sleep(seconds: 1) await future1.fulfill(throwing: CancellationError()) From 3b8a573e67ed075315b8c23fa600cbfa41f2855a Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sat, 24 Dec 2022 13:04:18 +0530 Subject: [PATCH 02/22] wip: fix tests fail due to race conditions --- .../Continuation/ContinuableCollection.swift | 2 +- .../ContinuableCollectionActor.swift | 2 +- Sources/AsyncObjects/Future.swift | 4 +- Sources/AsyncObjects/TaskQueue.swift | 14 +- .../AsyncCountdownEventTests.swift | 122 +++---- Tests/AsyncObjectsTests/AsyncEventTests.swift | 42 +-- .../AsyncObjectsTests/AsyncObjectTests.swift | 62 ++-- .../AsyncSemaphoreTests.swift | 68 ++-- .../CancellationSourceTests.swift | 70 ++--- .../NonThrowingFutureTests.swift | 48 +-- .../StandardLibraryTests.swift | 6 +- .../TaskOperationTests.swift | 66 ++-- Tests/AsyncObjectsTests/TaskQueueTests.swift | 297 +++++++++--------- .../ThrowingFutureTests.swift | 106 +++---- .../TrackedContinuationTests.swift | 16 +- Tests/AsyncObjectsTests/XCTestCase.swift | 22 +- 16 files changed, 483 insertions(+), 464 deletions(-) diff --git a/Sources/AsyncObjects/Continuation/ContinuableCollection.swift b/Sources/AsyncObjects/Continuation/ContinuableCollection.swift index 41672fb8..9e7f5329 100644 --- a/Sources/AsyncObjects/Continuation/ContinuableCollection.swift +++ b/Sources/AsyncObjects/Continuation/ContinuableCollection.swift @@ -85,7 +85,7 @@ where /// - Returns: The value continuation is resumed with. /// - Throws: If `resume(throwing:)` is called on the continuation, this function throws that error. @inlinable - nonisolated func withPromisedContinuation( + func withPromisedContinuation( withKey key: Key, file: String, function: String, line: UInt ) async rethrows -> Continuation.Success { diff --git a/Sources/AsyncObjects/Continuation/ContinuableCollectionActor.swift b/Sources/AsyncObjects/Continuation/ContinuableCollectionActor.swift index 2f91aae0..2ea7ddd8 100644 --- a/Sources/AsyncObjects/Continuation/ContinuableCollectionActor.swift +++ b/Sources/AsyncObjects/Continuation/ContinuableCollectionActor.swift @@ -92,7 +92,7 @@ where /// - Returns: The value continuation is resumed with. /// - Throws: If `resume(throwing:)` is called on the continuation, this function throws that error. @inlinable - nonisolated func withPromisedContinuation( + func withPromisedContinuation( withKey key: Key, file: String, function: String, line: UInt ) async rethrows -> Continuation.Success { diff --git a/Sources/AsyncObjects/Future.swift b/Sources/AsyncObjects/Future.swift index 3317147c..1e1e3e6a 100644 --- a/Sources/AsyncObjects/Future.swift +++ b/Sources/AsyncObjects/Future.swift @@ -312,7 +312,7 @@ extension Future where Failure == Never { /// /// - Returns: The value continuation is resumed with. @inlinable - internal nonisolated func withPromisedContinuation( + internal func withPromisedContinuation( withKey key: UUID, file: String, function: String, line: UInt ) async -> Output { @@ -688,7 +688,7 @@ extension Future where Failure == Error { /// - Returns: The value continuation is resumed with. /// - Throws: If `resume(throwing:)` is called on the continuation, this function throws that error. @inlinable - internal nonisolated func withPromisedContinuation( + internal func withPromisedContinuation( withKey key: UUID, file: String, function: String, line: UInt ) async throws -> Output { diff --git a/Sources/AsyncObjects/TaskQueue.swift b/Sources/AsyncObjects/TaskQueue.swift index 84e6dc07..94f984f7 100644 --- a/Sources/AsyncObjects/TaskQueue.swift +++ b/Sources/AsyncObjects/TaskQueue.swift @@ -407,7 +407,7 @@ public actor TaskQueue: AsyncObject, LoggableActor { /// /// - Throws: If `resume(throwing:)` is called on the continuation, this function throws that error. @inlinable - internal nonisolated func withPromisedContinuation( + internal func withPromisedContinuation( flags: Flags = [], withKey key: UUID, file: String, function: String, line: UInt @@ -585,12 +585,6 @@ public actor TaskQueue: AsyncObject, LoggableActor { "Waiting", flags: flags, id: key, file: file, function: function, line: line ) - defer { - log( - "Executed", flags: flags, id: key, - file: file, function: function, line: line - ) - } do { try await withPromisedContinuation( @@ -601,6 +595,12 @@ public actor TaskQueue: AsyncObject, LoggableActor { try cancellation(error) } + defer { + log( + "Executed", flags: flags, id: key, + file: file, function: function, line: line + ) + } return try await runTask(withKey: key, operation) } diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index 003b080f..1e45680d 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -6,7 +6,7 @@ class AsyncCountdownEventTests: XCTestCase { func testWaitWithoutIncrement() async throws { let event = AsyncCountdownEvent() - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { try await event.wait() } } @@ -14,9 +14,9 @@ class AsyncCountdownEventTests: XCTestCase { func testWaitWithIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await Self.sleep(seconds: 0.001) - Self.signalCountdownEvent(event, times: 10) - try await Self.checkExecInterval(durationInSeconds: 5) { + try await self.sleep(seconds: 0.001) + self.signalCountdownEvent(event, times: 10) + try await self.checkExecInterval(durationInSeconds: 5) { try await event.wait() } } @@ -24,9 +24,9 @@ class AsyncCountdownEventTests: XCTestCase { func testWaitWithLimitAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await Self.sleep(seconds: 0.001) - Self.signalCountdownEvent(event, times: 10) - try await Self.checkExecInterval(durationInRange: 3.5..<4.3) { + try await self.sleep(seconds: 0.001) + self.signalCountdownEvent(event, times: 10) + try await self.checkExecInterval(durationInRange: 3.5..<4.3) { try await event.wait() } } @@ -34,9 +34,9 @@ class AsyncCountdownEventTests: XCTestCase { func testWaitWithLimitInitialCountAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3, initial: 2) event.increment(by: 10) - try await Self.sleep(seconds: 0.001) - Self.signalCountdownEvent(event, times: 10) - try await Self.checkExecInterval(durationInRange: 4.5..<5.3) { + try await self.sleep(seconds: 0.001) + self.signalCountdownEvent(event, times: 10) + try await self.checkExecInterval(durationInRange: 4.5..<5.3) { try await event.wait() } } @@ -44,12 +44,12 @@ class AsyncCountdownEventTests: XCTestCase { func testWaitWithIncrementAndReset() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) Task.detached { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) event.reset() } - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await event.wait() } } @@ -57,13 +57,13 @@ class AsyncCountdownEventTests: XCTestCase { func testWaitWithIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) Task.detached { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) event.reset(to: 2) - Self.signalCountdownEvent(event, times: 10) + self.signalCountdownEvent(event, times: 10) } - try await Self.checkExecInterval(durationInSeconds: 4) { + try await self.checkExecInterval(durationInSeconds: 4) { try await event.wait() } } @@ -71,13 +71,13 @@ class AsyncCountdownEventTests: XCTestCase { func testWaitWithConcurrentIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) Task.detached { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) event.reset(to: 2) } - Self.signalCountdownEvent(event, times: 10) - try await Self.checkExecInterval(durationInRange: 2.5...3.2) { + self.signalCountdownEvent(event, times: 10) + try await self.checkExecInterval(durationInRange: 2.5...3.2) { try await event.wait() } } @@ -85,12 +85,12 @@ class AsyncCountdownEventTests: XCTestCase { func testDeinit() async throws { let event = AsyncCountdownEvent(until: 0, initial: 1) Task.detached { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) event.signal() } try await event.wait() self.addTeardownBlock { [weak event] in - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) XCTAssertNil(event) } } @@ -100,7 +100,7 @@ class AsyncCountdownEventTests: XCTestCase { for _ in 0..<10 { group.addTask { let event = AsyncCountdownEvent(initial: 1) - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { try await withThrowingTaskGroup(of: Void.self) { g in g.addTask { try await event.wait() } g.addTask { event.signal() } @@ -120,9 +120,9 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { func testWaitTimeoutWithIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await Self.sleep(seconds: 0.001) - Self.signalCountdownEvent(event, times: 10) - await Self.checkExecInterval(durationInSeconds: 3) { + try await self.sleep(seconds: 0.001) + self.signalCountdownEvent(event, times: 10) + await self.checkExecInterval(durationInSeconds: 3) { do { try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") @@ -135,9 +135,9 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { func testWaitTimeoutWithLimitAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await Self.sleep(seconds: 0.001) - Self.signalCountdownEvent(event, times: 10) - await Self.checkExecInterval(durationInSeconds: 2) { + try await self.sleep(seconds: 0.001) + self.signalCountdownEvent(event, times: 10) + await self.checkExecInterval(durationInSeconds: 2) { do { try await event.wait(forSeconds: 2) XCTFail("Unexpected task progression") @@ -150,9 +150,9 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { func testWaitTimeoutWithLimitInitialCountAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) - try await Self.sleep(seconds: 0.001) - Self.signalCountdownEvent(event, times: 10) - await Self.checkExecInterval(durationInSeconds: 3) { + try await self.sleep(seconds: 0.001) + self.signalCountdownEvent(event, times: 10) + await self.checkExecInterval(durationInSeconds: 3) { do { try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") @@ -165,12 +165,12 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { func testWaitTimeoutWithIncrementAndReset() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) Task.detached { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) event.reset() } - await Self.checkExecInterval(durationInSeconds: 2) { + await self.checkExecInterval(durationInSeconds: 2) { do { try await event.wait(forSeconds: 2) XCTFail("Unexpected task progression") @@ -183,13 +183,13 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { func testWaitTimeoutWithIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) Task.detached { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) event.reset(to: 6) - Self.signalCountdownEvent(event, times: 10) + self.signalCountdownEvent(event, times: 10) } - await Self.checkExecInterval(durationInSeconds: 3) { + await self.checkExecInterval(durationInSeconds: 3) { do { try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") @@ -213,9 +213,9 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent() event.increment(by: 10) - try await Self.sleep(seconds: 0.001, clock: clock) - Self.signalCountdownEvent(event, times: 10) - await Self.checkExecInterval(duration: .seconds(3), clock: clock) { + try await self.sleep(seconds: 0.001, clock: clock) + self.signalCountdownEvent(event, times: 10) + await self.checkExecInterval(duration: .seconds(3), clock: clock) { do { try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") @@ -236,9 +236,9 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await Self.sleep(seconds: 0.001, clock: clock) - Self.signalCountdownEvent(event, times: 10) - await Self.checkExecInterval(duration: .seconds(2), clock: clock) { + try await self.sleep(seconds: 0.001, clock: clock) + self.signalCountdownEvent(event, times: 10) + await self.checkExecInterval(duration: .seconds(2), clock: clock) { do { try await event.wait(forSeconds: 2, clock: clock) XCTFail("Unexpected task progression") @@ -259,9 +259,9 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) - try await Self.sleep(seconds: 0.001, clock: clock) - Self.signalCountdownEvent(event, times: 10) - await Self.checkExecInterval(duration: .seconds(3), clock: clock) { + try await self.sleep(seconds: 0.001, clock: clock) + self.signalCountdownEvent(event, times: 10) + await self.checkExecInterval(duration: .seconds(3), clock: clock) { do { try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") @@ -282,12 +282,12 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent() event.increment(by: 10) - try await Self.sleep(seconds: 0.001, clock: clock) + try await self.sleep(seconds: 0.001, clock: clock) Task.detached { - try await Self.sleep(seconds: 3, clock: clock) + try await self.sleep(seconds: 3, clock: clock) event.reset() } - await Self.checkExecInterval(duration: .seconds(2), clock: clock) { + await self.checkExecInterval(duration: .seconds(2), clock: clock) { do { try await event.wait(forSeconds: 2, clock: clock) XCTFail("Unexpected task progression") @@ -308,13 +308,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent() event.increment(by: 10) - try await Self.sleep(seconds: 0.001, clock: clock) + try await self.sleep(seconds: 0.001, clock: clock) Task.detached { - try await Self.sleep(seconds: 3, clock: clock) + try await self.sleep(seconds: 3, clock: clock) event.reset(to: 6) - Self.signalCountdownEvent(event, times: 10) + self.signalCountdownEvent(event, times: 10) } - await Self.checkExecInterval(duration: .seconds(3), clock: clock) { + await self.checkExecInterval(duration: .seconds(3), clock: clock) { do { try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") @@ -334,7 +334,7 @@ class AsyncCountdownEventCancellationTests: XCTestCase { func testWaitCancellation() async throws { let event = AsyncCountdownEvent(initial: 1) let task = Task.detached { - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { try await event.wait() } } @@ -345,9 +345,9 @@ class AsyncCountdownEventCancellationTests: XCTestCase { func testAlreadyCancelledTask() async throws { let event = AsyncCountdownEvent(initial: 1) let task = Task.detached { - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { do { - try await Self.sleep(seconds: 5) + try await self.sleep(seconds: 5) XCTFail("Unexpected task progression") } catch {} XCTAssertTrue(Task.isCancelled) @@ -361,7 +361,7 @@ class AsyncCountdownEventCancellationTests: XCTestCase { fileprivate extension XCTestCase { - static func signalCountdownEvent( + func signalCountdownEvent( _ event: AsyncCountdownEvent, times count: UInt ) { @@ -369,7 +369,7 @@ fileprivate extension XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in for i in 0..( + func checkSemaphoreWaitWithTimeOut( value: UInt = 3, taskCount count: Int = 1, withDelay delay: UInt64 = 2, @@ -215,7 +215,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { line: UInt = #line ) async throws where C.Duration == Duration { let semaphore = AsyncSemaphore(value: value) - try await Self.checkExecInterval( + try await self.checkExecInterval( durationInSeconds: seconds, file: file, function: function, line: line ) { @@ -232,7 +232,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { } catch { success = false } - try await Self.sleep(seconds: delay, clock: clock) + try await self.sleep(seconds: delay, clock: clock) if success { semaphore.signal() } return success } @@ -260,7 +260,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { } let clock: ContinuousClock = .continuous let mutex = AsyncSemaphore() - await Self.checkExecInterval(duration: .seconds(1), clock: clock) { + await self.checkExecInterval(duration: .seconds(1), clock: clock) { do { try await mutex.wait(forSeconds: 1, clock: clock) XCTFail("Unexpected task progression") @@ -281,10 +281,10 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let mutex = AsyncSemaphore() Task.detached { - try await Self.sleep(seconds: 1, clock: clock) + try await self.sleep(seconds: 1, clock: clock) mutex.signal() } - try await Self.checkExecInterval(duration: .seconds(1), clock: clock) { + try await self.checkExecInterval(duration: .seconds(1), clock: clock) { try await mutex.wait(forSeconds: 2, clock: clock) } } @@ -296,7 +296,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { throw XCTSkip("Clock API not available") } let clock: ContinuousClock = .continuous - try await Self.checkSemaphoreWaitWithTimeOut( + try await self.checkSemaphoreWaitWithTimeOut( taskCount: 3, timeout: 3, durationInSeconds: 2, @@ -311,7 +311,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { throw XCTSkip("Clock API not available") } let clock: ContinuousClock = .continuous - try await Self.checkSemaphoreWaitWithTimeOut( + try await self.checkSemaphoreWaitWithTimeOut( taskCount: 5, timeout: 1, durationInSeconds: 2, @@ -327,13 +327,13 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { } let clock: ContinuousClock = .continuous let semaphore = AsyncSemaphore(value: 3) - try await Self.checkExecInterval(duration: .seconds(4), clock: clock) { + try await self.checkExecInterval(duration: .seconds(4), clock: clock) { try await withThrowingTaskGroup(of: Void.self) { group in for index in 0..<8 { group.addTask { if index <= 3 || index.isMultiple(of: 2) { try await semaphore.wait() - try await Self.sleep(seconds: 2, clock: clock) + try await self.sleep(seconds: 2, clock: clock) semaphore.signal() } else { do { @@ -364,7 +364,7 @@ class AsyncSemaphoreCancellationTests: XCTestCase { func testWaitCancellation() async throws { let semaphore = AsyncSemaphore() let task = Task.detached { - await Self.checkExecInterval(durationInSeconds: 0) { + await self.checkExecInterval(durationInSeconds: 0) { try? await semaphore.wait() } } @@ -375,9 +375,9 @@ class AsyncSemaphoreCancellationTests: XCTestCase { func testAlreadyCancelledTask() async throws { let semaphore = AsyncSemaphore() let task = Task.detached { - await Self.checkExecInterval(durationInSeconds: 0) { + await self.checkExecInterval(durationInSeconds: 0) { do { - try await Self.sleep(seconds: 5) + try await self.sleep(seconds: 5) XCTFail("Unexpected task progression") } catch {} XCTAssertTrue(Task.isCancelled) @@ -391,7 +391,7 @@ class AsyncSemaphoreCancellationTests: XCTestCase { fileprivate extension XCTestCase { - static func checkSemaphoreWait( + func checkSemaphoreWait( for semaphore: AsyncSemaphore, taskCount count: Int = 1, withDelay delay: UInt64 = 1, @@ -400,7 +400,7 @@ fileprivate extension XCTestCase { function: StaticString = #function, line: UInt = #line ) async throws { - try await Self.checkExecInterval( + try await self.checkExecInterval( durationInSeconds: seconds, file: file, function: function, line: line ) { @@ -409,7 +409,7 @@ fileprivate extension XCTestCase { group.addTask { try await semaphore.wait() defer { semaphore.signal() } - try await Self.sleep(seconds: delay) + try await self.sleep(seconds: delay) } } try await group.waitForAll() diff --git a/Tests/AsyncObjectsTests/CancellationSourceTests.swift b/Tests/AsyncObjectsTests/CancellationSourceTests.swift index 1d10b2a1..b5081793 100644 --- a/Tests/AsyncObjectsTests/CancellationSourceTests.swift +++ b/Tests/AsyncObjectsTests/CancellationSourceTests.swift @@ -7,22 +7,22 @@ class CancellationSourceTests: XCTestCase { func testTaskCancellation() async throws { let source = CancellationSource() let task = Task { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } source.register(task: task) - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) source.cancel() - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) XCTAssertTrue(task.isCancelled) } func testTaskCancellationWithTimeout() async throws { let source = CancellationSource(cancelAfterNanoseconds: UInt64(1E9)) let task = Task { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } source.register(task: task) - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) XCTAssertTrue(task.isCancelled) } @@ -39,10 +39,10 @@ class CancellationSourceTests: XCTestCase { clock: ContinuousClock.continuous ) let task = Task { - try await Self.sleep(seconds: 2, clock: clock) + try await self.sleep(seconds: 2, clock: clock) } source.register(task: task) - try await Self.sleep(seconds: 2, clock: clock) + try await self.sleep(seconds: 2, clock: clock) XCTAssertTrue(task.isCancelled) } #endif @@ -51,12 +51,12 @@ class CancellationSourceTests: XCTestCase { let parentSource = CancellationSource() let source = CancellationSource(linkedWith: parentSource) let task = Task { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } source.register(task: task) - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) parentSource.cancel() - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) XCTAssertTrue(task.isCancelled) } @@ -67,12 +67,12 @@ class CancellationSourceTests: XCTestCase { linkedWith: parentSource1, parentSource2 ) let task = Task { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } source.register(task: task) - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) parentSource1.cancel() - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) XCTAssertTrue(task.isCancelled) } @@ -80,13 +80,13 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource() let task = Task(cancellationSource: source) { do { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) XCTFail("Unexpected task progression") } catch {} } - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) source.cancel() - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) XCTAssertTrue(task.isCancelled) } @@ -96,13 +96,13 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource() let task = Task.detached(cancellationSource: source) { do { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) XCTFail("Unexpected task progression") } catch {} } - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) source.cancel() - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) XCTAssertTrue(task.isCancelled) } @@ -111,11 +111,11 @@ class CancellationSourceTests: XCTestCase { { let source = CancellationSource() let task = Task(cancellationSource: source) { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) source.cancel() - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) XCTAssertTrue(task.isCancelled) } @@ -124,11 +124,11 @@ class CancellationSourceTests: XCTestCase { { let source = CancellationSource() let task = Task.detached(cancellationSource: source) { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) source.cancel() - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) XCTAssertTrue(task.isCancelled) } @@ -137,7 +137,7 @@ class CancellationSourceTests: XCTestCase { ) -> Task { return Task(cancellationSource: source) { do { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } catch { XCTAssertTrue(Task.isCancelled) } @@ -149,7 +149,7 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource() let task = createTaskWithCancellationSource(source) Task { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) source.cancel() } await task.value @@ -160,7 +160,7 @@ class CancellationSourceTests: XCTestCase { ) -> Task { return Task.detached(cancellationSource: source) { do { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) XCTFail("Unexpected task progression") } catch {} } @@ -172,7 +172,7 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource() let task = createDetachedTaskWithCancellationSource(source) Task { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) source.cancel() } await task.value @@ -182,7 +182,7 @@ class CancellationSourceTests: XCTestCase { _ source: CancellationSource ) -> Task { return Task(cancellationSource: source) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) XCTFail("Unexpected task progression") } } @@ -193,7 +193,7 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource() let task = createThrowingTaskWithCancellationSource(source) Task { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) source.cancel() } let value: Void? = try? await task.value @@ -204,7 +204,7 @@ class CancellationSourceTests: XCTestCase { _ source: CancellationSource ) throws -> Task { return Task.detached(cancellationSource: source) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) XCTFail("Unexpected task progression") } } @@ -216,7 +216,7 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource() let task = try createThrowingDetachedTaskWithCancellationSource(source) Task { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) source.cancel() } let value: Void? = try? await task.value @@ -227,12 +227,12 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource() let task = try createThrowingDetachedTaskWithCancellationSource(source) Task.detached { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) source.cancel() } try? await task.value self.addTeardownBlock { [weak source] in - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) XCTAssertNil(source) } } diff --git a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift index ef3462cb..32b5dbe5 100644 --- a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift @@ -19,7 +19,7 @@ class NonThrowingFutureTests: XCTestCase { XCTAssertEqual(value, 5) } group.addTask { - try! await Self.sleep(seconds: 1) + try! await self.sleep(seconds: 1) await future.fulfill(producing: 5) } await group.waitForAll() @@ -45,9 +45,9 @@ class NonThrowingFutureTests: XCTestCase { } func testFutureAsyncInitializerDuration() async throws { - await Self.checkExecInterval(durationInSeconds: 0) { + await self.checkExecInterval(durationInSeconds: 0) { let _ = Future { promise in - try! await Self.sleep(seconds: 1) + try! await self.sleep(seconds: 1) promise(.success(5)) } } @@ -56,12 +56,12 @@ class NonThrowingFutureTests: XCTestCase { func testDeinit() async throws { let future = Future() Task.detached { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future.fulfill(producing: 5) } let _ = await future.get() self.addTeardownBlock { [weak future] in - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) XCTAssertNil(future) } } @@ -71,7 +71,7 @@ class NonThrowingFutureTests: XCTestCase { for i in 0..<10 { group.addTask { let future = Future() - await Self.checkExecInterval(durationInSeconds: 0) { + await self.checkExecInterval(durationInSeconds: 0) { await withTaskGroup(of: Void.self) { group in group.addTask { let _ = await future.get() } group.addTask { await future.fulfill(producing: i) } @@ -93,22 +93,22 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.all(future1, future3, future2) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { let value = await allFuture.get() XCTAssertEqual(value, [1, 3, 2]) } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(producing: 1) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(producing: 2) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() @@ -121,7 +121,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.allSettled(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { let values = await allFuture.get() @@ -135,15 +135,15 @@ class NonThrowingFutureCombiningTests: XCTestCase { } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(producing: 1) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(producing: 2) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() @@ -156,24 +156,24 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.race(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - await Self.checkExecInterval(durationInSeconds: 1) { + await self.checkExecInterval(durationInSeconds: 1) { let value = await allFuture.get() XCTAssertEqual(value, 1) } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(producing: 1) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(producing: 2) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() @@ -186,24 +186,24 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.any(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - await Self.checkExecInterval(durationInSeconds: 1) { + await self.checkExecInterval(durationInSeconds: 1) { let value = await allFuture.get() XCTAssertEqual(value, 1) } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(producing: 1) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(producing: 2) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() diff --git a/Tests/AsyncObjectsTests/StandardLibraryTests.swift b/Tests/AsyncObjectsTests/StandardLibraryTests.swift index 159cbfea..b55e01fe 100644 --- a/Tests/AsyncObjectsTests/StandardLibraryTests.swift +++ b/Tests/AsyncObjectsTests/StandardLibraryTests.swift @@ -6,7 +6,7 @@ class StandardLibraryTests: XCTestCase { func testTaskValueFetchingCancelation() async throws { let task = Task { () -> Int in - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) return 5 } @@ -32,7 +32,7 @@ class StandardLibraryTests: XCTestCase { let time = DispatchTime.now() async let val: Void = Task { do { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) print("\(#function): Async task completed") } catch { XCTFail("Unrecognized task cancellation") @@ -156,7 +156,7 @@ class StandardLibraryTests: XCTestCase { func testCancellationHandlerFromAlreadyCancelledTask() async throws { let task = Task { do { - try await Self.sleep(seconds: 5) + try await self.sleep(seconds: 5) } catch { await withTaskCancellationHandler { XCTAssertTrue(Task.isCancelled) diff --git a/Tests/AsyncObjectsTests/TaskOperationTests.swift b/Tests/AsyncObjectsTests/TaskOperationTests.swift index 4d47d61e..12b27080 100644 --- a/Tests/AsyncObjectsTests/TaskOperationTests.swift +++ b/Tests/AsyncObjectsTests/TaskOperationTests.swift @@ -7,7 +7,7 @@ class TaskOperationTests: XCTestCase { func testTaskOperation() async throws { let operation = TaskOperation { - (try? await Self.sleep(seconds: 3)) != nil + (try? await self.sleep(seconds: 3)) != nil } XCTAssertTrue(operation.isAsynchronous) XCTAssertFalse(operation.isExecuting) @@ -42,7 +42,7 @@ class TaskOperationTests: XCTestCase { func testThrowingTaskOperation() async throws { let operation = TaskOperation { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) } XCTAssertFalse(operation.isExecuting) XCTAssertFalse(operation.isFinished) @@ -72,20 +72,20 @@ class TaskOperationTests: XCTestCase { func testTaskOperationAsyncWait() async throws { let operation = TaskOperation { - (try? await Self.sleep(seconds: 3)) != nil + (try? await self.sleep(seconds: 3)) != nil } operation.signal() - try await Self.checkExecInterval( + try await self.checkExecInterval( durationInRange: ...3 ) { try await operation.wait() } } func testDeinit() async throws { - let operation = TaskOperation { try await Self.sleep(seconds: 1) } + let operation = TaskOperation { try await self.sleep(seconds: 1) } operation.signal() try await operation.wait() self.addTeardownBlock { [weak operation] in - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) XCTAssertNil(operation) } } @@ -95,7 +95,7 @@ class TaskOperationTests: XCTestCase { for _ in 0..<10 { group.addTask { let operation = TaskOperation {} - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { try await withThrowingTaskGroup(of: Void.self) { g in g.addTask { try await operation.wait() } g.addTask { operation.signal() } @@ -114,10 +114,10 @@ class TaskOperationTimeoutTests: XCTestCase { func testWait() async throws { let operation = TaskOperation { - (try? await Self.sleep(seconds: 1)) != nil + (try? await self.sleep(seconds: 1)) != nil } operation.signal() - try await Self.checkExecInterval(durationInSeconds: 1) { + try await self.checkExecInterval(durationInSeconds: 1) { try await operation.wait(forSeconds: 2) } } @@ -130,10 +130,10 @@ class TaskOperationTimeoutTests: XCTestCase { func testWaitTimeout() async throws { let operation = TaskOperation { - (try? await Self.sleep(seconds: 3)) != nil + (try? await self.sleep(seconds: 3)) != nil } operation.signal() - await Self.checkExecInterval(durationInSeconds: 1) { + await self.checkExecInterval(durationInSeconds: 1) { do { try await operation.wait(forSeconds: 1) XCTFail("Unexpected task progression") @@ -156,10 +156,10 @@ class TaskOperationClockTimeoutTests: XCTestCase { } let clock: ContinuousClock = .continuous let operation = TaskOperation { - (try? await Self.sleep(seconds: 1, clock: clock)) != nil + (try? await self.sleep(seconds: 1, clock: clock)) != nil } operation.signal() - try await Self.checkExecInterval(duration: .seconds(1), clock: clock) { + try await self.checkExecInterval(duration: .seconds(1), clock: clock) { try await operation.wait(forSeconds: 2, clock: clock) } } @@ -173,7 +173,7 @@ class TaskOperationClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let operation = TaskOperation { /* Do nothing */ } operation.signal() - try await Self.checkExecInterval(duration: .seconds(0), clock: clock) { + try await self.checkExecInterval(duration: .seconds(0), clock: clock) { try await operation.wait(forSeconds: 2, clock: clock) } } @@ -186,10 +186,10 @@ class TaskOperationClockTimeoutTests: XCTestCase { } let clock: ContinuousClock = .continuous let operation = TaskOperation { - (try? await Self.sleep(seconds: 3, clock: clock)) != nil + (try? await self.sleep(seconds: 3, clock: clock)) != nil } operation.signal() - await Self.checkExecInterval(duration: .seconds(1), clock: clock) { + await self.checkExecInterval(duration: .seconds(1), clock: clock) { do { try await operation.wait(forSeconds: 1, clock: clock) XCTFail("Unexpected task progression") @@ -208,7 +208,7 @@ class TaskOperationCancellationTests: XCTestCase { func testCancellation() async throws { let operation = TaskOperation { - (try? await Self.sleep(seconds: 3)) != nil + (try? await self.sleep(seconds: 3)) != nil } XCTAssertFalse(operation.isExecuting) XCTAssertFalse(operation.isFinished) @@ -248,7 +248,7 @@ class TaskOperationCancellationTests: XCTestCase { func testThrowingCancellation() async throws { let operation = TaskOperation { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) } XCTAssertFalse(operation.isExecuting) XCTAssertFalse(operation.isFinished) @@ -283,9 +283,9 @@ class TaskOperationCancellationTests: XCTestCase { } func testWaitCancellation() async throws { - let operation = TaskOperation { try await Self.sleep(seconds: 10) } + let operation = TaskOperation { try await self.sleep(seconds: 10) } let task = Task.detached { - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { try await operation.wait() } } @@ -299,11 +299,11 @@ class TaskOperationCancellationTests: XCTestCase { } func testAlreadyCancelledTask() async throws { - let operation = TaskOperation { try await Self.sleep(seconds: 10) } + let operation = TaskOperation { try await self.sleep(seconds: 10) } let task = Task.detached { - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { do { - try await Self.sleep(seconds: 5) + try await self.sleep(seconds: 5) XCTFail("Unexpected task progression") } catch {} XCTAssertTrue(Task.isCancelled) @@ -322,7 +322,7 @@ class TaskOperationCancellationTests: XCTestCase { func testDeinit() async throws { let operation = TaskOperation { do { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) XCTFail("Unexpected task progression") } catch { XCTAssertTrue(type(of: error) == CancellationError.self) @@ -330,7 +330,7 @@ class TaskOperationCancellationTests: XCTestCase { } operation.signal() self.addTeardownBlock { [weak operation] in - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) XCTAssertNil(operation) } } @@ -344,16 +344,16 @@ class TaskOperationTaskManagementTests: XCTestCase { ) -> TaskOperation { return TaskOperation(flags: track ? .trackUnstructuredTasks : []) { Task { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } Task { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } Task { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) } Task.detached { - try await Self.sleep(seconds: 5) + try await self.sleep(seconds: 5) } } } @@ -361,7 +361,7 @@ class TaskOperationTaskManagementTests: XCTestCase { func testOperationWithoutTrackingChildTasks() async throws { let operation = createOperationWithChildTasks(track: false) operation.signal() - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { try await operation.wait() } } @@ -369,13 +369,13 @@ class TaskOperationTaskManagementTests: XCTestCase { func testOperationWithTrackingChildTasks() async throws { let operation = createOperationWithChildTasks(track: true) operation.signal() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await operation.wait() } } func testNotStartedError() async throws { - let operation = TaskOperation { try await Self.sleep(seconds: 1) } + let operation = TaskOperation { try await self.sleep(seconds: 1) } let result = await operation.result switch result { case .success: XCTFail("Unexpected operation result") @@ -389,7 +389,7 @@ class TaskOperationTaskManagementTests: XCTestCase { } func testNotStartedCancellationError() async throws { - let operation = TaskOperation { try await Self.sleep(seconds: 1) } + let operation = TaskOperation { try await self.sleep(seconds: 1) } operation.cancel() let result = await operation.result switch result { diff --git a/Tests/AsyncObjectsTests/TaskQueueTests.swift b/Tests/AsyncObjectsTests/TaskQueueTests.swift index 4d0cfced..f6d891d5 100644 --- a/Tests/AsyncObjectsTests/TaskQueueTests.swift +++ b/Tests/AsyncObjectsTests/TaskQueueTests.swift @@ -15,10 +15,10 @@ class TaskQueueTests: XCTestCase { let queue = TaskQueue() Task.detached { try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) } } - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) queue.signal() let blocked = await queue.blocked XCTAssertTrue(blocked) @@ -50,12 +50,12 @@ class TaskQueueTests: XCTestCase { func testTaskExecutionWithJustAddingTasks() async throws { let queue = TaskQueue() queue.addTask(flags: .barrier) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } // Make sure previous tasks started - try await Self.sleep(seconds: 0.001) - try await Self.checkExecInterval(durationInSeconds: 2) { - queue.addTask { try! await Self.sleep(seconds: 2) } + try await self.sleep(seconds: 0.001) + try await self.checkExecInterval(durationInSeconds: 2) { + queue.addTask { try! await self.sleep(seconds: 2) } try await queue.wait() } } @@ -63,14 +63,14 @@ class TaskQueueTests: XCTestCase { func testDeinit() async throws { let queue = TaskQueue() try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } try await queue.exec { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } - try await Self.sleep(seconds: 0.001) + try await self.sleep(seconds: 0.001) self.addTeardownBlock { [weak queue] in - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) XCTAssertNil(queue) } } @@ -86,7 +86,7 @@ class TaskQueueTimeoutTests: XCTestCase { line: UInt = #line ) async throws { let queue = TaskQueue(priority: option.queue) - try await Self.checkExecInterval( + try await self.checkExecInterval( name: "For queue priority: \(option.queue.str), " + "task priority: \(option.task.str) " + "and flags: \(option.flags.rawValue)", @@ -94,14 +94,14 @@ class TaskQueueTimeoutTests: XCTestCase { file: file, function: function, line: line ) { try await withThrowingTaskGroup(of: Void.self) { group in - await withUnsafeContinuation { continuation in + await waitForResume { continuation in group.addTask { try await queue.exec( priority: option.task, flags: [option.flags, .block] ) { continuation.resume() - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } @@ -159,7 +159,7 @@ class TaskQueueTimeoutTests: XCTestCase { line: UInt = #line ) async throws where C.Duration == Duration { let queue = TaskQueue(priority: option.queue) - try await Self.checkExecInterval( + try await self.checkExecInterval( name: "For queue priority: \(option.queue.str), " + "task priority: \(option.task.str) " + "and flags: \(option.flags.rawValue)", @@ -167,14 +167,14 @@ class TaskQueueTimeoutTests: XCTestCase { file: file, function: function, line: line ) { try await withThrowingTaskGroup(of: Void.self) { group in - await withUnsafeContinuation { continuation in + await waitForResume { continuation in group.addTask { try await queue.exec( priority: option.task, flags: [option.flags, .block] ) { continuation.resume() - try await Self.sleep(seconds: 2, clock: clock) + try await self.sleep(seconds: 2, clock: clock) } } } @@ -239,16 +239,16 @@ class TaskQueueBlockOperationTests: XCTestCase { func testExecutionOfTwoOperations() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } group.addTask { try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } try await group.waitForAll() @@ -258,19 +258,19 @@ class TaskQueueBlockOperationTests: XCTestCase { func testExecutionOfTaskBeforeOperation() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 1) { + try await self.checkExecInterval(durationInSeconds: 1) { try await withThrowingTaskGroup(of: Void.self) { group in - await withUnsafeContinuation { continuation in + await waitForResume { continuation in group.addTask { try await queue.exec { continuation.resume() - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } } group.addTask { try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } try await group.waitForAll() @@ -280,19 +280,19 @@ class TaskQueueBlockOperationTests: XCTestCase { func testExecutionOfTaskAfterOperation() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in - await withUnsafeContinuation { continuation in + await waitForResume { continuation in group.addTask { try await queue.exec(flags: .block) { continuation.resume() - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } group.addTask { try await queue.exec { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } try await group.waitForAll() @@ -303,10 +303,10 @@ class TaskQueueBlockOperationTests: XCTestCase { func testCancellation() async throws { let queue = TaskQueue() queue.addTask(flags: .block) { - try await Self.sleep(seconds: 10) + try await self.sleep(seconds: 10) } let task = Task.detached { - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { await queue.exec(flags: .block) {} try await queue.wait() } @@ -323,12 +323,12 @@ class TaskQueueBlockOperationTests: XCTestCase { func testAlreadyCancelledTask() async throws { let queue = TaskQueue() queue.addTask(flags: .block) { - try await Self.sleep(seconds: 10) + try await self.sleep(seconds: 10) } let task = Task.detached { - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { do { - try await Self.sleep(seconds: 5) + try await self.sleep(seconds: 5) XCTFail("Unexpected task progression") } catch {} XCTAssertTrue(Task.isCancelled) @@ -347,16 +347,16 @@ class TaskQueueBlockOperationTests: XCTestCase { func testCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) // Throws error for waiting method throw CancellationError() } group.addTask { try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } do { @@ -366,7 +366,7 @@ class TaskQueueBlockOperationTests: XCTestCase { group.cancelAll() } try await queue.exec { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } @@ -374,21 +374,21 @@ class TaskQueueBlockOperationTests: XCTestCase { func testMultipleCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) // Throws error for waiting method throw CancellationError() } group.addTask { try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } group.addTask { try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) } } do { @@ -398,7 +398,7 @@ class TaskQueueBlockOperationTests: XCTestCase { group.cancelAll() } try await queue.exec { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } @@ -406,26 +406,26 @@ class TaskQueueBlockOperationTests: XCTestCase { func testMixedeCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) // Throws error for waiting method throw CancellationError() } group.addTask { try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } group.addTask { try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) } } group.addTask { try await queue.exec { - try await Self.sleep(seconds: 4) + try await self.sleep(seconds: 4) } } do { @@ -435,7 +435,7 @@ class TaskQueueBlockOperationTests: XCTestCase { group.cancelAll() } try await queue.exec { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } @@ -447,16 +447,16 @@ class TaskQueueBarrierOperationTests: XCTestCase { func testExecutionOfTwoOperations() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } group.addTask { try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } try await group.waitForAll() @@ -466,19 +466,19 @@ class TaskQueueBarrierOperationTests: XCTestCase { func testExecutionOfTaskBeforeOperation() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in - await withUnsafeContinuation { continuation in + await waitForResume { continuation in group.addTask { try await queue.exec { continuation.resume() - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } group.addTask { try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } try await group.waitForAll() @@ -488,19 +488,19 @@ class TaskQueueBarrierOperationTests: XCTestCase { func testExecutionOfTaskAfterOperation() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in - await withUnsafeContinuation { continuation in + await waitForResume { continuation in group.addTask { try await queue.exec(flags: .barrier) { continuation.resume() - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } group.addTask { try await queue.exec { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } try await group.waitForAll() @@ -511,10 +511,10 @@ class TaskQueueBarrierOperationTests: XCTestCase { func testCancellation() async throws { let queue = TaskQueue() queue.addTask(flags: .barrier) { - try await Self.sleep(seconds: 10) + try await self.sleep(seconds: 10) } let task = Task.detached { - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { await queue.exec(flags: .barrier) {} try await queue.wait() } @@ -531,12 +531,12 @@ class TaskQueueBarrierOperationTests: XCTestCase { func testAlreadyCancelledTask() async throws { let queue = TaskQueue() queue.addTask(flags: .barrier) { - try await Self.sleep(seconds: 10) + try await self.sleep(seconds: 10) } let task = Task.detached { - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { do { - try await Self.sleep(seconds: 5) + try await self.sleep(seconds: 5) XCTFail("Unexpected task progression") } catch {} XCTAssertTrue(Task.isCancelled) @@ -555,16 +555,16 @@ class TaskQueueBarrierOperationTests: XCTestCase { func testCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) // Throws error for waiting method throw CancellationError() } group.addTask { try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } do { @@ -574,7 +574,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { group.cancelAll() } try await queue.exec { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } @@ -582,21 +582,21 @@ class TaskQueueBarrierOperationTests: XCTestCase { func testMultipleCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) // Throws error for waiting method throw CancellationError() } group.addTask { try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) } } group.addTask { try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } do { @@ -606,7 +606,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { group.cancelAll() } try await queue.exec { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } @@ -614,26 +614,26 @@ class TaskQueueBarrierOperationTests: XCTestCase { func testMixedCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) // Throws error for waiting method throw CancellationError() } group.addTask { try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) } } group.addTask { try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } group.addTask { try await queue.exec { - try await Self.sleep(seconds: 4) + try await self.sleep(seconds: 4) } } do { @@ -643,7 +643,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { group.cancelAll() } try await queue.exec { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } @@ -655,19 +655,19 @@ class TaskQueueMixedOperationTests: XCTestCase { func testExecutionOfBlockTaskBeforeBarrierOperation() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in - await withUnsafeContinuation { continuation in + await waitForResume { continuation in group.addTask { try await queue.exec(flags: .block) { continuation.resume() - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } group.addTask { try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } try await group.waitForAll() @@ -677,19 +677,19 @@ class TaskQueueMixedOperationTests: XCTestCase { func testExecutionOfBlockTaskAfterBarrierOperation() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in - await withUnsafeContinuation { continuation in + await waitForResume { continuation in group.addTask { try await queue.exec(flags: .barrier) { continuation.resume() - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } group.addTask { try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } try await group.waitForAll() @@ -702,31 +702,34 @@ class TaskQueueMixedOperationTests: XCTestCase { { let queue = TaskQueue() // Concurrent + Barrier - try await Self.checkExecInterval(durationInSeconds: 5) { + try await self.checkExecInterval(durationInSeconds: 5) { try await withThrowingTaskGroup(of: Void.self) { group in - await withUnsafeContinuation { continuation in + await waitForResume { continuation in group.addTask { try await queue.exec { continuation.resume() - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } } group.addTask { try await queue.exec(flags: .block) { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } - while let (_, (_, flags)) = await Task(priority: .background, operation: { - let items = await queue.queue - return items.reversed().first - }).value { + while let (_, (_, flags)) = await Task( + priority: .background, + operation: { + let items = await queue.queue + return items.reversed().first + } + ).value { guard flags.contains(.block) else { continue } break } group.addTask { try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) } } try await group.waitForAll() @@ -739,31 +742,34 @@ class TaskQueueMixedOperationTests: XCTestCase { { let queue = TaskQueue() // Concurrent + Barrier + Block - await Self.checkExecInterval(durationInSeconds: 6) { + await self.checkExecInterval(durationInSeconds: 6) { await withTaskGroup(of: Void.self) { group in - await withUnsafeContinuation { continuation in + await waitForResume { continuation in group.addTask { await queue.exec { continuation.resume() - try! await Self.sleep(seconds: 3) + try! await self.sleep(seconds: 3) } } } group.addTask { await queue.exec(flags: .barrier) { - try! await Self.sleep(seconds: 2) + try! await self.sleep(seconds: 2) } } - while let (_, (_, flags)) = await Task(priority: .background, operation: { - let items = await queue.queue - return items.reversed().first - }).value { + while let (_, (_, flags)) = await Task( + priority: .background, + operation: { + let items = await queue.queue + return items.reversed().first + } + ).value { guard flags.contains(.barrier) else { continue } break } group.addTask { await queue.exec(flags: .block) { - try! await Self.sleep(seconds: 1) + try! await self.sleep(seconds: 1) } } await group.waitForAll() @@ -775,16 +781,16 @@ class TaskQueueMixedOperationTests: XCTestCase { /// https://forums.swift.org/t/concurrency-suspending-an-actor-async-func-until-the-actor-meets-certain-conditions/56580 func testBarrierTaskWithMultipleConcurrentTasks() async throws { let queue = TaskQueue() - await Self.checkExecInterval(durationInSeconds: 8) { + await self.checkExecInterval(durationInSeconds: 8) { await withTaskGroup(of: Void.self) { group in await withTaskGroup(of: Void.self) { cgroup in for i in 0..<3 { cgroup.addTask { - await withUnsafeContinuation { continuation in + await waitForResume { continuation in Task { await queue.exec { continuation.resume() - try! await Self.sleep(seconds: i + 1) + try! await self.sleep(seconds: i + 1) } } } @@ -794,29 +800,32 @@ class TaskQueueMixedOperationTests: XCTestCase { } group.addTask { await queue.exec(flags: .barrier) { - try! await Self.sleep(seconds: 2) + try! await self.sleep(seconds: 2) } } - while let (_, (_, flags)) = await Task(priority: .background, operation: { - let items = await queue.queue - return items.reversed().first - }).value { + while let (_, (_, flags)) = await Task( + priority: .background, + operation: { + let items = await queue.queue + return items.reversed().first + } + ).value { guard flags.contains(.barrier) else { continue } break } group.addTask { await queue.exec { - try! await Self.sleep(seconds: 1) + try! await self.sleep(seconds: 1) } } group.addTask { await queue.exec { - try! await Self.sleep(seconds: 2) + try! await self.sleep(seconds: 2) } } group.addTask { await queue.exec { - try! await Self.sleep(seconds: 3) + try! await self.sleep(seconds: 3) } } } @@ -825,16 +834,16 @@ class TaskQueueMixedOperationTests: XCTestCase { func testCancellableAndNonCancellableTasksWithBarrier() async throws { let queue = TaskQueue() - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in try await withThrowingTaskGroup(of: Void.self) { cgroup in for i in 0..<3 { cgroup.addTask { - await withUnsafeContinuation { continuation in + await waitForResume { continuation in Task { try await queue.exec { continuation.resume() - try await Self.sleep(seconds: i + 1) + try await self.sleep(seconds: i + 1) } } } @@ -844,25 +853,28 @@ class TaskQueueMixedOperationTests: XCTestCase { } group.addTask { try await queue.exec(flags: .barrier) { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } - while let (_, (_, flags)) = await Task(priority: .background, operation: { - let items = await queue.queue - return items.reversed().first - }).value { + while let (_, (_, flags)) = await Task( + priority: .background, + operation: { + let items = await queue.queue + return items.reversed().first + } + ).value { guard flags.contains(.barrier) else { continue } break } group.addTask { try await queue.exec { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } group.addTask { await queue.exec { do { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) XCTFail("Unexpected task progression") } catch { XCTAssertTrue( @@ -874,7 +886,7 @@ class TaskQueueMixedOperationTests: XCTestCase { group.addTask { await queue.exec { do { - try await Self.sleep(seconds: 4) + try await self.sleep(seconds: 4) XCTFail("Unexpected task progression") } catch { XCTAssertTrue( @@ -883,9 +895,12 @@ class TaskQueueMixedOperationTests: XCTestCase { } } } - while await Task(priority: .background, operation: { - return !(await queue.blocked) - }).value { } + while await Task( + priority: .background, + operation: { + return !(await queue.blocked) + } + ).value {} group.cancelAll() } } @@ -898,10 +913,10 @@ class TaskQueueCancellationTests: XCTestCase { func testWaitCancellation() async throws { let queue = TaskQueue() queue.addTask(flags: .barrier) { - try await Self.sleep(seconds: 10) + try await self.sleep(seconds: 10) } let task = Task.detached { - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { try await queue.wait() } } @@ -917,12 +932,12 @@ class TaskQueueCancellationTests: XCTestCase { func testAlreadyCancelledTask() async throws { let queue = TaskQueue() queue.addTask(flags: .barrier) { - try await Self.sleep(seconds: 10) + try await self.sleep(seconds: 10) } let task = Task.detached { - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { do { - try await Self.sleep(seconds: 5) + try await self.sleep(seconds: 5) XCTFail("Unexpected task progression") } catch {} XCTAssertTrue(Task.isCancelled) @@ -940,22 +955,22 @@ class TaskQueueCancellationTests: XCTestCase { func testCancellableAndNonCancellableTasks() async throws { let queue = TaskQueue() - await Self.checkExecInterval(durationInSeconds: 0) { + await self.checkExecInterval(durationInSeconds: 0) { await withThrowingTaskGroup(of: Void.self) { group in group.addTask { try await queue.exec { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) } } group.addTask { try await queue.exec { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) } } group.addTask { await queue.exec { do { - try await Self.sleep(seconds: 4) + try await self.sleep(seconds: 4) XCTFail("Unexpected task progression") } catch { XCTAssertTrue( @@ -982,7 +997,7 @@ fileprivate extension XCTestCase { line: UInt = #line ) async throws { let queue = TaskQueue(priority: option.queue) - try await Self.checkExecInterval( + try await self.checkExecInterval( name: "For queue priority: \(option.queue.str), " + "task priority: \(option.task.str) " + "and flags: \(option.flags.rawValue)", @@ -990,14 +1005,14 @@ fileprivate extension XCTestCase { file: file, function: function, line: line ) { try await withThrowingTaskGroup(of: Void.self) { group in - await withUnsafeContinuation { continuation in + await waitForResume { continuation in group.addTask { try await queue.exec( priority: option.task, flags: option.flags ) { continuation.resume() - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } } diff --git a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift index c7f232ac..9a0ba780 100644 --- a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift @@ -19,7 +19,7 @@ class ThrowingFutureTests: XCTestCase { XCTAssertEqual(value, 5) } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future.fulfill(producing: 5) } try await group.waitForAll() @@ -38,7 +38,7 @@ class ThrowingFutureTests: XCTestCase { } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future.fulfill(throwing: CancellationError()) } try await group.waitForAll() @@ -57,11 +57,11 @@ class ThrowingFutureTests: XCTestCase { } try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) waitTask.cancel() } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future.fulfill(producing: 5) } try await group.waitForAll() @@ -78,12 +78,12 @@ class ThrowingFutureTests: XCTestCase { func testDeinit() async throws { let future = Future() Task.detached { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future.fulfill(producing: 5) } let _ = try await future.get() self.addTeardownBlock { [weak future] in - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) XCTAssertNil(future) } } @@ -91,7 +91,7 @@ class ThrowingFutureTests: XCTestCase { func testWaitCancellationWhenTaskCancelled() async throws { let future = Future() let task = Task.detached { - await Self.checkExecInterval(durationInSeconds: 0) { + await self.checkExecInterval(durationInSeconds: 0) { do { let _ = try await future.get() XCTFail("Unexpected task progression") @@ -107,9 +107,9 @@ class ThrowingFutureTests: XCTestCase { func testWaitCancellationForAlreadyCancelledTask() async throws { let future = Future() let task = Task.detached { - await Self.checkExecInterval(durationInSeconds: 0) { + await self.checkExecInterval(durationInSeconds: 0) { do { - try await Self.sleep(seconds: 5) + try await self.sleep(seconds: 5) XCTFail("Unexpected task progression") } catch {} XCTAssertTrue(Task.isCancelled) @@ -130,7 +130,7 @@ class ThrowingFutureTests: XCTestCase { for i in 0..<10 { group.addTask { let future = Future() - try await Self.checkExecInterval(durationInSeconds: 0) { + try await self.checkExecInterval(durationInSeconds: 0) { try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { let _ = try await future.get() } @@ -153,22 +153,22 @@ class ThrowingFutureCombiningAllTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.all(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { let value = try await allFuture.get() XCTAssertEqual(value, [1, 2, 3]) } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(producing: 1) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(producing: 2) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() @@ -181,10 +181,10 @@ class ThrowingFutureCombiningAllTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.all(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - await Self.checkExecInterval(durationInSeconds: 2) { + await self.checkExecInterval(durationInSeconds: 2) { do { let _ = try await allFuture.get() XCTFail("Future fulfillment did not fail") @@ -196,15 +196,15 @@ class ThrowingFutureCombiningAllTests: XCTestCase { } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(producing: 1) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(throwing: CancellationError()) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() @@ -227,7 +227,7 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.allSettled(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { let values = await allFuture.get() @@ -241,15 +241,15 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(producing: 1) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(producing: 2) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() @@ -262,10 +262,10 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.allSettled(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - await Self.checkExecInterval(durationInSeconds: 3) { + await self.checkExecInterval(durationInSeconds: 3) { let values = await allFuture.get() for (index, item) in values.enumerated() { switch item { @@ -281,15 +281,15 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(producing: 1) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(throwing: CancellationError()) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() @@ -312,24 +312,24 @@ class ThrowingFutureRacingTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.race(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - try await Self.checkExecInterval(durationInSeconds: 1) { + try await self.checkExecInterval(durationInSeconds: 1) { let value = try await allFuture.get() XCTAssertEqual(value, 1) } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(producing: 1) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(producing: 2) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() @@ -342,10 +342,10 @@ class ThrowingFutureRacingTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.race(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - await Self.checkExecInterval(durationInSeconds: 1) { + await self.checkExecInterval(durationInSeconds: 1) { do { let _ = try await allFuture.get() XCTFail("Future fulfillment did not fail") @@ -357,15 +357,15 @@ class ThrowingFutureRacingTests: XCTestCase { } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(throwing: CancellationError()) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(producing: 2) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() @@ -382,24 +382,24 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.any(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - try await Self.checkExecInterval(durationInSeconds: 1) { + try await self.checkExecInterval(durationInSeconds: 1) { let value = try await allFuture.get() XCTAssertEqual(value, 1) } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(producing: 1) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(producing: 2) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() @@ -412,24 +412,24 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.any(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - try await Self.checkExecInterval(durationInSeconds: 2) { + try await self.checkExecInterval(durationInSeconds: 2) { let value = try await allFuture.get() XCTAssertEqual(value, 2) } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(throwing: CancellationError()) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(producing: 2) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(producing: 3) } try await group.waitForAll() @@ -442,10 +442,10 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.any(future1, future2, future3) - try await Self.checkExecInterval(durationInSeconds: 3) { + try await self.checkExecInterval(durationInSeconds: 3) { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - await Self.checkExecInterval(durationInSeconds: 3) { + await self.checkExecInterval(durationInSeconds: 3) { do { let _ = try await allFuture.get() XCTFail("Future fulfillment did not fail") @@ -457,15 +457,15 @@ class ThrowingFutureSelectAnyTests: XCTestCase { } } group.addTask { - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) await future1.fulfill(throwing: CancellationError()) } group.addTask { - try await Self.sleep(seconds: 2) + try await self.sleep(seconds: 2) await future2.fulfill(throwing: CancellationError()) } group.addTask { - try await Self.sleep(seconds: 3) + try await self.sleep(seconds: 3) await future3.fulfill(throwing: CancellationError()) } try await group.waitForAll() diff --git a/Tests/AsyncObjectsTests/TrackedContinuationTests.swift b/Tests/AsyncObjectsTests/TrackedContinuationTests.swift index 845a7159..a70a6ca2 100644 --- a/Tests/AsyncObjectsTests/TrackedContinuationTests.swift +++ b/Tests/AsyncObjectsTests/TrackedContinuationTests.swift @@ -15,7 +15,7 @@ class TrackedContinuationTests: XCTestCase { } func testDirectResumeWithSuccess() async throws { - await Self.checkExecInterval(durationInSeconds: 0) { + await self.checkExecInterval(durationInSeconds: 0) { await TrackedContinuation>.with { XCTAssertFalse($0.resumed) $0.resume() @@ -26,7 +26,7 @@ class TrackedContinuationTests: XCTestCase { func testDirectResumeWithError() async throws { typealias C = GlobalContinuation - await Self.checkExecInterval(durationInSeconds: 0) { + await self.checkExecInterval(durationInSeconds: 0) { do { try await TrackedContinuation.with { c in XCTAssertFalse(c.resumed) @@ -55,7 +55,7 @@ class TrackedContinuationTests: XCTestCase { func testCancellationHandlerWhenTaskCancelled() async throws { typealias C = GlobalContinuation let task = Task.detached { - await Self.checkExecInterval(durationInSeconds: 0) { + await self.checkExecInterval(durationInSeconds: 0) { do { try await TrackedContinuation .withCancellation(id: .init()) { @@ -76,9 +76,9 @@ class TrackedContinuationTests: XCTestCase { func testCancellationHandlerForAlreadyCancelledTask() async throws { typealias C = GlobalContinuation let task = Task.detached { - await Self.checkExecInterval(durationInSeconds: 0) { + await self.checkExecInterval(durationInSeconds: 0) { do { - try await Self.sleep(seconds: 5) + try await self.sleep(seconds: 5) XCTFail("Unexpected task progression") } catch {} XCTAssertTrue(Task.isCancelled) @@ -102,9 +102,9 @@ class TrackedContinuationTests: XCTestCase { func testNonCancellableContinuation() async throws { typealias C = GlobalContinuation let task = Task.detached { - await Self.checkExecInterval(durationInSeconds: 1) { + await self.checkExecInterval(durationInSeconds: 1) { do { - try await Self.sleep(seconds: 5) + try await self.sleep(seconds: 5) XCTFail("Unexpected task progression") } catch {} XCTAssertTrue(Task.isCancelled) @@ -115,7 +115,7 @@ class TrackedContinuationTests: XCTestCase { preinit() Task { defer { continuation.resume() } - try await Self.sleep(seconds: 1) + try await self.sleep(seconds: 1) } } } diff --git a/Tests/AsyncObjectsTests/XCTestCase.swift b/Tests/AsyncObjectsTests/XCTestCase.swift index f2013a61..3f5b51ad 100644 --- a/Tests/AsyncObjectsTests/XCTestCase.swift +++ b/Tests/AsyncObjectsTests/XCTestCase.swift @@ -7,12 +7,12 @@ extension XCTestCase { private static var activitySupported = ProcessInfo.processInfo.environment .keys.contains("__XCODE_BUILT_PRODUCTS_DIR_PATHS") - private static func runAssertions( + private func runAssertions( with name: String?, _ assertions: () -> Void ) { #if canImport(Darwin) - if let name = name, activitySupported { + if let name = name, Self.activitySupported { XCTContext.runActivity(named: name) { _ in assertions() } @@ -24,7 +24,7 @@ extension XCTestCase { #endif } - static func checkExecInterval( + func checkExecInterval( name: String? = nil, durationInSeconds seconds: T = .zero, file: StaticString = #filePath, @@ -56,7 +56,7 @@ extension XCTestCase { runAssertions(with: name, assertions) } - static func checkExecInterval( + func checkExecInterval( name: String? = nil, durationInRange range: R, file: StaticString = #filePath, @@ -87,12 +87,12 @@ extension XCTestCase { runAssertions(with: name, assertions) } - static func sleep(seconds: T) async throws { + func sleep(seconds: T) async throws { let second: T = 1_000_000_000 try await Task.sleep(nanoseconds: UInt64(exactly: seconds * second)!) } - static func sleep(seconds: T) async throws { + func sleep(seconds: T) async throws { let second: T = 1_000_000_000 try await Task.sleep(nanoseconds: UInt64(exactly: seconds * second)!) } @@ -111,7 +111,7 @@ extension AsyncObject { @MainActor extension XCTestCase { - static func checkExecInterval( + func checkExecInterval( name: String? = nil, duration: C.Instant.Duration = .zero, clock: C, @@ -130,7 +130,7 @@ extension XCTestCase { runAssertions(with: name, assertions) } - static func sleep( + func sleep( seconds: T, clock: C ) async throws where C.Duration == Duration { @@ -140,7 +140,7 @@ extension XCTestCase { ) } - static func sleep( + func sleep( seconds: Double, clock: C ) async throws where C.Duration == Duration { @@ -176,3 +176,7 @@ protocol DivisiveArithmetic: Numeric { extension Int: DivisiveArithmetic {} extension Double: DivisiveArithmetic {} extension UInt64: DivisiveArithmetic {} + +func waitForResume(_ body: (UnsafeContinuation) -> Void) async { + await withUnsafeContinuation(body) +} From 79ce5355c0344a3036401e616a2753335ed7f42e Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sat, 24 Dec 2022 13:33:24 +0530 Subject: [PATCH 03/22] wip: fix test project build fail on older Swift versions --- Tests/AsyncObjectsTests/TaskQueueTests.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/AsyncObjectsTests/TaskQueueTests.swift b/Tests/AsyncObjectsTests/TaskQueueTests.swift index f6d891d5..dd5bf54e 100644 --- a/Tests/AsyncObjectsTests/TaskQueueTests.swift +++ b/Tests/AsyncObjectsTests/TaskQueueTests.swift @@ -717,14 +717,14 @@ class TaskQueueMixedOperationTests: XCTestCase { try await self.sleep(seconds: 1) } } - while let (_, (_, flags)) = await Task( + while let (_, c) = await Task( priority: .background, operation: { let items = await queue.queue return items.reversed().first } ).value { - guard flags.contains(.block) else { continue } + guard c.flags.contains(.block) else { continue } break } group.addTask { @@ -757,14 +757,14 @@ class TaskQueueMixedOperationTests: XCTestCase { try! await self.sleep(seconds: 2) } } - while let (_, (_, flags)) = await Task( + while let (_, c) = await Task( priority: .background, operation: { let items = await queue.queue return items.reversed().first } ).value { - guard flags.contains(.barrier) else { continue } + guard c.flags.contains(.barrier) else { continue } break } group.addTask { @@ -803,14 +803,14 @@ class TaskQueueMixedOperationTests: XCTestCase { try! await self.sleep(seconds: 2) } } - while let (_, (_, flags)) = await Task( + while let (_, c) = await Task( priority: .background, operation: { let items = await queue.queue return items.reversed().first } ).value { - guard flags.contains(.barrier) else { continue } + guard c.flags.contains(.barrier) else { continue } break } group.addTask { @@ -856,14 +856,14 @@ class TaskQueueMixedOperationTests: XCTestCase { try await self.sleep(seconds: 2) } } - while let (_, (_, flags)) = await Task( + while let (_, c) = await Task( priority: .background, operation: { let items = await queue.queue return items.reversed().first } ).value { - guard flags.contains(.barrier) else { continue } + guard c.flags.contains(.barrier) else { continue } break } group.addTask { From 00eaaceb66923e72ebf55fccca9d706aab8ad61b Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sun, 25 Dec 2022 11:51:47 +0530 Subject: [PATCH 04/22] wip: fix actor data race runtime warings --- Sources/AsyncObjects/Continuation/TrackableContinuable.swift | 4 ++-- Sources/AsyncObjects/TaskQueue.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/AsyncObjects/Continuation/TrackableContinuable.swift b/Sources/AsyncObjects/Continuation/TrackableContinuable.swift index 8bc77552..22ba2dd8 100644 --- a/Sources/AsyncObjects/Continuation/TrackableContinuable.swift +++ b/Sources/AsyncObjects/Continuation/TrackableContinuable.swift @@ -77,7 +77,7 @@ where Self: Sendable, Value: Sendable & ThrowingContinuable { function: String = #function, line: UInt = #line, handler: @Sendable (Self) -> Void, - operation: (Self, @escaping @Sendable () -> Void) -> Void + operation: @Sendable (Self, @escaping @Sendable () -> Void) -> Void ) async rethrows -> Success { let cancellable = Self( with: nil, id: id, @@ -168,7 +168,7 @@ where Self: Sendable, Value: Sendable & NonThrowingContinuable { function: String = #function, line: UInt = #line, handler: @Sendable (Self) -> Void, - operation: (Self, @escaping @Sendable () -> Void) -> Void + operation: @Sendable (Self, @escaping @Sendable () -> Void) -> Void ) async -> Success { let cancellable = Self( with: nil, id: id, diff --git a/Sources/AsyncObjects/TaskQueue.swift b/Sources/AsyncObjects/TaskQueue.swift index 94f984f7..ea773176 100644 --- a/Sources/AsyncObjects/TaskQueue.swift +++ b/Sources/AsyncObjects/TaskQueue.swift @@ -249,7 +249,7 @@ public actor TaskQueue: AsyncObject, LoggableActor { _ continuation: QueuedContinuation, atKey key: UUID, file: String, function: String, line: UInt, - preinit: @escaping @Sendable () -> Void + preinit: @Sendable () -> Void ) { preinit() log( From 7f7d7c12c3e6a190a3e422ae5cbab2f81029c64c Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sun, 25 Dec 2022 12:29:53 +0530 Subject: [PATCH 05/22] wip: fix build fail on older Swift versions --- Tests/AsyncObjectsTests/TaskQueueTests.swift | 56 +++++++++----------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/Tests/AsyncObjectsTests/TaskQueueTests.swift b/Tests/AsyncObjectsTests/TaskQueueTests.swift index dd5bf54e..7660adbb 100644 --- a/Tests/AsyncObjectsTests/TaskQueueTests.swift +++ b/Tests/AsyncObjectsTests/TaskQueueTests.swift @@ -717,14 +717,12 @@ class TaskQueueMixedOperationTests: XCTestCase { try await self.sleep(seconds: 1) } } - while let (_, c) = await Task( - priority: .background, - operation: { - let items = await queue.queue - return items.reversed().first - } - ).value { - guard c.flags.contains(.block) else { continue } + let waiter = Task(priority: .background) { + let items = await queue.queue + return items.reversed().first + } + while let (_, (_, flags)) = await waiter.value { + guard flags.contains(.block) else { continue } break } group.addTask { @@ -757,14 +755,12 @@ class TaskQueueMixedOperationTests: XCTestCase { try! await self.sleep(seconds: 2) } } - while let (_, c) = await Task( - priority: .background, - operation: { - let items = await queue.queue - return items.reversed().first - } - ).value { - guard c.flags.contains(.barrier) else { continue } + let waiter = Task(priority: .background) { + let items = await queue.queue + return items.reversed().first + } + while let (_, (_, flags)) = await waiter.value { + guard flags.contains(.barrier) else { continue } break } group.addTask { @@ -803,14 +799,12 @@ class TaskQueueMixedOperationTests: XCTestCase { try! await self.sleep(seconds: 2) } } - while let (_, c) = await Task( - priority: .background, - operation: { - let items = await queue.queue - return items.reversed().first - } - ).value { - guard c.flags.contains(.barrier) else { continue } + let waiter = Task(priority: .background) { + let items = await queue.queue + return items.reversed().first + } + while let (_, (_, flags)) = await waiter.value { + guard flags.contains(.barrier) else { continue } break } group.addTask { @@ -856,14 +850,12 @@ class TaskQueueMixedOperationTests: XCTestCase { try await self.sleep(seconds: 2) } } - while let (_, c) = await Task( - priority: .background, - operation: { - let items = await queue.queue - return items.reversed().first - } - ).value { - guard c.flags.contains(.barrier) else { continue } + let waiter = Task(priority: .background) { + let items = await queue.queue + return items.reversed().first + } + while let (_, (_, flags)) = await waiter.value { + guard flags.contains(.barrier) else { continue } break } group.addTask { From 0b96519c4e24602c9a7509a139ad5d805bf57756 Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sun, 25 Dec 2022 12:56:28 +0530 Subject: [PATCH 06/22] wip: fix build fail on older Swift versions --- Tests/AsyncObjectsTests/TaskQueueTests.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Tests/AsyncObjectsTests/TaskQueueTests.swift b/Tests/AsyncObjectsTests/TaskQueueTests.swift index 7660adbb..6ea1bc1f 100644 --- a/Tests/AsyncObjectsTests/TaskQueueTests.swift +++ b/Tests/AsyncObjectsTests/TaskQueueTests.swift @@ -1,6 +1,9 @@ import XCTest +import OrderedCollections @testable import AsyncObjects +typealias QE = OrderedDictionary.Element + @MainActor class TaskQueueTests: XCTestCase { @@ -717,7 +720,7 @@ class TaskQueueMixedOperationTests: XCTestCase { try await self.sleep(seconds: 1) } } - let waiter = Task(priority: .background) { + let waiter = Task(priority: .background) { () -> QE? in let items = await queue.queue return items.reversed().first } @@ -755,7 +758,7 @@ class TaskQueueMixedOperationTests: XCTestCase { try! await self.sleep(seconds: 2) } } - let waiter = Task(priority: .background) { + let waiter = Task(priority: .background) { () -> QE? in let items = await queue.queue return items.reversed().first } @@ -799,7 +802,7 @@ class TaskQueueMixedOperationTests: XCTestCase { try! await self.sleep(seconds: 2) } } - let waiter = Task(priority: .background) { + let waiter = Task(priority: .background) { () -> QE? in let items = await queue.queue return items.reversed().first } @@ -850,7 +853,7 @@ class TaskQueueMixedOperationTests: XCTestCase { try await self.sleep(seconds: 2) } } - let waiter = Task(priority: .background) { + let waiter = Task(priority: .background) { () -> QE? in let items = await queue.queue return items.reversed().first } From eeacf6230065aa39a18405923cc17940a42269ae Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sun, 25 Dec 2022 14:31:02 +0530 Subject: [PATCH 07/22] wip: increase task timeout checks --- .../AsyncCountdownEventTests.swift | 140 ++++++++---------- Tests/AsyncObjectsTests/AsyncEventTests.swift | 44 ++---- .../AsyncObjectsTests/AsyncObjectTests.swift | 131 ++++++++-------- .../AsyncSemaphoreTests.swift | 36 ++--- .../TaskOperationTests.swift | 42 ++---- 5 files changed, 164 insertions(+), 229 deletions(-) diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index 1e45680d..50edfa8b 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -122,13 +122,11 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { event.increment(by: 10) try await self.sleep(seconds: 0.001) self.signalCountdownEvent(event, times: 10) - await self.checkExecInterval(durationInSeconds: 3) { - do { - try await event.wait(forSeconds: 3) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + do { + try await event.wait(forSeconds: 3) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue(type(of: error) == DurationTimeoutError.self) } } @@ -137,13 +135,11 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { event.increment(by: 10) try await self.sleep(seconds: 0.001) self.signalCountdownEvent(event, times: 10) - await self.checkExecInterval(durationInSeconds: 2) { - do { - try await event.wait(forSeconds: 2) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + do { + try await event.wait(forSeconds: 2) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue(type(of: error) == DurationTimeoutError.self) } } @@ -152,13 +148,11 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { event.increment(by: 10) try await self.sleep(seconds: 0.001) self.signalCountdownEvent(event, times: 10) - await self.checkExecInterval(durationInSeconds: 3) { - do { - try await event.wait(forSeconds: 3) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + do { + try await event.wait(forSeconds: 3) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue(type(of: error) == DurationTimeoutError.self) } } @@ -170,13 +164,11 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { try await self.sleep(seconds: 3) event.reset() } - await self.checkExecInterval(durationInSeconds: 2) { - do { - try await event.wait(forSeconds: 2) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + do { + try await event.wait(forSeconds: 2) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue(type(of: error) == DurationTimeoutError.self) } } @@ -189,13 +181,11 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { event.reset(to: 6) self.signalCountdownEvent(event, times: 10) } - await self.checkExecInterval(durationInSeconds: 3) { - do { - try await event.wait(forSeconds: 3) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + do { + try await event.wait(forSeconds: 3) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue(type(of: error) == DurationTimeoutError.self) } } } @@ -215,15 +205,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { event.increment(by: 10) try await self.sleep(seconds: 0.001, clock: clock) self.signalCountdownEvent(event, times: 10) - await self.checkExecInterval(duration: .seconds(3), clock: clock) { - do { - try await event.wait(forSeconds: 3, clock: clock) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + do { + try await event.wait(forSeconds: 3, clock: clock) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue( + type(of: error) == TimeoutError.self + ) } } @@ -238,15 +226,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { event.increment(by: 10) try await self.sleep(seconds: 0.001, clock: clock) self.signalCountdownEvent(event, times: 10) - await self.checkExecInterval(duration: .seconds(2), clock: clock) { - do { - try await event.wait(forSeconds: 2, clock: clock) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + do { + try await event.wait(forSeconds: 2, clock: clock) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue( + type(of: error) == TimeoutError.self + ) } } @@ -261,15 +247,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { event.increment(by: 10) try await self.sleep(seconds: 0.001, clock: clock) self.signalCountdownEvent(event, times: 10) - await self.checkExecInterval(duration: .seconds(3), clock: clock) { - do { - try await event.wait(forSeconds: 3, clock: clock) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + do { + try await event.wait(forSeconds: 3, clock: clock) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue( + type(of: error) == TimeoutError.self + ) } } @@ -287,15 +271,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { try await self.sleep(seconds: 3, clock: clock) event.reset() } - await self.checkExecInterval(duration: .seconds(2), clock: clock) { - do { - try await event.wait(forSeconds: 2, clock: clock) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + do { + try await event.wait(forSeconds: 2, clock: clock) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue( + type(of: error) == TimeoutError.self + ) } } @@ -314,15 +296,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { event.reset(to: 6) self.signalCountdownEvent(event, times: 10) } - await self.checkExecInterval(duration: .seconds(3), clock: clock) { - do { - try await event.wait(forSeconds: 3, clock: clock) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + do { + try await event.wait(forSeconds: 3, clock: clock) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue( + type(of: error) == TimeoutError.self + ) } } } diff --git a/Tests/AsyncObjectsTests/AsyncEventTests.swift b/Tests/AsyncObjectsTests/AsyncEventTests.swift index 346c7fbc..568f8255 100644 --- a/Tests/AsyncObjectsTests/AsyncEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncEventTests.swift @@ -62,27 +62,21 @@ class AsyncEventTimeoutTests: XCTestCase { try await self.sleep(seconds: 1) event.signal() } - try await self.checkExecInterval(durationInSeconds: 1) { - try await event.wait(forSeconds: 2) - } + try await event.wait(forSeconds: 5) } func testReleasedWait() async throws { let event = AsyncEvent() - try await self.checkExecInterval(durationInSeconds: 0) { - try await event.wait(forSeconds: 2) - } + try await event.wait(forSeconds: 5) } func testWaitTimeout() async throws { let event = AsyncEvent(signaledInitially: false) - await self.checkExecInterval(durationInSeconds: 1) { - do { - try await event.wait(forSeconds: 1) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + do { + try await event.wait(forSeconds: 1) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue(type(of: error) == DurationTimeoutError.self) } } } @@ -103,9 +97,7 @@ class AsyncEventClockTimeoutTests: XCTestCase { try await self.sleep(seconds: 1, clock: clock) event.signal() } - try await self.checkExecInterval(duration: .seconds(1), clock: clock) { - try await event.wait(forSeconds: 2, clock: clock) - } + try await event.wait(forSeconds: 5, clock: clock) } func testReleasedWait() async throws { @@ -116,9 +108,7 @@ class AsyncEventClockTimeoutTests: XCTestCase { } let clock: ContinuousClock = .continuous let event = AsyncEvent() - try await self.checkExecInterval(duration: .seconds(0), clock: clock) { - try await event.wait(forSeconds: 2, clock: clock) - } + try await event.wait(forSeconds: 5, clock: clock) } func testWaitTimeout() async throws { @@ -129,15 +119,13 @@ class AsyncEventClockTimeoutTests: XCTestCase { } let clock: ContinuousClock = .continuous let event = AsyncEvent(signaledInitially: false) - await self.checkExecInterval(duration: .seconds(1), clock: clock) { - do { - try await event.wait(forSeconds: 1, clock: clock) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + do { + try await event.wait(forSeconds: 1, clock: clock) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue( + type(of: error) == TimeoutError.self + ) } } } diff --git a/Tests/AsyncObjectsTests/AsyncObjectTests.swift b/Tests/AsyncObjectsTests/AsyncObjectTests.swift index 6f726b24..6bb2c441 100644 --- a/Tests/AsyncObjectsTests/AsyncObjectTests.swift +++ b/Tests/AsyncObjectsTests/AsyncObjectTests.swift @@ -91,33 +91,26 @@ class AsyncObjectTimeoutTests: XCTestCase { func testMultipleObjectWaitAllTimeout() async throws { let event = AsyncEvent(signaledInitially: false) let mutex = AsyncSemaphore() - await self.checkExecInterval(durationInSeconds: 1) { - do { - try await waitForAll( - event, mutex, - forNanoseconds: UInt64(1E9) - ) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + do { + try await waitForAll(event, mutex, forNanoseconds: UInt64(1E9)) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue(type(of: error) == DurationTimeoutError.self) } } func testMultipleObjectWaitAnyTimeout() async throws { let event = AsyncEvent(signaledInitially: false) let mutex = AsyncSemaphore() - await self.checkExecInterval(durationInSeconds: 1) { - do { - try await waitForAny( - event, mutex, - count: 2, - forNanoseconds: UInt64(1E9) - ) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + do { + try await waitForAny( + event, mutex, + count: 2, + forNanoseconds: UInt64(1E9) + ) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue(type(of: error) == DurationTimeoutError.self) } } @@ -136,17 +129,15 @@ class AsyncObjectTimeoutTests: XCTestCase { mutex.signal() } op.signal() - await self.checkExecInterval(durationInSeconds: 2) { - do { - try await waitForAny( - event, mutex, op, - count: 2, - forNanoseconds: UInt64(2E9) - ) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + do { + try await waitForAny( + event, mutex, op, + count: 2, + forNanoseconds: UInt64(2E9) + ) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue(type(of: error) == DurationTimeoutError.self) } } } @@ -211,19 +202,17 @@ class AsyncObjectClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncEvent(signaledInitially: false) let mutex = AsyncSemaphore() - await self.checkExecInterval(duration: .seconds(1), clock: clock) { - do { - try await waitForAll( - event, mutex, - until: .now + .seconds(1), - clock: clock - ) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + do { + try await waitForAll( + event, mutex, + until: .now + .seconds(1), + clock: clock + ) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue( + type(of: error) == TimeoutError.self + ) } } @@ -236,20 +225,18 @@ class AsyncObjectClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncEvent(signaledInitially: false) let mutex = AsyncSemaphore() - await self.checkExecInterval(duration: .seconds(1), clock: clock) { - do { - try await waitForAny( - event, mutex, - count: 2, - until: .now + .seconds(1), - clock: clock - ) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + do { + try await waitForAny( + event, mutex, + count: 2, + until: .now + .seconds(1), + clock: clock + ) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue( + type(of: error) == TimeoutError.self + ) } } @@ -274,20 +261,18 @@ class AsyncObjectClockTimeoutTests: XCTestCase { mutex.signal() } op.signal() - await self.checkExecInterval(duration: .seconds(2), clock: clock) { - do { - try await waitForAny( - event, mutex, op, - count: 2, - until: .now + .seconds(2), - clock: clock - ) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + do { + try await waitForAny( + event, mutex, op, + count: 2, + until: .now + .seconds(2), + clock: clock + ) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue( + type(of: error) == TimeoutError.self + ) } } } diff --git a/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift b/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift index 859187d5..59208a1c 100644 --- a/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift +++ b/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift @@ -133,13 +133,11 @@ class AsyncSemaphoreTimeoutTests: XCTestCase { func testMutexWaitTimeout() async throws { let mutex = AsyncSemaphore() - await self.checkExecInterval(durationInSeconds: 1) { - do { - try await mutex.wait(forSeconds: 1) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + do { + try await mutex.wait(forSeconds: 1) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue(type(of: error) == DurationTimeoutError.self) } } @@ -149,9 +147,7 @@ class AsyncSemaphoreTimeoutTests: XCTestCase { try await self.sleep(seconds: 1) mutex.signal() } - try await self.checkExecInterval(durationInSeconds: 1) { - try await mutex.wait(forSeconds: 2) - } + try await mutex.wait(forSeconds: 5) } func testWaitTimeoutWithTasksLessThanCount() async throws { @@ -260,15 +256,13 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { } let clock: ContinuousClock = .continuous let mutex = AsyncSemaphore() - await self.checkExecInterval(duration: .seconds(1), clock: clock) { - do { - try await mutex.wait(forSeconds: 1, clock: clock) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + do { + try await mutex.wait(forSeconds: 1, clock: clock) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue( + type(of: error) == TimeoutError.self + ) } } @@ -284,9 +278,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { try await self.sleep(seconds: 1, clock: clock) mutex.signal() } - try await self.checkExecInterval(duration: .seconds(1), clock: clock) { - try await mutex.wait(forSeconds: 2, clock: clock) - } + try await mutex.wait(forSeconds: 5, clock: clock) } func testWaitTimeoutWithTasksLessThanCount() async throws { diff --git a/Tests/AsyncObjectsTests/TaskOperationTests.swift b/Tests/AsyncObjectsTests/TaskOperationTests.swift index 12b27080..bdf3d55e 100644 --- a/Tests/AsyncObjectsTests/TaskOperationTests.swift +++ b/Tests/AsyncObjectsTests/TaskOperationTests.swift @@ -117,15 +117,13 @@ class TaskOperationTimeoutTests: XCTestCase { (try? await self.sleep(seconds: 1)) != nil } operation.signal() - try await self.checkExecInterval(durationInSeconds: 1) { - try await operation.wait(forSeconds: 2) - } + try await operation.wait(forSeconds: 5) } func testFinishedWait() async throws { let operation = TaskOperation { /* Do nothing */ } operation.signal() - try await operation.wait(forSeconds: 1) + try await operation.wait(forSeconds: 5) } func testWaitTimeout() async throws { @@ -133,13 +131,11 @@ class TaskOperationTimeoutTests: XCTestCase { (try? await self.sleep(seconds: 3)) != nil } operation.signal() - await self.checkExecInterval(durationInSeconds: 1) { - do { - try await operation.wait(forSeconds: 1) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + do { + try await operation.wait(forSeconds: 1) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue(type(of: error) == DurationTimeoutError.self) } } } @@ -159,9 +155,7 @@ class TaskOperationClockTimeoutTests: XCTestCase { (try? await self.sleep(seconds: 1, clock: clock)) != nil } operation.signal() - try await self.checkExecInterval(duration: .seconds(1), clock: clock) { - try await operation.wait(forSeconds: 2, clock: clock) - } + try await operation.wait(forSeconds: 5, clock: clock) } func testFinishedWait() async throws { @@ -173,9 +167,7 @@ class TaskOperationClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let operation = TaskOperation { /* Do nothing */ } operation.signal() - try await self.checkExecInterval(duration: .seconds(0), clock: clock) { - try await operation.wait(forSeconds: 2, clock: clock) - } + try await operation.wait(forSeconds: 5, clock: clock) } func testWaitTimeout() async throws { @@ -189,15 +181,13 @@ class TaskOperationClockTimeoutTests: XCTestCase { (try? await self.sleep(seconds: 3, clock: clock)) != nil } operation.signal() - await self.checkExecInterval(duration: .seconds(1), clock: clock) { - do { - try await operation.wait(forSeconds: 1, clock: clock) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + do { + try await operation.wait(forSeconds: 1, clock: clock) + XCTFail("Unexpected task progression") + } catch { + XCTAssertTrue( + type(of: error) == TimeoutError.self + ) } } } From ceacbb840f5a5cdfe4ce43082cb6b981e77ffb99 Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Wed, 28 Dec 2022 22:49:20 +0530 Subject: [PATCH 08/22] wip: improve test performance --- AsyncObjects.xcodeproj/project.pbxproj | 24 +- Sources/AsyncObjects/AsyncSemaphore.swift | 6 +- .../AsyncObjects/Base/AsyncObject+Clock.swift | 18 +- .../Base/AsyncObject+Duration.swift | 18 +- .../AsyncCountdownEventTests.swift | 284 ++--- Tests/AsyncObjectsTests/AsyncEventTests.swift | 154 +-- .../AsyncObjectsTests/AsyncObjectTests.swift | 191 +-- .../AsyncSemaphoreTests.swift | 433 +++---- .../CancellationSourceTests.swift | 222 ++-- Tests/AsyncObjectsTests/LockerTests.swift | 57 +- .../NonThrowingFutureTests.swift | 197 ++- .../StandardLibraryTests.swift | 6 +- .../TaskOperationTests.swift | 287 ++--- Tests/AsyncObjectsTests/TaskQueueTests.swift | 1094 +++++++---------- .../ThrowingFutureTests.swift | 425 +++---- .../TrackedContinuationTests.swift | 116 +- Tests/AsyncObjectsTests/XCAsyncTestCase.swift | 96 ++ Tests/AsyncObjectsTests/XCTestCase.swift | 182 --- 18 files changed, 1381 insertions(+), 2429 deletions(-) create mode 100644 Tests/AsyncObjectsTests/XCAsyncTestCase.swift delete mode 100644 Tests/AsyncObjectsTests/XCTestCase.swift diff --git a/AsyncObjects.xcodeproj/project.pbxproj b/AsyncObjects.xcodeproj/project.pbxproj index 39b7d6dc..cb0672e8 100644 --- a/AsyncObjects.xcodeproj/project.pbxproj +++ b/AsyncObjects.xcodeproj/project.pbxproj @@ -21,7 +21,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 286654AE55C95B05A5086634 /* AsyncObjects.docc in Sources */ = {isa = PBXBuildFile; fileRef = 64070662BE047028E995157C /* AsyncObjects.docc */; }; + 4628E75B869ACA19AA7E5BDE /* AsyncObjects.docc in Sources */ = {isa = PBXBuildFile; fileRef = B2A9446673C71002FDE80C4B /* AsyncObjects.docc */; }; OBJ_122 /* AsyncCountdownEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* AsyncCountdownEvent.swift */; }; OBJ_123 /* AsyncEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* AsyncEvent.swift */; }; OBJ_124 /* AsyncSemaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* AsyncSemaphore.swift */; }; @@ -58,7 +58,7 @@ OBJ_173 /* TaskQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_49 /* TaskQueueTests.swift */; }; OBJ_174 /* ThrowingFutureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_50 /* ThrowingFutureTests.swift */; }; OBJ_175 /* TrackedContinuationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_51 /* TrackedContinuationTests.swift */; }; - OBJ_176 /* XCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_52 /* XCTestCase.swift */; }; + OBJ_176 /* XCAsyncTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_52 /* XCAsyncTestCase.swift */; }; OBJ_178 /* AsyncObjects.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = asyncobjects::AsyncObjects::Product /* AsyncObjects.framework */; }; OBJ_179 /* OrderedCollections.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = swift-collections::OrderedCollections::Product /* OrderedCollections.framework */; }; OBJ_186 /* _HashTable+Bucket.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_59 /* _HashTable+Bucket.swift */; }; @@ -115,7 +115,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 64070662BE047028E995157C /* AsyncObjects.docc */ = {isa = PBXFileReference; includeInIndex = 1; path = AsyncObjects.docc; sourceTree = ""; }; + B2A9446673C71002FDE80C4B /* AsyncObjects.docc */ = {isa = PBXFileReference; includeInIndex = 1; path = AsyncObjects.docc; sourceTree = ""; }; OBJ_100 /* OrderedSet+Partial SetAlgebra+Operations.swift */ = {isa = PBXFileReference; path = "OrderedSet+Partial SetAlgebra+Operations.swift"; sourceTree = ""; }; OBJ_101 /* OrderedSet+Partial SetAlgebra+Predicates.swift */ = {isa = PBXFileReference; path = "OrderedSet+Partial SetAlgebra+Predicates.swift"; sourceTree = ""; }; OBJ_102 /* OrderedSet+RandomAccessCollection.swift */ = {isa = PBXFileReference; path = "OrderedSet+RandomAccessCollection.swift"; sourceTree = ""; }; @@ -162,7 +162,7 @@ OBJ_49 /* TaskQueueTests.swift */ = {isa = PBXFileReference; path = TaskQueueTests.swift; sourceTree = ""; }; OBJ_50 /* ThrowingFutureTests.swift */ = {isa = PBXFileReference; path = ThrowingFutureTests.swift; sourceTree = ""; }; OBJ_51 /* TrackedContinuationTests.swift */ = {isa = PBXFileReference; path = TrackedContinuationTests.swift; sourceTree = ""; }; - OBJ_52 /* XCTestCase.swift */ = {isa = PBXFileReference; path = XCTestCase.swift; sourceTree = ""; }; + OBJ_52 /* XCAsyncTestCase.swift */ = {isa = PBXFileReference; path = XCAsyncTestCase.swift; sourceTree = ""; }; OBJ_59 /* _HashTable+Bucket.swift */ = {isa = PBXFileReference; path = "_HashTable+Bucket.swift"; sourceTree = ""; }; OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; OBJ_60 /* _HashTable+BucketIterator.swift */ = {isa = PBXFileReference; path = "_HashTable+BucketIterator.swift"; sourceTree = ""; }; @@ -247,7 +247,7 @@ OBJ_35 /* TaskOperation.swift */, OBJ_36 /* TaskQueue.swift */, OBJ_37 /* TaskTracker.swift */, - 64070662BE047028E995157C /* AsyncObjects.docc */, + B2A9446673C71002FDE80C4B /* AsyncObjects.docc */, ); name = AsyncObjects; path = Sources/AsyncObjects; @@ -352,7 +352,7 @@ OBJ_49 /* TaskQueueTests.swift */, OBJ_50 /* ThrowingFutureTests.swift */, OBJ_51 /* TrackedContinuationTests.swift */, - OBJ_52 /* XCTestCase.swift */, + OBJ_52 /* XCAsyncTestCase.swift */, ); name = AsyncObjectsTests; path = Tests/AsyncObjectsTests; @@ -648,7 +648,7 @@ OBJ_141 /* TaskOperation.swift in Sources */, OBJ_142 /* TaskQueue.swift in Sources */, OBJ_143 /* TaskTracker.swift in Sources */, - 286654AE55C95B05A5086634 /* AsyncObjects.docc in Sources */, + 4628E75B869ACA19AA7E5BDE /* AsyncObjects.docc in Sources */, ); }; OBJ_152 /* Sources */ = { @@ -672,7 +672,7 @@ OBJ_173 /* TaskQueueTests.swift in Sources */, OBJ_174 /* ThrowingFutureTests.swift in Sources */, OBJ_175 /* TrackedContinuationTests.swift in Sources */, - OBJ_176 /* XCTestCase.swift in Sources */, + OBJ_176 /* XCAsyncTestCase.swift in Sources */, ); }; OBJ_185 /* Sources */ = { @@ -824,7 +824,7 @@ isa = XCBuildConfiguration; buildSettings = { LD = /usr/bin/true; - OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/ManifestAPI -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk -package-description-version 5.6.0"; + OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/ManifestAPI -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.1.sdk -package-description-version 5.6.0"; SWIFT_VERSION = 5.0; }; name = Debug; @@ -833,7 +833,7 @@ isa = XCBuildConfiguration; buildSettings = { LD = /usr/bin/true; - OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/ManifestAPI -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk -package-description-version 5.6.0"; + OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/ManifestAPI -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.1.sdk -package-description-version 5.6.0"; SWIFT_VERSION = 5.0; }; name = Release; @@ -972,7 +972,7 @@ isa = XCBuildConfiguration; buildSettings = { LD = /usr/bin/true; - OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/ManifestAPI -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk -package-description-version 5.3.0"; + OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/ManifestAPI -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.1.sdk -package-description-version 5.3.0"; SWIFT_VERSION = 5.0; }; name = Debug; @@ -981,7 +981,7 @@ isa = XCBuildConfiguration; buildSettings = { LD = /usr/bin/true; - OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/ManifestAPI -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk -package-description-version 5.3.0"; + OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/ManifestAPI -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.1.sdk -package-description-version 5.3.0"; SWIFT_VERSION = 5.0; }; name = Release; diff --git a/Sources/AsyncObjects/AsyncSemaphore.swift b/Sources/AsyncObjects/AsyncSemaphore.swift index 1dc01fbf..020a5167 100644 --- a/Sources/AsyncObjects/AsyncSemaphore.swift +++ b/Sources/AsyncObjects/AsyncSemaphore.swift @@ -147,7 +147,11 @@ public actor AsyncSemaphore: AsyncObject, ContinuableCollectionActor, /// - line: The line signal originates from (there's usually no need to pass it /// explicitly as it defaults to `#line`). @inlinable - internal func signalSemaphore(file: String, function: String, line: UInt) { + internal func signalSemaphore( + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { incrementCount() guard !continuations.isEmpty else { return } log("Signalling", file: file, function: function, line: line) diff --git a/Sources/AsyncObjects/Base/AsyncObject+Clock.swift b/Sources/AsyncObjects/Base/AsyncObject+Clock.swift index 9892c51f..b5d4256a 100644 --- a/Sources/AsyncObjects/Base/AsyncObject+Clock.swift +++ b/Sources/AsyncObjects/Base/AsyncObject+Clock.swift @@ -230,27 +230,28 @@ public func waitForAny( /// pass it explicitly as it defaults to `#function`). /// - line: The line task passed from (there's usually no need to pass it /// explicitly as it defaults to `#line`). -/// - task: The task to execute and wait for completion. +/// - task: The action to execute and wait for completion result. /// +/// - Returns: The result of the action provided. /// - Throws: `CancellationError` if cancelled /// or `TimeoutError` if timed out. @available(swift 5.7) @available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) @Sendable -public func waitForTaskCompletion( +public func waitForTaskCompletion( until deadline: C.Instant, tolerance: C.Instant.Duration? = nil, clock: C, file: String = #fileID, function: String = #function, line: UInt = #line, - _ task: @escaping @Sendable () async throws -> Void -) async throws { - try await withThrowingTaskGroup(of: Void.self) { group in + _ task: @escaping @Sendable () async throws -> T +) async throws -> T { + try await withThrowingTaskGroup(of: T.self) { group in await GlobalContinuation.with { continuation in group.addTask { continuation.resume() - try await task() + return try await task() } } group.addTask { @@ -263,7 +264,10 @@ public func waitForTaskCompletion( throw TimeoutError(until: deadline, tolerance: tolerance) } defer { group.cancelAll() } - try await group.next() + guard + let result = try await group.next() + else { throw CancellationError() } + return result } } diff --git a/Sources/AsyncObjects/Base/AsyncObject+Duration.swift b/Sources/AsyncObjects/Base/AsyncObject+Duration.swift index 0a356a3b..dde992c4 100644 --- a/Sources/AsyncObjects/Base/AsyncObject+Duration.swift +++ b/Sources/AsyncObjects/Base/AsyncObject+Duration.swift @@ -187,23 +187,24 @@ public func waitForAny( /// pass it explicitly as it defaults to `#function`). /// - line: The line task passed from (there's usually no need to pass it /// explicitly as it defaults to `#line`). -/// - task: The task to execute and wait for completion. +/// - task: The action to execute and wait for completion result. /// +/// - Returns: The result of the action provided. /// - Throws: `CancellationError` if cancelled /// or `DurationTimeoutError` if timed out. @Sendable -public func waitForTaskCompletion( +public func waitForTaskCompletion( withTimeoutInNanoseconds timeout: UInt64, file: String = #fileID, function: String = #function, line: UInt = #line, - _ task: @escaping @Sendable () async throws -> Void -) async throws { - try await withThrowingTaskGroup(of: Void.self) { group in + _ task: @escaping @Sendable () async throws -> T +) async throws -> T { + return try await withThrowingTaskGroup(of: T.self) { group in await GlobalContinuation.with { continuation in group.addTask { continuation.resume() - try await task() + return try await task() } } group.addTask { @@ -212,7 +213,10 @@ public func waitForTaskCompletion( throw DurationTimeoutError(for: timeout, tolerance: 1_000) } defer { group.cancelAll() } - try await group.next() + guard + let result = try await group.next() + else { throw CancellationError() } + return result } } diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index 50edfa8b..0823aa02 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -4,94 +4,78 @@ import XCTest @MainActor class AsyncCountdownEventTests: XCTestCase { - func testWaitWithoutIncrement() async throws { + func testWithoutIncrement() async throws { let event = AsyncCountdownEvent() - try await self.checkExecInterval(durationInSeconds: 0) { - try await event.wait() - } + try await event.wait(forSeconds: 3) } - func testWaitWithIncrement() async throws { + func testWithIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await self.sleep(seconds: 0.001) - self.signalCountdownEvent(event, times: 10) - try await self.checkExecInterval(durationInSeconds: 5) { - try await event.wait() - } + event.signal(concurrent: 10) + try await event.wait(forSeconds: 3) + } + + func testWithOverIncrement() async throws { + let event = AsyncCountdownEvent() + event.increment(by: 10) + event.signal(concurrent: 15) + try await event.wait(forSeconds: 3) + try await waitUntil(event, timeout: 3) { $0.currentCount == 0 } } - func testWaitWithLimitAndIncrement() async throws { + func testWithLimitAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await self.sleep(seconds: 0.001) - self.signalCountdownEvent(event, times: 10) - try await self.checkExecInterval(durationInRange: 3.5..<4.3) { - try await event.wait() - } + event.signal(concurrent: 7) + try await event.wait(forSeconds: 3) } - func testWaitWithLimitInitialCountAndIncrement() async throws { + func testWithLimitInitialCountAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3, initial: 2) event.increment(by: 10) - try await self.sleep(seconds: 0.001) - self.signalCountdownEvent(event, times: 10) - try await self.checkExecInterval(durationInRange: 4.5..<5.3) { - try await event.wait() - } + event.signal(concurrent: 9) + try await event.wait(forSeconds: 3) } - func testWaitWithIncrementAndReset() async throws { + func testWithIncrementAndReset() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await self.sleep(seconds: 0.001) - Task.detached { - try await self.sleep(seconds: 3) - event.reset() - } - try await self.checkExecInterval(durationInSeconds: 3) { - try await event.wait() - } + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + event.reset() + try await event.wait(forSeconds: 3) } - func testWaitWithIncrementAndResetToCount() async throws { + func testWithIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await self.sleep(seconds: 0.001) - Task.detached { - try await self.sleep(seconds: 3) - event.reset(to: 2) - self.signalCountdownEvent(event, times: 10) - } - try await self.checkExecInterval(durationInSeconds: 4) { - try await event.wait() - } + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + event.reset(to: 2) + try await waitUntil(event, timeout: 3) { $0.currentCount == 2 } + event.signal(concurrent: 2) + try await event.wait(forSeconds: 3) } - func testWaitWithConcurrentIncrementAndResetToCount() async throws { + func testWithConcurrentIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await self.sleep(seconds: 0.001) + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } Task.detached { - try await self.sleep(seconds: 2) + try await waitUntil(event, timeout: 3) { $0.currentCount == 6 } event.reset(to: 2) } - self.signalCountdownEvent(event, times: 10) - try await self.checkExecInterval(durationInRange: 2.5...3.2) { - try await event.wait() - } + event.signal(concurrent: 4) + try await waitUntil(event, timeout: 3) { $0.currentCount == 2 } + event.signal(concurrent: 2) + try await event.wait(forSeconds: 3) } func testDeinit() async throws { let event = AsyncCountdownEvent(until: 0, initial: 1) - Task.detached { - try await self.sleep(seconds: 1) - event.signal() - } - try await event.wait() + Task.detached { event.signal() } + try await event.wait(forSeconds: 3) self.addTeardownBlock { [weak event] in - try await self.sleep(seconds: 1) - XCTAssertNil(event) + XCTAssertEqual(event.retainCount(), 0) } } @@ -100,12 +84,10 @@ class AsyncCountdownEventTests: XCTestCase { for _ in 0..<10 { group.addTask { let event = AsyncCountdownEvent(initial: 1) - try await self.checkExecInterval(durationInSeconds: 0) { - try await withThrowingTaskGroup(of: Void.self) { g in - g.addTask { try await event.wait() } - g.addTask { event.signal() } - try await g.waitForAll() - } + try await withThrowingTaskGroup(of: Void.self) { g in + g.addTask { try await event.wait(forSeconds: 3) } + g.addTask { event.signal() } + try await g.waitForAll() } } try await group.waitForAll() @@ -117,75 +99,61 @@ class AsyncCountdownEventTests: XCTestCase { @MainActor class AsyncCountdownEventTimeoutTests: XCTestCase { - func testWaitTimeoutWithIncrement() async throws { + func testTimeoutWithIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await self.sleep(seconds: 0.001) - self.signalCountdownEvent(event, times: 10) + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + event.signal(concurrent: 9) do { try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) + } catch is DurationTimeoutError { + try await waitUntil(event, timeout: 3) { $0.currentCount == 1 } } } - func testWaitTimeoutWithLimitAndIncrement() async throws { + func testTimeoutWithLimitAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await self.sleep(seconds: 0.001) - self.signalCountdownEvent(event, times: 10) + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + event.signal(concurrent: 6) do { - try await event.wait(forSeconds: 2) + try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) + } catch is DurationTimeoutError { + try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } } } - func testWaitTimeoutWithLimitInitialCountAndIncrement() async throws { + func testTimeoutWithLimitInitialCountAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) - try await self.sleep(seconds: 0.001) - self.signalCountdownEvent(event, times: 10) + try await waitUntil(event, timeout: 3) { $0.currentCount == 13 } + event.signal(concurrent: 9) do { try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } - } - - func testWaitTimeoutWithIncrementAndReset() async throws { - let event = AsyncCountdownEvent() - event.increment(by: 10) - try await self.sleep(seconds: 0.001) - Task.detached { - try await self.sleep(seconds: 3) - event.reset() - } - do { - try await event.wait(forSeconds: 2) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) + } catch is DurationTimeoutError { + try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } } } - func testWaitTimeoutWithIncrementAndResetToCount() async throws { + func testTimeoutWithIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await self.sleep(seconds: 0.001) + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + event.signal(concurrent: 8) Task.detached { - try await self.sleep(seconds: 3) + try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } event.reset(to: 6) - self.signalCountdownEvent(event, times: 10) } do { try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) + } catch is DurationTimeoutError { + try await waitUntil(event, timeout: 3) { + [6, 2].contains($0.currentCount) + } } } } @@ -194,7 +162,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { @MainActor class AsyncCountdownEventClockTimeoutTests: XCTestCase { - func testWaitTimeoutWithIncrement() async throws { + func testTimeoutWithIncrement() async throws { guard #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) else { @@ -203,19 +171,17 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent() event.increment(by: 10) - try await self.sleep(seconds: 0.001, clock: clock) - self.signalCountdownEvent(event, times: 10) + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + event.signal(concurrent: 9) do { try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) + } catch is TimeoutError { + try await waitUntil(event, timeout: 3) { $0.currentCount == 1 } } } - func testWaitTimeoutWithLimitAndIncrement() async throws { + func testTimeoutWithLimitAndIncrement() async throws { guard #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) else { @@ -224,19 +190,17 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await self.sleep(seconds: 0.001, clock: clock) - self.signalCountdownEvent(event, times: 10) + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + event.signal(concurrent: 6) do { - try await event.wait(forSeconds: 2, clock: clock) + try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) + } catch is TimeoutError { + try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } } } - func testWaitTimeoutWithLimitInitialCountAndIncrement() async throws { + func testTimeoutWithLimitInitialCountAndIncrement() async throws { guard #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) else { @@ -245,19 +209,17 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) - try await self.sleep(seconds: 0.001, clock: clock) - self.signalCountdownEvent(event, times: 10) + try await waitUntil(event, timeout: 3) { $0.currentCount == 13 } + event.signal(concurrent: 9) do { try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) + } catch is TimeoutError { + try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } } } - func testWaitTimeoutWithIncrementAndReset() async throws { + func testTimeoutWithIncrementAndResetToCount() async throws { guard #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) else { @@ -266,43 +228,17 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent() event.increment(by: 10) - try await self.sleep(seconds: 0.001, clock: clock) + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + event.signal(concurrent: 8) Task.detached { - try await self.sleep(seconds: 3, clock: clock) - event.reset() - } - do { - try await event.wait(forSeconds: 2, clock: clock) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } - } - - func testWaitTimeoutWithIncrementAndResetToCount() async throws { - guard - #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) - else { - throw XCTSkip("Clock API not available") - } - let clock: ContinuousClock = .continuous - let event = AsyncCountdownEvent() - event.increment(by: 10) - try await self.sleep(seconds: 0.001, clock: clock) - Task.detached { - try await self.sleep(seconds: 3, clock: clock) + try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } event.reset(to: 6) - self.signalCountdownEvent(event, times: 10) } do { try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) + } catch is TimeoutError { + try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } } } } @@ -311,46 +247,42 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { @MainActor class AsyncCountdownEventCancellationTests: XCTestCase { - func testWaitCancellation() async throws { + func testCancellation() async throws { let event = AsyncCountdownEvent(initial: 1) - let task = Task.detached { - try await self.checkExecInterval(durationInSeconds: 0) { - try await event.wait() - } - } + let task = Task.detached { try await event.wait() } task.cancel() - try? await task.value + do { + try await task.value + XCTFail("Unexpected task progression") + } catch {} } func testAlreadyCancelledTask() async throws { let event = AsyncCountdownEvent(initial: 1) let task = Task.detached { - try await self.checkExecInterval(durationInSeconds: 0) { - do { - try await self.sleep(seconds: 5) - XCTFail("Unexpected task progression") - } catch {} - XCTAssertTrue(Task.isCancelled) + do { try await event.wait() - } + XCTFail("Unexpected task progression") + } catch {} + XCTAssertTrue(Task.isCancelled) + try await event.wait() } task.cancel() - try? await task.value + do { + try await task.value + XCTFail("Unexpected task progression") + } catch {} } } -fileprivate extension XCTestCase { +fileprivate extension AsyncCountdownEvent { - func signalCountdownEvent( - _ event: AsyncCountdownEvent, - times count: UInt - ) { + nonisolated func signal(concurrent count: UInt) { Task.detached { try await withThrowingTaskGroup(of: Void.self) { group in - for i in 0.. {} } - func testReleasedWait() async throws { + func testResetSignal() async throws { guard #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) else { @@ -108,25 +97,12 @@ class AsyncEventClockTimeoutTests: XCTestCase { } let clock: ContinuousClock = .continuous let event = AsyncEvent() - try await event.wait(forSeconds: 5, clock: clock) - } - - func testWaitTimeout() async throws { - guard - #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) - else { - throw XCTSkip("Clock API not available") - } - let clock: ContinuousClock = .continuous - let event = AsyncEvent(signaledInitially: false) + event.reset() + try await waitUntil(event, timeout: 3) { !$0.signalled } do { - try await event.wait(forSeconds: 1, clock: clock) + try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + } catch is TimeoutError {} } } #endif @@ -134,56 +110,30 @@ class AsyncEventClockTimeoutTests: XCTestCase { @MainActor class AsyncEventCancellationTests: XCTestCase { - func testWaitCancellation() async throws { + func testCancellation() async throws { let event = AsyncEvent(signaledInitially: false) - let task = Task.detached { - await self.checkExecInterval(durationInSeconds: 0) { - do { - try await event.wait() - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } - } - } + let task = Task.detached { try await event.wait() } task.cancel() - await task.value + do { + try await task.value + XCTFail("Unexpected task progression") + } catch {} } func testAlreadyCancelledTask() async throws { let event = AsyncEvent(signaledInitially: false) let task = Task.detached { - await self.checkExecInterval(durationInSeconds: 0) { - do { - try await self.sleep(seconds: 5) - XCTFail("Unexpected task progression") - } catch {} - XCTAssertTrue(Task.isCancelled) - try? await event.wait() - } + do { + try await event.wait() + XCTFail("Unexpected task progression") + } catch {} + XCTAssertTrue(Task.isCancelled) + try await event.wait() } task.cancel() - await task.value - } -} - -fileprivate extension XCTestCase { - - func checkWait( - for event: AsyncEvent, - signalIn interval: UInt64 = 1, - durationInSeconds seconds: Int = 1, - file: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line - ) async throws { - Task.detached { - try await self.sleep(seconds: interval) - event.signal() - } - try await self.checkExecInterval( - durationInSeconds: seconds, - file: file, function: function, line: line - ) { try await event.wait() } + do { + try await task.value + XCTFail("Unexpected task progression") + } catch {} } } diff --git a/Tests/AsyncObjectsTests/AsyncObjectTests.swift b/Tests/AsyncObjectsTests/AsyncObjectTests.swift index 6bb2c441..14992e4d 100644 --- a/Tests/AsyncObjectsTests/AsyncObjectTests.swift +++ b/Tests/AsyncObjectsTests/AsyncObjectTests.swift @@ -6,13 +6,9 @@ class AsyncObjectTests: XCTestCase { func testMultipleObjectWaitAll() async throws { let event = AsyncEvent(signaledInitially: false) - let mutex = AsyncSemaphore() - Task.detached { - try await self.sleep(seconds: 1) - event.signal() - mutex.signal() - } - try await self.checkExecInterval(durationInSeconds: 1) { + let mutex = AsyncSemaphore(value: 1) + Task.detached { event.signal(); mutex.signal() } + try await waitForTaskCompletion(withTimeoutInNanoseconds: UInt64(3E9)) { try await waitForAll(event, mutex) } } @@ -20,13 +16,8 @@ class AsyncObjectTests: XCTestCase { func testMultipleObjectWaitAny() async throws { let event = AsyncEvent(signaledInitially: false) let mutex = AsyncSemaphore() - Task.detached { - try await self.sleep(seconds: 1) - event.signal() - try await self.sleep(seconds: 1) - mutex.signal() - } - try await self.checkExecInterval(durationInSeconds: 1) { + Task.detached { event.signal() } + try await waitForTaskCompletion(withTimeoutInNanoseconds: UInt64(3E9)) { try await waitForAny(event, mutex) } } @@ -34,19 +25,10 @@ class AsyncObjectTests: XCTestCase { func testMultipleObjectWaitMultiple() async throws { let event = AsyncEvent(signaledInitially: false) let mutex = AsyncSemaphore() - let op = TaskOperation { - try await self.sleep(seconds: 3) - } - Task.detached { - try await self.sleep(seconds: 1) - event.signal() - } - Task.detached { - try await self.sleep(seconds: 2) - mutex.signal() - } + let op = TaskOperation { /* Do nothing */ } + Task.detached { event.signal() } op.signal() - try await self.checkExecInterval(durationInSeconds: 2) { + try await waitForTaskCompletion(withTimeoutInNanoseconds: UInt64(3E9)) { try await waitForAny(event, mutex, op, count: 2) } } @@ -56,89 +38,41 @@ class AsyncObjectTests: XCTestCase { class AsyncObjectTimeoutTests: XCTestCase { func testMultipleObjectWaitAll() async throws { - let event = AsyncEvent(signaledInitially: false) - let mutex = AsyncSemaphore() - Task.detached { - try await self.sleep(seconds: 1) - event.signal() - mutex.signal() - } - try await self.checkExecInterval(durationInSeconds: 1) { - try await waitForAll( - event, mutex, - forNanoseconds: UInt64(2E9) - ) - } - } - - func testMultipleObjectWaitAny() async throws { - let event = AsyncEvent(signaledInitially: false) - let mutex = AsyncSemaphore() - Task.detached { - try await self.sleep(seconds: 1) - event.signal() - try await self.sleep(seconds: 1) - mutex.signal() - } - try await self.checkExecInterval(durationInSeconds: 1) { - try await waitForAny( - event, mutex, - forNanoseconds: UInt64(2E9) - ) - } - } - - func testMultipleObjectWaitAllTimeout() async throws { let event = AsyncEvent(signaledInitially: false) let mutex = AsyncSemaphore() do { - try await waitForAll(event, mutex, forNanoseconds: UInt64(1E9)) + try await waitForAll(event, mutex, forNanoseconds: UInt64(3E9)) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + } catch is DurationTimeoutError {} } - func testMultipleObjectWaitAnyTimeout() async throws { + func testMultipleObjectWaitAny() async throws { let event = AsyncEvent(signaledInitially: false) let mutex = AsyncSemaphore() do { try await waitForAny( event, mutex, count: 2, - forNanoseconds: UInt64(1E9) + forNanoseconds: UInt64(3E9) ) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + } catch is DurationTimeoutError {} } - func testMultipleObjectWaitMultipleTimeout() async throws { + func testMultipleObjectWaitMultiple() async throws { let event = AsyncEvent(signaledInitially: false) let mutex = AsyncSemaphore() - let op = TaskOperation { - try await self.sleep(seconds: 4) - } - Task.detached { - try await self.sleep(seconds: 1) - event.signal() - } - Task.detached { - try await self.sleep(seconds: 3) - mutex.signal() - } + let op = TaskOperation { try await mutex.wait() } op.signal() + Task.detached { event.signal() } do { try await waitForAny( event, mutex, op, count: 2, - forNanoseconds: UInt64(2E9) + forNanoseconds: UInt64(3E9) ) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + } catch is DurationTimeoutError {} } } @@ -147,53 +81,6 @@ class AsyncObjectTimeoutTests: XCTestCase { class AsyncObjectClockTimeoutTests: XCTestCase { func testMultipleObjectWaitAll() async throws { - guard - #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) - else { - throw XCTSkip("Clock API not available") - } - let clock: ContinuousClock = .continuous - let event = AsyncEvent(signaledInitially: false) - let mutex = AsyncSemaphore() - Task.detached { - try await self.sleep(seconds: 1) - event.signal() - mutex.signal() - } - try await self.checkExecInterval(duration: .seconds(1), clock: clock) { - try await waitForAll( - event, mutex, - until: .now + .seconds(2), - clock: clock - ) - } - } - - func testMultipleObjectWaitAny() async throws { - guard - #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) - else { - throw XCTSkip("Clock API not available") - } - let clock: ContinuousClock = .continuous - let event = AsyncEvent(signaledInitially: false) - let mutex = AsyncSemaphore() - Task.detached { - try await self.sleep(seconds: 1) - event.signal() - try await self.sleep(seconds: 1) - mutex.signal() - } - try await self.checkExecInterval(duration: .seconds(1), clock: clock) { - try await waitForAny( - event, mutex, - until: .now + .seconds(2), - clock: clock - ) - } - } - - func testMultipleObjectWaitAllTimeout() async throws { guard #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) else { @@ -205,18 +92,14 @@ class AsyncObjectClockTimeoutTests: XCTestCase { do { try await waitForAll( event, mutex, - until: .now + .seconds(1), + until: .now + .seconds(3), clock: clock ) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + } catch is TimeoutError {} } - func testMultipleObjectWaitAnyTimeout() async throws { + func testMultipleObjectWaitAny() async throws { guard #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) else { @@ -228,19 +111,14 @@ class AsyncObjectClockTimeoutTests: XCTestCase { do { try await waitForAny( event, mutex, - count: 2, - until: .now + .seconds(1), + until: .now + .seconds(3), clock: clock ) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + } catch is TimeoutError {} } - func testMultipleObjectWaitMultipleTimeout() async throws { + func testMultipleObjectWaitMultiple() async throws { guard #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) else { @@ -249,31 +127,18 @@ class AsyncObjectClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncEvent(signaledInitially: false) let mutex = AsyncSemaphore() - let op = TaskOperation { - try await self.sleep(seconds: 4) - } - Task.detached { - try await self.sleep(seconds: 1) - event.signal() - } - Task.detached { - try await self.sleep(seconds: 3) - mutex.signal() - } + let op = TaskOperation { try await mutex.wait() } op.signal() + Task.detached { event.signal() } do { try await waitForAny( event, mutex, op, count: 2, - until: .now + .seconds(2), + until: .now + .seconds(3), clock: clock ) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + } catch is TimeoutError {} } } #endif diff --git a/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift b/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift index 59208a1c..27bb5fb9 100644 --- a/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift +++ b/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift @@ -4,34 +4,38 @@ import XCTest @MainActor class AsyncSemaphoreTests: XCTestCase { - func testWaitWithTasksLessThanCount() async throws { + func testWithTasksLessThanCount() async throws { let semaphore = AsyncSemaphore(value: 3) - try await self.checkSemaphoreWait(for: semaphore, taskCount: 2) + await semaphore.spinTasks(count: 2, duration: 10) + try await semaphore.wait(forSeconds: 3) } - func testWaitWithTasksEqualToCount() async throws { + func testWithTasksEqualToCount() async throws { let semaphore = AsyncSemaphore(value: 3) - try await self.checkSemaphoreWait(for: semaphore, taskCount: 3) + await semaphore.spinTasks(count: 3, duration: 10) + do { + try await semaphore.wait(forSeconds: 3) + XCTFail("Unexpected task progression") + } catch is DurationTimeoutError {} } - func testWaitWithTasksGreaterThanCount() async throws { + func testWithTasksGreaterThanCount() async throws { let semaphore = AsyncSemaphore(value: 3) - try await self.checkSemaphoreWait( - for: semaphore, - taskCount: 5, - durationInSeconds: 2 - ) + await semaphore.spinTasks(count: 5, duration: 10) + do { + try await semaphore.wait(forSeconds: 3) + XCTFail("Unexpected task progression") + } catch is DurationTimeoutError {} } func testSignaledWaitWithTasksGreaterThanCount() async throws { let semaphore = AsyncSemaphore(value: 3) - semaphore.signal() - try await self.sleep(seconds: 0.001) - try await self.checkSemaphoreWait( - for: semaphore, - taskCount: 4, - durationInSeconds: 2 - ) + await semaphore.signalSemaphore() + await semaphore.spinTasks(count: 4, duration: 10) + do { + try await semaphore.wait(forSeconds: 3) + XCTFail("Unexpected task progression") + } catch is DurationTimeoutError {} } func testConcurrentMutation() async throws { @@ -50,146 +54,70 @@ class AsyncSemaphoreTests: XCTestCase { XCTAssertEqual(data.items.count, 10) } - func testDeinit() async throws { - let semaphore = AsyncSemaphore() - Task.detached { - try await self.sleep(seconds: 1) - semaphore.signal() - } - try await semaphore.wait() - self.addTeardownBlock { [weak semaphore] in - try await self.sleep(seconds: 1) - XCTAssertNil(semaphore) - } - } - func testConcurrentAccess() async throws { try await withThrowingTaskGroup(of: Void.self) { group in for _ in 0..<10 { group.addTask { let semaphore = AsyncSemaphore(value: 1) - try await self.checkExecInterval(durationInSeconds: 0) { - try await withThrowingTaskGroup(of: Void.self) { - group in - group.addTask { try await semaphore.wait() } - group.addTask { semaphore.signal() } - try await group.waitForAll() - } + try await withThrowingTaskGroup(of: Void.self) { g in + g.addTask { try await semaphore.wait(forSeconds: 3) } + g.addTask { semaphore.signal() } + try await g.waitForAll() } } try await group.waitForAll() } } } + + func testDeinit() async throws { + let semaphore = AsyncSemaphore(value: 1) + try await semaphore.wait(forSeconds: 3) + self.addTeardownBlock { [weak semaphore] in + XCTAssertEqual(semaphore.retainCount(), 0) + } + } } @MainActor class AsyncSemaphoreTimeoutTests: XCTestCase { - func checkSemaphoreWaitWithTimeOut( - value: UInt = 3, - taskCount count: Int = 1, - withDelay delay: UInt64 = 2, - timeout: UInt64 = 1, - durationInSeconds seconds: Int = 0, - file: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line - ) async throws { - let semaphore = AsyncSemaphore(value: value) - try await self.checkExecInterval( - durationInSeconds: seconds, - file: file, function: function, line: line - ) { - try await withThrowingTaskGroup(of: Bool.self) { group in - for _ in 0..( - value: UInt = 3, - taskCount count: Int = 1, - withDelay delay: UInt64 = 2, - timeout: UInt64 = 1, - durationInSeconds seconds: Int = 0, - clock: C, - file: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line - ) async throws where C.Duration == Duration { - let semaphore = AsyncSemaphore(value: value) - try await self.checkExecInterval( - durationInSeconds: seconds, - file: file, function: function, line: line - ) { - try await withThrowingTaskGroup(of: Bool.self) { group in - for _ in 0...self - ) - } - } - - func testMutexWait() async throws { - guard - #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) - else { - throw XCTSkip("Clock API not available") - } - let clock: ContinuousClock = .continuous - let mutex = AsyncSemaphore() - Task.detached { - try await self.sleep(seconds: 1, clock: clock) - mutex.signal() - } - try await mutex.wait(forSeconds: 5, clock: clock) + } catch is TimeoutError {} } func testWaitTimeoutWithTasksLessThanCount() async throws { @@ -288,10 +147,9 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { throw XCTSkip("Clock API not available") } let clock: ContinuousClock = .continuous - try await self.checkSemaphoreWaitWithTimeOut( - taskCount: 3, - timeout: 3, - durationInSeconds: 2, + let semaphore = AsyncSemaphore(value: 3) + try await semaphore.spinTasks( + count: 3, duration: 2, timeout: 3, clock: clock ) } @@ -303,10 +161,9 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { throw XCTSkip("Clock API not available") } let clock: ContinuousClock = .continuous - try await self.checkSemaphoreWaitWithTimeOut( - taskCount: 5, - timeout: 1, - durationInSeconds: 2, + let semaphore = AsyncSemaphore(value: 3) + try await semaphore.spinTasks( + count: 5, duration: 5, timeout: 3, clock: clock ) } @@ -319,32 +176,23 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { } let clock: ContinuousClock = .continuous let semaphore = AsyncSemaphore(value: 3) - try await self.checkExecInterval(duration: .seconds(4), clock: clock) { - try await withThrowingTaskGroup(of: Void.self) { group in - for index in 0..<8 { - group.addTask { - if index <= 3 || index.isMultiple(of: 2) { - try await semaphore.wait() - try await self.sleep(seconds: 2, clock: clock) - semaphore.signal() - } else { - do { - try await semaphore.wait( - forSeconds: 1, - clock: clock - ) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) - == TimeoutError.self - ) - } - } + try await withThrowingTaskGroup(of: Void.self) { group in + for index in 0..<8 { + group.addTask { + if index <= 3 || index.isMultiple(of: 2) { + try await semaphore.wait() + try await Task.sleep(seconds: 5, clock: clock) + semaphore.signal() + } else { + do { + try await semaphore.wait( + forSeconds: 3, clock: clock) + XCTFail("Unexpected task progression") + } catch is TimeoutError {} } } - try await group.waitForAll() } + try await group.waitForAll() } } } @@ -355,62 +203,123 @@ class AsyncSemaphoreCancellationTests: XCTestCase { func testWaitCancellation() async throws { let semaphore = AsyncSemaphore() - let task = Task.detached { - await self.checkExecInterval(durationInSeconds: 0) { - try? await semaphore.wait() - } - } + let task = Task.detached { try await semaphore.wait() } task.cancel() - await task.value + do { + try await task.value + XCTFail("Unexpected task progression") + } catch {} } func testAlreadyCancelledTask() async throws { let semaphore = AsyncSemaphore() let task = Task.detached { - await self.checkExecInterval(durationInSeconds: 0) { - do { - try await self.sleep(seconds: 5) - XCTFail("Unexpected task progression") - } catch {} - XCTAssertTrue(Task.isCancelled) - try? await semaphore.wait() - } + do { + try await Task.sleep(seconds: 5) + XCTFail("Unexpected task progression") + } catch {} + XCTAssertTrue(Task.isCancelled) + try await semaphore.wait() } task.cancel() - await task.value + do { + try await task.value + XCTFail("Unexpected task progression") + } catch {} } } -fileprivate extension XCTestCase { +final class ArrayDataStore: @unchecked Sendable { + var items: [Int] = [] + func add(_ item: Int) { items.append(item) } +} - func checkSemaphoreWait( - for semaphore: AsyncSemaphore, - taskCount count: Int = 1, - withDelay delay: UInt64 = 1, - durationInSeconds seconds: Int = 1, +fileprivate extension AsyncSemaphore { + + func spinTasks(count: UInt, duration: UInt64) async { + let stream = AsyncStream { continuation in + for _ in 0..=5.7) + @available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) + func spinTasks( + count: UInt, duration: UInt64, timeout: UInt64, clock: C, + file: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line + ) async throws where C.Duration == Duration { + try await withThrowingTaskGroup(of: Bool.self) { group in + for _ in 0.. Task { - return Task(cancellationSource: source) { - do { - try await self.sleep(seconds: 1) - } catch { - XCTAssertTrue(Task.isCancelled) - } - } - } - - func testTaskCancellationWithSourcePassedOnSyncInitialization() async throws - { - let source = CancellationSource() - let task = createTaskWithCancellationSource(source) - Task { - try await self.sleep(seconds: 2) - source.cancel() - } - await task.value - } - - func createDetachedTaskWithCancellationSource( - _ source: CancellationSource - ) -> Task { - return Task.detached(cancellationSource: source) { - do { - try await self.sleep(seconds: 2) - XCTFail("Unexpected task progression") - } catch {} - } - } - - func testDetachedTaskCancellationWithSourcePassedOnSyncInitialization() - async throws - { - let source = CancellationSource() - let task = createDetachedTaskWithCancellationSource(source) - Task { - try await self.sleep(seconds: 1) - source.cancel() - } - await task.value - } - - func createThrowingTaskWithCancellationSource( - _ source: CancellationSource - ) -> Task { - return Task(cancellationSource: source) { - try await self.sleep(seconds: 2) - XCTFail("Unexpected task progression") - } - } - - func testThrowingTaskCancellationWithSourcePassedOnSyncInitialization() - async throws - { - let source = CancellationSource() - let task = createThrowingTaskWithCancellationSource(source) - Task { - try await self.sleep(seconds: 1) - source.cancel() - } - let value: Void? = try? await task.value - XCTAssertNil(value) - } - - func createThrowingDetachedTaskWithCancellationSource( - _ source: CancellationSource - ) throws -> Task { - return Task.detached(cancellationSource: source) { - try await self.sleep(seconds: 2) - XCTFail("Unexpected task progression") - } - } - - func - testThrowingDetachedTaskCancellationWithSourcePassedOnSyncInitialization() - async throws - { - let source = CancellationSource() - let task = try createThrowingDetachedTaskWithCancellationSource(source) - Task { - try await self.sleep(seconds: 1) - source.cancel() - } - let value: Void? = try? await task.value - XCTAssertNil(value) - } - func testDeinit() async throws { let source = CancellationSource() - let task = try createThrowingDetachedTaskWithCancellationSource(source) - Task.detached { - try await self.sleep(seconds: 1) - source.cancel() + let task = Task.detached(cancellationSource: source) { + try await Task.sleep(seconds: 10) + XCTFail("Unexpected task progression") } + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } + source.cancel() + try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } try? await task.value self.addTeardownBlock { [weak source] in - try await self.sleep(seconds: 1) - XCTAssertNil(source) + XCTAssertEqual(source.retainCount(), 0) } } } diff --git a/Tests/AsyncObjectsTests/LockerTests.swift b/Tests/AsyncObjectsTests/LockerTests.swift index 8060d02a..77219fdb 100644 --- a/Tests/AsyncObjectsTests/LockerTests.swift +++ b/Tests/AsyncObjectsTests/LockerTests.swift @@ -27,66 +27,57 @@ class LockerTests: XCTestCase { XCTAssertEqual(iterations, 5) } - static func threadLocalValue(forKey key: NSCopying) -> T? { - let threadDictionary = Thread.current.threadDictionary - return threadDictionary[key] as? T - } - func testLockReleasedAfterError() throws { let lock = Locker() - XCTAssertFalse(Self.threadLocalValue(forKey: lock) ?? false) + XCTAssertFalse(lock.isNested) XCTAssertThrowsError( try lock.perform { - defer { - XCTAssertTrue(Self.threadLocalValue(forKey: lock) ?? false) - } - XCTAssertTrue(Self.threadLocalValue(forKey: lock) ?? false) + defer { XCTAssertTrue(lock.isNested) } + XCTAssertTrue(lock.isNested) throw CancellationError() } ) - XCTAssertFalse(Self.threadLocalValue(forKey: lock) ?? false) + XCTAssertFalse(lock.isNested) } func testNestedLocking() { let lock = Locker() - XCTAssertFalse(Self.threadLocalValue(forKey: lock) ?? false) + XCTAssertFalse(lock.isNested) let _: UInt64 = lock.perform { - defer { - XCTAssertTrue(Self.threadLocalValue(forKey: lock) ?? false) - } - XCTAssertTrue(Self.threadLocalValue(forKey: lock) ?? false) + defer { XCTAssertTrue(lock.isNested) } + XCTAssertTrue(lock.isNested) var generator = SystemRandomNumberGenerator() return lock.perform { - defer { - XCTAssertTrue(Self.threadLocalValue(forKey: lock) ?? false) - } - XCTAssertTrue(Self.threadLocalValue(forKey: lock) ?? false) + defer { XCTAssertTrue(lock.isNested) } + XCTAssertTrue(lock.isNested) return generator.next() } } - XCTAssertFalse(Self.threadLocalValue(forKey: lock) ?? false) + XCTAssertFalse(lock.isNested) } func testLockReleasedAfterNestedError() throws { let lock = Locker() - XCTAssertFalse(Self.threadLocalValue(forKey: lock) ?? false) + XCTAssertFalse(lock.isNested) XCTAssertThrowsError( try lock.perform { - defer { - XCTAssertTrue(Self.threadLocalValue(forKey: lock) ?? false) - } - XCTAssertTrue(Self.threadLocalValue(forKey: lock) ?? false) + defer { XCTAssertTrue(lock.isNested) } + XCTAssertTrue(lock.isNested) return try lock.perform { - defer { - XCTAssertTrue( - Self.threadLocalValue(forKey: lock) ?? false - ) - } - XCTAssertTrue(Self.threadLocalValue(forKey: lock) ?? false) + defer { XCTAssertTrue(lock.isNested) } + XCTAssertTrue(lock.isNested) throw CancellationError() } } as UInt64 ) - XCTAssertFalse(Self.threadLocalValue(forKey: lock) ?? false) + XCTAssertFalse(lock.isNested) + } +} + +fileprivate extension Locker { + + var isNested: Bool { + let threadDictionary = Thread.current.threadDictionary + return threadDictionary[self] as? Bool ?? false } } diff --git a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift index 32b5dbe5..752fb146 100644 --- a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift @@ -5,81 +5,76 @@ import Dispatch @MainActor class NonThrowingFutureTests: XCTestCase { - func testFutureFulfilledInitialization() async throws { + func testFulfilledInitialization() async throws { let future = Future(with: .success(5)) - let value = await future.get() + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } - func testFutureFulfillAfterInitialization() async throws { + func testFulfillAfterInitialization() async throws { let future = Future() - await withTaskGroup(of: Void.self) { group in + try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { - let value = await future.get() + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } group.addTask { - try! await self.sleep(seconds: 1) await future.fulfill(producing: 5) } - await group.waitForAll() + try await group.waitForAll() } } - func testFutureFulfilledWithAttemptClosure() async throws { + func testFulfilledWithAttemptClosure() async throws { let future = Future { promise in DispatchQueue.global(qos: .background) .asyncAfter(deadline: .now() + 2) { promise(.success(5)) } } - let value = await future.get() + let value = try await future.wait(forSeconds: 5) XCTAssertEqual(value, 5) } func testMultipleTimesFutureFulfilled() async throws { let future = Future(with: .success(5)) await future.fulfill(producing: 10) - let value = await future.get() + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } - func testFutureAsyncInitializerDuration() async throws { - await self.checkExecInterval(durationInSeconds: 0) { - let _ = Future { promise in - try! await self.sleep(seconds: 1) - promise(.success(5)) - } + func testAsyncInitializerDuration() async throws { + let future = Future { promise in + try! await Task.sleep(seconds: 2) + promise(.success(5)) } + let value = try await future.wait(forSeconds: 5) + XCTAssertEqual(value, 5) } func testDeinit() async throws { let future = Future() - Task.detached { - try await self.sleep(seconds: 1) - await future.fulfill(producing: 5) - } - let _ = await future.get() + Task.detached { await future.fulfill(producing: 5) } + let _ = try await future.wait(forSeconds: 3) self.addTeardownBlock { [weak future] in - try await self.sleep(seconds: 1) - XCTAssertNil(future) + XCTAssertEqual(future.retainCount(), 0) } } func testConcurrentAccess() async throws { - await withTaskGroup(of: Void.self) { group in + try await withThrowingTaskGroup(of: Void.self) { group in for i in 0..<10 { group.addTask { let future = Future() - await self.checkExecInterval(durationInSeconds: 0) { - await withTaskGroup(of: Void.self) { group in - group.addTask { let _ = await future.get() } - group.addTask { await future.fulfill(producing: i) } - await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { + let _ = try await future.wait(forSeconds: 3) } + group.addTask { await future.fulfill(producing: i) } + try await group.waitForAll() } } - await group.waitForAll() + try await group.waitForAll() } } } @@ -93,26 +88,15 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.all(future1, future3, future2) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - let value = await allFuture.get() - XCTAssertEqual(value, [1, 3, 2]) - } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(producing: 1) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(producing: 2) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + let value = try await allFuture.wait(forSeconds: 3) + XCTAssertEqual(value, [1, 3, 2]) } + group.addTask { await future1.fulfill(producing: 1) } + group.addTask { await future2.fulfill(producing: 2) } + group.addTask { await future3.fulfill(producing: 3) } + try await group.waitForAll() } } @@ -121,33 +105,22 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.allSettled(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - let values = await allFuture.get() - for (index, item) in values.enumerated() { - switch item { - case .success(let value): - XCTAssertEqual(value, index + 1) - default: - XCTFail("Unexpected future fulfillment") - } + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + let values = try await allFuture.wait(forSeconds: 3) + for (index, item) in values.enumerated() { + switch item { + case .success(let value): + XCTAssertEqual(value, index + 1) + default: + XCTFail("Unexpected future fulfillment") } } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(producing: 1) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(producing: 2) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() } + await future1.fulfill(producing: 1) + await future2.fulfill(producing: 2) + await future3.fulfill(producing: 3) + try await group.waitForAll() } } @@ -156,28 +129,15 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.race(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - await self.checkExecInterval(durationInSeconds: 1) { - let value = await allFuture.get() - XCTAssertEqual(value, 1) - } - } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(producing: 1) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(producing: 2) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + let value = try await allFuture.wait(forSeconds: 3) + XCTAssertEqual(value, 1) } + await future1.fulfill(producing: 1) + await future2.fulfill(producing: 2) + await future3.fulfill(producing: 3) + try await group.waitForAll() } } @@ -186,40 +146,39 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.any(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - await self.checkExecInterval(durationInSeconds: 1) { - let value = await allFuture.get() - XCTAssertEqual(value, 1) - } - } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(producing: 1) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(producing: 2) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + let value = try await allFuture.wait(forSeconds: 3) + XCTAssertEqual(value, 1) } + await future1.fulfill(producing: 1) + await future2.fulfill(producing: 2) + await future3.fulfill(producing: 3) + try await group.waitForAll() } } - func testConstructingAllFutureFromEmpty() async { + func testConstructingAllFutureFromEmpty() async throws { let future = Future.all() - let value = await future.get() + let value = try await future.wait(forSeconds: 3) XCTAssertTrue(value.isEmpty) } - func testConstructingAllSettledFutureFromEmpty() async { + func testConstructingAllSettledFutureFromEmpty() async throws { let future = Future.allSettled() - let value = await future.get() + let value = try await future.wait(forSeconds: 3) XCTAssertTrue(value.isEmpty) } } + +extension Future where Failure == Never { + @Sendable + @inlinable + func wait(forSeconds seconds: UInt64) async throws -> Output { + return try await waitForTaskCompletion( + withTimeoutInNanoseconds: seconds * 1_000_000_000 + ) { + return await self.get() + } + } +} diff --git a/Tests/AsyncObjectsTests/StandardLibraryTests.swift b/Tests/AsyncObjectsTests/StandardLibraryTests.swift index b55e01fe..761afa73 100644 --- a/Tests/AsyncObjectsTests/StandardLibraryTests.swift +++ b/Tests/AsyncObjectsTests/StandardLibraryTests.swift @@ -6,7 +6,7 @@ class StandardLibraryTests: XCTestCase { func testTaskValueFetchingCancelation() async throws { let task = Task { () -> Int in - try await self.sleep(seconds: 1) + try await Task.sleep(seconds: 1) return 5 } @@ -32,7 +32,7 @@ class StandardLibraryTests: XCTestCase { let time = DispatchTime.now() async let val: Void = Task { do { - try await self.sleep(seconds: 1) + try await Task.sleep(seconds: 1) print("\(#function): Async task completed") } catch { XCTFail("Unrecognized task cancellation") @@ -156,7 +156,7 @@ class StandardLibraryTests: XCTestCase { func testCancellationHandlerFromAlreadyCancelledTask() async throws { let task = Task { do { - try await self.sleep(seconds: 5) + try await Task.sleep(seconds: 1) } catch { await withTaskCancellationHandler { XCTAssertTrue(Task.isCancelled) diff --git a/Tests/AsyncObjectsTests/TaskOperationTests.swift b/Tests/AsyncObjectsTests/TaskOperationTests.swift index bdf3d55e..cb1c8550 100644 --- a/Tests/AsyncObjectsTests/TaskOperationTests.swift +++ b/Tests/AsyncObjectsTests/TaskOperationTests.swift @@ -5,9 +5,9 @@ import Dispatch @MainActor class TaskOperationTests: XCTestCase { - func testTaskOperation() async throws { + func testExecution() async throws { let operation = TaskOperation { - (try? await self.sleep(seconds: 3)) != nil + (try? await Task.sleep(seconds: 1)) != nil } XCTAssertTrue(operation.isAsynchronous) XCTAssertFalse(operation.isExecuting) @@ -19,17 +19,20 @@ class TaskOperationTests: XCTestCase { #else operation.start() #endif - expectation( - for: NSPredicate { _, _ in operation.isExecuting }, - evaluatedWith: nil, - handler: nil - ) - waitForExpectations(timeout: 2) - await GlobalContinuation.with { continuation in - DispatchQueue.global(qos: .default).async { - operation.waitUntilFinished() - continuation.resume() + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { + try await waitUntil( + operation, + timeout: 3, + satisfies: \.isExecuting + ) + } + group.addTask { + await GlobalContinuation.with { continuation in + operation.completionBlock = { continuation.resume() } + } } + try await group.waitForAll() } XCTAssertTrue(operation.isFinished) XCTAssertFalse(operation.isExecuting) @@ -40,9 +43,9 @@ class TaskOperationTests: XCTestCase { } } - func testThrowingTaskOperation() async throws { + func testThrowingExecution() async throws { let operation = TaskOperation { - try await self.sleep(seconds: 3) + try await Task.sleep(seconds: 1) } XCTAssertFalse(operation.isExecuting) XCTAssertFalse(operation.isFinished) @@ -53,12 +56,7 @@ class TaskOperationTests: XCTestCase { #else operation.start() #endif - expectation( - for: NSPredicate { _, _ in operation.isExecuting }, - evaluatedWith: nil, - handler: nil - ) - waitForExpectations(timeout: 2) + try await waitUntil(operation, timeout: 3, satisfies: \.isExecuting) await GlobalContinuation.with { continuation in DispatchQueue.global(qos: .default).async { operation.waitUntilFinished() @@ -70,23 +68,26 @@ class TaskOperationTests: XCTestCase { XCTAssertFalse(operation.isCancelled) } - func testTaskOperationAsyncWait() async throws { - let operation = TaskOperation { - (try? await self.sleep(seconds: 3)) != nil - } + func testAsyncWait() async throws { + let operation = TaskOperation { /* Do nothing */ } + operation.signal() + try await operation.wait(forSeconds: 3) + } + + func testFinisheAsyncdWait() async throws { + let operation = TaskOperation { /* Do nothing */ } operation.signal() - try await self.checkExecInterval( - durationInRange: ...3 - ) { try await operation.wait() } + try await operation.wait(forSeconds: 3) } func testDeinit() async throws { - let operation = TaskOperation { try await self.sleep(seconds: 1) } + let operation = TaskOperation { + try await Task.sleep(seconds: 1) + } operation.signal() - try await operation.wait() + try await operation.wait(forSeconds: 3) self.addTeardownBlock { [weak operation] in - try await self.sleep(seconds: 1) - XCTAssertNil(operation) + XCTAssertEqual(operation.retainCount(), 0) } } @@ -95,12 +96,10 @@ class TaskOperationTests: XCTestCase { for _ in 0..<10 { group.addTask { let operation = TaskOperation {} - try await self.checkExecInterval(durationInSeconds: 0) { - try await withThrowingTaskGroup(of: Void.self) { g in - g.addTask { try await operation.wait() } - g.addTask { operation.signal() } - try await g.waitForAll() - } + try await withThrowingTaskGroup(of: Void.self) { g in + g.addTask { try await operation.wait(forSeconds: 3) } + g.addTask { operation.signal() } + try await g.waitForAll() } } try await group.waitForAll() @@ -112,31 +111,15 @@ class TaskOperationTests: XCTestCase { @MainActor class TaskOperationTimeoutTests: XCTestCase { - func testWait() async throws { - let operation = TaskOperation { - (try? await self.sleep(seconds: 1)) != nil - } - operation.signal() - try await operation.wait(forSeconds: 5) - } - - func testFinishedWait() async throws { - let operation = TaskOperation { /* Do nothing */ } - operation.signal() - try await operation.wait(forSeconds: 5) - } - func testWaitTimeout() async throws { let operation = TaskOperation { - (try? await self.sleep(seconds: 3)) != nil + try await Task.sleep(seconds: 10) } operation.signal() do { - try await operation.wait(forSeconds: 1) + try await operation.wait(forSeconds: 3) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == DurationTimeoutError.self) - } + } catch is DurationTimeoutError {} } } @@ -144,32 +127,6 @@ class TaskOperationTimeoutTests: XCTestCase { @MainActor class TaskOperationClockTimeoutTests: XCTestCase { - func testWait() async throws { - guard - #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) - else { - throw XCTSkip("Clock API not available") - } - let clock: ContinuousClock = .continuous - let operation = TaskOperation { - (try? await self.sleep(seconds: 1, clock: clock)) != nil - } - operation.signal() - try await operation.wait(forSeconds: 5, clock: clock) - } - - func testFinishedWait() async throws { - guard - #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) - else { - throw XCTSkip("Clock API not available") - } - let clock: ContinuousClock = .continuous - let operation = TaskOperation { /* Do nothing */ } - operation.signal() - try await operation.wait(forSeconds: 5, clock: clock) - } - func testWaitTimeout() async throws { guard #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) @@ -178,17 +135,13 @@ class TaskOperationClockTimeoutTests: XCTestCase { } let clock: ContinuousClock = .continuous let operation = TaskOperation { - (try? await self.sleep(seconds: 3, clock: clock)) != nil + try await Task.sleep(until: .now + .seconds(10), clock: clock) } operation.signal() do { - try await operation.wait(forSeconds: 1, clock: clock) + try await operation.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == TimeoutError.self - ) - } + } catch is TimeoutError {} } } #endif @@ -198,7 +151,7 @@ class TaskOperationCancellationTests: XCTestCase { func testCancellation() async throws { let operation = TaskOperation { - (try? await self.sleep(seconds: 3)) != nil + (try? await Task.sleep(seconds: 1)) != nil } XCTAssertFalse(operation.isExecuting) XCTAssertFalse(operation.isFinished) @@ -209,25 +162,9 @@ class TaskOperationCancellationTests: XCTestCase { #else operation.start() #endif - expectation( - for: NSPredicate { _, _ in operation.isExecuting }, - evaluatedWith: nil, - handler: nil - ) - waitForExpectations(timeout: 2) + try await waitUntil(operation, timeout: 3, satisfies: \.isExecuting) operation.cancel() - await GlobalContinuation.with { continuation in - DispatchQueue.global(qos: .default).async { - operation.waitUntilFinished() - continuation.resume() - } - } - expectation( - for: NSPredicate { _, _ in operation.isCancelled }, - evaluatedWith: nil, - handler: nil - ) - waitForExpectations(timeout: 2) + try await waitUntil(operation, timeout: 3, satisfies: \.isCancelled) XCTAssertTrue(operation.isFinished) XCTAssertFalse(operation.isExecuting) switch await operation.result { @@ -238,7 +175,7 @@ class TaskOperationCancellationTests: XCTestCase { func testThrowingCancellation() async throws { let operation = TaskOperation { - try await self.sleep(seconds: 3) + try await Task.sleep(seconds: 1) } XCTAssertFalse(operation.isExecuting) XCTAssertFalse(operation.isFinished) @@ -249,79 +186,56 @@ class TaskOperationCancellationTests: XCTestCase { #else operation.start() #endif - expectation( - for: NSPredicate { _, _ in operation.isExecuting }, - evaluatedWith: nil, - handler: nil - ) - waitForExpectations(timeout: 2) + try await waitUntil(operation, timeout: 3, satisfies: \.isExecuting) operation.cancel() - await GlobalContinuation.with { continuation in - DispatchQueue.global(qos: .default).async { - operation.waitUntilFinished() - continuation.resume() - } - } - expectation( - for: NSPredicate { _, _ in operation.isCancelled }, - evaluatedWith: nil, - handler: nil - ) - waitForExpectations(timeout: 2) + try await waitUntil(operation, timeout: 3, satisfies: \.isCancelled) XCTAssertTrue(operation.isFinished) XCTAssertFalse(operation.isExecuting) } func testWaitCancellation() async throws { - let operation = TaskOperation { try await self.sleep(seconds: 10) } + let operation = TaskOperation { + try await Task.sleep(seconds: 1) + } let task = Task.detached { - try await self.checkExecInterval(durationInSeconds: 0) { - try await operation.wait() - } + try await operation.wait(forSeconds: 3) } task.cancel() do { try await task.value XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } + } catch is CancellationError {} } func testAlreadyCancelledTask() async throws { - let operation = TaskOperation { try await self.sleep(seconds: 10) } + let operation = TaskOperation { try await Task.sleep(seconds: 10) } let task = Task.detached { - try await self.checkExecInterval(durationInSeconds: 0) { - do { - try await self.sleep(seconds: 5) - XCTFail("Unexpected task progression") - } catch {} - XCTAssertTrue(Task.isCancelled) - try await operation.wait() - } + do { + try await Task.sleep(seconds: 1) + XCTFail("Unexpected task progression") + } catch {} + XCTAssertTrue(Task.isCancelled) + try await operation.wait() } task.cancel() do { try await task.value XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } + } catch is CancellationError {} } func testDeinit() async throws { let operation = TaskOperation { do { - try await self.sleep(seconds: 2) + try await Task.sleep(seconds: 1) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } + } catch is CancellationError {} } operation.signal() + operation.cancel() + try await waitUntil(operation, timeout: 3, satisfies: \.isCancelled) self.addTeardownBlock { [weak operation] in - try await self.sleep(seconds: 1) - XCTAssertNil(operation) + XCTAssertEqual(operation.retainCount(), 0) } } } @@ -329,67 +243,54 @@ class TaskOperationCancellationTests: XCTestCase { @MainActor class TaskOperationTaskManagementTests: XCTestCase { - func createOperationWithChildTasks( - track: Bool = false - ) -> TaskOperation { - return TaskOperation(flags: track ? .trackUnstructuredTasks : []) { - Task { - try await self.sleep(seconds: 1) - } - Task { - try await self.sleep(seconds: 2) - } - Task { - try await self.sleep(seconds: 3) - } - Task.detached { - try await self.sleep(seconds: 5) - } - } - } - func testOperationWithoutTrackingChildTasks() async throws { - let operation = createOperationWithChildTasks(track: false) + let operation = TaskOperation(track: false) operation.signal() - try await self.checkExecInterval(durationInSeconds: 0) { - try await operation.wait() - } + try await operation.wait(forSeconds: 3) } func testOperationWithTrackingChildTasks() async throws { - let operation = createOperationWithChildTasks(track: true) + let operation = TaskOperation(track: true) operation.signal() - try await self.checkExecInterval(durationInSeconds: 3) { - try await operation.wait() - } + try await operation.wait(forSeconds: 8) } func testNotStartedError() async throws { - let operation = TaskOperation { try await self.sleep(seconds: 1) } + let operation = TaskOperation { + try await Task.sleep(seconds: 1) + } let result = await operation.result switch result { - case .success: XCTFail("Unexpected operation result") - case .failure(let error): - XCTAssertTrue(type(of: error) == EarlyInvokeError.self) - print( - "[\(#function)] [\(type(of: error))] \(error.localizedDescription)" - ) + case .failure(let error as EarlyInvokeError): XCTAssertFalse(error.localizedDescription.isEmpty) + default: XCTFail("Unexpected operation result") } } func testNotStartedCancellationError() async throws { - let operation = TaskOperation { try await self.sleep(seconds: 1) } + let operation = TaskOperation { + try await Task.sleep(seconds: 1) + } operation.cancel() let result = await operation.result switch result { - case .success: XCTFail("Unexpected operation result") - case .failure(let error): - XCTAssertTrue(type(of: error) == CancellationError.self) - print( - "[\(#function)] [\(type(of: error))] \(error.localizedDescription)" - ) + case .failure(let error as CancellationError): XCTAssertFalse(error.localizedDescription.isEmpty) + default: XCTFail("Unexpected operation result") + } + } +} + +fileprivate extension TaskOperation { + + convenience init(track: Bool) where R == Void { + self.init(flags: track ? .trackUnstructuredTasks : []) { + for i in 0..<5 { + Task { + let duration = UInt64(Double(i + 1) * 1E9) + try await Task.sleep(nanoseconds: duration) + } + } } } } diff --git a/Tests/AsyncObjectsTests/TaskQueueTests.swift b/Tests/AsyncObjectsTests/TaskQueueTests.swift index 6ea1bc1f..642e46ad 100644 --- a/Tests/AsyncObjectsTests/TaskQueueTests.swift +++ b/Tests/AsyncObjectsTests/TaskQueueTests.swift @@ -3,6 +3,9 @@ import OrderedCollections @testable import AsyncObjects typealias QE = OrderedDictionary.Element +typealias TaskOption = ( + queue: TaskPriority?, task: TaskPriority?, flags: TaskQueue.Flags +) @MainActor class TaskQueueTests: XCTestCase { @@ -16,15 +19,16 @@ class TaskQueueTests: XCTestCase { func testSignalingBlockedDoesNothing() async throws { let queue = TaskQueue() - Task.detached { + let task = Task.detached { try await queue.exec(flags: .block) { - try await self.sleep(seconds: 3) + try await Task.sleep(seconds: 10) } } - try await self.sleep(seconds: 1) + try await waitUntil(queue, timeout: 3) { $0.blocked } queue.signal() let blocked = await queue.blocked XCTAssertTrue(blocked) + task.cancel() } func testWait() async throws { @@ -42,9 +46,7 @@ class TaskQueueTests: XCTestCase { ] try await withThrowingTaskGroup(of: Void.self) { group in options.forEach { option in - group.addTask { - try await self.checkWaitOnQueue(option: option) - } + group.addTask { try await TaskQueue().checkWait(for: option) } } try await group.waitForAll() } @@ -52,29 +54,23 @@ class TaskQueueTests: XCTestCase { func testTaskExecutionWithJustAddingTasks() async throws { let queue = TaskQueue() - queue.addTask(flags: .barrier) { - try await self.sleep(seconds: 2) - } - // Make sure previous tasks started - try await self.sleep(seconds: 0.001) - try await self.checkExecInterval(durationInSeconds: 2) { - queue.addTask { try! await self.sleep(seconds: 2) } - try await queue.wait() + let stream = AsyncStream { c in + Task.detached { + await queue.addTaskAndStart(flags: .barrier) { c.yield(1) } + await queue.addTaskAndStart { c.yield(2) } + c.finish() + } } + await stream.assertElements() + try await queue.wait(forSeconds: 3) } func testDeinit() async throws { let queue = TaskQueue() - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 1) - } - try await queue.exec { - try await self.sleep(seconds: 1) - } - try await self.sleep(seconds: 0.001) + await queue.exec(flags: .barrier) { /* Do nothing */ } + await queue.exec { /* Do nothing */ } self.addTeardownBlock { [weak queue] in - try await self.sleep(seconds: 1) - XCTAssertNil(queue) + XCTAssertEqual(queue.retainCount(), 0) } } } @@ -82,53 +78,6 @@ class TaskQueueTests: XCTestCase { @MainActor class TaskQueueTimeoutTests: XCTestCase { - private func checkWaitTimeoutOnQueue( - option: TaskOption, - file: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line - ) async throws { - let queue = TaskQueue(priority: option.queue) - try await self.checkExecInterval( - name: "For queue priority: \(option.queue.str), " - + "task priority: \(option.task.str) " - + "and flags: \(option.flags.rawValue)", - durationInSeconds: 1, - file: file, function: function, line: line - ) { - try await withThrowingTaskGroup(of: Void.self) { group in - await waitForResume { continuation in - group.addTask { - try await queue.exec( - priority: option.task, - flags: [option.flags, .block] - ) { - continuation.resume() - try await self.sleep(seconds: 2) - } - } - } - group.addTask { - do { - try await queue.wait(forSeconds: 1) - XCTFail( - "Unexpected task progression", - file: file, line: line - ) - } catch { - XCTAssertTrue( - type(of: error) == DurationTimeoutError.self, - file: file, line: line - ) - } - } - for try await _ in group.prefix(1) { - group.cancelAll() - } - } - } - } - func testWaitTimeout() async throws { let options: [TaskOption] = [ (queue: nil, task: nil, flags: []), @@ -145,7 +94,7 @@ class TaskQueueTimeoutTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in options.forEach { option in group.addTask { - try await self.checkWaitTimeoutOnQueue(option: option) + try await TaskQueue().checkWaitTimeout(for: option) } } try await group.waitForAll() @@ -153,56 +102,6 @@ class TaskQueueTimeoutTests: XCTestCase { } #if swift(>=5.7) - @available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) - private func checkWaitTimeoutOnQueue( - option: TaskOption, - clock: C, - file: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line - ) async throws where C.Duration == Duration { - let queue = TaskQueue(priority: option.queue) - try await self.checkExecInterval( - name: "For queue priority: \(option.queue.str), " - + "task priority: \(option.task.str) " - + "and flags: \(option.flags.rawValue)", - durationInSeconds: 1, - file: file, function: function, line: line - ) { - try await withThrowingTaskGroup(of: Void.self) { group in - await waitForResume { continuation in - group.addTask { - try await queue.exec( - priority: option.task, - flags: [option.flags, .block] - ) { - continuation.resume() - try await self.sleep(seconds: 2, clock: clock) - } - } - } - group.addTask { - do { - try await queue.wait(forSeconds: 1, clock: clock) - XCTFail( - "Unexpected task progression", - file: file, line: line - ) - } catch { - XCTAssertTrue( - type(of: error) - == TimeoutError.self, - file: file, line: line - ) - } - } - for try await _ in group.prefix(1) { - group.cancelAll() - } - } - } - } - func testWaitClockTimeout() async throws { guard #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) @@ -225,8 +124,8 @@ class TaskQueueTimeoutTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in options.forEach { option in group.addTask { - try await self.checkWaitTimeoutOnQueue( - option: option, + try await TaskQueue().checkWaitTimeout( + for: option, clock: clock ) } @@ -242,206 +141,150 @@ class TaskQueueBlockOperationTests: XCTestCase { func testExecutionOfTwoOperations() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - group.addTask { - try await queue.exec(flags: .block) { - try await self.sleep(seconds: 1) - } - } - group.addTask { - try await queue.exec(flags: .block) { - try await self.sleep(seconds: 2) - } - } - try await group.waitForAll() + let stream = AsyncStream { c in + Task.detached { + await queue.addTaskAndStart(flags: .block) { c.yield(1) } + await queue.addTaskAndStart(flags: .block) { c.yield(2) } + c.finish() } } + await stream.assertElements() + try await queue.wait(forSeconds: 3) } func testExecutionOfTaskBeforeOperation() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 1) { - try await withThrowingTaskGroup(of: Void.self) { group in - await waitForResume { continuation in - group.addTask { - try await queue.exec { - continuation.resume() - try await self.sleep(seconds: 1) - } - } - } - group.addTask { - try await queue.exec(flags: .block) { - try await self.sleep(seconds: 1) - } + let stream = AsyncStream { c in + Task.detached { + await queue.addTaskAndStart { + c.yield(1) + try await Task.sleep(seconds: 10) } - try await group.waitForAll() + await queue.addTaskAndStart(flags: .block) { c.yield(2) } + c.finish() } } + try await queue.wait(forSeconds: 3) + await stream.assertElements() } func testExecutionOfTaskAfterOperation() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await waitForResume { continuation in - group.addTask { - try await queue.exec(flags: .block) { - continuation.resume() - try await self.sleep(seconds: 2) - } - } - } - group.addTask { - try await queue.exec { - try await self.sleep(seconds: 1) - } + let stream = AsyncStream { c in + Task.detached { + await queue.addTaskAndStart(flags: .block) { + c.yield(1) + try await Task.sleep(seconds: 1) + c.yield(2) } - try await group.waitForAll() + await queue.addTaskAndStart { c.yield(3) } + c.finish() } } + await stream.assertElements() + try await queue.wait(forSeconds: 3) } func testCancellation() async throws { let queue = TaskQueue() - queue.addTask(flags: .block) { - try await self.sleep(seconds: 10) + await queue.addTaskAndStart(flags: .block) { + try await Task.sleep(seconds: 10) } let task = Task.detached { - try await self.checkExecInterval(durationInSeconds: 0) { - await queue.exec(flags: .block) {} - try await queue.wait() - } + await queue.exec(flags: .block) {} + try await queue.wait(forSeconds: 3) + XCTFail("Unexpected task progression") } - task.cancel() do { + task.cancel() try await task.value XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } + } catch is CancellationError {} } func testAlreadyCancelledTask() async throws { let queue = TaskQueue() - queue.addTask(flags: .block) { - try await self.sleep(seconds: 10) + await queue.addTaskAndStart(flags: .block) { + try await Task.sleep(seconds: 10) } let task = Task.detached { - try await self.checkExecInterval(durationInSeconds: 0) { - do { - try await self.sleep(seconds: 5) - XCTFail("Unexpected task progression") - } catch {} - XCTAssertTrue(Task.isCancelled) - await queue.exec(flags: .block) {} - try await queue.wait() - } + do { + try await Task.sleep(seconds: 10) + XCTFail("Unexpected task progression") + } catch {} + XCTAssertTrue(Task.isCancelled) + await queue.exec(flags: .block) {} + try await queue.wait(forSeconds: 3) + XCTFail("Unexpected task progression") } - task.cancel() do { + task.cancel() try await task.value XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } + } catch is CancellationError {} } func testCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await self.sleep(seconds: 1) - // Throws error for waiting method - throw CancellationError() - } - group.addTask { - try await queue.exec(flags: .block) { - try await self.sleep(seconds: 2) - } - } - do { - try await group.waitForAll() - } catch { - // Cancels block task - group.cancelAll() - } - try await queue.exec { - try await self.sleep(seconds: 2) + await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { throw CancellationError() } + group.addTask { + try await queue.exec(flags: .block) { + try await Task.sleep(seconds: 10) } } + try? await group.waitForAll() + // Cancels block task + group.cancelAll() } + try await queue.wait(forSeconds: 3) } func testMultipleCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await self.sleep(seconds: 1) - // Throws error for waiting method - throw CancellationError() - } - group.addTask { - try await queue.exec(flags: .block) { - try await self.sleep(seconds: 2) - } + await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { throw CancellationError() } + group.addTask { + try await queue.exec(flags: .block) { + try await Task.sleep(seconds: 10) } - group.addTask { - try await queue.exec(flags: .block) { - try await self.sleep(seconds: 3) - } - } - do { - try await group.waitForAll() - } catch { - // Cancels block tasks - group.cancelAll() - } - try await queue.exec { - try await self.sleep(seconds: 2) + } + group.addTask { + try await queue.exec(flags: .block) { + try await Task.sleep(seconds: 10) } } + try? await group.waitForAll() + // Cancels block task + group.cancelAll() } + try await queue.wait(forSeconds: 3) } func testMixedeCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await self.sleep(seconds: 1) - // Throws error for waiting method - throw CancellationError() - } - group.addTask { - try await queue.exec(flags: .block) { - try await self.sleep(seconds: 2) - } - } - group.addTask { - try await queue.exec(flags: .block) { - try await self.sleep(seconds: 3) - } - } - group.addTask { - try await queue.exec { - try await self.sleep(seconds: 4) - } + await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { throw CancellationError() } + group.addTask { + try await queue.exec(flags: .block) { + try await Task.sleep(seconds: 10) } - do { - try await group.waitForAll() - } catch { - // Cancels block tasks - group.cancelAll() + } + group.addTask { + try await queue.exec(flags: .block) { + try await Task.sleep(seconds: 10) } + } + group.addTask { try await queue.exec { - try await self.sleep(seconds: 2) + try await Task.sleep(seconds: 3) } } + try? await group.waitForAll() + // Cancels block task + group.cancelAll() } + try await queue.wait(forSeconds: 3) } } @@ -450,206 +293,151 @@ class TaskQueueBarrierOperationTests: XCTestCase { func testExecutionOfTwoOperations() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - group.addTask { - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 2) - } - } - group.addTask { - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 1) - } - } - try await group.waitForAll() + let stream = AsyncStream { c in + Task.detached { + await queue.addTaskAndStart(flags: .barrier) { c.yield(1) } + await queue.addTaskAndStart(flags: .barrier) { c.yield(2) } + c.finish() } } + await stream.assertElements() + try await queue.wait(forSeconds: 3) } func testExecutionOfTaskBeforeOperation() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await waitForResume { continuation in - group.addTask { - try await queue.exec { - continuation.resume() - try await self.sleep(seconds: 2) - } - } + let stream = AsyncStream { c in + Task.detached { + await queue.addTaskAndStart { + c.yield(1) + try await Task.sleep(seconds: 1) + c.yield(2) } - group.addTask { - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 1) - } - } - try await group.waitForAll() + await queue.addTaskAndStart(flags: .barrier) { c.yield(3) } + c.finish() } } + try await queue.wait(forSeconds: 3) + await stream.assertElements() } func testExecutionOfTaskAfterOperation() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await waitForResume { continuation in - group.addTask { - try await queue.exec(flags: .barrier) { - continuation.resume() - try await self.sleep(seconds: 2) - } - } - } - group.addTask { - try await queue.exec { - try await self.sleep(seconds: 1) - } + let stream = AsyncStream { c in + Task.detached { + await queue.addTaskAndStart(flags: .barrier) { + c.yield(1) + try await Task.sleep(seconds: 1) + c.yield(2) } - try await group.waitForAll() + await queue.addTaskAndStart { c.yield(3) } + c.finish() } } + await stream.assertElements() + try await queue.wait(forSeconds: 3) } func testCancellation() async throws { let queue = TaskQueue() - queue.addTask(flags: .barrier) { - try await self.sleep(seconds: 10) + await queue.addTaskAndStart(flags: .barrier) { + try await Task.sleep(seconds: 10) } let task = Task.detached { - try await self.checkExecInterval(durationInSeconds: 0) { - await queue.exec(flags: .barrier) {} - try await queue.wait() - } + await queue.exec(flags: .block) {} + try await queue.wait(forSeconds: 3) + XCTFail("Unexpected task progression") } - task.cancel() do { + task.cancel() try await task.value XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } + } catch is CancellationError {} } func testAlreadyCancelledTask() async throws { let queue = TaskQueue() - queue.addTask(flags: .barrier) { - try await self.sleep(seconds: 10) + await queue.addTaskAndStart(flags: .barrier) { + try await Task.sleep(seconds: 10) } let task = Task.detached { - try await self.checkExecInterval(durationInSeconds: 0) { - do { - try await self.sleep(seconds: 5) - XCTFail("Unexpected task progression") - } catch {} - XCTAssertTrue(Task.isCancelled) - await queue.exec(flags: .barrier) {} - try await queue.wait() - } + do { + try await Task.sleep(seconds: 10) + XCTFail("Unexpected task progression") + } catch {} + XCTAssertTrue(Task.isCancelled) + await queue.exec(flags: .barrier) {} + try await queue.wait(forSeconds: 3) + XCTFail("Unexpected task progression") } - task.cancel() do { + task.cancel() try await task.value XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } + } catch is CancellationError {} } func testCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await self.sleep(seconds: 1) - // Throws error for waiting method - throw CancellationError() - } - group.addTask { - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 2) - } - } - do { - try await group.waitForAll() - } catch { - // Cancels block task - group.cancelAll() - } - try await queue.exec { - try await self.sleep(seconds: 2) + await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { throw CancellationError() } + group.addTask { + try await queue.exec(flags: .barrier) { + try await Task.sleep(seconds: 10) } } + try? await group.waitForAll() + // Cancels block task + group.cancelAll() } + try await queue.wait(forSeconds: 3) } func testMultipleCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await self.sleep(seconds: 1) - // Throws error for waiting method - throw CancellationError() + await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { throw CancellationError() } + group.addTask { + try await queue.exec(flags: .barrier) { + try await Task.sleep(seconds: 10) } - group.addTask { - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 3) - } - } - group.addTask { - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 2) - } - } - do { - try await group.waitForAll() - } catch { - // Cancels block tasks - group.cancelAll() - } - try await queue.exec { - try await self.sleep(seconds: 2) + } + group.addTask { + try await queue.exec(flags: .barrier) { + try await Task.sleep(seconds: 10) } } + try? await group.waitForAll() + // Cancels block task + group.cancelAll() } + try await queue.wait(forSeconds: 3) } func testMixedCancellationWithoutBlocking() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await self.sleep(seconds: 1) - // Throws error for waiting method - throw CancellationError() - } - group.addTask { - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 3) - } - } - group.addTask { - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 2) - } - } - group.addTask { - try await queue.exec { - try await self.sleep(seconds: 4) - } + await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { throw CancellationError() } + group.addTask { + try await queue.exec(flags: .barrier) { + try await Task.sleep(seconds: 10) } - do { - try await group.waitForAll() - } catch { - // Cancels block tasks - group.cancelAll() + } + group.addTask { + try await queue.exec(flags: .barrier) { + try await Task.sleep(seconds: 10) } + } + group.addTask { try await queue.exec { - try await self.sleep(seconds: 2) + try await Task.sleep(seconds: 3) } } + try? await group.waitForAll() + // Cancels block task + group.cancelAll() } + try await queue.wait(forSeconds: 3) } } @@ -658,84 +446,57 @@ class TaskQueueMixedOperationTests: XCTestCase { func testExecutionOfBlockTaskBeforeBarrierOperation() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await waitForResume { continuation in - group.addTask { - try await queue.exec(flags: .block) { - continuation.resume() - try await self.sleep(seconds: 2) - } - } + let stream = AsyncStream { c in + Task.detached { + await queue.addTaskAndStart(flags: .block) { + c.yield(1) + try await Task.sleep(seconds: 1) + c.yield(2) } - group.addTask { - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 1) - } - } - try await group.waitForAll() + await queue.addTaskAndStart(flags: .barrier) { c.yield(3) } + c.finish() } } + await stream.assertElements() + try await queue.wait(forSeconds: 3) } func testExecutionOfBlockTaskAfterBarrierOperation() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await waitForResume { continuation in - group.addTask { - try await queue.exec(flags: .barrier) { - continuation.resume() - try await self.sleep(seconds: 2) - } - } + let stream = AsyncStream { c in + Task.detached { + await queue.addTaskAndStart(flags: .barrier) { + c.yield(1) + try await Task.sleep(seconds: 1) + c.yield(2) } - group.addTask { - try await queue.exec(flags: .block) { - try await self.sleep(seconds: 1) - } - } - try await group.waitForAll() + await queue.addTaskAndStart(flags: .block) { c.yield(3) } + c.finish() } } + await stream.assertElements() + try await queue.wait(forSeconds: 3) } func testLongRunningConcurrentTaskWithShortBlockTaskBeforeBarrierOperation() async throws { let queue = TaskQueue() - // Concurrent + Barrier - try await self.checkExecInterval(durationInSeconds: 5) { - try await withThrowingTaskGroup(of: Void.self) { group in - await waitForResume { continuation in - group.addTask { - try await queue.exec { - continuation.resume() - try await self.sleep(seconds: 2) - } - } + // Concurrent + Block + Barrier + let stream = AsyncStream { c in + Task.detached { + await queue.addTaskAndStart { + c.yield(1) + try await Task.sleep(seconds: 3) + c.yield(2) } - group.addTask { - try await queue.exec(flags: .block) { - try await self.sleep(seconds: 1) - } - } - let waiter = Task(priority: .background) { () -> QE? in - let items = await queue.queue - return items.reversed().first - } - while let (_, (_, flags)) = await waiter.value { - guard flags.contains(.block) else { continue } - break - } - group.addTask { - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 3) - } - } - try await group.waitForAll() + await queue.addTaskAndStart(flags: .block) { c.yield(2) } + await queue.addTaskAndStart(flags: .barrier) { c.yield(3) } + c.finish() } } + await stream.assertElements() + try await queue.wait(forSeconds: 3) } func testLongRunningConcurrentTaskWithShortBlockTaskAfterBarrierOperation() @@ -743,288 +504,259 @@ class TaskQueueMixedOperationTests: XCTestCase { { let queue = TaskQueue() // Concurrent + Barrier + Block - await self.checkExecInterval(durationInSeconds: 6) { - await withTaskGroup(of: Void.self) { group in - await waitForResume { continuation in - group.addTask { - await queue.exec { - continuation.resume() - try! await self.sleep(seconds: 3) - } - } - } - group.addTask { - await queue.exec(flags: .barrier) { - try! await self.sleep(seconds: 2) - } - } - let waiter = Task(priority: .background) { () -> QE? in - let items = await queue.queue - return items.reversed().first + let stream = AsyncStream { c in + Task.detached { + await queue.addTaskAndStart { + c.yield(1) + try await Task.sleep(seconds: 1) + c.yield(2) } - while let (_, (_, flags)) = await waiter.value { - guard flags.contains(.barrier) else { continue } - break + await queue.addTaskAndStart(flags: .barrier) { + c.yield(3) + try await Task.sleep(seconds: 1) + c.yield(4) } - group.addTask { - await queue.exec(flags: .block) { - try! await self.sleep(seconds: 1) - } - } - await group.waitForAll() + await queue.addTaskAndStart(flags: .block) { c.yield(5) } + c.finish() } } + await stream.assertElements() + try await queue.wait(forSeconds: 3) } /// Scenario described in: /// https://forums.swift.org/t/concurrency-suspending-an-actor-async-func-until-the-actor-meets-certain-conditions/56580 func testBarrierTaskWithMultipleConcurrentTasks() async throws { let queue = TaskQueue() - await self.checkExecInterval(durationInSeconds: 8) { - await withTaskGroup(of: Void.self) { group in - await withTaskGroup(of: Void.self) { cgroup in - for i in 0..<3 { - cgroup.addTask { - await waitForResume { continuation in - Task { - await queue.exec { - continuation.resume() - try! await self.sleep(seconds: i + 1) - } - } + let stream = AsyncStream { c in + Task.detached { + await withTaskGroup(of: Void.self) { group in + for _ in 0..<3 { + await group.addTaskAndStart { + await queue.addTaskAndStart { + c.yield(1) + try await Task.sleep(seconds: 1) + c.yield(1) } } } - await cgroup.waitForAll() + await group.waitForAll() } - group.addTask { - await queue.exec(flags: .barrier) { - try! await self.sleep(seconds: 2) - } + await queue.addTaskAndStart(flags: .barrier) { + c.yield(2) + try await Task.sleep(seconds: 1) + c.yield(3) } - let waiter = Task(priority: .background) { () -> QE? in - let items = await queue.queue - return items.reversed().first - } - while let (_, (_, flags)) = await waiter.value { - guard flags.contains(.barrier) else { continue } - break - } - group.addTask { - await queue.exec { - try! await self.sleep(seconds: 1) - } + await queue.addTaskAndStart { c.yield(4) } + await queue.addTaskAndStart { c.yield(4) } + await queue.addTaskAndStart { c.yield(4) } + c.finish() + } + } + await stream.assertElements() + try await queue.wait(forSeconds: 3) + } + + func testCancellableAndNonCancellableTasks() async throws { + let queue = TaskQueue() + await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { + try await queue.exec { + try await Task.sleep(seconds: 10) } - group.addTask { - await queue.exec { - try! await self.sleep(seconds: 2) - } + } + group.addTask { + try await queue.exec { + try await Task.sleep(seconds: 10) } - group.addTask { - await queue.exec { - try! await self.sleep(seconds: 3) + } + group.addTask { + await queue.exec { + do { + try await Task.sleep(seconds: 10) + XCTFail("Unexpected task progression") + } catch is CancellationError { + /* Do nothing */ + } catch { + XCTFail("Unexpected error \(error)") } } } + group.cancelAll() } } func testCancellableAndNonCancellableTasksWithBarrier() async throws { let queue = TaskQueue() - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - try await withThrowingTaskGroup(of: Void.self) { cgroup in - for i in 0..<3 { - cgroup.addTask { - await waitForResume { continuation in - Task { - try await queue.exec { - continuation.resume() - try await self.sleep(seconds: i + 1) - } - } - } + try await withThrowingTaskGroup(of: Void.self) { group in + await withTaskGroup(of: Void.self) { g in + for _ in 0..<3 { + await g.addTaskAndStart { + await queue.addTaskAndStart { + try await Task.sleep(seconds: 1) } } - try await cgroup.waitForAll() - } - group.addTask { - try await queue.exec(flags: .barrier) { - try await self.sleep(seconds: 2) - } } - let waiter = Task(priority: .background) { () -> QE? in - let items = await queue.queue - return items.reversed().first - } - while let (_, (_, flags)) = await waiter.value { - guard flags.contains(.barrier) else { continue } - break + await g.waitForAll() + } + group.addTask { + try await queue.exec(flags: .barrier) { + try await Task.sleep(seconds: 10) } - group.addTask { - try await queue.exec { - try await self.sleep(seconds: 2) - } + } + try await waitUntil(queue, timeout: 5) { + guard + let (_, (_, flags)) = $0.queue.reversed().first + else { return false } + return flags.contains(.barrier) + } + group.addTask { + try await queue.exec { + try await Task.sleep(seconds: 1) + XCTFail("Unexpected task progression") } - group.addTask { - await queue.exec { - do { - try await self.sleep(seconds: 3) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == CancellationError.self - ) - } + } + group.addTask { + await queue.exec { + do { + try await Task.sleep(seconds: 1) + XCTFail("Unexpected task progression") + } catch is CancellationError { + /* Do nothing */ + } catch { + XCTFail("Unexpected error \(error)") } } - group.addTask { - await queue.exec { - do { - try await self.sleep(seconds: 4) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == CancellationError.self - ) - } + } + group.addTask { + await queue.exec { + do { + try await Task.sleep(seconds: 1) + XCTFail("Unexpected task progression") + } catch is CancellationError { + /* Do nothing */ + } catch { + XCTFail("Unexpected error \(error)") } } - while await Task( - priority: .background, - operation: { - return !(await queue.blocked) - } - ).value {} - group.cancelAll() } + try await waitUntil(queue, timeout: 5) { $0.blocked } + group.cancelAll() } + try await queue.wait(forSeconds: 3) } } -@MainActor -class TaskQueueCancellationTests: XCTestCase { - - func testWaitCancellation() async throws { - let queue = TaskQueue() - queue.addTask(flags: .barrier) { - try await self.sleep(seconds: 10) +extension Optional where Wrapped == TaskPriority { + var str: String { + switch self { + case .none: + return "none" + case .some(let wrapped): + return "\(wrapped.rawValue)" } - let task = Task.detached { - try await self.checkExecInterval(durationInSeconds: 0) { - try await queue.wait() + } +} + +fileprivate extension TaskQueue { + func addTaskAndStart( + priority: TaskPriority? = nil, + flags: Flags = [], + operation: @Sendable @escaping () async -> T + ) async { + await withUnsafeContinuation { continuation in + self.addTask(priority: priority, flags: flags) { + continuation.resume() + return await operation() } } - task.cancel() - do { - try await task.value - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } } - func testAlreadyCancelledTask() async throws { - let queue = TaskQueue() - queue.addTask(flags: .barrier) { - try await self.sleep(seconds: 10) - } - let task = Task.detached { - try await self.checkExecInterval(durationInSeconds: 0) { - do { - try await self.sleep(seconds: 5) - XCTFail("Unexpected task progression") - } catch {} - XCTAssertTrue(Task.isCancelled) - try await queue.wait() + func addTaskAndStart( + priority: TaskPriority? = nil, + flags: Flags = [], + operation: @Sendable @escaping () async throws -> T + ) async { + await withUnsafeContinuation { continuation in + self.addTask(priority: priority, flags: flags) { + continuation.resume() + return try await operation() } } - task.cancel() - do { - try await task.value - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } } - func testCancellableAndNonCancellableTasks() async throws { - let queue = TaskQueue() - await self.checkExecInterval(durationInSeconds: 0) { - await withThrowingTaskGroup(of: Void.self) { group in - group.addTask { - try await queue.exec { - try await self.sleep(seconds: 2) - } - } - group.addTask { - try await queue.exec { - try await self.sleep(seconds: 3) - } - } - group.addTask { - await queue.exec { - do { - try await self.sleep(seconds: 4) - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue( - type(of: error) == CancellationError.self - ) - } - } - } - group.cancelAll() + @MainActor + func checkWait( + for option: TaskOption, + file: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line + ) async throws { + try await withThrowingTaskGroup(of: Void.self) { group in + await addTaskAndStart(priority: option.task, flags: option.flags) { + try await Task.sleep(seconds: 1) } + group.addTask { try await self.wait(forSeconds: 3) } + try await group.waitForAll() } + try await self.wait(forSeconds: 3) } -} -fileprivate extension XCTestCase { - typealias TaskOption = ( - queue: TaskPriority?, task: TaskPriority?, flags: TaskQueue.Flags - ) - - func checkWaitOnQueue( - option: TaskOption, + @MainActor + func checkWaitTimeout( + for option: TaskOption, file: StaticString = #filePath, function: StaticString = #function, line: UInt = #line ) async throws { - let queue = TaskQueue(priority: option.queue) - try await self.checkExecInterval( - name: "For queue priority: \(option.queue.str), " - + "task priority: \(option.task.str) " - + "and flags: \(option.flags.rawValue)", - durationInSeconds: 1, - file: file, function: function, line: line + await addTaskAndStart( + priority: option.task, + flags: [option.flags, .block] ) { - try await withThrowingTaskGroup(of: Void.self) { group in - await waitForResume { continuation in - group.addTask { - try await queue.exec( - priority: option.task, - flags: option.flags - ) { - continuation.resume() - try await self.sleep(seconds: 1) - } - } - } - group.addTask { try await queue.wait() } - try await group.waitForAll() - } + try await Task.sleep(seconds: 10) } + do { + try await self.wait(forSeconds: 5) + XCTFail("Unexpected task progression", file: file, line: line) + } catch is DurationTimeoutError {} } + + #if swift(>=5.7) + @available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) + @MainActor + func checkWaitTimeout( + for option: TaskOption, + clock: C, + file: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line + ) async throws where C.Duration == Duration { + await addTaskAndStart( + priority: option.task, + flags: [option.flags, .block] + ) { + try await Task.sleep(seconds: 10) + } + do { + try await self.wait(forSeconds: 5) + XCTFail("Unexpected task progression", file: file, line: line) + } catch is DurationTimeoutError {} + } + #endif } -extension Optional where Wrapped == TaskPriority { - var str: String { - switch self { - case .none: - return "none" - case .some(let wrapped): - return "\(wrapped.rawValue)" +fileprivate extension AsyncSequence where Element: BinaryInteger { + func assertElements( + initial value: Element = .zero, + diff: Element = 1, + file: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line + ) async rethrows { + var value = value + for try await val in self where value != val { + XCTAssertEqual(val, value + diff, file: file, line: line) + value = val } } } diff --git a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift index 9a0ba780..b411bf24 100644 --- a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift @@ -7,7 +7,7 @@ class ThrowingFutureTests: XCTestCase { func testFutureFulfilledInitialization() async throws { let future = Future(with: .success(5)) - let value = try await future.get() + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } @@ -15,11 +15,10 @@ class ThrowingFutureTests: XCTestCase { let future = Future() try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { - let value = try await future.get() + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } group.addTask { - try await self.sleep(seconds: 1) await future.fulfill(producing: 5) } try await group.waitForAll() @@ -31,14 +30,11 @@ class ThrowingFutureTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { do { - let _ = try await future.get() + let _ = try await future.wait(forSeconds: 3) XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } + } catch is CancellationError {} } group.addTask { - try await self.sleep(seconds: 1) await future.fulfill(throwing: CancellationError()) } try await group.waitForAll() @@ -49,80 +45,57 @@ class ThrowingFutureTests: XCTestCase { let future = Future() let waitTask = Task { do { - let _ = try await future.get() + let _ = try await future.wait(forSeconds: 3) XCTFail("Future fulfillments wait not cancelled") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } - } - try await withThrowingTaskGroup(of: Void.self) { group in - group.addTask { - try await self.sleep(seconds: 1) - waitTask.cancel() - } - group.addTask { - try await self.sleep(seconds: 2) - await future.fulfill(producing: 5) - } - try await group.waitForAll() + } catch is CancellationError {} } + waitTask.cancel() + try await waitTask.value } func testMultipleTimesFutureFulfilled() async throws { let future = Future(with: .success(5)) await future.fulfill(producing: 10) - let value = try await future.get() + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } func testDeinit() async throws { let future = Future() - Task.detached { - try await self.sleep(seconds: 1) - await future.fulfill(producing: 5) - } - let _ = try await future.get() + Task.detached { await future.fulfill(producing: 5) } + let _ = try await future.wait(forSeconds: 3) self.addTeardownBlock { [weak future] in - try await self.sleep(seconds: 1) - XCTAssertNil(future) + XCTAssertEqual(future.retainCount(), 0) } } func testWaitCancellationWhenTaskCancelled() async throws { let future = Future() let task = Task.detached { - await self.checkExecInterval(durationInSeconds: 0) { - do { - let _ = try await future.get() - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } - } + do { + let _ = try await future.wait(forSeconds: 3) + XCTFail("Unexpected task progression") + } catch is CancellationError {} } task.cancel() - await task.value + try await task.value } func testWaitCancellationForAlreadyCancelledTask() async throws { let future = Future() let task = Task.detached { - await self.checkExecInterval(durationInSeconds: 0) { - do { - try await self.sleep(seconds: 5) - XCTFail("Unexpected task progression") - } catch {} - XCTAssertTrue(Task.isCancelled) - do { - let _ = try await future.get() - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } - } + do { + try await Task.sleep(seconds: 10) + XCTFail("Unexpected task progression") + } catch {} + XCTAssertTrue(Task.isCancelled) + do { + let _ = try await future.wait(forSeconds: 3) + XCTFail("Unexpected task progression") + } catch is CancellationError {} } task.cancel() - await task.value + try await task.value } func testConcurrentAccess() async throws { @@ -130,13 +103,12 @@ class ThrowingFutureTests: XCTestCase { for i in 0..<10 { group.addTask { let future = Future() - try await self.checkExecInterval(durationInSeconds: 0) { - try await withThrowingTaskGroup(of: Void.self) { - group in - group.addTask { let _ = try await future.get() } - group.addTask { await future.fulfill(producing: i) } - try await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { + let _ = try await future.wait(forSeconds: 3) } + group.addTask { await future.fulfill(producing: i) } + try await group.waitForAll() } } try await group.waitForAll() @@ -153,26 +125,15 @@ class ThrowingFutureCombiningAllTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.all(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - let value = try await allFuture.get() - XCTAssertEqual(value, [1, 2, 3]) - } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(producing: 1) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(producing: 2) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + let value = try await allFuture.wait(forSeconds: 3) + XCTAssertEqual(value, [1, 2, 3]) } + group.addTask { await future1.fulfill(producing: 1) } + group.addTask { await future2.fulfill(producing: 2) } + group.addTask { await future3.fulfill(producing: 3) } + try await group.waitForAll() } } @@ -181,40 +142,25 @@ class ThrowingFutureCombiningAllTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.all(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - await self.checkExecInterval(durationInSeconds: 2) { - do { - let _ = try await allFuture.get() - XCTFail("Future fulfillment did not fail") - } catch { - XCTAssertTrue( - type(of: error) == CancellationError.self - ) - } - } - } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(producing: 1) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(throwing: CancellationError()) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + do { + let _ = try await allFuture.wait(forSeconds: 3) + XCTFail("Future fulfillment did not fail") + } catch is CancellationError {} + } + group.addTask { await future1.fulfill(producing: 1) } + group.addTask { + await future2.fulfill(throwing: CancellationError()) } + group.addTask { await future3.fulfill(producing: 3) } + try await group.waitForAll() } } func testEmptyConstructing() async throws { let future = Future.all() - let value = try await future.get() + let value = try await future.wait(forSeconds: 3) XCTAssertTrue(value.isEmpty) } } @@ -227,33 +173,22 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.allSettled(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - let values = await allFuture.get() - for (index, item) in values.enumerated() { - switch item { - case .success(let value): - XCTAssertEqual(value, index + 1) - default: - XCTFail("Unexpected future fulfillment") - } + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + let values = try await allFuture.wait(forSeconds: 3) + for (index, item) in values.enumerated() { + switch item { + case .success(let value): + XCTAssertEqual(value, index + 1) + default: + XCTFail("Unexpected future fulfillment") } } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(producing: 1) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(producing: 2) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() } + await future1.fulfill(producing: 1) + await future2.fulfill(producing: 2) + await future3.fulfill(producing: 3) + try await group.waitForAll() } } @@ -262,44 +197,30 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.allSettled(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - await self.checkExecInterval(durationInSeconds: 3) { - let values = await allFuture.get() - for (index, item) in values.enumerated() { - switch item { - case .success(let value): - XCTAssertEqual(value, index + 1) - case .failure(let error): - XCTAssertTrue( - type(of: error) == CancellationError.self - ) - XCTAssertEqual(index + 1, 2) - } - } + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + let values = try await allFuture.wait(forSeconds: 3) + for (index, item) in values.enumerated() { + switch item { + case .success(let value): + XCTAssertEqual(value, index + 1) + case .failure(is CancellationError): + XCTAssertEqual(index + 1, 2) + default: + XCTFail("Unexpected future fulfillment") } } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(producing: 1) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(throwing: CancellationError()) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() } + await future1.fulfill(producing: 1) + await future2.fulfill(throwing: CancellationError()) + await future3.fulfill(producing: 3) + try await group.waitForAll() } } func testEmptyConstructing() async throws { let future = Future.allSettled() - let value = await future.get() + let value = try await future.wait(forSeconds: 3) XCTAssertTrue(value.isEmpty) } } @@ -312,28 +233,15 @@ class ThrowingFutureRacingTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.race(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await self.checkExecInterval(durationInSeconds: 1) { - let value = try await allFuture.get() - XCTAssertEqual(value, 1) - } - } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(producing: 1) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(producing: 2) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + let value = try await allFuture.wait(forSeconds: 3) + XCTAssertEqual(value, 1) } + await future1.fulfill(producing: 1) + await future2.fulfill(producing: 2) + await future3.fulfill(producing: 3) + try await group.waitForAll() } } @@ -342,34 +250,17 @@ class ThrowingFutureRacingTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.race(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - await self.checkExecInterval(durationInSeconds: 1) { - do { - let _ = try await allFuture.get() - XCTFail("Future fulfillment did not fail") - } catch { - XCTAssertTrue( - type(of: error) == CancellationError.self - ) - } - } - } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(throwing: CancellationError()) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(producing: 2) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + do { + let _ = try await allFuture.wait(forSeconds: 3) + XCTFail("Future fulfillment did not fail") + } catch is CancellationError {} } + await future1.fulfill(throwing: CancellationError()) + await future2.fulfill(producing: 2) + await future3.fulfill(producing: 3) + try await group.waitForAll() } } } @@ -382,28 +273,15 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.any(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await self.checkExecInterval(durationInSeconds: 1) { - let value = try await allFuture.get() - XCTAssertEqual(value, 1) - } - } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(producing: 1) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(producing: 2) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + let value = try await allFuture.wait(forSeconds: 3) + XCTAssertEqual(value, 1) } + await future1.fulfill(producing: 1) + await future2.fulfill(producing: 2) + await future3.fulfill(producing: 3) + try await group.waitForAll() } } @@ -412,28 +290,15 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.any(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - try await self.checkExecInterval(durationInSeconds: 2) { - let value = try await allFuture.get() - XCTAssertEqual(value, 2) - } - } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(throwing: CancellationError()) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(producing: 2) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(producing: 3) - } - try await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + let value = try await allFuture.wait(forSeconds: 3) + XCTAssertEqual(value, 2) } + await future1.fulfill(throwing: CancellationError()) + await future2.fulfill(producing: 2) + await future3.fulfill(producing: 3) + try await group.waitForAll() } } @@ -442,34 +307,23 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future2 = Future() let future3 = Future() let allFuture = Future.any(future1, future2, future3) - try await self.checkExecInterval(durationInSeconds: 3) { - try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { - await self.checkExecInterval(durationInSeconds: 3) { - do { - let _ = try await allFuture.get() - XCTFail("Future fulfillment did not fail") - } catch { - XCTAssertTrue( - type(of: error) == CancellationError.self - ) - } - } - } - group.addTask { - try await self.sleep(seconds: 1) - await future1.fulfill(throwing: CancellationError()) - } - group.addTask { - try await self.sleep(seconds: 2) - await future2.fulfill(throwing: CancellationError()) - } - group.addTask { - try await self.sleep(seconds: 3) - await future3.fulfill(throwing: CancellationError()) - } - try await group.waitForAll() + try await withThrowingTaskGroup(of: Void.self) { group in + await group.addTaskAndStart { + do { + let _ = try await allFuture.wait(forSeconds: 3) + XCTFail("Future fulfillment did not fail") + } catch is CancellationError {} + } + group.addTask { + await future1.fulfill(throwing: CancellationError()) } + group.addTask { + await future2.fulfill(throwing: CancellationError()) + } + group.addTask { + await future3.fulfill(throwing: CancellationError()) + } + try await group.waitForAll() } } @@ -477,9 +331,20 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future = Future.any() let result = await future.result switch result { - case .failure(let error): - XCTAssertTrue(type(of: error) == CancellationError.self) + case .failure(is CancellationError): break default: XCTFail("Unexpected future fulfillment") } } } + +extension Future where Failure == Error { + @Sendable + @inlinable + func wait(forSeconds seconds: UInt64) async throws -> Output { + return try await waitForTaskCompletion( + withTimeoutInNanoseconds: seconds * 1_000_000_000 + ) { + return try await self.get() + } + } +} diff --git a/Tests/AsyncObjectsTests/TrackedContinuationTests.swift b/Tests/AsyncObjectsTests/TrackedContinuationTests.swift index a70a6ca2..b680d33f 100644 --- a/Tests/AsyncObjectsTests/TrackedContinuationTests.swift +++ b/Tests/AsyncObjectsTests/TrackedContinuationTests.swift @@ -15,29 +15,23 @@ class TrackedContinuationTests: XCTestCase { } func testDirectResumeWithSuccess() async throws { - await self.checkExecInterval(durationInSeconds: 0) { - await TrackedContinuation>.with { - XCTAssertFalse($0.resumed) - $0.resume() - XCTAssertTrue($0.resumed) - } + await TrackedContinuation>.with { + XCTAssertFalse($0.resumed) + $0.resume() + XCTAssertTrue($0.resumed) } } func testDirectResumeWithError() async throws { typealias C = GlobalContinuation - await self.checkExecInterval(durationInSeconds: 0) { - do { - try await TrackedContinuation.with { c in - XCTAssertFalse(c.resumed) - c.resume(throwing: CancellationError()) - XCTAssertTrue(c.resumed) - } - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) + do { + try await TrackedContinuation.with { c in + XCTAssertFalse(c.resumed) + c.resume(throwing: CancellationError()) + XCTAssertTrue(c.resumed) } - } + XCTFail("Unexpected task progression") + } catch is CancellationError {} } func testInitializedWithoutContinuationWithStatusWaiting() async throws { @@ -55,70 +49,60 @@ class TrackedContinuationTests: XCTestCase { func testCancellationHandlerWhenTaskCancelled() async throws { typealias C = GlobalContinuation let task = Task.detached { - await self.checkExecInterval(durationInSeconds: 0) { - do { - try await TrackedContinuation - .withCancellation(id: .init()) { - $0.cancel() - } operation: { _, preinit in - preinit() - } - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } - } + do { + try await TrackedContinuation + .withCancellation(id: .init()) { + $0.cancel() + } operation: { _, preinit in + preinit() + } + XCTFail("Unexpected task progression") + } catch is CancellationError {} } task.cancel() - await task.value + try await task.value } func testCancellationHandlerForAlreadyCancelledTask() async throws { typealias C = GlobalContinuation let task = Task.detached { - await self.checkExecInterval(durationInSeconds: 0) { - do { - try await self.sleep(seconds: 5) - XCTFail("Unexpected task progression") - } catch {} - XCTAssertTrue(Task.isCancelled) - do { - try await TrackedContinuation - .withCancellation(id: .init()) { - $0.cancel() - } operation: { _, preinit in - preinit() - } - XCTFail("Unexpected task progression") - } catch { - XCTAssertTrue(type(of: error) == CancellationError.self) - } - } + do { + try await Task.sleep(seconds: 5) + XCTFail("Unexpected task progression") + } catch {} + XCTAssertTrue(Task.isCancelled) + do { + try await TrackedContinuation + .withCancellation(id: .init()) { + $0.cancel() + } operation: { _, preinit in + preinit() + } + XCTFail("Unexpected task progression") + } catch is CancellationError {} } task.cancel() - await task.value + try await task.value } func testNonCancellableContinuation() async throws { typealias C = GlobalContinuation let task = Task.detached { - await self.checkExecInterval(durationInSeconds: 1) { - do { - try await self.sleep(seconds: 5) - XCTFail("Unexpected task progression") - } catch {} - XCTAssertTrue(Task.isCancelled) - await TrackedContinuation - .withCancellation(id: .init()) { _ in - // Do nothing - } operation: { continuation, preinit in - preinit() - Task { - defer { continuation.resume() } - try await self.sleep(seconds: 1) - } + do { + try await Task.sleep(seconds: 5) + XCTFail("Unexpected task progression") + } catch {} + XCTAssertTrue(Task.isCancelled) + await TrackedContinuation + .withCancellation(id: .init()) { _ in + // Do nothing + } operation: { continuation, preinit in + preinit() + Task { + defer { continuation.resume() } + try await Task.sleep(seconds: 1) } - } + } } task.cancel() await task.value diff --git a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift new file mode 100644 index 00000000..6e1ddf5c --- /dev/null +++ b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift @@ -0,0 +1,96 @@ +import XCTest +import Dispatch +@testable import AsyncObjects + +func waitForResume(_ body: (UnsafeContinuation) -> Void) async { + await withUnsafeContinuation(body) +} + +func waitUntil( + _ actor: T, + timeout: TimeInterval, + satisfies condition: @escaping (isolated T) async throws -> Bool +) async throws { + let maxWait = timeout * 1E9 + try await waitForTaskCompletion(withTimeoutInNanoseconds: UInt64(maxWait)) { + var interval = maxWait + var retryWait = 2.0 + while case let result = try await Task( + priority: .background, + operation: { try await condition(actor) } + ).value { + guard !result else { break } + try Task.checkCancellation() + try await Task.sleep(nanoseconds: UInt64(retryWait)) + interval -= retryWait + if interval < 0 { + throw DurationTimeoutError( + for: UInt64(maxWait), + tolerance: UInt64(interval * -1) + ) + } else if interval < retryWait / 2 { + retryWait = max(1E9, retryWait.squareRoot()) + } else { + retryWait *= retryWait + } + } + } +} + +extension Task where Success == Never, Failure == Never { + static func sleep(seconds: T) async throws { + let second: T = 1_000_000_000 + try await Task.sleep(nanoseconds: UInt64(exactly: seconds * second)!) + } +} + +extension AsyncObject { + @Sendable + @inlinable + func wait(forSeconds seconds: UInt64) async throws { + return try await self.wait(forNanoseconds: seconds * 1_000_000_000) + } +} + +#if swift(>=5.7) +@available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) +extension Task where Success == Never, Failure == Never { + + static func sleep( + seconds: T, + clock: C + ) async throws where C.Duration == Duration { + try await Task.sleep( + until: clock.now.advanced(by: .seconds(seconds)), + clock: clock + ) + } +} + +@available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) +extension AsyncObject { + @Sendable + @inlinable + func wait( + forSeconds seconds: T, + clock: C + ) async throws where C.Duration == Duration { + return try await self.wait( + until: clock.now.advanced(by: .seconds(seconds)), + tolerance: .microseconds(1), + clock: clock + ) + } +} +#endif + +extension Optional where Wrapped: AnyObject { + func retainCount() -> Int { + switch self { + case .none: + return 0 + case .some(let wrapped): + return CFGetRetainCount(wrapped) + } + } +} diff --git a/Tests/AsyncObjectsTests/XCTestCase.swift b/Tests/AsyncObjectsTests/XCTestCase.swift deleted file mode 100644 index 3f5b51ad..00000000 --- a/Tests/AsyncObjectsTests/XCTestCase.swift +++ /dev/null @@ -1,182 +0,0 @@ -import XCTest -import Dispatch -@testable import AsyncObjects - -@MainActor -extension XCTestCase { - private static var activitySupported = ProcessInfo.processInfo.environment - .keys.contains("__XCODE_BUILT_PRODUCTS_DIR_PATHS") - - private func runAssertions( - with name: String?, - _ assertions: () -> Void - ) { - #if canImport(Darwin) - if let name = name, Self.activitySupported { - XCTContext.runActivity(named: name) { _ in - assertions() - } - } else { - assertions() - } - #else - assertions() - #endif - } - - func checkExecInterval( - name: String? = nil, - durationInSeconds seconds: T = .zero, - file: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - for task: () async throws -> Void - ) async rethrows where T: Comparable { - let second: T = 1_000_000_000 - let time = DispatchTime.now().uptimeNanoseconds - try await task() - guard - let span = T(exactly: DispatchTime.now().uptimeNanoseconds - time), - case let duration = span / second - else { - XCTFail("Invalid number type: \(T.self)", file: file, line: line) - return - } - - let assertions = { - XCTAssertLessThanOrEqual( - duration, seconds + 3, - file: file, line: line - ) - XCTAssertGreaterThanOrEqual( - duration, seconds - 3, - file: file, line: line - ) - } - runAssertions(with: name, assertions) - } - - func checkExecInterval( - name: String? = nil, - durationInRange range: R, - file: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - for task: () async throws -> Void - ) async rethrows where R.Bound: DivisiveArithmetic { - let second: R.Bound = 1_000_000_000 - let time = DispatchTime.now().uptimeNanoseconds - try await task() - guard - let span = R.Bound( - exactly: DispatchTime.now().uptimeNanoseconds - time - ), - case let duration = span / second - else { - XCTFail("Invalid range type: \(R.self)", file: file, line: line) - return - } - - let assertions = { - XCTAssertTrue( - range.contains(duration), - "\(duration) not present in \(range)", - file: file, line: line - ) - } - runAssertions(with: name, assertions) - } - - func sleep(seconds: T) async throws { - let second: T = 1_000_000_000 - try await Task.sleep(nanoseconds: UInt64(exactly: seconds * second)!) - } - - func sleep(seconds: T) async throws { - let second: T = 1_000_000_000 - try await Task.sleep(nanoseconds: UInt64(exactly: seconds * second)!) - } -} - -extension AsyncObject { - @Sendable - @inlinable - func wait(forSeconds seconds: UInt64) async throws { - return try await self.wait(forNanoseconds: seconds * 1_000_000_000) - } -} - -#if swift(>=5.7) -@available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) -@MainActor -extension XCTestCase { - - func checkExecInterval( - name: String? = nil, - duration: C.Instant.Duration = .zero, - clock: C, - file: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - for task: () async throws -> Void - ) async rethrows where C.Duration == Duration { - let result = try await clock.measure { try await task() } - let assertions = { - XCTAssertLessThanOrEqual( - abs(duration.components.seconds - result.components.seconds), 3, - file: file, line: line - ) - } - runAssertions(with: name, assertions) - } - - func sleep( - seconds: T, - clock: C - ) async throws where C.Duration == Duration { - try await Task.sleep( - until: clock.now.advanced(by: .seconds(seconds)), - clock: clock - ) - } - - func sleep( - seconds: Double, - clock: C - ) async throws where C.Duration == Duration { - try await Task.sleep( - until: clock.now.advanced(by: .seconds(seconds)), - clock: clock - ) - } -} - -@available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) -extension AsyncObject { - @Sendable - @inlinable - func wait( - forSeconds seconds: T, - clock: C - ) async throws where C.Duration == Duration { - return try await self.wait( - until: clock.now.advanced(by: .seconds(seconds)), - tolerance: .microseconds(1), - clock: clock - ) - } -} -#endif - -protocol DivisiveArithmetic: Numeric { - static func / (lhs: Self, rhs: Self) -> Self - static func /= (lhs: inout Self, rhs: Self) -} - -extension Int: DivisiveArithmetic {} -extension Double: DivisiveArithmetic {} -extension UInt64: DivisiveArithmetic {} - -func waitForResume(_ body: (UnsafeContinuation) -> Void) async { - await withUnsafeContinuation(body) -} From 4568dcb85f12f8e03ca6d9dbf1d38e23c9a55a8e Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Wed, 28 Dec 2022 23:06:26 +0530 Subject: [PATCH 09/22] wip: remove `CFGetRetainCount` usage for non-darwin --- .../AsyncCountdownEventTests.swift | 2 +- Tests/AsyncObjectsTests/AsyncEventTests.swift | 2 +- Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift | 2 +- .../CancellationSourceTests.swift | 4 ++-- .../AsyncObjectsTests/NonThrowingFutureTests.swift | 2 +- Tests/AsyncObjectsTests/TaskOperationTests.swift | 4 ++-- Tests/AsyncObjectsTests/TaskQueueTests.swift | 2 +- Tests/AsyncObjectsTests/ThrowingFutureTests.swift | 2 +- Tests/AsyncObjectsTests/XCAsyncTestCase.swift | 14 +++++++++++--- 9 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index 0823aa02..e691c77d 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -75,7 +75,7 @@ class AsyncCountdownEventTests: XCTestCase { Task.detached { event.signal() } try await event.wait(forSeconds: 3) self.addTeardownBlock { [weak event] in - XCTAssertEqual(event.retainCount(), 0) + event.assertReleased() } } diff --git a/Tests/AsyncObjectsTests/AsyncEventTests.swift b/Tests/AsyncObjectsTests/AsyncEventTests.swift index 7e794d00..d702554c 100644 --- a/Tests/AsyncObjectsTests/AsyncEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncEventTests.swift @@ -28,7 +28,7 @@ class AsyncEventTests: XCTestCase { Task.detached { event.signal() } try await event.wait(forSeconds: 3) self.addTeardownBlock { [weak event] in - XCTAssertEqual(event.retainCount(), 0) + event.assertReleased() } } diff --git a/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift b/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift index 27bb5fb9..a3523219 100644 --- a/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift +++ b/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift @@ -74,7 +74,7 @@ class AsyncSemaphoreTests: XCTestCase { let semaphore = AsyncSemaphore(value: 1) try await semaphore.wait(forSeconds: 3) self.addTeardownBlock { [weak semaphore] in - XCTAssertEqual(semaphore.retainCount(), 0) + semaphore.assertReleased() } } } diff --git a/Tests/AsyncObjectsTests/CancellationSourceTests.swift b/Tests/AsyncObjectsTests/CancellationSourceTests.swift index aef789dc..fc871355 100644 --- a/Tests/AsyncObjectsTests/CancellationSourceTests.swift +++ b/Tests/AsyncObjectsTests/CancellationSourceTests.swift @@ -78,7 +78,7 @@ class CancellationSourceTests: XCTestCase { try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } try? await task.value self.addTeardownBlock { [weak source] in - XCTAssertEqual(source.retainCount(), 0) + source.assertReleased() } } @@ -171,7 +171,7 @@ class CancellationSourceInitializationTests: XCTestCase { try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } try? await task.value self.addTeardownBlock { [weak source] in - XCTAssertEqual(source.retainCount(), 0) + source.assertReleased() } } } diff --git a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift index 752fb146..b7a2c2b1 100644 --- a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift @@ -57,7 +57,7 @@ class NonThrowingFutureTests: XCTestCase { Task.detached { await future.fulfill(producing: 5) } let _ = try await future.wait(forSeconds: 3) self.addTeardownBlock { [weak future] in - XCTAssertEqual(future.retainCount(), 0) + future.assertReleased() } } diff --git a/Tests/AsyncObjectsTests/TaskOperationTests.swift b/Tests/AsyncObjectsTests/TaskOperationTests.swift index cb1c8550..646d5d58 100644 --- a/Tests/AsyncObjectsTests/TaskOperationTests.swift +++ b/Tests/AsyncObjectsTests/TaskOperationTests.swift @@ -87,7 +87,7 @@ class TaskOperationTests: XCTestCase { operation.signal() try await operation.wait(forSeconds: 3) self.addTeardownBlock { [weak operation] in - XCTAssertEqual(operation.retainCount(), 0) + operation.assertReleased() } } @@ -235,7 +235,7 @@ class TaskOperationCancellationTests: XCTestCase { operation.cancel() try await waitUntil(operation, timeout: 3, satisfies: \.isCancelled) self.addTeardownBlock { [weak operation] in - XCTAssertEqual(operation.retainCount(), 0) + operation.assertReleased() } } } diff --git a/Tests/AsyncObjectsTests/TaskQueueTests.swift b/Tests/AsyncObjectsTests/TaskQueueTests.swift index 642e46ad..0302a7eb 100644 --- a/Tests/AsyncObjectsTests/TaskQueueTests.swift +++ b/Tests/AsyncObjectsTests/TaskQueueTests.swift @@ -70,7 +70,7 @@ class TaskQueueTests: XCTestCase { await queue.exec(flags: .barrier) { /* Do nothing */ } await queue.exec { /* Do nothing */ } self.addTeardownBlock { [weak queue] in - XCTAssertEqual(queue.retainCount(), 0) + queue.assertReleased() } } } diff --git a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift index b411bf24..7ecbe012 100644 --- a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift @@ -65,7 +65,7 @@ class ThrowingFutureTests: XCTestCase { Task.detached { await future.fulfill(producing: 5) } let _ = try await future.wait(forSeconds: 3) self.addTeardownBlock { [weak future] in - XCTAssertEqual(future.retainCount(), 0) + future.assertReleased() } } diff --git a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift index 6e1ddf5c..43446898 100644 --- a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift +++ b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift @@ -85,12 +85,20 @@ extension AsyncObject { #endif extension Optional where Wrapped: AnyObject { - func retainCount() -> Int { + func assertReleased( + file: StaticString = #filePath, + function: StaticString = #function, + line: UInt = #line + ) { switch self { case .none: - return 0 + break case .some(let wrapped): - return CFGetRetainCount(wrapped) + #if canImport(Darwin) + XCTAssertEqual(CFGetRetainCount(wrapped), 0, file: file, line: line) + #else + XCTFail("Has some value \(wrapped)", file: file, line: line) + #endif } } } From af058e9944e91cb15ef25eff35efcd70632fcb98 Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Thu, 29 Dec 2022 13:58:39 +0530 Subject: [PATCH 10/22] wip: fix build fail on older Swift version --- .../AsyncCountdownEventTests.swift | 86 +++++++++---------- Tests/AsyncObjectsTests/AsyncEventTests.swift | 24 +++--- .../AsyncSemaphoreTests.swift | 28 +++--- .../CancellationSourceTests.swift | 46 +++++----- .../NonThrowingFutureTests.swift | 26 +++--- .../TaskOperationTests.swift | 30 +++---- Tests/AsyncObjectsTests/TaskQueueTests.swift | 64 +++++++------- .../ThrowingFutureTests.swift | 40 ++++----- Tests/AsyncObjectsTests/XCAsyncTestCase.swift | 4 - 9 files changed, 172 insertions(+), 176 deletions(-) diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index e691c77d..d6ce1c41 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -6,74 +6,74 @@ class AsyncCountdownEventTests: XCTestCase { func testWithoutIncrement() async throws { let event = AsyncCountdownEvent() - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testWithIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) event.signal(concurrent: 10) - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testWithOverIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) event.signal(concurrent: 15) - try await event.wait(forSeconds: 3) - try await waitUntil(event, timeout: 3) { $0.currentCount == 0 } + try await event.wait(forSeconds: 5) + try await waitUntil(event, timeout: 5) { $0.currentCount == 0 } } func testWithLimitAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) event.signal(concurrent: 7) - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testWithLimitInitialCountAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3, initial: 2) event.increment(by: 10) event.signal(concurrent: 9) - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testWithIncrementAndReset() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.reset() - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testWithIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.reset(to: 2) - try await waitUntil(event, timeout: 3) { $0.currentCount == 2 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 2 } event.signal(concurrent: 2) - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testWithConcurrentIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } Task.detached { - try await waitUntil(event, timeout: 3) { $0.currentCount == 6 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 6 } event.reset(to: 2) } event.signal(concurrent: 4) - try await waitUntil(event, timeout: 3) { $0.currentCount == 2 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 2 } event.signal(concurrent: 2) - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testDeinit() async throws { let event = AsyncCountdownEvent(until: 0, initial: 1) Task.detached { event.signal() } - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) self.addTeardownBlock { [weak event] in event.assertReleased() } @@ -85,7 +85,7 @@ class AsyncCountdownEventTests: XCTestCase { group.addTask { let event = AsyncCountdownEvent(initial: 1) try await withThrowingTaskGroup(of: Void.self) { g in - g.addTask { try await event.wait(forSeconds: 3) } + g.addTask { try await event.wait(forSeconds: 5) } g.addTask { event.signal() } try await g.waitForAll() } @@ -102,56 +102,56 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { func testTimeoutWithIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 9) do { - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError { - try await waitUntil(event, timeout: 3) { $0.currentCount == 1 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 1 } } } func testTimeoutWithLimitAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 6) do { - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError { - try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 4 } } } func testTimeoutWithLimitInitialCountAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 13 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 13 } event.signal(concurrent: 9) do { - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError { - try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 4 } } } func testTimeoutWithIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 8) Task.detached { - try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } + try await waitUntil(event, timeout: 5) { $0.currentCount <= 6 } event.reset(to: 6) } do { - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError { - try await waitUntil(event, timeout: 3) { + try await waitUntil(event, timeout: 5) { [6, 2].contains($0.currentCount) } } @@ -171,13 +171,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 9) do { - try await event.wait(forSeconds: 3, clock: clock) + try await event.wait(forSeconds: 5, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError { - try await waitUntil(event, timeout: 3) { $0.currentCount == 1 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 1 } } } @@ -190,13 +190,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 6) do { - try await event.wait(forSeconds: 3, clock: clock) + try await event.wait(forSeconds: 5, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError { - try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 4 } } } @@ -209,13 +209,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 13 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 13 } event.signal(concurrent: 9) do { - try await event.wait(forSeconds: 3, clock: clock) + try await event.wait(forSeconds: 5, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError { - try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 4 } } } @@ -228,17 +228,17 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 8) Task.detached { - try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } + try await waitUntil(event, timeout: 5) { $0.currentCount <= 6 } event.reset(to: 6) } do { - try await event.wait(forSeconds: 3, clock: clock) + try await event.wait(forSeconds: 5, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError { - try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } + try await waitUntil(event, timeout: 5) { $0.currentCount <= 6 } } } } diff --git a/Tests/AsyncObjectsTests/AsyncEventTests.swift b/Tests/AsyncObjectsTests/AsyncEventTests.swift index d702554c..cd98bd63 100644 --- a/Tests/AsyncObjectsTests/AsyncEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncEventTests.swift @@ -7,26 +7,26 @@ class AsyncEventTests: XCTestCase { func testSignal() async throws { let event = AsyncEvent(signaledInitially: false) event.signal() - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testResetSignal() async throws { let event = AsyncEvent() event.reset() - try await waitUntil(event, timeout: 3) { !$0.signalled } + try await waitUntil(event, timeout: 5) { !$0.signalled } event.signal() - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testSignalled() async throws { let event = AsyncEvent() - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testDeinit() async throws { let event = AsyncEvent(signaledInitially: false) Task.detached { event.signal() } - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) self.addTeardownBlock { [weak event] in event.assertReleased() } @@ -38,7 +38,7 @@ class AsyncEventTests: XCTestCase { group.addTask { let event = AsyncEvent(signaledInitially: false) try await withThrowingTaskGroup(of: Void.self) { g in - g.addTask { try await event.wait(forSeconds: 3) } + g.addTask { try await event.wait(forSeconds: 5) } g.addTask { event.signal() } try await g.waitForAll() } @@ -55,7 +55,7 @@ class AsyncEventTimeoutTests: XCTestCase { func testSignal() async throws { let event = AsyncEvent(signaledInitially: false) do { - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -63,9 +63,9 @@ class AsyncEventTimeoutTests: XCTestCase { func testResetSignal() async throws { let event = AsyncEvent() event.reset() - try await waitUntil(event, timeout: 3) { !$0.signalled } + try await waitUntil(event, timeout: 5) { !$0.signalled } do { - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -84,7 +84,7 @@ class AsyncEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncEvent(signaledInitially: false) do { - try await event.wait(forSeconds: 3, clock: clock) + try await event.wait(forSeconds: 5, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError {} } @@ -98,9 +98,9 @@ class AsyncEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncEvent() event.reset() - try await waitUntil(event, timeout: 3) { !$0.signalled } + try await waitUntil(event, timeout: 5) { !$0.signalled } do { - try await event.wait(forSeconds: 3, clock: clock) + try await event.wait(forSeconds: 5, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError {} } diff --git a/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift b/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift index a3523219..a9df7102 100644 --- a/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift +++ b/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift @@ -7,14 +7,14 @@ class AsyncSemaphoreTests: XCTestCase { func testWithTasksLessThanCount() async throws { let semaphore = AsyncSemaphore(value: 3) await semaphore.spinTasks(count: 2, duration: 10) - try await semaphore.wait(forSeconds: 3) + try await semaphore.wait(forSeconds: 5) } func testWithTasksEqualToCount() async throws { let semaphore = AsyncSemaphore(value: 3) await semaphore.spinTasks(count: 3, duration: 10) do { - try await semaphore.wait(forSeconds: 3) + try await semaphore.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -23,7 +23,7 @@ class AsyncSemaphoreTests: XCTestCase { let semaphore = AsyncSemaphore(value: 3) await semaphore.spinTasks(count: 5, duration: 10) do { - try await semaphore.wait(forSeconds: 3) + try await semaphore.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -33,7 +33,7 @@ class AsyncSemaphoreTests: XCTestCase { await semaphore.signalSemaphore() await semaphore.spinTasks(count: 4, duration: 10) do { - try await semaphore.wait(forSeconds: 3) + try await semaphore.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -60,7 +60,7 @@ class AsyncSemaphoreTests: XCTestCase { group.addTask { let semaphore = AsyncSemaphore(value: 1) try await withThrowingTaskGroup(of: Void.self) { g in - g.addTask { try await semaphore.wait(forSeconds: 3) } + g.addTask { try await semaphore.wait(forSeconds: 5) } g.addTask { semaphore.signal() } try await g.waitForAll() } @@ -72,7 +72,7 @@ class AsyncSemaphoreTests: XCTestCase { func testDeinit() async throws { let semaphore = AsyncSemaphore(value: 1) - try await semaphore.wait(forSeconds: 3) + try await semaphore.wait(forSeconds: 5) self.addTeardownBlock { [weak semaphore] in semaphore.assertReleased() } @@ -85,19 +85,19 @@ class AsyncSemaphoreTimeoutTests: XCTestCase { func testMutexWaitTimeout() async throws { let mutex = AsyncSemaphore() do { - try await mutex.wait(forSeconds: 3) + try await mutex.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } func testWaitTimeoutWithTasksLessThanCount() async throws { let semaphore = AsyncSemaphore(value: 3) - try await semaphore.spinTasks(count: 3, duration: 2, timeout: 3) + try await semaphore.spinTasks(count: 3, duration: 2, timeout: 5) } func testWaitTimeoutWithTasksGreaterThanCount() async throws { let semaphore = AsyncSemaphore(value: 3) - try await semaphore.spinTasks(count: 5, duration: 5, timeout: 3) + try await semaphore.spinTasks(count: 5, duration: 5, timeout: 5) } func testWaitCancellationOnTimeoutWithTasksGreaterThanCount() async throws { @@ -111,7 +111,7 @@ class AsyncSemaphoreTimeoutTests: XCTestCase { semaphore.signal() } else { do { - try await semaphore.wait(forSeconds: 3) + try await semaphore.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -135,7 +135,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let mutex = AsyncSemaphore() do { - try await mutex.wait(forSeconds: 3, clock: clock) + try await mutex.wait(forSeconds: 5, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError {} } @@ -149,7 +149,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let semaphore = AsyncSemaphore(value: 3) try await semaphore.spinTasks( - count: 3, duration: 2, timeout: 3, + count: 3, duration: 2, timeout: 5, clock: clock ) } @@ -163,7 +163,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let semaphore = AsyncSemaphore(value: 3) try await semaphore.spinTasks( - count: 5, duration: 5, timeout: 3, + count: 5, duration: 5, timeout: 5, clock: clock ) } @@ -186,7 +186,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { } else { do { try await semaphore.wait( - forSeconds: 3, clock: clock) + forSeconds: 5, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError {} } diff --git a/Tests/AsyncObjectsTests/CancellationSourceTests.swift b/Tests/AsyncObjectsTests/CancellationSourceTests.swift index fc871355..d6ff5a14 100644 --- a/Tests/AsyncObjectsTests/CancellationSourceTests.swift +++ b/Tests/AsyncObjectsTests/CancellationSourceTests.swift @@ -8,9 +8,9 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource() let task = Task { try await Task.sleep(seconds: 3) } source.register(task: task) - try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -18,7 +18,7 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource(cancelAfterNanoseconds: UInt64(1E9)) let task = Task { try await Task.sleep(seconds: 3) } source.register(task: task) - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 10) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -36,7 +36,7 @@ class CancellationSourceTests: XCTestCase { ) let task = Task { try await Task.sleep(seconds: 3, clock: clock) } source.register(task: task) - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 10) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } #endif @@ -46,9 +46,9 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource(linkedWith: parentSource) let task = Task { try await Task.sleep(seconds: 3) } source.register(task: task) - try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } parentSource.cancel() - try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -60,9 +60,9 @@ class CancellationSourceTests: XCTestCase { ) let task = Task { try await Task.sleep(seconds: 3) } source.register(task: task) - try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } parentSource1.cancel() - try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -73,9 +73,9 @@ class CancellationSourceTests: XCTestCase { XCTFail("Unexpected task progression") } source.register(task: task) - try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } try? await task.value self.addTeardownBlock { [weak source] in source.assertReleased() @@ -91,7 +91,7 @@ class CancellationSourceTests: XCTestCase { task.cancel() source.register(task: task) do { - try await waitUntil(source, timeout: 3) { + try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } XCTFail("Unexpected task progression") @@ -102,8 +102,8 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource() let task = Task.detached { try await Task.sleep(seconds: 1) } source.register(task: task) - try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 10) { $0.registeredTasks.isEmpty } } } @@ -118,9 +118,9 @@ class CancellationSourceInitializationTests: XCTestCase { XCTFail("Unexpected task progression") } catch {} } - try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -132,9 +132,9 @@ class CancellationSourceInitializationTests: XCTestCase { XCTFail("Unexpected task progression") } catch {} } - try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -143,9 +143,9 @@ class CancellationSourceInitializationTests: XCTestCase { let task = Task(cancellationSource: source) { try await Task.sleep(seconds: 10) } - try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -154,9 +154,9 @@ class CancellationSourceInitializationTests: XCTestCase { let task = Task.detached(cancellationSource: source) { try await Task.sleep(seconds: 10) } - try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -166,9 +166,9 @@ class CancellationSourceInitializationTests: XCTestCase { try await Task.sleep(seconds: 10) XCTFail("Unexpected task progression") } - try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } try? await task.value self.addTeardownBlock { [weak source] in source.assertReleased() diff --git a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift index b7a2c2b1..27deed99 100644 --- a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift @@ -7,7 +7,7 @@ class NonThrowingFutureTests: XCTestCase { func testFulfilledInitialization() async throws { let future = Future(with: .success(5)) - let value = try await future.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 5) XCTAssertEqual(value, 5) } @@ -15,7 +15,7 @@ class NonThrowingFutureTests: XCTestCase { let future = Future() try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { - let value = try await future.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 5) XCTAssertEqual(value, 5) } group.addTask { @@ -32,14 +32,14 @@ class NonThrowingFutureTests: XCTestCase { promise(.success(5)) } } - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 10) XCTAssertEqual(value, 5) } func testMultipleTimesFutureFulfilled() async throws { let future = Future(with: .success(5)) await future.fulfill(producing: 10) - let value = try await future.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 5) XCTAssertEqual(value, 5) } @@ -48,14 +48,14 @@ class NonThrowingFutureTests: XCTestCase { try! await Task.sleep(seconds: 2) promise(.success(5)) } - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 10) XCTAssertEqual(value, 5) } func testDeinit() async throws { let future = Future() Task.detached { await future.fulfill(producing: 5) } - let _ = try await future.wait(forSeconds: 3) + let _ = try await future.wait(forSeconds: 5) self.addTeardownBlock { [weak future] in future.assertReleased() } @@ -68,7 +68,7 @@ class NonThrowingFutureTests: XCTestCase { let future = Future() try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { - let _ = try await future.wait(forSeconds: 3) + let _ = try await future.wait(forSeconds: 5) } group.addTask { await future.fulfill(producing: i) } try await group.waitForAll() @@ -90,7 +90,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let allFuture = Future.all(future1, future3, future2) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await allFuture.wait(forSeconds: 5) XCTAssertEqual(value, [1, 3, 2]) } group.addTask { await future1.fulfill(producing: 1) } @@ -107,7 +107,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let allFuture = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let values = try await allFuture.wait(forSeconds: 3) + let values = try await allFuture.wait(forSeconds: 5) for (index, item) in values.enumerated() { switch item { case .success(let value): @@ -131,7 +131,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let allFuture = Future.race(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await allFuture.wait(forSeconds: 5) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) @@ -148,7 +148,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let allFuture = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await allFuture.wait(forSeconds: 5) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) @@ -160,13 +160,13 @@ class NonThrowingFutureCombiningTests: XCTestCase { func testConstructingAllFutureFromEmpty() async throws { let future = Future.all() - let value = try await future.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 5) XCTAssertTrue(value.isEmpty) } func testConstructingAllSettledFutureFromEmpty() async throws { let future = Future.allSettled() - let value = try await future.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 5) XCTAssertTrue(value.isEmpty) } } diff --git a/Tests/AsyncObjectsTests/TaskOperationTests.swift b/Tests/AsyncObjectsTests/TaskOperationTests.swift index 646d5d58..a8bda770 100644 --- a/Tests/AsyncObjectsTests/TaskOperationTests.swift +++ b/Tests/AsyncObjectsTests/TaskOperationTests.swift @@ -23,7 +23,7 @@ class TaskOperationTests: XCTestCase { group.addTask { try await waitUntil( operation, - timeout: 3, + timeout: 5, satisfies: \.isExecuting ) } @@ -56,7 +56,7 @@ class TaskOperationTests: XCTestCase { #else operation.start() #endif - try await waitUntil(operation, timeout: 3, satisfies: \.isExecuting) + try await waitUntil(operation, timeout: 5, satisfies: \.isExecuting) await GlobalContinuation.with { continuation in DispatchQueue.global(qos: .default).async { operation.waitUntilFinished() @@ -71,13 +71,13 @@ class TaskOperationTests: XCTestCase { func testAsyncWait() async throws { let operation = TaskOperation { /* Do nothing */ } operation.signal() - try await operation.wait(forSeconds: 3) + try await operation.wait(forSeconds: 5) } func testFinisheAsyncdWait() async throws { let operation = TaskOperation { /* Do nothing */ } operation.signal() - try await operation.wait(forSeconds: 3) + try await operation.wait(forSeconds: 5) } func testDeinit() async throws { @@ -85,7 +85,7 @@ class TaskOperationTests: XCTestCase { try await Task.sleep(seconds: 1) } operation.signal() - try await operation.wait(forSeconds: 3) + try await operation.wait(forSeconds: 5) self.addTeardownBlock { [weak operation] in operation.assertReleased() } @@ -97,7 +97,7 @@ class TaskOperationTests: XCTestCase { group.addTask { let operation = TaskOperation {} try await withThrowingTaskGroup(of: Void.self) { g in - g.addTask { try await operation.wait(forSeconds: 3) } + g.addTask { try await operation.wait(forSeconds: 5) } g.addTask { operation.signal() } try await g.waitForAll() } @@ -117,7 +117,7 @@ class TaskOperationTimeoutTests: XCTestCase { } operation.signal() do { - try await operation.wait(forSeconds: 3) + try await operation.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -139,7 +139,7 @@ class TaskOperationClockTimeoutTests: XCTestCase { } operation.signal() do { - try await operation.wait(forSeconds: 3, clock: clock) + try await operation.wait(forSeconds: 5, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError {} } @@ -162,9 +162,9 @@ class TaskOperationCancellationTests: XCTestCase { #else operation.start() #endif - try await waitUntil(operation, timeout: 3, satisfies: \.isExecuting) + try await waitUntil(operation, timeout: 5, satisfies: \.isExecuting) operation.cancel() - try await waitUntil(operation, timeout: 3, satisfies: \.isCancelled) + try await waitUntil(operation, timeout: 5, satisfies: \.isCancelled) XCTAssertTrue(operation.isFinished) XCTAssertFalse(operation.isExecuting) switch await operation.result { @@ -186,9 +186,9 @@ class TaskOperationCancellationTests: XCTestCase { #else operation.start() #endif - try await waitUntil(operation, timeout: 3, satisfies: \.isExecuting) + try await waitUntil(operation, timeout: 5, satisfies: \.isExecuting) operation.cancel() - try await waitUntil(operation, timeout: 3, satisfies: \.isCancelled) + try await waitUntil(operation, timeout: 5, satisfies: \.isCancelled) XCTAssertTrue(operation.isFinished) XCTAssertFalse(operation.isExecuting) } @@ -198,7 +198,7 @@ class TaskOperationCancellationTests: XCTestCase { try await Task.sleep(seconds: 1) } let task = Task.detached { - try await operation.wait(forSeconds: 3) + try await operation.wait(forSeconds: 5) } task.cancel() do { @@ -233,7 +233,7 @@ class TaskOperationCancellationTests: XCTestCase { } operation.signal() operation.cancel() - try await waitUntil(operation, timeout: 3, satisfies: \.isCancelled) + try await waitUntil(operation, timeout: 5, satisfies: \.isCancelled) self.addTeardownBlock { [weak operation] in operation.assertReleased() } @@ -246,7 +246,7 @@ class TaskOperationTaskManagementTests: XCTestCase { func testOperationWithoutTrackingChildTasks() async throws { let operation = TaskOperation(track: false) operation.signal() - try await operation.wait(forSeconds: 3) + try await operation.wait(forSeconds: 5) } func testOperationWithTrackingChildTasks() async throws { diff --git a/Tests/AsyncObjectsTests/TaskQueueTests.swift b/Tests/AsyncObjectsTests/TaskQueueTests.swift index 0302a7eb..9ba34579 100644 --- a/Tests/AsyncObjectsTests/TaskQueueTests.swift +++ b/Tests/AsyncObjectsTests/TaskQueueTests.swift @@ -24,7 +24,7 @@ class TaskQueueTests: XCTestCase { try await Task.sleep(seconds: 10) } } - try await waitUntil(queue, timeout: 3) { $0.blocked } + try await waitUntil(queue, timeout: 5) { $0.blocked } queue.signal() let blocked = await queue.blocked XCTAssertTrue(blocked) @@ -62,7 +62,7 @@ class TaskQueueTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testDeinit() async throws { @@ -149,7 +149,7 @@ class TaskQueueBlockOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testExecutionOfTaskBeforeOperation() async throws { @@ -164,7 +164,7 @@ class TaskQueueBlockOperationTests: XCTestCase { c.finish() } } - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) await stream.assertElements() } @@ -182,7 +182,7 @@ class TaskQueueBlockOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testCancellation() async throws { @@ -192,7 +192,7 @@ class TaskQueueBlockOperationTests: XCTestCase { } let task = Task.detached { await queue.exec(flags: .block) {} - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) XCTFail("Unexpected task progression") } do { @@ -214,7 +214,7 @@ class TaskQueueBlockOperationTests: XCTestCase { } catch {} XCTAssertTrue(Task.isCancelled) await queue.exec(flags: .block) {} - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) XCTFail("Unexpected task progression") } do { @@ -237,7 +237,7 @@ class TaskQueueBlockOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testMultipleCancellationWithoutBlocking() async throws { @@ -258,7 +258,7 @@ class TaskQueueBlockOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testMixedeCancellationWithoutBlocking() async throws { @@ -284,7 +284,7 @@ class TaskQueueBlockOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } } @@ -301,7 +301,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testExecutionOfTaskBeforeOperation() async throws { @@ -317,7 +317,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { c.finish() } } - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) await stream.assertElements() } @@ -335,7 +335,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testCancellation() async throws { @@ -345,7 +345,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { } let task = Task.detached { await queue.exec(flags: .block) {} - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) XCTFail("Unexpected task progression") } do { @@ -367,7 +367,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { } catch {} XCTAssertTrue(Task.isCancelled) await queue.exec(flags: .barrier) {} - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) XCTFail("Unexpected task progression") } do { @@ -390,7 +390,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testMultipleCancellationWithoutBlocking() async throws { @@ -411,7 +411,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testMixedCancellationWithoutBlocking() async throws { @@ -437,7 +437,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } } @@ -458,7 +458,7 @@ class TaskQueueMixedOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testExecutionOfBlockTaskAfterBarrierOperation() async throws { @@ -475,7 +475,7 @@ class TaskQueueMixedOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testLongRunningConcurrentTaskWithShortBlockTaskBeforeBarrierOperation() @@ -496,7 +496,7 @@ class TaskQueueMixedOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testLongRunningConcurrentTaskWithShortBlockTaskAfterBarrierOperation() @@ -521,7 +521,7 @@ class TaskQueueMixedOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } /// Scenario described in: @@ -554,7 +554,7 @@ class TaskQueueMixedOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } func testCancellableAndNonCancellableTasks() async throws { @@ -604,7 +604,7 @@ class TaskQueueMixedOperationTests: XCTestCase { try await Task.sleep(seconds: 10) } } - try await waitUntil(queue, timeout: 5) { + try await waitUntil(queue, timeout: 10) { guard let (_, (_, flags)) = $0.queue.reversed().first else { return false } @@ -640,10 +640,10 @@ class TaskQueueMixedOperationTests: XCTestCase { } } } - try await waitUntil(queue, timeout: 5) { $0.blocked } + try await waitUntil(queue, timeout: 10) { $0.blocked } group.cancelAll() } - try await queue.wait(forSeconds: 3) + try await queue.wait(forSeconds: 5) } } @@ -664,7 +664,7 @@ fileprivate extension TaskQueue { flags: Flags = [], operation: @Sendable @escaping () async -> T ) async { - await withUnsafeContinuation { continuation in + await GlobalContinuation.with { continuation in self.addTask(priority: priority, flags: flags) { continuation.resume() return await operation() @@ -677,7 +677,7 @@ fileprivate extension TaskQueue { flags: Flags = [], operation: @Sendable @escaping () async throws -> T ) async { - await withUnsafeContinuation { continuation in + await GlobalContinuation.with { continuation in self.addTask(priority: priority, flags: flags) { continuation.resume() return try await operation() @@ -696,10 +696,10 @@ fileprivate extension TaskQueue { await addTaskAndStart(priority: option.task, flags: option.flags) { try await Task.sleep(seconds: 1) } - group.addTask { try await self.wait(forSeconds: 3) } + group.addTask { try await self.wait(forSeconds: 5) } try await group.waitForAll() } - try await self.wait(forSeconds: 3) + try await self.wait(forSeconds: 5) } @MainActor @@ -716,7 +716,7 @@ fileprivate extension TaskQueue { try await Task.sleep(seconds: 10) } do { - try await self.wait(forSeconds: 5) + try await self.wait(forSeconds: 10) XCTFail("Unexpected task progression", file: file, line: line) } catch is DurationTimeoutError {} } @@ -738,7 +738,7 @@ fileprivate extension TaskQueue { try await Task.sleep(seconds: 10) } do { - try await self.wait(forSeconds: 5) + try await self.wait(forSeconds: 10) XCTFail("Unexpected task progression", file: file, line: line) } catch is DurationTimeoutError {} } diff --git a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift index 7ecbe012..8e35ed9d 100644 --- a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift @@ -7,7 +7,7 @@ class ThrowingFutureTests: XCTestCase { func testFutureFulfilledInitialization() async throws { let future = Future(with: .success(5)) - let value = try await future.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 5) XCTAssertEqual(value, 5) } @@ -15,7 +15,7 @@ class ThrowingFutureTests: XCTestCase { let future = Future() try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { - let value = try await future.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 5) XCTAssertEqual(value, 5) } group.addTask { @@ -30,7 +30,7 @@ class ThrowingFutureTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { do { - let _ = try await future.wait(forSeconds: 3) + let _ = try await future.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is CancellationError {} } @@ -45,7 +45,7 @@ class ThrowingFutureTests: XCTestCase { let future = Future() let waitTask = Task { do { - let _ = try await future.wait(forSeconds: 3) + let _ = try await future.wait(forSeconds: 5) XCTFail("Future fulfillments wait not cancelled") } catch is CancellationError {} } @@ -56,14 +56,14 @@ class ThrowingFutureTests: XCTestCase { func testMultipleTimesFutureFulfilled() async throws { let future = Future(with: .success(5)) await future.fulfill(producing: 10) - let value = try await future.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 5) XCTAssertEqual(value, 5) } func testDeinit() async throws { let future = Future() Task.detached { await future.fulfill(producing: 5) } - let _ = try await future.wait(forSeconds: 3) + let _ = try await future.wait(forSeconds: 5) self.addTeardownBlock { [weak future] in future.assertReleased() } @@ -73,7 +73,7 @@ class ThrowingFutureTests: XCTestCase { let future = Future() let task = Task.detached { do { - let _ = try await future.wait(forSeconds: 3) + let _ = try await future.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is CancellationError {} } @@ -90,7 +90,7 @@ class ThrowingFutureTests: XCTestCase { } catch {} XCTAssertTrue(Task.isCancelled) do { - let _ = try await future.wait(forSeconds: 3) + let _ = try await future.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is CancellationError {} } @@ -105,7 +105,7 @@ class ThrowingFutureTests: XCTestCase { let future = Future() try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { - let _ = try await future.wait(forSeconds: 3) + let _ = try await future.wait(forSeconds: 5) } group.addTask { await future.fulfill(producing: i) } try await group.waitForAll() @@ -127,7 +127,7 @@ class ThrowingFutureCombiningAllTests: XCTestCase { let allFuture = Future.all(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await allFuture.wait(forSeconds: 5) XCTAssertEqual(value, [1, 2, 3]) } group.addTask { await future1.fulfill(producing: 1) } @@ -145,7 +145,7 @@ class ThrowingFutureCombiningAllTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { do { - let _ = try await allFuture.wait(forSeconds: 3) + let _ = try await allFuture.wait(forSeconds: 5) XCTFail("Future fulfillment did not fail") } catch is CancellationError {} } @@ -160,7 +160,7 @@ class ThrowingFutureCombiningAllTests: XCTestCase { func testEmptyConstructing() async throws { let future = Future.all() - let value = try await future.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 5) XCTAssertTrue(value.isEmpty) } } @@ -175,7 +175,7 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let allFuture = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let values = try await allFuture.wait(forSeconds: 3) + let values = try await allFuture.wait(forSeconds: 5) for (index, item) in values.enumerated() { switch item { case .success(let value): @@ -199,7 +199,7 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let allFuture = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let values = try await allFuture.wait(forSeconds: 3) + let values = try await allFuture.wait(forSeconds: 5) for (index, item) in values.enumerated() { switch item { case .success(let value): @@ -220,7 +220,7 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { func testEmptyConstructing() async throws { let future = Future.allSettled() - let value = try await future.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 5) XCTAssertTrue(value.isEmpty) } } @@ -235,7 +235,7 @@ class ThrowingFutureRacingTests: XCTestCase { let allFuture = Future.race(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await allFuture.wait(forSeconds: 5) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) @@ -253,7 +253,7 @@ class ThrowingFutureRacingTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { do { - let _ = try await allFuture.wait(forSeconds: 3) + let _ = try await allFuture.wait(forSeconds: 5) XCTFail("Future fulfillment did not fail") } catch is CancellationError {} } @@ -275,7 +275,7 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let allFuture = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await allFuture.wait(forSeconds: 5) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) @@ -292,7 +292,7 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let allFuture = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await allFuture.wait(forSeconds: 5) XCTAssertEqual(value, 2) } await future1.fulfill(throwing: CancellationError()) @@ -310,7 +310,7 @@ class ThrowingFutureSelectAnyTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { do { - let _ = try await allFuture.wait(forSeconds: 3) + let _ = try await allFuture.wait(forSeconds: 5) XCTFail("Future fulfillment did not fail") } catch is CancellationError {} } diff --git a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift index 43446898..6774578e 100644 --- a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift +++ b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift @@ -2,10 +2,6 @@ import XCTest import Dispatch @testable import AsyncObjects -func waitForResume(_ body: (UnsafeContinuation) -> Void) async { - await withUnsafeContinuation(body) -} - func waitUntil( _ actor: T, timeout: TimeInterval, From 8b9ebc16acf8ae1865f8763aeeeed507b5b3a8cf Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Thu, 29 Dec 2022 14:10:00 +0530 Subject: [PATCH 11/22] Revert "wip: fix build fail on older Swift version" This reverts commit af058e9944e91cb15ef25eff35efcd70632fcb98. --- .../AsyncCountdownEventTests.swift | 86 +++++++++---------- Tests/AsyncObjectsTests/AsyncEventTests.swift | 24 +++--- .../AsyncSemaphoreTests.swift | 28 +++--- .../CancellationSourceTests.swift | 46 +++++----- .../NonThrowingFutureTests.swift | 26 +++--- .../TaskOperationTests.swift | 30 +++---- Tests/AsyncObjectsTests/TaskQueueTests.swift | 64 +++++++------- .../ThrowingFutureTests.swift | 40 ++++----- Tests/AsyncObjectsTests/XCAsyncTestCase.swift | 4 + 9 files changed, 176 insertions(+), 172 deletions(-) diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index d6ce1c41..e691c77d 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -6,74 +6,74 @@ class AsyncCountdownEventTests: XCTestCase { func testWithoutIncrement() async throws { let event = AsyncCountdownEvent() - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) } func testWithIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) event.signal(concurrent: 10) - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) } func testWithOverIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) event.signal(concurrent: 15) - try await event.wait(forSeconds: 5) - try await waitUntil(event, timeout: 5) { $0.currentCount == 0 } + try await event.wait(forSeconds: 3) + try await waitUntil(event, timeout: 3) { $0.currentCount == 0 } } func testWithLimitAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) event.signal(concurrent: 7) - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) } func testWithLimitInitialCountAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3, initial: 2) event.increment(by: 10) event.signal(concurrent: 9) - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) } func testWithIncrementAndReset() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } event.reset() - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) } func testWithIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } event.reset(to: 2) - try await waitUntil(event, timeout: 5) { $0.currentCount == 2 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 2 } event.signal(concurrent: 2) - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) } func testWithConcurrentIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } Task.detached { - try await waitUntil(event, timeout: 5) { $0.currentCount == 6 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 6 } event.reset(to: 2) } event.signal(concurrent: 4) - try await waitUntil(event, timeout: 5) { $0.currentCount == 2 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 2 } event.signal(concurrent: 2) - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) } func testDeinit() async throws { let event = AsyncCountdownEvent(until: 0, initial: 1) Task.detached { event.signal() } - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) self.addTeardownBlock { [weak event] in event.assertReleased() } @@ -85,7 +85,7 @@ class AsyncCountdownEventTests: XCTestCase { group.addTask { let event = AsyncCountdownEvent(initial: 1) try await withThrowingTaskGroup(of: Void.self) { g in - g.addTask { try await event.wait(forSeconds: 5) } + g.addTask { try await event.wait(forSeconds: 3) } g.addTask { event.signal() } try await g.waitForAll() } @@ -102,56 +102,56 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { func testTimeoutWithIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } event.signal(concurrent: 9) do { - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError { - try await waitUntil(event, timeout: 5) { $0.currentCount == 1 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 1 } } } func testTimeoutWithLimitAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } event.signal(concurrent: 6) do { - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError { - try await waitUntil(event, timeout: 5) { $0.currentCount == 4 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } } } func testTimeoutWithLimitInitialCountAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 5) { $0.currentCount == 13 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 13 } event.signal(concurrent: 9) do { - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError { - try await waitUntil(event, timeout: 5) { $0.currentCount == 4 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } } } func testTimeoutWithIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } event.signal(concurrent: 8) Task.detached { - try await waitUntil(event, timeout: 5) { $0.currentCount <= 6 } + try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } event.reset(to: 6) } do { - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError { - try await waitUntil(event, timeout: 5) { + try await waitUntil(event, timeout: 3) { [6, 2].contains($0.currentCount) } } @@ -171,13 +171,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } event.signal(concurrent: 9) do { - try await event.wait(forSeconds: 5, clock: clock) + try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError { - try await waitUntil(event, timeout: 5) { $0.currentCount == 1 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 1 } } } @@ -190,13 +190,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } event.signal(concurrent: 6) do { - try await event.wait(forSeconds: 5, clock: clock) + try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError { - try await waitUntil(event, timeout: 5) { $0.currentCount == 4 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } } } @@ -209,13 +209,13 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 5) { $0.currentCount == 13 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 13 } event.signal(concurrent: 9) do { - try await event.wait(forSeconds: 5, clock: clock) + try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError { - try await waitUntil(event, timeout: 5) { $0.currentCount == 4 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 4 } } } @@ -228,17 +228,17 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } event.signal(concurrent: 8) Task.detached { - try await waitUntil(event, timeout: 5) { $0.currentCount <= 6 } + try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } event.reset(to: 6) } do { - try await event.wait(forSeconds: 5, clock: clock) + try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError { - try await waitUntil(event, timeout: 5) { $0.currentCount <= 6 } + try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } } } } diff --git a/Tests/AsyncObjectsTests/AsyncEventTests.swift b/Tests/AsyncObjectsTests/AsyncEventTests.swift index cd98bd63..d702554c 100644 --- a/Tests/AsyncObjectsTests/AsyncEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncEventTests.swift @@ -7,26 +7,26 @@ class AsyncEventTests: XCTestCase { func testSignal() async throws { let event = AsyncEvent(signaledInitially: false) event.signal() - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) } func testResetSignal() async throws { let event = AsyncEvent() event.reset() - try await waitUntil(event, timeout: 5) { !$0.signalled } + try await waitUntil(event, timeout: 3) { !$0.signalled } event.signal() - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) } func testSignalled() async throws { let event = AsyncEvent() - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) } func testDeinit() async throws { let event = AsyncEvent(signaledInitially: false) Task.detached { event.signal() } - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) self.addTeardownBlock { [weak event] in event.assertReleased() } @@ -38,7 +38,7 @@ class AsyncEventTests: XCTestCase { group.addTask { let event = AsyncEvent(signaledInitially: false) try await withThrowingTaskGroup(of: Void.self) { g in - g.addTask { try await event.wait(forSeconds: 5) } + g.addTask { try await event.wait(forSeconds: 3) } g.addTask { event.signal() } try await g.waitForAll() } @@ -55,7 +55,7 @@ class AsyncEventTimeoutTests: XCTestCase { func testSignal() async throws { let event = AsyncEvent(signaledInitially: false) do { - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -63,9 +63,9 @@ class AsyncEventTimeoutTests: XCTestCase { func testResetSignal() async throws { let event = AsyncEvent() event.reset() - try await waitUntil(event, timeout: 5) { !$0.signalled } + try await waitUntil(event, timeout: 3) { !$0.signalled } do { - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -84,7 +84,7 @@ class AsyncEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncEvent(signaledInitially: false) do { - try await event.wait(forSeconds: 5, clock: clock) + try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError {} } @@ -98,9 +98,9 @@ class AsyncEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncEvent() event.reset() - try await waitUntil(event, timeout: 5) { !$0.signalled } + try await waitUntil(event, timeout: 3) { !$0.signalled } do { - try await event.wait(forSeconds: 5, clock: clock) + try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError {} } diff --git a/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift b/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift index a9df7102..a3523219 100644 --- a/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift +++ b/Tests/AsyncObjectsTests/AsyncSemaphoreTests.swift @@ -7,14 +7,14 @@ class AsyncSemaphoreTests: XCTestCase { func testWithTasksLessThanCount() async throws { let semaphore = AsyncSemaphore(value: 3) await semaphore.spinTasks(count: 2, duration: 10) - try await semaphore.wait(forSeconds: 5) + try await semaphore.wait(forSeconds: 3) } func testWithTasksEqualToCount() async throws { let semaphore = AsyncSemaphore(value: 3) await semaphore.spinTasks(count: 3, duration: 10) do { - try await semaphore.wait(forSeconds: 5) + try await semaphore.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -23,7 +23,7 @@ class AsyncSemaphoreTests: XCTestCase { let semaphore = AsyncSemaphore(value: 3) await semaphore.spinTasks(count: 5, duration: 10) do { - try await semaphore.wait(forSeconds: 5) + try await semaphore.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -33,7 +33,7 @@ class AsyncSemaphoreTests: XCTestCase { await semaphore.signalSemaphore() await semaphore.spinTasks(count: 4, duration: 10) do { - try await semaphore.wait(forSeconds: 5) + try await semaphore.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -60,7 +60,7 @@ class AsyncSemaphoreTests: XCTestCase { group.addTask { let semaphore = AsyncSemaphore(value: 1) try await withThrowingTaskGroup(of: Void.self) { g in - g.addTask { try await semaphore.wait(forSeconds: 5) } + g.addTask { try await semaphore.wait(forSeconds: 3) } g.addTask { semaphore.signal() } try await g.waitForAll() } @@ -72,7 +72,7 @@ class AsyncSemaphoreTests: XCTestCase { func testDeinit() async throws { let semaphore = AsyncSemaphore(value: 1) - try await semaphore.wait(forSeconds: 5) + try await semaphore.wait(forSeconds: 3) self.addTeardownBlock { [weak semaphore] in semaphore.assertReleased() } @@ -85,19 +85,19 @@ class AsyncSemaphoreTimeoutTests: XCTestCase { func testMutexWaitTimeout() async throws { let mutex = AsyncSemaphore() do { - try await mutex.wait(forSeconds: 5) + try await mutex.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } func testWaitTimeoutWithTasksLessThanCount() async throws { let semaphore = AsyncSemaphore(value: 3) - try await semaphore.spinTasks(count: 3, duration: 2, timeout: 5) + try await semaphore.spinTasks(count: 3, duration: 2, timeout: 3) } func testWaitTimeoutWithTasksGreaterThanCount() async throws { let semaphore = AsyncSemaphore(value: 3) - try await semaphore.spinTasks(count: 5, duration: 5, timeout: 5) + try await semaphore.spinTasks(count: 5, duration: 5, timeout: 3) } func testWaitCancellationOnTimeoutWithTasksGreaterThanCount() async throws { @@ -111,7 +111,7 @@ class AsyncSemaphoreTimeoutTests: XCTestCase { semaphore.signal() } else { do { - try await semaphore.wait(forSeconds: 5) + try await semaphore.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -135,7 +135,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let mutex = AsyncSemaphore() do { - try await mutex.wait(forSeconds: 5, clock: clock) + try await mutex.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError {} } @@ -149,7 +149,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let semaphore = AsyncSemaphore(value: 3) try await semaphore.spinTasks( - count: 3, duration: 2, timeout: 5, + count: 3, duration: 2, timeout: 3, clock: clock ) } @@ -163,7 +163,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let semaphore = AsyncSemaphore(value: 3) try await semaphore.spinTasks( - count: 5, duration: 5, timeout: 5, + count: 5, duration: 5, timeout: 3, clock: clock ) } @@ -186,7 +186,7 @@ class AsyncSemaphoreClockTimeoutTests: XCTestCase { } else { do { try await semaphore.wait( - forSeconds: 5, clock: clock) + forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError {} } diff --git a/Tests/AsyncObjectsTests/CancellationSourceTests.swift b/Tests/AsyncObjectsTests/CancellationSourceTests.swift index d6ff5a14..fc871355 100644 --- a/Tests/AsyncObjectsTests/CancellationSourceTests.swift +++ b/Tests/AsyncObjectsTests/CancellationSourceTests.swift @@ -8,9 +8,9 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource() let task = Task { try await Task.sleep(seconds: 3) } source.register(task: task) - try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -18,7 +18,7 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource(cancelAfterNanoseconds: UInt64(1E9)) let task = Task { try await Task.sleep(seconds: 3) } source.register(task: task) - try await waitUntil(source, timeout: 10) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -36,7 +36,7 @@ class CancellationSourceTests: XCTestCase { ) let task = Task { try await Task.sleep(seconds: 3, clock: clock) } source.register(task: task) - try await waitUntil(source, timeout: 10) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } #endif @@ -46,9 +46,9 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource(linkedWith: parentSource) let task = Task { try await Task.sleep(seconds: 3) } source.register(task: task) - try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } parentSource.cancel() - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -60,9 +60,9 @@ class CancellationSourceTests: XCTestCase { ) let task = Task { try await Task.sleep(seconds: 3) } source.register(task: task) - try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } parentSource1.cancel() - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -73,9 +73,9 @@ class CancellationSourceTests: XCTestCase { XCTFail("Unexpected task progression") } source.register(task: task) - try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } try? await task.value self.addTeardownBlock { [weak source] in source.assertReleased() @@ -91,7 +91,7 @@ class CancellationSourceTests: XCTestCase { task.cancel() source.register(task: task) do { - try await waitUntil(source, timeout: 5) { + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } XCTFail("Unexpected task progression") @@ -102,8 +102,8 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource() let task = Task.detached { try await Task.sleep(seconds: 1) } source.register(task: task) - try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } - try await waitUntil(source, timeout: 10) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } } } @@ -118,9 +118,9 @@ class CancellationSourceInitializationTests: XCTestCase { XCTFail("Unexpected task progression") } catch {} } - try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -132,9 +132,9 @@ class CancellationSourceInitializationTests: XCTestCase { XCTFail("Unexpected task progression") } catch {} } - try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -143,9 +143,9 @@ class CancellationSourceInitializationTests: XCTestCase { let task = Task(cancellationSource: source) { try await Task.sleep(seconds: 10) } - try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -154,9 +154,9 @@ class CancellationSourceInitializationTests: XCTestCase { let task = Task.detached(cancellationSource: source) { try await Task.sleep(seconds: 10) } - try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } @@ -166,9 +166,9 @@ class CancellationSourceInitializationTests: XCTestCase { try await Task.sleep(seconds: 10) XCTFail("Unexpected task progression") } - try await waitUntil(source, timeout: 5) { !$0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } source.cancel() - try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } + try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } try? await task.value self.addTeardownBlock { [weak source] in source.assertReleased() diff --git a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift index 27deed99..b7a2c2b1 100644 --- a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift @@ -7,7 +7,7 @@ class NonThrowingFutureTests: XCTestCase { func testFulfilledInitialization() async throws { let future = Future(with: .success(5)) - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } @@ -15,7 +15,7 @@ class NonThrowingFutureTests: XCTestCase { let future = Future() try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } group.addTask { @@ -32,14 +32,14 @@ class NonThrowingFutureTests: XCTestCase { promise(.success(5)) } } - let value = try await future.wait(forSeconds: 10) + let value = try await future.wait(forSeconds: 5) XCTAssertEqual(value, 5) } func testMultipleTimesFutureFulfilled() async throws { let future = Future(with: .success(5)) await future.fulfill(producing: 10) - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } @@ -48,14 +48,14 @@ class NonThrowingFutureTests: XCTestCase { try! await Task.sleep(seconds: 2) promise(.success(5)) } - let value = try await future.wait(forSeconds: 10) + let value = try await future.wait(forSeconds: 5) XCTAssertEqual(value, 5) } func testDeinit() async throws { let future = Future() Task.detached { await future.fulfill(producing: 5) } - let _ = try await future.wait(forSeconds: 5) + let _ = try await future.wait(forSeconds: 3) self.addTeardownBlock { [weak future] in future.assertReleased() } @@ -68,7 +68,7 @@ class NonThrowingFutureTests: XCTestCase { let future = Future() try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { - let _ = try await future.wait(forSeconds: 5) + let _ = try await future.wait(forSeconds: 3) } group.addTask { await future.fulfill(producing: i) } try await group.waitForAll() @@ -90,7 +90,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let allFuture = Future.all(future1, future3, future2) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 5) + let value = try await allFuture.wait(forSeconds: 3) XCTAssertEqual(value, [1, 3, 2]) } group.addTask { await future1.fulfill(producing: 1) } @@ -107,7 +107,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let allFuture = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let values = try await allFuture.wait(forSeconds: 5) + let values = try await allFuture.wait(forSeconds: 3) for (index, item) in values.enumerated() { switch item { case .success(let value): @@ -131,7 +131,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let allFuture = Future.race(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 5) + let value = try await allFuture.wait(forSeconds: 3) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) @@ -148,7 +148,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let allFuture = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 5) + let value = try await allFuture.wait(forSeconds: 3) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) @@ -160,13 +160,13 @@ class NonThrowingFutureCombiningTests: XCTestCase { func testConstructingAllFutureFromEmpty() async throws { let future = Future.all() - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 3) XCTAssertTrue(value.isEmpty) } func testConstructingAllSettledFutureFromEmpty() async throws { let future = Future.allSettled() - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 3) XCTAssertTrue(value.isEmpty) } } diff --git a/Tests/AsyncObjectsTests/TaskOperationTests.swift b/Tests/AsyncObjectsTests/TaskOperationTests.swift index a8bda770..646d5d58 100644 --- a/Tests/AsyncObjectsTests/TaskOperationTests.swift +++ b/Tests/AsyncObjectsTests/TaskOperationTests.swift @@ -23,7 +23,7 @@ class TaskOperationTests: XCTestCase { group.addTask { try await waitUntil( operation, - timeout: 5, + timeout: 3, satisfies: \.isExecuting ) } @@ -56,7 +56,7 @@ class TaskOperationTests: XCTestCase { #else operation.start() #endif - try await waitUntil(operation, timeout: 5, satisfies: \.isExecuting) + try await waitUntil(operation, timeout: 3, satisfies: \.isExecuting) await GlobalContinuation.with { continuation in DispatchQueue.global(qos: .default).async { operation.waitUntilFinished() @@ -71,13 +71,13 @@ class TaskOperationTests: XCTestCase { func testAsyncWait() async throws { let operation = TaskOperation { /* Do nothing */ } operation.signal() - try await operation.wait(forSeconds: 5) + try await operation.wait(forSeconds: 3) } func testFinisheAsyncdWait() async throws { let operation = TaskOperation { /* Do nothing */ } operation.signal() - try await operation.wait(forSeconds: 5) + try await operation.wait(forSeconds: 3) } func testDeinit() async throws { @@ -85,7 +85,7 @@ class TaskOperationTests: XCTestCase { try await Task.sleep(seconds: 1) } operation.signal() - try await operation.wait(forSeconds: 5) + try await operation.wait(forSeconds: 3) self.addTeardownBlock { [weak operation] in operation.assertReleased() } @@ -97,7 +97,7 @@ class TaskOperationTests: XCTestCase { group.addTask { let operation = TaskOperation {} try await withThrowingTaskGroup(of: Void.self) { g in - g.addTask { try await operation.wait(forSeconds: 5) } + g.addTask { try await operation.wait(forSeconds: 3) } g.addTask { operation.signal() } try await g.waitForAll() } @@ -117,7 +117,7 @@ class TaskOperationTimeoutTests: XCTestCase { } operation.signal() do { - try await operation.wait(forSeconds: 5) + try await operation.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is DurationTimeoutError {} } @@ -139,7 +139,7 @@ class TaskOperationClockTimeoutTests: XCTestCase { } operation.signal() do { - try await operation.wait(forSeconds: 5, clock: clock) + try await operation.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError {} } @@ -162,9 +162,9 @@ class TaskOperationCancellationTests: XCTestCase { #else operation.start() #endif - try await waitUntil(operation, timeout: 5, satisfies: \.isExecuting) + try await waitUntil(operation, timeout: 3, satisfies: \.isExecuting) operation.cancel() - try await waitUntil(operation, timeout: 5, satisfies: \.isCancelled) + try await waitUntil(operation, timeout: 3, satisfies: \.isCancelled) XCTAssertTrue(operation.isFinished) XCTAssertFalse(operation.isExecuting) switch await operation.result { @@ -186,9 +186,9 @@ class TaskOperationCancellationTests: XCTestCase { #else operation.start() #endif - try await waitUntil(operation, timeout: 5, satisfies: \.isExecuting) + try await waitUntil(operation, timeout: 3, satisfies: \.isExecuting) operation.cancel() - try await waitUntil(operation, timeout: 5, satisfies: \.isCancelled) + try await waitUntil(operation, timeout: 3, satisfies: \.isCancelled) XCTAssertTrue(operation.isFinished) XCTAssertFalse(operation.isExecuting) } @@ -198,7 +198,7 @@ class TaskOperationCancellationTests: XCTestCase { try await Task.sleep(seconds: 1) } let task = Task.detached { - try await operation.wait(forSeconds: 5) + try await operation.wait(forSeconds: 3) } task.cancel() do { @@ -233,7 +233,7 @@ class TaskOperationCancellationTests: XCTestCase { } operation.signal() operation.cancel() - try await waitUntil(operation, timeout: 5, satisfies: \.isCancelled) + try await waitUntil(operation, timeout: 3, satisfies: \.isCancelled) self.addTeardownBlock { [weak operation] in operation.assertReleased() } @@ -246,7 +246,7 @@ class TaskOperationTaskManagementTests: XCTestCase { func testOperationWithoutTrackingChildTasks() async throws { let operation = TaskOperation(track: false) operation.signal() - try await operation.wait(forSeconds: 5) + try await operation.wait(forSeconds: 3) } func testOperationWithTrackingChildTasks() async throws { diff --git a/Tests/AsyncObjectsTests/TaskQueueTests.swift b/Tests/AsyncObjectsTests/TaskQueueTests.swift index 9ba34579..0302a7eb 100644 --- a/Tests/AsyncObjectsTests/TaskQueueTests.swift +++ b/Tests/AsyncObjectsTests/TaskQueueTests.swift @@ -24,7 +24,7 @@ class TaskQueueTests: XCTestCase { try await Task.sleep(seconds: 10) } } - try await waitUntil(queue, timeout: 5) { $0.blocked } + try await waitUntil(queue, timeout: 3) { $0.blocked } queue.signal() let blocked = await queue.blocked XCTAssertTrue(blocked) @@ -62,7 +62,7 @@ class TaskQueueTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testDeinit() async throws { @@ -149,7 +149,7 @@ class TaskQueueBlockOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testExecutionOfTaskBeforeOperation() async throws { @@ -164,7 +164,7 @@ class TaskQueueBlockOperationTests: XCTestCase { c.finish() } } - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) await stream.assertElements() } @@ -182,7 +182,7 @@ class TaskQueueBlockOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testCancellation() async throws { @@ -192,7 +192,7 @@ class TaskQueueBlockOperationTests: XCTestCase { } let task = Task.detached { await queue.exec(flags: .block) {} - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) XCTFail("Unexpected task progression") } do { @@ -214,7 +214,7 @@ class TaskQueueBlockOperationTests: XCTestCase { } catch {} XCTAssertTrue(Task.isCancelled) await queue.exec(flags: .block) {} - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) XCTFail("Unexpected task progression") } do { @@ -237,7 +237,7 @@ class TaskQueueBlockOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testMultipleCancellationWithoutBlocking() async throws { @@ -258,7 +258,7 @@ class TaskQueueBlockOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testMixedeCancellationWithoutBlocking() async throws { @@ -284,7 +284,7 @@ class TaskQueueBlockOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } } @@ -301,7 +301,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testExecutionOfTaskBeforeOperation() async throws { @@ -317,7 +317,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { c.finish() } } - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) await stream.assertElements() } @@ -335,7 +335,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testCancellation() async throws { @@ -345,7 +345,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { } let task = Task.detached { await queue.exec(flags: .block) {} - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) XCTFail("Unexpected task progression") } do { @@ -367,7 +367,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { } catch {} XCTAssertTrue(Task.isCancelled) await queue.exec(flags: .barrier) {} - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) XCTFail("Unexpected task progression") } do { @@ -390,7 +390,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testMultipleCancellationWithoutBlocking() async throws { @@ -411,7 +411,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testMixedCancellationWithoutBlocking() async throws { @@ -437,7 +437,7 @@ class TaskQueueBarrierOperationTests: XCTestCase { // Cancels block task group.cancelAll() } - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } } @@ -458,7 +458,7 @@ class TaskQueueMixedOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testExecutionOfBlockTaskAfterBarrierOperation() async throws { @@ -475,7 +475,7 @@ class TaskQueueMixedOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testLongRunningConcurrentTaskWithShortBlockTaskBeforeBarrierOperation() @@ -496,7 +496,7 @@ class TaskQueueMixedOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testLongRunningConcurrentTaskWithShortBlockTaskAfterBarrierOperation() @@ -521,7 +521,7 @@ class TaskQueueMixedOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } /// Scenario described in: @@ -554,7 +554,7 @@ class TaskQueueMixedOperationTests: XCTestCase { } } await stream.assertElements() - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } func testCancellableAndNonCancellableTasks() async throws { @@ -604,7 +604,7 @@ class TaskQueueMixedOperationTests: XCTestCase { try await Task.sleep(seconds: 10) } } - try await waitUntil(queue, timeout: 10) { + try await waitUntil(queue, timeout: 5) { guard let (_, (_, flags)) = $0.queue.reversed().first else { return false } @@ -640,10 +640,10 @@ class TaskQueueMixedOperationTests: XCTestCase { } } } - try await waitUntil(queue, timeout: 10) { $0.blocked } + try await waitUntil(queue, timeout: 5) { $0.blocked } group.cancelAll() } - try await queue.wait(forSeconds: 5) + try await queue.wait(forSeconds: 3) } } @@ -664,7 +664,7 @@ fileprivate extension TaskQueue { flags: Flags = [], operation: @Sendable @escaping () async -> T ) async { - await GlobalContinuation.with { continuation in + await withUnsafeContinuation { continuation in self.addTask(priority: priority, flags: flags) { continuation.resume() return await operation() @@ -677,7 +677,7 @@ fileprivate extension TaskQueue { flags: Flags = [], operation: @Sendable @escaping () async throws -> T ) async { - await GlobalContinuation.with { continuation in + await withUnsafeContinuation { continuation in self.addTask(priority: priority, flags: flags) { continuation.resume() return try await operation() @@ -696,10 +696,10 @@ fileprivate extension TaskQueue { await addTaskAndStart(priority: option.task, flags: option.flags) { try await Task.sleep(seconds: 1) } - group.addTask { try await self.wait(forSeconds: 5) } + group.addTask { try await self.wait(forSeconds: 3) } try await group.waitForAll() } - try await self.wait(forSeconds: 5) + try await self.wait(forSeconds: 3) } @MainActor @@ -716,7 +716,7 @@ fileprivate extension TaskQueue { try await Task.sleep(seconds: 10) } do { - try await self.wait(forSeconds: 10) + try await self.wait(forSeconds: 5) XCTFail("Unexpected task progression", file: file, line: line) } catch is DurationTimeoutError {} } @@ -738,7 +738,7 @@ fileprivate extension TaskQueue { try await Task.sleep(seconds: 10) } do { - try await self.wait(forSeconds: 10) + try await self.wait(forSeconds: 5) XCTFail("Unexpected task progression", file: file, line: line) } catch is DurationTimeoutError {} } diff --git a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift index 8e35ed9d..7ecbe012 100644 --- a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift @@ -7,7 +7,7 @@ class ThrowingFutureTests: XCTestCase { func testFutureFulfilledInitialization() async throws { let future = Future(with: .success(5)) - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } @@ -15,7 +15,7 @@ class ThrowingFutureTests: XCTestCase { let future = Future() try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } group.addTask { @@ -30,7 +30,7 @@ class ThrowingFutureTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { do { - let _ = try await future.wait(forSeconds: 5) + let _ = try await future.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is CancellationError {} } @@ -45,7 +45,7 @@ class ThrowingFutureTests: XCTestCase { let future = Future() let waitTask = Task { do { - let _ = try await future.wait(forSeconds: 5) + let _ = try await future.wait(forSeconds: 3) XCTFail("Future fulfillments wait not cancelled") } catch is CancellationError {} } @@ -56,14 +56,14 @@ class ThrowingFutureTests: XCTestCase { func testMultipleTimesFutureFulfilled() async throws { let future = Future(with: .success(5)) await future.fulfill(producing: 10) - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 5) } func testDeinit() async throws { let future = Future() Task.detached { await future.fulfill(producing: 5) } - let _ = try await future.wait(forSeconds: 5) + let _ = try await future.wait(forSeconds: 3) self.addTeardownBlock { [weak future] in future.assertReleased() } @@ -73,7 +73,7 @@ class ThrowingFutureTests: XCTestCase { let future = Future() let task = Task.detached { do { - let _ = try await future.wait(forSeconds: 5) + let _ = try await future.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is CancellationError {} } @@ -90,7 +90,7 @@ class ThrowingFutureTests: XCTestCase { } catch {} XCTAssertTrue(Task.isCancelled) do { - let _ = try await future.wait(forSeconds: 5) + let _ = try await future.wait(forSeconds: 3) XCTFail("Unexpected task progression") } catch is CancellationError {} } @@ -105,7 +105,7 @@ class ThrowingFutureTests: XCTestCase { let future = Future() try await withThrowingTaskGroup(of: Void.self) { group in group.addTask { - let _ = try await future.wait(forSeconds: 5) + let _ = try await future.wait(forSeconds: 3) } group.addTask { await future.fulfill(producing: i) } try await group.waitForAll() @@ -127,7 +127,7 @@ class ThrowingFutureCombiningAllTests: XCTestCase { let allFuture = Future.all(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 5) + let value = try await allFuture.wait(forSeconds: 3) XCTAssertEqual(value, [1, 2, 3]) } group.addTask { await future1.fulfill(producing: 1) } @@ -145,7 +145,7 @@ class ThrowingFutureCombiningAllTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { do { - let _ = try await allFuture.wait(forSeconds: 5) + let _ = try await allFuture.wait(forSeconds: 3) XCTFail("Future fulfillment did not fail") } catch is CancellationError {} } @@ -160,7 +160,7 @@ class ThrowingFutureCombiningAllTests: XCTestCase { func testEmptyConstructing() async throws { let future = Future.all() - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 3) XCTAssertTrue(value.isEmpty) } } @@ -175,7 +175,7 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let allFuture = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let values = try await allFuture.wait(forSeconds: 5) + let values = try await allFuture.wait(forSeconds: 3) for (index, item) in values.enumerated() { switch item { case .success(let value): @@ -199,7 +199,7 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let allFuture = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let values = try await allFuture.wait(forSeconds: 5) + let values = try await allFuture.wait(forSeconds: 3) for (index, item) in values.enumerated() { switch item { case .success(let value): @@ -220,7 +220,7 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { func testEmptyConstructing() async throws { let future = Future.allSettled() - let value = try await future.wait(forSeconds: 5) + let value = try await future.wait(forSeconds: 3) XCTAssertTrue(value.isEmpty) } } @@ -235,7 +235,7 @@ class ThrowingFutureRacingTests: XCTestCase { let allFuture = Future.race(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 5) + let value = try await allFuture.wait(forSeconds: 3) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) @@ -253,7 +253,7 @@ class ThrowingFutureRacingTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { do { - let _ = try await allFuture.wait(forSeconds: 5) + let _ = try await allFuture.wait(forSeconds: 3) XCTFail("Future fulfillment did not fail") } catch is CancellationError {} } @@ -275,7 +275,7 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let allFuture = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 5) + let value = try await allFuture.wait(forSeconds: 3) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) @@ -292,7 +292,7 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let allFuture = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 5) + let value = try await allFuture.wait(forSeconds: 3) XCTAssertEqual(value, 2) } await future1.fulfill(throwing: CancellationError()) @@ -310,7 +310,7 @@ class ThrowingFutureSelectAnyTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { do { - let _ = try await allFuture.wait(forSeconds: 5) + let _ = try await allFuture.wait(forSeconds: 3) XCTFail("Future fulfillment did not fail") } catch is CancellationError {} } diff --git a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift index 6774578e..43446898 100644 --- a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift +++ b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift @@ -2,6 +2,10 @@ import XCTest import Dispatch @testable import AsyncObjects +func waitForResume(_ body: (UnsafeContinuation) -> Void) async { + await withUnsafeContinuation(body) +} + func waitUntil( _ actor: T, timeout: TimeInterval, From 85888b475ff23fe588501e1fd38aa57c98304ed1 Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Thu, 29 Dec 2022 14:13:15 +0530 Subject: [PATCH 12/22] wip: fix build fail on older Swift version --- Tests/AsyncObjectsTests/TaskQueueTests.swift | 8 ++++---- Tests/AsyncObjectsTests/XCAsyncTestCase.swift | 4 ---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Tests/AsyncObjectsTests/TaskQueueTests.swift b/Tests/AsyncObjectsTests/TaskQueueTests.swift index 0302a7eb..c8dc2aa8 100644 --- a/Tests/AsyncObjectsTests/TaskQueueTests.swift +++ b/Tests/AsyncObjectsTests/TaskQueueTests.swift @@ -664,8 +664,8 @@ fileprivate extension TaskQueue { flags: Flags = [], operation: @Sendable @escaping () async -> T ) async { - await withUnsafeContinuation { continuation in - self.addTask(priority: priority, flags: flags) { + await GlobalContinuation.with { continuation in + self.addTask(priority: priority, flags: flags) { () -> T in continuation.resume() return await operation() } @@ -677,8 +677,8 @@ fileprivate extension TaskQueue { flags: Flags = [], operation: @Sendable @escaping () async throws -> T ) async { - await withUnsafeContinuation { continuation in - self.addTask(priority: priority, flags: flags) { + await GlobalContinuation.with { continuation in + self.addTask(priority: priority, flags: flags) { () -> T in continuation.resume() return try await operation() } diff --git a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift index 43446898..6774578e 100644 --- a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift +++ b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift @@ -2,10 +2,6 @@ import XCTest import Dispatch @testable import AsyncObjects -func waitForResume(_ body: (UnsafeContinuation) -> Void) async { - await withUnsafeContinuation(body) -} - func waitUntil( _ actor: T, timeout: TimeInterval, From 4f9e565f8d04ab5ae145ed6c8148e76811e0a7f9 Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Thu, 29 Dec 2022 16:34:50 +0530 Subject: [PATCH 13/22] wip: improve test timeouts --- Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift | 4 ++-- Tests/AsyncObjectsTests/TaskOperationTests.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index e691c77d..ef3f052f 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -13,14 +13,14 @@ class AsyncCountdownEventTests: XCTestCase { let event = AsyncCountdownEvent() event.increment(by: 10) event.signal(concurrent: 10) - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testWithOverIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) event.signal(concurrent: 15) - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) try await waitUntil(event, timeout: 3) { $0.currentCount == 0 } } diff --git a/Tests/AsyncObjectsTests/TaskOperationTests.swift b/Tests/AsyncObjectsTests/TaskOperationTests.swift index 646d5d58..e579bfbc 100644 --- a/Tests/AsyncObjectsTests/TaskOperationTests.swift +++ b/Tests/AsyncObjectsTests/TaskOperationTests.swift @@ -151,7 +151,7 @@ class TaskOperationCancellationTests: XCTestCase { func testCancellation() async throws { let operation = TaskOperation { - (try? await Task.sleep(seconds: 1)) != nil + (try? await Task.sleep(seconds: 10)) != nil } XCTAssertFalse(operation.isExecuting) XCTAssertFalse(operation.isFinished) From 610f50390bb65d36df43eda41f43eac8e739ed11 Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sun, 1 Jan 2023 16:22:47 +0530 Subject: [PATCH 14/22] wip: fix deinit and future tests --- .../NonThrowingFutureTests.swift | 18 +++++---- .../ThrowingFutureTests.swift | 40 ++++++++++--------- Tests/AsyncObjectsTests/XCAsyncTestCase.swift | 6 +-- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift index b7a2c2b1..c311e622 100644 --- a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift @@ -87,10 +87,10 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.all(future1, future3, future2) + let future = Future.all(future1, future3, future2) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, [1, 3, 2]) } group.addTask { await future1.fulfill(producing: 1) } @@ -104,10 +104,10 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.allSettled(future1, future2, future3) + let future = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let values = try await allFuture.wait(forSeconds: 3) + let values = try await future.wait(forSeconds: 3) for (index, item) in values.enumerated() { switch item { case .success(let value): @@ -128,13 +128,14 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.race(future1, future2, future3) + let future = Future.race(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) + try await waitUntil(future, timeout: 5) { $0.result != nil } await future2.fulfill(producing: 2) await future3.fulfill(producing: 3) try await group.waitForAll() @@ -145,13 +146,14 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.any(future1, future2, future3) + let future = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) + try await waitUntil(future, timeout: 5) { $0.result != nil } await future2.fulfill(producing: 2) await future3.fulfill(producing: 3) try await group.waitForAll() diff --git a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift index 7ecbe012..76596d59 100644 --- a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift @@ -124,10 +124,10 @@ class ThrowingFutureCombiningAllTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.all(future1, future2, future3) + let future = Future.all(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, [1, 2, 3]) } group.addTask { await future1.fulfill(producing: 1) } @@ -141,11 +141,11 @@ class ThrowingFutureCombiningAllTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.all(future1, future2, future3) + let future = Future.all(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { do { - let _ = try await allFuture.wait(forSeconds: 3) + let _ = try await future.wait(forSeconds: 3) XCTFail("Future fulfillment did not fail") } catch is CancellationError {} } @@ -172,10 +172,10 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.allSettled(future1, future2, future3) + let future = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let values = try await allFuture.wait(forSeconds: 3) + let values = try await future.wait(forSeconds: 3) for (index, item) in values.enumerated() { switch item { case .success(let value): @@ -196,10 +196,10 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.allSettled(future1, future2, future3) + let future = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let values = try await allFuture.wait(forSeconds: 3) + let values = try await future.wait(forSeconds: 3) for (index, item) in values.enumerated() { switch item { case .success(let value): @@ -232,13 +232,14 @@ class ThrowingFutureRacingTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.race(future1, future2, future3) + let future = Future.race(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) + try await waitUntil(future, timeout: 5) { $0.result != nil } await future2.fulfill(producing: 2) await future3.fulfill(producing: 3) try await group.waitForAll() @@ -249,15 +250,16 @@ class ThrowingFutureRacingTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.race(future1, future2, future3) + let future = Future.race(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { do { - let _ = try await allFuture.wait(forSeconds: 3) + let _ = try await future.wait(forSeconds: 3) XCTFail("Future fulfillment did not fail") } catch is CancellationError {} } await future1.fulfill(throwing: CancellationError()) + try await waitUntil(future, timeout: 5) { $0.result != nil } await future2.fulfill(producing: 2) await future3.fulfill(producing: 3) try await group.waitForAll() @@ -272,13 +274,14 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.any(future1, future2, future3) + let future = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 1) } await future1.fulfill(producing: 1) + try await waitUntil(future, timeout: 5) { $0.result != nil } await future2.fulfill(producing: 2) await future3.fulfill(producing: 3) try await group.waitForAll() @@ -289,14 +292,15 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.any(future1, future2, future3) + let future = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { - let value = try await allFuture.wait(forSeconds: 3) + let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 2) } await future1.fulfill(throwing: CancellationError()) await future2.fulfill(producing: 2) + try await waitUntil(future, timeout: 5) { $0.result != nil } await future3.fulfill(producing: 3) try await group.waitForAll() } @@ -306,11 +310,11 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future1 = Future() let future2 = Future() let future3 = Future() - let allFuture = Future.any(future1, future2, future3) + let future = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in await group.addTaskAndStart { do { - let _ = try await allFuture.wait(forSeconds: 3) + let _ = try await future.wait(forSeconds: 3) XCTFail("Future fulfillment did not fail") } catch is CancellationError {} } diff --git a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift index 6774578e..9cba31f4 100644 --- a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift +++ b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift @@ -90,11 +90,7 @@ extension Optional where Wrapped: AnyObject { case .none: break case .some(let wrapped): - #if canImport(Darwin) - XCTAssertEqual(CFGetRetainCount(wrapped), 0, file: file, line: line) - #else - XCTFail("Has some value \(wrapped)", file: file, line: line) - #endif + XCTAssertEqual(_getRetainCount(wrapped), 0, file: file, line: line) } } } From 97571dd57b6d82b0ee2d244868f24c6d749e2548 Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sun, 1 Jan 2023 17:28:16 +0530 Subject: [PATCH 15/22] wip: fix test timeouts and deinit tests --- Sources/AsyncObjects/CancellationSource.swift | 6 +-- .../AsyncCountdownEventTests.swift | 46 +++++++++---------- .../CancellationSourceTests.swift | 10 ++-- .../NonThrowingFutureTests.swift | 2 +- Tests/AsyncObjectsTests/XCAsyncTestCase.swift | 6 ++- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/Sources/AsyncObjects/CancellationSource.swift b/Sources/AsyncObjects/CancellationSource.swift index baac2059..a5818eba 100644 --- a/Sources/AsyncObjects/CancellationSource.swift +++ b/Sources/AsyncObjects/CancellationSource.swift @@ -72,16 +72,16 @@ public actor CancellationSource { /// Propagate cancellation to linked cancellation sources. @inlinable - internal nonisolated func propagateCancellation() async { + internal func propagateCancellation() async { await withTaskGroup(of: Void.self) { group in - let linkedSources = await linkedSources - linkedSources.forEach { s in group.addTask { s.cancel() } } + linkedSources.forEach { s in group.addTask { await s.cancelAll() } } await group.waitForAll() } } /// Trigger cancellation event, initiate cooperative cancellation of registered tasks /// and propagate cancellation to linked cancellation sources. + @usableFromInline internal func cancelAll() async { registeredTasks.forEach { $1() } registeredTasks = [:] diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index ef3f052f..6e1d74ad 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -13,14 +13,14 @@ class AsyncCountdownEventTests: XCTestCase { let event = AsyncCountdownEvent() event.increment(by: 10) event.signal(concurrent: 10) - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 10) } func testWithOverIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) event.signal(concurrent: 15) - try await event.wait(forSeconds: 5) + try await event.wait(forSeconds: 10) try await waitUntil(event, timeout: 3) { $0.currentCount == 0 } } @@ -28,52 +28,52 @@ class AsyncCountdownEventTests: XCTestCase { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) event.signal(concurrent: 7) - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 10) } func testWithLimitInitialCountAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3, initial: 2) event.increment(by: 10) event.signal(concurrent: 9) - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 10) } func testWithIncrementAndReset() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.reset() - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testWithIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.reset(to: 2) - try await waitUntil(event, timeout: 3) { $0.currentCount == 2 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 2 } event.signal(concurrent: 2) - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testWithConcurrentIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } Task.detached { - try await waitUntil(event, timeout: 3) { $0.currentCount == 6 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 6 } event.reset(to: 2) } event.signal(concurrent: 4) - try await waitUntil(event, timeout: 3) { $0.currentCount == 2 } + try await waitUntil(event, timeout: 10) { $0.currentCount == 2 } event.signal(concurrent: 2) - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) } func testDeinit() async throws { let event = AsyncCountdownEvent(until: 0, initial: 1) Task.detached { event.signal() } - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) self.addTeardownBlock { [weak event] in event.assertReleased() } @@ -85,7 +85,7 @@ class AsyncCountdownEventTests: XCTestCase { group.addTask { let event = AsyncCountdownEvent(initial: 1) try await withThrowingTaskGroup(of: Void.self) { g in - g.addTask { try await event.wait(forSeconds: 3) } + g.addTask { try await event.wait(forSeconds: 5) } g.addTask { event.signal() } try await g.waitForAll() } @@ -102,7 +102,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { func testTimeoutWithIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 9) do { try await event.wait(forSeconds: 3) @@ -115,7 +115,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { func testTimeoutWithLimitAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 6) do { try await event.wait(forSeconds: 3) @@ -128,7 +128,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { func testTimeoutWithLimitInitialCountAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 13 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 13 } event.signal(concurrent: 9) do { try await event.wait(forSeconds: 3) @@ -141,7 +141,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { func testTimeoutWithIncrementAndResetToCount() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 8) Task.detached { try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } @@ -171,7 +171,7 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 9) do { try await event.wait(forSeconds: 3, clock: clock) @@ -190,7 +190,7 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 6) do { try await event.wait(forSeconds: 3, clock: clock) @@ -209,7 +209,7 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 13 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 13 } event.signal(concurrent: 9) do { try await event.wait(forSeconds: 3, clock: clock) @@ -228,7 +228,7 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let clock: ContinuousClock = .continuous let event = AsyncCountdownEvent() event.increment(by: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 10 } + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 8) Task.detached { try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } diff --git a/Tests/AsyncObjectsTests/CancellationSourceTests.swift b/Tests/AsyncObjectsTests/CancellationSourceTests.swift index fc871355..b18a0313 100644 --- a/Tests/AsyncObjectsTests/CancellationSourceTests.swift +++ b/Tests/AsyncObjectsTests/CancellationSourceTests.swift @@ -6,7 +6,7 @@ class CancellationSourceTests: XCTestCase { func testTaskCancellation() async throws { let source = CancellationSource() - let task = Task { try await Task.sleep(seconds: 3) } + let task = Task { try await Task.sleep(seconds: 10) } source.register(task: task) try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } source.cancel() @@ -16,7 +16,7 @@ class CancellationSourceTests: XCTestCase { func testTaskCancellationWithTimeout() async throws { let source = CancellationSource(cancelAfterNanoseconds: UInt64(1E9)) - let task = Task { try await Task.sleep(seconds: 3) } + let task = Task { try await Task.sleep(seconds: 10) } source.register(task: task) try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) @@ -34,7 +34,7 @@ class CancellationSourceTests: XCTestCase { at: .now + .seconds(1), clock: ContinuousClock.continuous ) - let task = Task { try await Task.sleep(seconds: 3, clock: clock) } + let task = Task { try await Task.sleep(seconds: 10, clock: clock) } source.register(task: task) try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) @@ -44,7 +44,7 @@ class CancellationSourceTests: XCTestCase { func testTaskCancellationWithLinkedSource() async throws { let parentSource = CancellationSource() let source = CancellationSource(linkedWith: parentSource) - let task = Task { try await Task.sleep(seconds: 3) } + let task = Task { try await Task.sleep(seconds: 10) } source.register(task: task) try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } parentSource.cancel() @@ -58,7 +58,7 @@ class CancellationSourceTests: XCTestCase { let source = CancellationSource( linkedWith: parentSource1, parentSource2 ) - let task = Task { try await Task.sleep(seconds: 3) } + let task = Task { try await Task.sleep(seconds: 10) } source.register(task: task) try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } parentSource1.cancel() diff --git a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift index c311e622..54ce8570 100644 --- a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift @@ -28,7 +28,7 @@ class NonThrowingFutureTests: XCTestCase { func testFulfilledWithAttemptClosure() async throws { let future = Future { promise in DispatchQueue.global(qos: .background) - .asyncAfter(deadline: .now() + 2) { + .asyncAfter(deadline: .now() + 0.5) { promise(.success(5)) } } diff --git a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift index 9cba31f4..a3c16471 100644 --- a/Tests/AsyncObjectsTests/XCAsyncTestCase.swift +++ b/Tests/AsyncObjectsTests/XCAsyncTestCase.swift @@ -89,8 +89,10 @@ extension Optional where Wrapped: AnyObject { switch self { case .none: break - case .some(let wrapped): - XCTAssertEqual(_getRetainCount(wrapped), 0, file: file, line: line) + case .some(let value): + let wr = _getUnownedRetainCount(value) + _getWeakRetainCount(value) + let rc = _getRetainCount(value) - wr + XCTAssertEqual(rc, 0, file: file, line: line) } } } From eed875b84ea06cade525d702425546556e03c84e Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sun, 1 Jan 2023 18:45:05 +0530 Subject: [PATCH 16/22] wip: fix timeout tests fail --- Sources/AsyncObjects/Future.swift | 2 +- Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift | 7 ++++--- Tests/AsyncObjectsTests/CancellationSourceTests.swift | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Sources/AsyncObjects/Future.swift b/Sources/AsyncObjects/Future.swift index 1e1e3e6a..225df1c3 100644 --- a/Sources/AsyncObjects/Future.swift +++ b/Sources/AsyncObjects/Future.swift @@ -285,7 +285,7 @@ public actor Future: LoggableActor { continuations.forEach { key, value in value.resume(with: result) log( - "Fulfilled", id: key, + "Fulfilling", id: key, file: file, function: function, line: line ) } diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index 6e1d74ad..0b01937e 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -12,16 +12,17 @@ class AsyncCountdownEventTests: XCTestCase { func testWithIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 10) - try await event.wait(forSeconds: 10) + try await event.wait(forSeconds: 5) } func testWithOverIncrement() async throws { let event = AsyncCountdownEvent() event.increment(by: 10) + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 15) - try await event.wait(forSeconds: 10) - try await waitUntil(event, timeout: 3) { $0.currentCount == 0 } + try await event.wait(forSeconds: 5) } func testWithLimitAndIncrement() async throws { diff --git a/Tests/AsyncObjectsTests/CancellationSourceTests.swift b/Tests/AsyncObjectsTests/CancellationSourceTests.swift index b18a0313..a5a7f4ff 100644 --- a/Tests/AsyncObjectsTests/CancellationSourceTests.swift +++ b/Tests/AsyncObjectsTests/CancellationSourceTests.swift @@ -15,8 +15,8 @@ class CancellationSourceTests: XCTestCase { } func testTaskCancellationWithTimeout() async throws { - let source = CancellationSource(cancelAfterNanoseconds: UInt64(1E9)) let task = Task { try await Task.sleep(seconds: 10) } + let source = CancellationSource(cancelAfterNanoseconds: UInt64(1E9)) source.register(task: task) try await waitUntil(source, timeout: 5) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) From e1a2659431ac533d39cb8bbc92ec16241f83ed8f Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sun, 1 Jan 2023 20:12:49 +0530 Subject: [PATCH 17/22] wip: fix timeout tests fail --- .../AsyncCountdownEventTests.swift | 21 ++++++++++++------- Tests/AsyncObjectsTests/AsyncEventTests.swift | 3 ++- .../NonThrowingFutureTests.swift | 3 ++- .../TaskOperationTests.swift | 2 +- .../ThrowingFutureTests.swift | 3 ++- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index 0b01937e..8b7f6000 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -28,15 +28,17 @@ class AsyncCountdownEventTests: XCTestCase { func testWithLimitAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) + try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 7) - try await event.wait(forSeconds: 10) + try await event.wait(forSeconds: 5) } func testWithLimitInitialCountAndIncrement() async throws { let event = AsyncCountdownEvent(until: 3, initial: 2) event.increment(by: 10) + try await waitUntil(event, timeout: 5) { $0.currentCount == 12 } event.signal(concurrent: 9) - try await event.wait(forSeconds: 10) + try await event.wait(forSeconds: 5) } func testWithIncrementAndReset() async throws { @@ -73,8 +75,9 @@ class AsyncCountdownEventTests: XCTestCase { func testDeinit() async throws { let event = AsyncCountdownEvent(until: 0, initial: 1) - Task.detached { event.signal() } + let task = Task.detached { event.signal() } try await event.wait(forSeconds: 5) + await task.value self.addTeardownBlock { [weak event] in event.assertReleased() } @@ -106,7 +109,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 9) do { - try await event.wait(forSeconds: 3) + try await event.wait(forSeconds: 5) XCTFail("Unexpected task progression") } catch is DurationTimeoutError { try await waitUntil(event, timeout: 3) { $0.currentCount == 1 } @@ -145,7 +148,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 8) Task.detached { - try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } + try await waitUntil(event, timeout: 5) { $0.currentCount <= 6 } event.reset(to: 6) } do { @@ -153,7 +156,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { XCTFail("Unexpected task progression") } catch is DurationTimeoutError { try await waitUntil(event, timeout: 3) { - [6, 2].contains($0.currentCount) + (2...6).contains($0.currentCount) } } } @@ -232,14 +235,16 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.signal(concurrent: 8) Task.detached { - try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } + try await waitUntil(event, timeout: 5) { $0.currentCount <= 6 } event.reset(to: 6) } do { try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") } catch is TimeoutError { - try await waitUntil(event, timeout: 3) { $0.currentCount <= 6 } + try await waitUntil(event, timeout: 3) { + (2...6).contains($0.currentCount) + } } } } diff --git a/Tests/AsyncObjectsTests/AsyncEventTests.swift b/Tests/AsyncObjectsTests/AsyncEventTests.swift index d702554c..6e888b93 100644 --- a/Tests/AsyncObjectsTests/AsyncEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncEventTests.swift @@ -25,8 +25,9 @@ class AsyncEventTests: XCTestCase { func testDeinit() async throws { let event = AsyncEvent(signaledInitially: false) - Task.detached { event.signal() } + let task = Task.detached { event.signal() } try await event.wait(forSeconds: 3) + await task.value self.addTeardownBlock { [weak event] in event.assertReleased() } diff --git a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift index 54ce8570..ce15e6b2 100644 --- a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift @@ -54,8 +54,9 @@ class NonThrowingFutureTests: XCTestCase { func testDeinit() async throws { let future = Future() - Task.detached { await future.fulfill(producing: 5) } + let task = Task.detached { await future.fulfill(producing: 5) } let _ = try await future.wait(forSeconds: 3) + await task.value self.addTeardownBlock { [weak future] in future.assertReleased() } diff --git a/Tests/AsyncObjectsTests/TaskOperationTests.swift b/Tests/AsyncObjectsTests/TaskOperationTests.swift index e579bfbc..936b42a1 100644 --- a/Tests/AsyncObjectsTests/TaskOperationTests.swift +++ b/Tests/AsyncObjectsTests/TaskOperationTests.swift @@ -85,7 +85,7 @@ class TaskOperationTests: XCTestCase { try await Task.sleep(seconds: 1) } operation.signal() - try await operation.wait(forSeconds: 3) + try await operation.wait(forSeconds: 5) self.addTeardownBlock { [weak operation] in operation.assertReleased() } diff --git a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift index 76596d59..dfd7df6e 100644 --- a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift @@ -62,8 +62,9 @@ class ThrowingFutureTests: XCTestCase { func testDeinit() async throws { let future = Future() - Task.detached { await future.fulfill(producing: 5) } + let task = Task.detached { await future.fulfill(producing: 5) } let _ = try await future.wait(forSeconds: 3) + await task.value self.addTeardownBlock { [weak future] in future.assertReleased() } From 63ede7e076cc05b654093d0e5906aa35844acf54 Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sun, 1 Jan 2023 22:30:32 +0530 Subject: [PATCH 18/22] wip: add logging to `CancellationSource` --- Sources/AsyncObjects/CancellationSource.swift | 200 +++++++++++++++--- .../CancellationSourceTests.swift | 19 +- 2 files changed, 175 insertions(+), 44 deletions(-) diff --git a/Sources/AsyncObjects/CancellationSource.swift b/Sources/AsyncObjects/CancellationSource.swift index a5818eba..52240e33 100644 --- a/Sources/AsyncObjects/CancellationSource.swift +++ b/Sources/AsyncObjects/CancellationSource.swift @@ -30,7 +30,7 @@ /// /// - Warning: Cancellation sources propagate cancellation event to other linked cancellation sources. /// In case of circular dependency between cancellation sources, app will go into infinite recursion. -public actor CancellationSource { +public actor CancellationSource: LoggableActor { /// All the registered tasks for cooperative cancellation. @usableFromInline internal private(set) var registeredTasks: [AnyHashable: () -> Void] = [:] @@ -47,45 +47,92 @@ public actor CancellationSource { /// Add task to registered cooperative cancellation tasks list. /// - /// - Parameter task: The task to register. + /// - Parameters: + /// - task: The task to register. + /// - file: The file registration request originates from. + /// - function: The function registration request originates from. + /// - line: The line registration request originates from. @inlinable - internal func add(task: Task) { - guard !task.isCancelled else { return } + internal func add( + task: Task, + file: String, function: String, line: UInt + ) { + guard !task.isCancelled else { + log("Already cancelled", file: file, function: function, line: line) + return + } + registeredTasks[task] = { task.cancel() } + log("Registered", file: file, function: function, line: line) } /// Remove task from registered cooperative cancellation tasks list. /// - /// - Parameter task: The task to remove. + /// - Parameters: + /// - task: The task to remove. + /// - file: The file remove request originates from. + /// - function: The function remove request originates from. + /// - line: The line remove request originates from. @inlinable - internal func remove(task: Task) { + internal func remove( + task: Task, + file: String, function: String, line: UInt + ) { registeredTasks.removeValue(forKey: task) + log("Removed", file: file, function: function, line: line) } /// Add cancellation source to linked cancellation sources list to propagate cancellation event. /// - /// - Parameter task: The source to link. + /// - Parameters: + /// - source: The source to link. + /// - file: The file link request originates from. + /// - function: The function link request originates from. + /// - line: The line link request originates from. @inlinable - internal func addSource(_ source: CancellationSource) { + internal func addSource( + _ source: CancellationSource, + file: String, function: String, line: UInt + ) { linkedSources.append(source) + log("Added", file: file, function: function, line: line) } /// Propagate cancellation to linked cancellation sources. + /// + /// - Parameters: + /// - file: The file cancel request originates from. + /// - function: The function cancel request originates from. + /// - line: The line cancel request originates from. @inlinable - internal func propagateCancellation() async { + internal func propagateCancellation( + file: String, function: String, line: UInt + ) async { await withTaskGroup(of: Void.self) { group in - linkedSources.forEach { s in group.addTask { await s.cancelAll() } } + linkedSources.forEach { s in + group.addTask { + await s.cancelAll( + file: file, function: function, line: line + ) + } + } await group.waitForAll() } } /// Trigger cancellation event, initiate cooperative cancellation of registered tasks /// and propagate cancellation to linked cancellation sources. + /// + /// - Parameters: + /// - file: The file cancel request originates from. + /// - function: The function cancel request originates from. + /// - line: The line cancel request originates from. @usableFromInline - internal func cancelAll() async { + internal func cancelAll(file: String, function: String, line: UInt) async { registeredTasks.forEach { $1() } registeredTasks = [:] - await propagateCancellation() + await propagateCancellation(file: file, function: function, line: line) + log("Cancelled", file: file, function: function, line: line) } // MARK: Public @@ -101,15 +148,32 @@ public actor CancellationSource { /// Initiating cancellation in any of the provided cancellation sources /// will ensure newly created cancellation source receive cancellation event. /// - /// - Parameter sources: The cancellation sources the newly created object will be linked to. + /// - Parameters: + /// - sources: The cancellation sources the newly created object will be linked to. + /// - file: The file link request originates from (there's usually no need to pass it + /// explicitly as it defaults to `#fileID`). + /// - function: The function link request originates from (there's usually no need to + /// pass it explicitly as it defaults to `#function`). + /// - line: The line link request originates from (there's usually no need to pass it + /// explicitly as it defaults to `#line`). /// /// - Returns: The newly created cancellation source. - public init(linkedWith sources: [CancellationSource]) { + public init( + linkedWith sources: [CancellationSource], + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { self.init() Task { await withTaskGroup(of: Void.self) { group in sources.forEach { source in - group.addTask { await source.addSource(self) } + group.addTask { + await source.addSource( + self, + file: file, function: function, line: line + ) + } } await group.waitForAll() } @@ -121,11 +185,26 @@ public actor CancellationSource { /// Initiating cancellation in any of the provided cancellation sources /// will ensure newly created cancellation source receive cancellation event. /// - /// - Parameter sources: The cancellation sources the newly created object will be linked to. + /// - Parameters: + /// - sources: The cancellation sources the newly created object will be linked to. + /// - file: The file link request originates from (there's usually no need to pass it + /// explicitly as it defaults to `#fileID`). + /// - function: The function link request originates from (there's usually no need to + /// pass it explicitly as it defaults to `#function`). + /// - line: The line link request originates from (there's usually no need to pass it + /// explicitly as it defaults to `#line`). /// /// - Returns: The newly created cancellation source. - public init(linkedWith sources: CancellationSource...) { - self.init(linkedWith: sources) + public init( + linkedWith sources: CancellationSource..., + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { + self.init( + linkedWith: sources, + file: file, function: function, line: line + ) } /// Creates a new cancellation source object @@ -151,9 +230,7 @@ public actor CancellationSource { Task { [weak self] in try await self?.cancel( afterNanoseconds: nanoseconds, - file: file, - function: function, - line: line + file: file, function: function, line: line ) } } @@ -195,15 +272,32 @@ public actor CancellationSource { /// Initiating cancellation in any of the provided cancellation sources /// will ensure newly created cancellation source receive cancellation event. /// - /// - Parameter sources: The cancellation sources the newly created object will be linked to. + /// - Parameters: + /// - sources: The cancellation sources the newly created object will be linked to. + /// - file: The file link request originates from (there's usually no need to pass it + /// explicitly as it defaults to `#fileID`). + /// - function: The function link request originates from (there's usually no need to + /// pass it explicitly as it defaults to `#function`). + /// - line: The line link request originates from (there's usually no need to pass it + /// explicitly as it defaults to `#line`). /// /// - Returns: The newly created cancellation source. - public convenience init(linkedWith sources: [CancellationSource]) { + public convenience init( + linkedWith sources: [CancellationSource], + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { self.init() Task { await withTaskGroup(of: Void.self) { group in sources.forEach { source in - group.addTask { await source.addSource(self) } + group.addTask { + await source.addSource( + self, + file: file, function: function, line: line + ) + } } await group.waitForAll() } @@ -215,11 +309,26 @@ public actor CancellationSource { /// Initiating cancellation in any of the provided cancellation sources /// will ensure newly created cancellation source receive cancellation event. /// - /// - Parameter sources: The cancellation sources the newly created object will be linked to. + /// - Parameters: + /// - sources: The cancellation sources the newly created object will be linked to. + /// - file: The file link request originates from (there's usually no need to pass it + /// explicitly as it defaults to `#fileID`). + /// - function: The function link request originates from (there's usually no need to + /// pass it explicitly as it defaults to `#function`). + /// - line: The line link request originates from (there's usually no need to pass it + /// explicitly as it defaults to `#line`). /// /// - Returns: The newly created cancellation source. - public convenience init(linkedWith sources: CancellationSource...) { - self.init(linkedWith: sources) + public convenience init( + linkedWith sources: CancellationSource..., + file: String = #fileID, + function: String = #function, + line: UInt = #line + ) { + self.init( + linkedWith: sources, + file: file, function: function, line: line + ) } /// Creates a new cancellation source object @@ -245,9 +354,7 @@ public actor CancellationSource { Task { [weak self] in try await self?.cancel( afterNanoseconds: nanoseconds, - file: file, - function: function, - line: line + file: file, function: function, line: line ) } } @@ -273,9 +380,16 @@ public actor CancellationSource { line: UInt = #line ) { Task { [weak self] in - await self?.add(task: task) + await self?.add( + task: task, + file: file, function: function, line: line + ) + let _ = await task.result - await self?.remove(task: task) + await self?.remove( + task: task, + file: file, function: function, line: line + ) } } @@ -295,7 +409,7 @@ public actor CancellationSource { function: String = #function, line: UInt = #line ) { - Task { await cancelAll() } + Task { await cancelAll(file: file, function: function, line: line) } } /// Trigger cancellation event after provided delay. @@ -321,7 +435,7 @@ public actor CancellationSource { line: UInt = #line ) async throws { try await Task.sleep(nanoseconds: nanoseconds) - await cancelAll() + await cancelAll(file: file, function: function, line: line) } #if swift(>=5.7) @@ -352,7 +466,7 @@ public actor CancellationSource { line: UInt = #line ) async throws { try await Task.sleep(until: deadline, clock: clock) - await cancelAll() + await cancelAll(file: file, function: function, line: line) } #endif } @@ -500,3 +614,19 @@ public extension Task { return task } } + +#if canImport(Logging) +import Logging + +extension CancellationSource { + /// Type specific metadata to attach to all log messages. + @usableFromInline + var metadata: Logger.Metadata { + return [ + "obj": "\(self)(\(Unmanaged.passUnretained(self).toOpaque()))", + "linked_sources": "\(linkedSources.count)", + "registered_tasks": "\(registeredTasks.count)", + ] + } +} +#endif diff --git a/Tests/AsyncObjectsTests/CancellationSourceTests.swift b/Tests/AsyncObjectsTests/CancellationSourceTests.swift index a5a7f4ff..ad99eae0 100644 --- a/Tests/AsyncObjectsTests/CancellationSourceTests.swift +++ b/Tests/AsyncObjectsTests/CancellationSourceTests.swift @@ -42,26 +42,27 @@ class CancellationSourceTests: XCTestCase { #endif func testTaskCancellationWithLinkedSource() async throws { - let parentSource = CancellationSource() - let source = CancellationSource(linkedWith: parentSource) + let pSource = CancellationSource() + let source = CancellationSource(linkedWith: pSource) + try await waitUntil(pSource, timeout: 3) { !$0.linkedSources.isEmpty } let task = Task { try await Task.sleep(seconds: 10) } source.register(task: task) try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } - parentSource.cancel() + pSource.cancel() try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } func testTaskCancellationWithMultipleLinkedSources() async throws { - let parentSource1 = CancellationSource() - let parentSource2 = CancellationSource() - let source = CancellationSource( - linkedWith: parentSource1, parentSource2 - ) + let pSource1 = CancellationSource() + let pSource2 = CancellationSource() + let source = CancellationSource(linkedWith: pSource1, pSource2) + try await waitUntil(pSource1, timeout: 3) { !$0.linkedSources.isEmpty } + try await waitUntil(pSource2, timeout: 3) { !$0.linkedSources.isEmpty } let task = Task { try await Task.sleep(seconds: 10) } source.register(task: task) try await waitUntil(source, timeout: 3) { !$0.registeredTasks.isEmpty } - parentSource1.cancel() + pSource1.cancel() try await waitUntil(source, timeout: 3) { $0.registeredTasks.isEmpty } XCTAssertTrue(task.isCancelled) } From 220e4b70081ba2ba5ed5c36043c76505bfe2909c Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Mon, 2 Jan 2023 17:10:08 +0530 Subject: [PATCH 19/22] wip: fix timeout tests fail --- .../NonThrowingFutureTests.swift | 8 ++++---- .../StandardLibraryTests.swift | 16 ++++++++++++++++ Tests/AsyncObjectsTests/TaskQueueTests.swift | 8 ++++---- .../ThrowingFutureTests.swift | 18 +++++++++--------- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift index ce15e6b2..fd8b9eae 100644 --- a/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/NonThrowingFutureTests.swift @@ -90,7 +90,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future3 = Future() let future = Future.all(future1, future3, future2) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, [1, 3, 2]) } @@ -107,7 +107,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future3 = Future() let future = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { let values = try await future.wait(forSeconds: 3) for (index, item) in values.enumerated() { switch item { @@ -131,7 +131,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future3 = Future() let future = Future.race(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 1) } @@ -149,7 +149,7 @@ class NonThrowingFutureCombiningTests: XCTestCase { let future3 = Future() let future = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 1) } diff --git a/Tests/AsyncObjectsTests/StandardLibraryTests.swift b/Tests/AsyncObjectsTests/StandardLibraryTests.swift index 761afa73..784716c4 100644 --- a/Tests/AsyncObjectsTests/StandardLibraryTests.swift +++ b/Tests/AsyncObjectsTests/StandardLibraryTests.swift @@ -169,4 +169,20 @@ class StandardLibraryTests: XCTestCase { task.cancel() await task.value } + + func testTaskGroupTaskStart() async throws { + await withTaskGroup(of: Void.self) { group in + let store = ArrayDataStore() + await group.addTaskAndStart { XCTAssertTrue(store.items.isEmpty) } + store.add(1) + } + } + + func testThrowingTaskGroupTaskStart() async throws { + await withThrowingTaskGroup(of: Void.self) { group in + let store = ArrayDataStore() + await group.addTaskAndStart { XCTAssertTrue(store.items.isEmpty) } + store.add(1) + } + } } diff --git a/Tests/AsyncObjectsTests/TaskQueueTests.swift b/Tests/AsyncObjectsTests/TaskQueueTests.swift index c8dc2aa8..349ee563 100644 --- a/Tests/AsyncObjectsTests/TaskQueueTests.swift +++ b/Tests/AsyncObjectsTests/TaskQueueTests.swift @@ -264,7 +264,7 @@ class TaskQueueBlockOperationTests: XCTestCase { func testMixedeCancellationWithoutBlocking() async throws { let queue = TaskQueue() await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { throw CancellationError() } + group.addTask { throw CancellationError() } group.addTask { try await queue.exec(flags: .block) { try await Task.sleep(seconds: 10) @@ -532,7 +532,7 @@ class TaskQueueMixedOperationTests: XCTestCase { Task.detached { await withTaskGroup(of: Void.self) { group in for _ in 0..<3 { - await group.addTaskAndStart { + group.addTask { await queue.addTaskAndStart { c.yield(1) try await Task.sleep(seconds: 1) @@ -591,7 +591,7 @@ class TaskQueueMixedOperationTests: XCTestCase { try await withThrowingTaskGroup(of: Void.self) { group in await withTaskGroup(of: Void.self) { g in for _ in 0..<3 { - await g.addTaskAndStart { + g.addTask { await queue.addTaskAndStart { try await Task.sleep(seconds: 1) } @@ -607,7 +607,7 @@ class TaskQueueMixedOperationTests: XCTestCase { try await waitUntil(queue, timeout: 5) { guard let (_, (_, flags)) = $0.queue.reversed().first - else { return false } + else { return $0.blocked } return flags.contains(.barrier) } group.addTask { diff --git a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift index dfd7df6e..01c226ea 100644 --- a/Tests/AsyncObjectsTests/ThrowingFutureTests.swift +++ b/Tests/AsyncObjectsTests/ThrowingFutureTests.swift @@ -127,7 +127,7 @@ class ThrowingFutureCombiningAllTests: XCTestCase { let future3 = Future() let future = Future.all(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, [1, 2, 3]) } @@ -144,7 +144,7 @@ class ThrowingFutureCombiningAllTests: XCTestCase { let future3 = Future() let future = Future.all(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { do { let _ = try await future.wait(forSeconds: 3) XCTFail("Future fulfillment did not fail") @@ -175,7 +175,7 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let future3 = Future() let future = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { let values = try await future.wait(forSeconds: 3) for (index, item) in values.enumerated() { switch item { @@ -199,7 +199,7 @@ class ThrowingFutureCombiningAllSettledTests: XCTestCase { let future3 = Future() let future = Future.allSettled(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { let values = try await future.wait(forSeconds: 3) for (index, item) in values.enumerated() { switch item { @@ -235,7 +235,7 @@ class ThrowingFutureRacingTests: XCTestCase { let future3 = Future() let future = Future.race(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 1) } @@ -253,7 +253,7 @@ class ThrowingFutureRacingTests: XCTestCase { let future3 = Future() let future = Future.race(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { do { let _ = try await future.wait(forSeconds: 3) XCTFail("Future fulfillment did not fail") @@ -277,7 +277,7 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future3 = Future() let future = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 1) } @@ -295,7 +295,7 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future3 = Future() let future = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { let value = try await future.wait(forSeconds: 3) XCTAssertEqual(value, 2) } @@ -313,7 +313,7 @@ class ThrowingFutureSelectAnyTests: XCTestCase { let future3 = Future() let future = Future.any(future1, future2, future3) try await withThrowingTaskGroup(of: Void.self) { group in - await group.addTaskAndStart { + group.addTask { do { let _ = try await future.wait(forSeconds: 3) XCTFail("Future fulfillment did not fail") From feece6cb28020fc43adf10b347fcfed489db7d12 Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Fri, 6 Jan 2023 20:49:20 +0530 Subject: [PATCH 20/22] wip: fix timeout tests fail --- .../AsyncObjects/AsyncCountdownEvent.swift | 11 ++--- .../AsyncCountdownEventTests.swift | 44 +++++++++---------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/Sources/AsyncObjects/AsyncCountdownEvent.swift b/Sources/AsyncObjects/AsyncCountdownEvent.swift index df2f1fcf..52a1d942 100644 --- a/Sources/AsyncObjects/AsyncCountdownEvent.swift +++ b/Sources/AsyncObjects/AsyncCountdownEvent.swift @@ -178,7 +178,9 @@ public actor AsyncCountdownEvent: AsyncObject, ContinuableCollectionActor, @inlinable internal func decrementCount( by number: UInt = 1, - file: String, function: String, line: UInt + file: String = #fileID, + function: String = #function, + line: UInt = #line ) { defer { resumeContinuations(file: file, function: function, line: line) @@ -352,12 +354,7 @@ public actor AsyncCountdownEvent: AsyncObject, ContinuableCollectionActor, function: String = #function, line: UInt = #line ) { - Task { - await decrementCount( - by: 1, - file: file, function: function, line: line - ) - } + self.signal(repeat: 1, file: file, function: function, line: line) } /// Registers multiple signals (decrements by provided count) with the countdown event. diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index 8b7f6000..4dea384b 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -13,7 +13,7 @@ class AsyncCountdownEventTests: XCTestCase { let event = AsyncCountdownEvent() event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } - event.signal(concurrent: 10) + await event.signal(concurrent: 10) try await event.wait(forSeconds: 5) } @@ -21,7 +21,7 @@ class AsyncCountdownEventTests: XCTestCase { let event = AsyncCountdownEvent() event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } - event.signal(concurrent: 15) + await event.signal(concurrent: 15) try await event.wait(forSeconds: 5) } @@ -29,7 +29,7 @@ class AsyncCountdownEventTests: XCTestCase { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } - event.signal(concurrent: 7) + await event.signal(concurrent: 7) try await event.wait(forSeconds: 5) } @@ -37,7 +37,7 @@ class AsyncCountdownEventTests: XCTestCase { let event = AsyncCountdownEvent(until: 3, initial: 2) event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 12 } - event.signal(concurrent: 9) + await event.signal(concurrent: 9) try await event.wait(forSeconds: 5) } @@ -55,7 +55,7 @@ class AsyncCountdownEventTests: XCTestCase { try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } event.reset(to: 2) try await waitUntil(event, timeout: 5) { $0.currentCount == 2 } - event.signal(concurrent: 2) + await event.signal(concurrent: 2) try await event.wait(forSeconds: 5) } @@ -67,9 +67,9 @@ class AsyncCountdownEventTests: XCTestCase { try await waitUntil(event, timeout: 5) { $0.currentCount == 6 } event.reset(to: 2) } - event.signal(concurrent: 4) + await event.signal(concurrent: 4) try await waitUntil(event, timeout: 10) { $0.currentCount == 2 } - event.signal(concurrent: 2) + await event.signal(concurrent: 2) try await event.wait(forSeconds: 5) } @@ -107,7 +107,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { let event = AsyncCountdownEvent() event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } - event.signal(concurrent: 9) + await event.signal(concurrent: 9) do { try await event.wait(forSeconds: 5) XCTFail("Unexpected task progression") @@ -120,7 +120,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } - event.signal(concurrent: 6) + await event.signal(concurrent: 6) do { try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") @@ -133,7 +133,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 13 } - event.signal(concurrent: 9) + await event.signal(concurrent: 9) do { try await event.wait(forSeconds: 3) XCTFail("Unexpected task progression") @@ -146,7 +146,7 @@ class AsyncCountdownEventTimeoutTests: XCTestCase { let event = AsyncCountdownEvent() event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } - event.signal(concurrent: 8) + Task.detached { await event.signal(concurrent: 8) } Task.detached { try await waitUntil(event, timeout: 5) { $0.currentCount <= 6 } event.reset(to: 6) @@ -176,7 +176,7 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let event = AsyncCountdownEvent() event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } - event.signal(concurrent: 9) + await event.signal(concurrent: 9) do { try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") @@ -195,7 +195,7 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let event = AsyncCountdownEvent(until: 3) event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } - event.signal(concurrent: 6) + await event.signal(concurrent: 6) do { try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") @@ -214,7 +214,7 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let event = AsyncCountdownEvent(until: 3, initial: 3) event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 13 } - event.signal(concurrent: 9) + await event.signal(concurrent: 9) do { try await event.wait(forSeconds: 3, clock: clock) XCTFail("Unexpected task progression") @@ -233,7 +233,7 @@ class AsyncCountdownEventClockTimeoutTests: XCTestCase { let event = AsyncCountdownEvent() event.increment(by: 10) try await waitUntil(event, timeout: 5) { $0.currentCount == 10 } - event.signal(concurrent: 8) + Task.detached { await event.signal(concurrent: 8) } Task.detached { try await waitUntil(event, timeout: 5) { $0.currentCount <= 6 } event.reset(to: 6) @@ -283,16 +283,12 @@ class AsyncCountdownEventCancellationTests: XCTestCase { fileprivate extension AsyncCountdownEvent { - nonisolated func signal(concurrent count: UInt) { - Task.detached { - try await withThrowingTaskGroup(of: Void.self) { group in - for _ in 0.. Date: Fri, 6 Jan 2023 22:43:45 +0530 Subject: [PATCH 21/22] wip: parallelize cocoapods tests accross multple runners --- .github/workflows/main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1d4c6169..93465b67 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -68,6 +68,11 @@ jobs: cocoapods-test: name: CocoaPods uses: SwiftyLab/ci/.github/workflows/cocoapods.yml@main + strategy: + matrix: + platforms: ['macos tvos', 'ios'] + with: + platforms: ${{ matrix.platforms }} xcode-test: name: Xcode From 8f9cae20fad58eb9ae3a86d9eaefab90a4aad415 Mon Sep 17 00:00:00 2001 From: soumyamahunt Date: Sat, 7 Jan 2023 12:18:29 +0530 Subject: [PATCH 22/22] wip: refactor countdown tests --- Sources/AsyncObjects/AsyncCountdownEvent.swift | 15 ++++----------- .../AsyncCountdownEventTests.swift | 3 +-- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/Sources/AsyncObjects/AsyncCountdownEvent.swift b/Sources/AsyncObjects/AsyncCountdownEvent.swift index 52a1d942..b864d369 100644 --- a/Sources/AsyncObjects/AsyncCountdownEvent.swift +++ b/Sources/AsyncObjects/AsyncCountdownEvent.swift @@ -71,7 +71,7 @@ public actor AsyncCountdownEvent: AsyncObject, ContinuableCollectionActor, /// Indicates whether countdown event current count is within ``limit``. /// /// Queued tasks are resumed from suspension when event is set and until current count exceeds limit. - public var isSet: Bool { currentCount >= 0 && currentCount <= limit } + public var isSet: Bool { currentCount <= limit } // MARK: Internal @@ -182,9 +182,7 @@ public actor AsyncCountdownEvent: AsyncObject, ContinuableCollectionActor, function: String = #function, line: UInt = #line ) { - defer { - resumeContinuations(file: file, function: function, line: line) - } + defer { resume(file: file, function: function, line: line) } guard currentCount > 0 else { log("Least count", file: file, function: function, line: line) @@ -205,9 +203,7 @@ public actor AsyncCountdownEvent: AsyncObject, ContinuableCollectionActor, /// - line: The line resume originates from (there's usually no need to pass it /// explicitly as it defaults to `#line`). @inlinable - internal func resumeContinuations( - file: String, function: String, line: UInt - ) { + internal func resume(file: String, function: String, line: UInt) { while !continuations.isEmpty && isSet { let (key, continuation) = continuations.removeFirst() resumeContinuation(continuation) @@ -249,10 +245,7 @@ public actor AsyncCountdownEvent: AsyncObject, ContinuableCollectionActor, to count: UInt?, file: String, function: String, line: UInt ) { - defer { - resumeContinuations(file: file, function: function, line: line) - } - + defer { resume(file: file, function: function, line: line) } let count = count ?? initialCount initialCount = count self.currentCount = count diff --git a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift index 4dea384b..8edff022 100644 --- a/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift +++ b/Tests/AsyncObjectsTests/AsyncCountdownEventTests.swift @@ -75,9 +75,8 @@ class AsyncCountdownEventTests: XCTestCase { func testDeinit() async throws { let event = AsyncCountdownEvent(until: 0, initial: 1) - let task = Task.detached { event.signal() } + event.signal() try await event.wait(forSeconds: 5) - await task.value self.addTeardownBlock { [weak event] in event.assertReleased() }