Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Require DSL to be (mostly) Sendable. #1130

Merged
merged 2 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions Sources/Nimble/DSL+Require.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public func requires(file: FileString = #file, line: UInt = #line, customError:
/// `require` will return the result of the expression if the matcher passes, and throw an error if not.
/// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown.
@discardableResult
public func require<T>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @escaping () async throws -> T?) -> AsyncRequirement<T> {
public func require<T: Sendable>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @escaping @Sendable () async throws -> T?) -> AsyncRequirement<T> {
return AsyncRequirement(
expression: AsyncExpression(
expression: expression,
Expand All @@ -137,7 +137,7 @@ public func require<T>(file: FileString = #file, line: UInt = #line, customError
/// `require` will return the result of the expression if the matcher passes, and throw an error if not.
/// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown.
@discardableResult
public func require<T>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: () -> (() async throws -> T)) -> AsyncRequirement<T> {
public func require<T: Sendable>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: () -> (@Sendable () async throws -> T)) -> AsyncRequirement<T> {
return AsyncRequirement(
expression: AsyncExpression(
expression: expression(),
Expand All @@ -151,7 +151,7 @@ public func require<T>(file: FileString = #file, line: UInt = #line, customError
/// `require` will return the result of the expression if the matcher passes, and throw an error if not.
/// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown.
@discardableResult
public func require<T>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: () -> (() async throws -> T?)) -> AsyncRequirement<T> {
public func require<T: Sendable>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: () -> (@Sendable () async throws -> T?)) -> AsyncRequirement<T> {
return AsyncRequirement(
expression: AsyncExpression(
expression: expression(),
Expand All @@ -167,7 +167,7 @@ public func require<T>(file: FileString = #file, line: UInt = #line, customError
///
/// This is provided to avoid confusion between `require -> SyncRequirement` and `require -> AsyncRequirement`.
@discardableResult
public func requirea<T>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @autoclosure @escaping () async throws -> T?) async -> AsyncRequirement<T> {
public func requirea<T: Sendable>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @autoclosure @escaping @Sendable () async throws -> T?) async -> AsyncRequirement<T> {
return AsyncRequirement(
expression: AsyncExpression(
expression: expression,
Expand All @@ -183,7 +183,7 @@ public func requirea<T>(file: FileString = #file, line: UInt = #line, customErro
///
/// This is provided to avoid confusion between `require -> SyncRequirement` and `require -> AsyncRequirement`
@discardableResult
public func requirea<T>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @autoclosure () -> (() async throws -> T)) async -> AsyncRequirement<T> {
public func requirea<T: Sendable>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () async throws -> T)) async -> AsyncRequirement<T> {
return AsyncRequirement(
expression: AsyncExpression(
expression: expression(),
Expand All @@ -199,7 +199,7 @@ public func requirea<T>(file: FileString = #file, line: UInt = #line, customErro
///
/// This is provided to avoid confusion between `require -> SyncRequirement` and `require -> AsyncRequirement`
@discardableResult
public func requirea<T>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @autoclosure () -> (() async throws -> T?)) async -> AsyncRequirement<T> {
public func requirea<T: Sendable>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () async throws -> T?)) async -> AsyncRequirement<T> {
return AsyncRequirement(
expression: AsyncExpression(
expression: expression(),
Expand Down Expand Up @@ -256,7 +256,7 @@ public func unwraps<T>(file: FileString = #file, line: UInt = #line, customError
/// `unwrap` will return the result of the expression if it is non-nil, and throw an error if the value is nil.
/// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown.
@discardableResult
public func unwrap<T>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @escaping () async throws -> T?) async throws -> T {
public func unwrap<T: Sendable>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @escaping @Sendable () async throws -> T?) async throws -> T {
try await requirea(file: file, line: line, customError: customError, try await expression()).toNot(beNil())
}

Expand All @@ -266,7 +266,7 @@ public func unwrap<T>(file: FileString = #file, line: UInt = #line, customError:
/// `unwrap` will return the result of the expression if it is non-nil, and throw an error if the value is nil.
/// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown.
@discardableResult
public func unwrap<T>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: () -> (() async throws -> T?)) async throws -> T {
public func unwrap<T: Sendable>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: () -> (@Sendable () async throws -> T?)) async throws -> T {
try await requirea(file: file, line: line, customError: customError, expression()).toNot(beNil())
}

Expand All @@ -276,7 +276,7 @@ public func unwrap<T>(file: FileString = #file, line: UInt = #line, customError:
/// `unwrapa` will return the result of the expression if it is non-nil, and throw an error if the value is nil.
/// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown.
@discardableResult
public func unwrapa<T>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @autoclosure @escaping () async throws -> T?) async throws -> T {
public func unwrapa<T: Sendable>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @autoclosure @escaping @Sendable () async throws -> T?) async throws -> T {
try await requirea(file: file, line: line, customError: customError, try await expression()).toNot(beNil())
}

Expand All @@ -286,6 +286,6 @@ public func unwrapa<T>(file: FileString = #file, line: UInt = #line, customError
/// `unwrapa` will return the result of the expression if it is non-nil, and throw an error if the value is nil.
/// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown.
@discardableResult
public func unwrapa<T>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @autoclosure () -> (() async throws -> T?)) async throws -> T {
public func unwrapa<T: Sendable>(file: FileString = #file, line: UInt = #line, customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () async throws -> T?)) async throws -> T {
try await requirea(file: file, line: line, customError: customError, expression()).toNot(beNil())
}
2 changes: 1 addition & 1 deletion Sources/Nimble/Matchers/PostNotification.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ private func _postNotifications<Out>(
let message = ExpectationMessage
.expectedTo("post notifications - but was called off the main thread.")
.appended(details: "postNotifications and postDistributedNotifications attempted to run their predicate off the main thread. This is a bug in Nimble.")
return PredicateResult(status: .fail, message: message)
return MatcherResult(status: .fail, message: message)
}

let collectorNotificationsExpression = Expression(
Expand Down
10 changes: 6 additions & 4 deletions Sources/Nimble/Polling+Require.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ extension SyncRequirement {
public func alwaysTo(_ matcher: Matcher<Value>, until: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil) throws -> Value {
return try toAlways(matcher, until: until, pollInterval: pollInterval, description: description)
}
}

extension SyncRequirement where Value: Sendable {
// MARK: - Async Polling with Synchronous Matchers
/// Tests the actual value using a matcher to match by checking continuously
/// at each pollInterval until the timeout is reached.
Expand Down Expand Up @@ -734,28 +736,28 @@ public func pollUnwraps<T>(file: FileString = #file, line: UInt = #line, _ expre
/// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error.
/// As you can tell, this is a much less verbose equivalent to `requirea(expression).toEventuallyNot(beNil())`
@discardableResult
public func pollUnwrap<T>(file: FileString = #file, line: UInt = #line, _ expression: @escaping () async throws -> T?) async throws -> T {
public func pollUnwrap<T: Sendable>(file: FileString = #file, line: UInt = #line, _ expression: @escaping @Sendable () async throws -> T?) async throws -> T {
try await requirea(file: file, line: line, try await expression()).toEventuallyNot(beNil())
}

/// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error.
/// As you can tell, this is a much less verbose equivalent to `requirea(expression).toEventuallyNot(beNil())`
@discardableResult
public func pollUnwrap<T>(file: FileString = #file, line: UInt = #line, _ expression: () -> (() async throws -> T?)) async throws -> T {
public func pollUnwrap<T: Sendable>(file: FileString = #file, line: UInt = #line, _ expression: () -> (@Sendable () async throws -> T?)) async throws -> T {
try await requirea(file: file, line: line, expression()).toEventuallyNot(beNil())
}

/// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error.
/// As you can tell, this is a much less verbose equivalent to `requirea(expression).toEventuallyNot(beNil())`
@discardableResult
public func pollUnwrapa<T>(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure @escaping () async throws -> T?) async throws -> T {
public func pollUnwrapa<T: Sendable>(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure @escaping @Sendable () async throws -> T?) async throws -> T {
try await requirea(file: file, line: line, try await expression()).toEventuallyNot(beNil())
}

/// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error.
/// As you can tell, this is a much less verbose equivalent to `requirea(expression).toEventuallyNot(beNil())`
@discardableResult
public func pollUnwrapa<T>(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure () -> (() async throws -> T?)) async throws -> T {
public func pollUnwrapa<T: Sendable>(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure () -> (@Sendable () async throws -> T?)) async throws -> T {
try await requirea(file: file, line: line, expression()).toEventuallyNot(beNil())
}

Expand Down
6 changes: 4 additions & 2 deletions Sources/Nimble/Requirement.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

public struct RequireError: Error, CustomNSError {
public struct RequireError: Error, CustomNSError, Sendable {
let message: String
let location: SourceLocation

Expand Down Expand Up @@ -115,7 +115,9 @@ public struct SyncRequirement<Value> {
public func notTo(_ matcher: Matcher<Value>, description: String? = nil) throws -> Value {
try toNot(matcher, description: description)
}
}

extension SyncRequirement where Value: Sendable {
// MARK: - AsyncMatchers
/// Tests the actual value using a matcher to match.
@discardableResult
Expand All @@ -140,7 +142,7 @@ public struct SyncRequirement<Value> {
}
}

public struct AsyncRequirement<Value> {
public struct AsyncRequirement<Value: Sendable>: Sendable {
public let expression: AsyncExpression<Value>

/// A custom error to throw.
Expand Down
Loading
Loading