Skip to content

Commit

Permalink
The async variants of expect now require Sendable values (#1071)
Browse files Browse the repository at this point in the history
  • Loading branch information
younata committed Jul 29, 2023
1 parent af03b43 commit 9cf3e53
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 15 deletions.
6 changes: 3 additions & 3 deletions Sources/Nimble/AsyncExpression.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
private actor MemoizedClosure<T> {
private actor MemoizedClosure<T: Sendable> {
var closure: @Sendable () async throws -> T
var cache: T?

Expand All @@ -25,7 +25,7 @@ private actor MemoizedClosure<T> {

// Memoizes the given closure, only calling the passed
// closure once; even if repeat calls to the returned closure
private func memoizedClosure<T>(_ closure: @escaping @Sendable () async throws -> T) -> @Sendable (Bool) async throws -> T {
private func memoizedClosure<T: Sendable>(_ closure: @escaping @Sendable () async throws -> T) -> @Sendable (Bool) async throws -> T {
let memoized = MemoizedClosure(closure)
return { withoutCaching in
try await memoized.call(withoutCaching)
Expand All @@ -43,7 +43,7 @@ private func memoizedClosure<T>(_ closure: @escaping @Sendable () async throws -
///
/// This provides a common consumable API for matchers to utilize to allow
/// Nimble to change internals to how the captured closure is managed.
public struct AsyncExpression<Value>: Sendable {
public struct AsyncExpression<Value: Sendable>: Sendable {
internal let _expression: @Sendable (Bool) async throws -> Value?
internal let _withoutCaching: Bool
public let location: SourceLocation
Expand Down
12 changes: 6 additions & 6 deletions Sources/Nimble/DSL+AsyncAwait.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Dispatch
#endif

/// Make an ``AsyncExpectation`` on a given actual value. The value given is lazily evaluated.
public func expect<T>(file: FileString = #file, line: UInt = #line, _ expression: @escaping @Sendable () async throws -> T?) -> AsyncExpectation<T> {
public func expect<T: Sendable>(file: FileString = #file, line: UInt = #line, _ expression: @escaping @Sendable () async throws -> T?) -> AsyncExpectation<T> {
return AsyncExpectation(
expression: AsyncExpression(
expression: expression,
Expand All @@ -12,7 +12,7 @@ public func expect<T>(file: FileString = #file, line: UInt = #line, _ expression
}

/// Make an ``AsyncExpectation`` on a given actual value. The closure is lazily invoked.
public func expect<T>(file: FileString = #file, line: UInt = #line, _ expression: @Sendable () -> (@Sendable () async throws -> T)) -> AsyncExpectation<T> {
public func expect<T: Sendable>(file: FileString = #file, line: UInt = #line, _ expression: @Sendable () -> (@Sendable () async throws -> T)) -> AsyncExpectation<T> {
return AsyncExpectation(
expression: AsyncExpression(
expression: expression(),
Expand All @@ -21,7 +21,7 @@ public func expect<T>(file: FileString = #file, line: UInt = #line, _ expression
}

/// Make an ``AsyncExpectation`` on a given actual value. The closure is lazily invoked.
public func expect<T>(file: FileString = #file, line: UInt = #line, _ expression: @Sendable () -> (@Sendable () async throws -> T?)) -> AsyncExpectation<T> {
public func expect<T: Sendable>(file: FileString = #file, line: UInt = #line, _ expression: @Sendable () -> (@Sendable () async throws -> T?)) -> AsyncExpectation<T> {
return AsyncExpectation(
expression: AsyncExpression(
expression: expression(),
Expand All @@ -40,7 +40,7 @@ public func expect(file: FileString = #file, line: UInt = #line, _ expression: @

/// Make an ``AsyncExpectation`` on a given actual value. The value given is lazily evaluated.
/// This is provided to avoid confusion between `expect -> SyncExpectation` and `expect -> AsyncExpectation`.
public func expecta<T>(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure @escaping @Sendable () async throws -> T?) async -> AsyncExpectation<T> {
public func expecta<T: Sendable>(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure @escaping @Sendable () async throws -> T?) async -> AsyncExpectation<T> {
return AsyncExpectation(
expression: AsyncExpression(
expression: expression,
Expand All @@ -50,7 +50,7 @@ public func expecta<T>(file: FileString = #file, line: UInt = #line, _ expressio

/// Make an ``AsyncExpectation`` on a given actual value. The closure is lazily invoked.
/// This is provided to avoid confusion between `expect -> SyncExpectation` and `expect -> AsyncExpectation`
public func expecta<T>(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> T)) async -> AsyncExpectation<T> {
public func expecta<T: Sendable>(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> T)) async -> AsyncExpectation<T> {
return AsyncExpectation(
expression: AsyncExpression(
expression: expression(),
Expand All @@ -60,7 +60,7 @@ public func expecta<T>(file: FileString = #file, line: UInt = #line, _ expressio

/// Make an ``AsyncExpectation`` on a given actual value. The closure is lazily invoked.
/// This is provided to avoid confusion between `expect -> SyncExpectation` and `expect -> AsyncExpectation`
public func expecta<T>(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> T?)) async -> AsyncExpectation<T> {
public func expecta<T: Sendable>(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> T?)) async -> AsyncExpectation<T> {
return AsyncExpectation(
expression: AsyncExpression(
expression: expression(),
Expand Down
6 changes: 4 additions & 2 deletions Sources/Nimble/Expectation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ internal func execute<T>(_ expression: AsyncExpression<T>, _ style: ExpectationS
}
}

public enum ExpectationStatus: Equatable {
public enum ExpectationStatus: Equatable, Sendable {

/// No predicates have been performed.
case pending
Expand Down Expand Up @@ -208,7 +208,9 @@ public struct SyncExpectation<Value>: Expectation {
public func notTo(_ predicate: Predicate<Value>, description: String? = nil) -> Self {
toNot(predicate, description: description)
}
}

extension SyncExpectation where Value: Sendable {
// MARK: - AsyncPredicates
/// Tests the actual value using a matcher to match.
@discardableResult
Expand Down Expand Up @@ -237,7 +239,7 @@ public struct SyncExpectation<Value>: Expectation {
// - NMBExpectation for Objective-C interface
}

public struct AsyncExpectation<Value>: Expectation {
public struct AsyncExpectation<Value: Sendable>: Expectation, Sendable {
public let expression: AsyncExpression<Value>

/// The status of the test after predicates have been evaluated.
Expand Down
8 changes: 4 additions & 4 deletions Sources/Nimble/Matchers/AsyncPredicate.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
public protocol AsyncablePredicate<T> {
associatedtype T
func satisfies(_ expression: AsyncExpression<T>) async throws -> PredicateResult
public protocol AsyncablePredicate<Value> {
associatedtype Value: Sendable
func satisfies(_ expression: AsyncExpression<Value>) async throws -> PredicateResult
}

extension Predicate: AsyncablePredicate {
public func satisfies(_ expression: AsyncExpression<T>) async throws -> PredicateResult {
public func satisfies(_ expression: AsyncExpression<T>) async throws -> PredicateResult where T: Sendable {
try satisfies(await expression.toSynchronousExpression())
}
}
Expand Down

0 comments on commit 9cf3e53

Please sign in to comment.