Skip to content

Commit

Permalink
test: improve async tests correctness (#14)
Browse files Browse the repository at this point in the history
* test: improve async tests correctness

* wip: fix tests fail due to race conditions

* wip: fix test project build fail on older Swift versions

* wip: fix actor data race runtime warings

* wip: fix build fail on older Swift versions

* wip: fix build fail on older Swift versions

* wip: increase task timeout checks

* wip: improve test performance

* wip: remove `CFGetRetainCount` usage for non-darwin

* wip: fix build fail on older Swift version

* Revert "wip: fix build fail on older Swift version"

This reverts commit af058e9.

* wip: fix build fail on older Swift version

* wip: improve test timeouts

* wip: fix deinit and future tests

* wip: fix test timeouts and deinit tests

* wip: fix timeout tests fail

* wip: fix timeout tests fail

* wip: add logging to `CancellationSource`

* wip: fix timeout tests fail

* wip: fix timeout tests fail

* wip: parallelize cocoapods tests accross multple runners

* wip: refactor countdown tests
  • Loading branch information
soumyamahunt committed Jan 7, 2023
1 parent 3ae2525 commit 19405e3
Show file tree
Hide file tree
Showing 26 changed files with 1,691 additions and 2,646 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 12 additions & 12 deletions AsyncObjects.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -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 */; };
Expand Down Expand Up @@ -115,7 +115,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
64070662BE047028E995157C /* AsyncObjects.docc */ = {isa = PBXFileReference; includeInIndex = 1; path = AsyncObjects.docc; sourceTree = "<group>"; };
B2A9446673C71002FDE80C4B /* AsyncObjects.docc */ = {isa = PBXFileReference; includeInIndex = 1; path = AsyncObjects.docc; sourceTree = "<group>"; };
OBJ_100 /* OrderedSet+Partial SetAlgebra+Operations.swift */ = {isa = PBXFileReference; path = "OrderedSet+Partial SetAlgebra+Operations.swift"; sourceTree = "<group>"; };
OBJ_101 /* OrderedSet+Partial SetAlgebra+Predicates.swift */ = {isa = PBXFileReference; path = "OrderedSet+Partial SetAlgebra+Predicates.swift"; sourceTree = "<group>"; };
OBJ_102 /* OrderedSet+RandomAccessCollection.swift */ = {isa = PBXFileReference; path = "OrderedSet+RandomAccessCollection.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -162,7 +162,7 @@
OBJ_49 /* TaskQueueTests.swift */ = {isa = PBXFileReference; path = TaskQueueTests.swift; sourceTree = "<group>"; };
OBJ_50 /* ThrowingFutureTests.swift */ = {isa = PBXFileReference; path = ThrowingFutureTests.swift; sourceTree = "<group>"; };
OBJ_51 /* TrackedContinuationTests.swift */ = {isa = PBXFileReference; path = TrackedContinuationTests.swift; sourceTree = "<group>"; };
OBJ_52 /* XCTestCase.swift */ = {isa = PBXFileReference; path = XCTestCase.swift; sourceTree = "<group>"; };
OBJ_52 /* XCAsyncTestCase.swift */ = {isa = PBXFileReference; path = XCAsyncTestCase.swift; sourceTree = "<group>"; };
OBJ_59 /* _HashTable+Bucket.swift */ = {isa = PBXFileReference; path = "_HashTable+Bucket.swift"; sourceTree = "<group>"; };
OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
OBJ_60 /* _HashTable+BucketIterator.swift */ = {isa = PBXFileReference; path = "_HashTable+BucketIterator.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */ = {
Expand All @@ -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 */ = {
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
26 changes: 8 additions & 18 deletions Sources/AsyncObjects/AsyncCountdownEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -178,11 +178,11 @@ 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)
}
defer { resume(file: file, function: function, line: line) }

guard currentCount > 0 else {
log("Least count", file: file, function: function, line: line)
Expand All @@ -203,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)
Expand Down Expand Up @@ -247,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
Expand Down Expand Up @@ -352,12 +347,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.
Expand Down
6 changes: 5 additions & 1 deletion Sources/AsyncObjects/AsyncSemaphore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 11 additions & 7 deletions Sources/AsyncObjects/Base/AsyncObject+Clock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -230,27 +230,28 @@ public func waitForAny<C: Clock>(
/// 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<C>` if timed out.
@available(swift 5.7)
@available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *)
@Sendable
public func waitForTaskCompletion<C: Clock>(
public func waitForTaskCompletion<C: Clock, T: Sendable>(
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<Void, Never>.with { continuation in
group.addTask {
continuation.resume()
try await task()
return try await task()
}
}
group.addTask {
Expand All @@ -263,7 +264,10 @@ public func waitForTaskCompletion<C: Clock>(
throw TimeoutError<C>(until: deadline, tolerance: tolerance)
}
defer { group.cancelAll() }
try await group.next()
guard
let result = try await group.next()
else { throw CancellationError() }
return result
}
}

Expand Down
18 changes: 11 additions & 7 deletions Sources/AsyncObjects/Base/AsyncObject+Duration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: Sendable>(
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<Void, Never>.with { continuation in
group.addTask {
continuation.resume()
try await task()
return try await task()
}
}
group.addTask {
Expand All @@ -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
}
}

Expand Down

0 comments on commit 19405e3

Please sign in to comment.