Skip to content

Polling expectations (under Experimental spi) #1115

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
167 changes: 167 additions & 0 deletions Sources/Testing/Expectations/Expectation+Macro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -572,3 +572,170 @@ public macro require(
sourceLocation: SourceLocation = #_sourceLocation,
performing expression: @escaping @Sendable @convention(thin) () async throws -> Void
) -> ExitTest.Result = #externalMacro(module: "TestingMacros", type: "ExitTestRequireMacro")

// MARK: - Polling Expectations

/// Continuously check an expression until it matches the given PollingBehavior
///
/// - Parameters:
/// - until: The desired PollingBehavior to check for.
/// - timeout: How long to run poll the expression until stopping.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which the recorded expectations
/// and issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// Use this overload of `#expect()` when you wish to poll whether a value
/// changes as the result of activity in another task/queue/thread.
@_spi(Experimental)
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
@freestanding(expression) public macro expect(
until pollingBehavior: PollingBehavior,
timeout: Duration = .seconds(60),
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
expression: @Sendable () async throws -> Bool
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro")

/// Continuously check an expression until it matches the given PollingBehavior
///
/// - Parameters:
/// - until: The desired PollingBehavior to check for.
/// - throws: The error the expression should throw.
/// - timeout: How long to run poll the expression until stopping.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which the recorded expectations
/// and issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// Use this overload of `#expect()` when you wish to poll whether a value
/// changes as the result of activity in another task/queue/thread.
@_spi(Experimental)
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
@freestanding(expression) public macro expect<E>(
until pollingBehavior: PollingBehavior,
throws error: E,
timeout: Duration = .seconds(60),
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
expression: @Sendable () async throws -> Bool
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro")
where E: Error & Equatable

/// Continuously check an expression until it matches the given PollingBehavior
///
/// - Parameters:
/// - until: The desired PollingBehavior to check for.
/// - timeout: How long to run poll the expression until stopping.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which the recorded expectations
/// and issues should be attributed.
/// - expression: The expression to be evaluated.
/// - throws: A closure to confirm if the expression throws the expected error.
///
/// Use this overload of `#expect()` when you wish to poll whether a value
/// changes as the result of activity in another task/queue/thread.
@_spi(Experimental)
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
@freestanding(expression) public macro expect(
until pollingBehavior: PollingBehavior,
timeout: Duration = .seconds(60),
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
performing: @Sendable () async throws -> Bool,
throws errorMatcher: @Sendable (any Error) async throws -> Bool
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro")

/// Continuously check an expression until it matches the given PollingBehavior
///
/// - Parameters:
/// - until: The desired PollingBehavior to check for.
/// - timeout: How long to run poll the expression until stopping.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which the recorded expectations
/// and issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// Use this overload of `#require()` when you wish to poll whether a value
/// changes as the result of activity in another task/queue/thread.
@_spi(Experimental)
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
@freestanding(expression) public macro require(
until pollingBehavior: PollingBehavior,
timeout: Duration = .seconds(60),
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
expression: @Sendable () async throws -> Bool
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")

/// Continuously check an expression until it matches the given PollingBehavior
///
/// - Parameters:
/// - until: The desired PollingBehavior to check for.
/// - timeout: How long to run poll the expression until stopping.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which the recorded expectations
/// and issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// Use this overload of `#require()` when you wish to poll whether a value
/// changes as the result of activity in another task/queue/thread.
@_spi(Experimental)
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
@freestanding(expression) public macro require<R>(
until pollingBehavior: PollingBehavior,
timeout: Duration = .seconds(60),
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
expression: @Sendable () async throws -> R?
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")
where R: Sendable

/// Continuously check an expression until it matches the given PollingBehavior
///
/// - Parameters:
/// - until: The desired PollingBehavior to check for.
/// - throws: The error the expression should throw
/// - timeout: How long to run poll the expression until stopping.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which the recorded expectations
/// and issues should be attributed.
/// - expression: The expression to be evaluated.
///
/// Use this overload of `#require()` when you wish to poll whether a value
/// changes as the result of activity in another task/queue/thread.
@_spi(Experimental)
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
@freestanding(expression) public macro require<E>(
until pollingBehavior: PollingBehavior,
throws error: E,
timeout: Duration = .seconds(60),
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
expression: @Sendable () async throws -> Bool
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")
where E: Error & Equatable

/// Continuously check an expression until it matches the given PollingBehavior
///
/// - Parameters:
/// - until: The desired PollingBehavior to check for.
/// - timeout: How long to run poll the expression until stopping.
/// - comment: A comment describing the expectation.
/// - sourceLocation: The source location to which the recorded expectations
/// and issues should be attributed.
/// - expression: The expression to be evaluated.
/// - throws: A closure to confirm if the expression throws the expected error.
///
/// Use this overload of `#require()` when you wish to poll whether a value
/// changes as the result of activity in another task/queue/thread.
@_spi(Experimental)
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
@freestanding(expression) public macro require<E>(
until pollingBehavior: PollingBehavior,
timeout: Duration = .seconds(60),
_ comment: @autoclosure () -> Comment? = nil,
sourceLocation: SourceLocation = #_sourceLocation,
expression: @Sendable () async throws -> Bool,
throws errorMatcher: @Sendable (any Error) async throws -> Bool
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")
96 changes: 96 additions & 0 deletions Sources/Testing/Expectations/ExpectationChecking+Macro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,102 @@ public func __checkClosureCall<each T>(
}
#endif

// MARK: - Polling

@_spi(Experimental)
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
public func __checkClosureCall(
until behavior: PollingBehavior,
timeout: Duration = .seconds(60),
performing closure: @escaping @Sendable () async throws -> Bool,
expression: __Expression,
comments: @autoclosure () -> [Comment],
isRequired: Bool,
sourceLocation: SourceLocation
) async -> Result<Void, any Error> {
await callPolling(
behavior: behavior,
timeout: timeout,
closure: closure,
expression: expression,
comments: comments(),
isRequired: isRequired,
sourceLocation: sourceLocation
)
}

@_spi(Experimental)
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
public func __checkClosureCall<E>(
until behavior: PollingBehavior,
throws error: E,
timeout: Duration = .seconds(60),
performing closure: @escaping @Sendable () async throws -> Bool,
expression: __Expression,
comments: @autoclosure () -> [Comment],
isRequired: Bool,
sourceLocation: SourceLocation
) async -> Result<Void, any Error> where E: Error & Equatable {
await callPolling(
behavior: behavior,
throws: error,
timeout: timeout,
closure: closure,
expression: expression,
comments: comments(),
isRequired: isRequired,
sourceLocation: sourceLocation
)
}

@_spi(Experimental)
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
public func __checkClosureCall(
until behavior: PollingBehavior,
timeout: Duration = .seconds(60),
performing closure: @escaping @Sendable () async throws -> Bool,
throws errorMatcher: @escaping @Sendable (any Error) async throws -> Bool,
expression: __Expression,
comments: @autoclosure () -> [Comment],
isRequired: Bool,
sourceLocation: SourceLocation
) async -> Result<Void, any Error> {
await callPolling(
behavior: behavior,
timeout: timeout,
closure: closure,
errorMatcher: errorMatcher,
expression: expression,
comments: comments(),
isRequired: isRequired,
sourceLocation: sourceLocation
)
}

@_spi(Experimental)
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
public func __checkClosureCall<R>(
until behavior: PollingBehavior,
timeout: Duration = .seconds(60),
performing closure: @escaping @Sendable () async throws -> R,
throws errorMatcher: @escaping @Sendable (any Error) async throws -> Bool,
expression: __Expression,
comments: @autoclosure () -> [Comment],
isRequired: Bool,
sourceLocation: SourceLocation
) async -> Result<R, any Error> where R: Sendable {
await callPolling(
behavior: behavior,
timeout: timeout,
closure: closure,
errorMatcher: errorMatcher,
expression: expression,
comments: comments(),
isRequired: isRequired,
sourceLocation: sourceLocation
)
}

// MARK: -

/// Generate a description of an error that includes its type name if not
Expand Down
Loading