Skip to content

Commit 1d9ea96

Browse files
committed
Preliminary implementation of polling expectations
1 parent 1932a1b commit 1d9ea96

File tree

6 files changed

+1128
-0
lines changed

6 files changed

+1128
-0
lines changed

Sources/Testing/Expectations/Expectation+Macro.swift

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,3 +572,170 @@ public macro require(
572572
sourceLocation: SourceLocation = #_sourceLocation,
573573
performing expression: @escaping @Sendable @convention(thin) () async throws -> Void
574574
) -> ExitTest.Result = #externalMacro(module: "TestingMacros", type: "ExitTestRequireMacro")
575+
576+
// MARK: - Polling Expectations
577+
578+
/// Continuously check an expression until it matches the given PollingBehavior
579+
///
580+
/// - Parameters:
581+
/// - until: The desired PollingBehavior to check for.
582+
/// - timeout: How long to run poll the expression until stopping.
583+
/// - comment: A comment describing the expectation.
584+
/// - sourceLocation: The source location to which the recorded expectations
585+
/// and issues should be attributed.
586+
/// - expression: The expression to be evaluated.
587+
///
588+
/// Use this overload of `#expect()` when you wish to poll whether a value
589+
/// changes as the result of activity in another task/queue/thread.
590+
@_spi(Experimental)
591+
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
592+
@freestanding(expression) public macro expect(
593+
until pollingBehavior: PollingBehavior,
594+
timeout: Duration = .seconds(60),
595+
_ comment: @autoclosure () -> Comment? = nil,
596+
sourceLocation: SourceLocation = #_sourceLocation,
597+
expression: @Sendable () async throws -> Bool
598+
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro")
599+
600+
/// Continuously check an expression until it matches the given PollingBehavior
601+
///
602+
/// - Parameters:
603+
/// - until: The desired PollingBehavior to check for.
604+
/// - throws: The error the expression should throw.
605+
/// - timeout: How long to run poll the expression until stopping.
606+
/// - comment: A comment describing the expectation.
607+
/// - sourceLocation: The source location to which the recorded expectations
608+
/// and issues should be attributed.
609+
/// - expression: The expression to be evaluated.
610+
///
611+
/// Use this overload of `#expect()` when you wish to poll whether a value
612+
/// changes as the result of activity in another task/queue/thread.
613+
@_spi(Experimental)
614+
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
615+
@freestanding(expression) public macro expect<E>(
616+
until pollingBehavior: PollingBehavior,
617+
throws error: E,
618+
timeout: Duration = .seconds(60),
619+
_ comment: @autoclosure () -> Comment? = nil,
620+
sourceLocation: SourceLocation = #_sourceLocation,
621+
expression: @Sendable () async throws -> Bool
622+
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro")
623+
where E: Error & Equatable
624+
625+
/// Continuously check an expression until it matches the given PollingBehavior
626+
///
627+
/// - Parameters:
628+
/// - until: The desired PollingBehavior to check for.
629+
/// - timeout: How long to run poll the expression until stopping.
630+
/// - comment: A comment describing the expectation.
631+
/// - sourceLocation: The source location to which the recorded expectations
632+
/// and issues should be attributed.
633+
/// - expression: The expression to be evaluated.
634+
/// - throws: A closure to confirm if the expression throws the expected error.
635+
///
636+
/// Use this overload of `#expect()` when you wish to poll whether a value
637+
/// changes as the result of activity in another task/queue/thread.
638+
@_spi(Experimental)
639+
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
640+
@freestanding(expression) public macro expect(
641+
until pollingBehavior: PollingBehavior,
642+
timeout: Duration = .seconds(60),
643+
_ comment: @autoclosure () -> Comment? = nil,
644+
sourceLocation: SourceLocation = #_sourceLocation,
645+
performing: @Sendable () async throws -> Bool,
646+
throws errorMatcher: @Sendable (any Error) async throws -> Bool
647+
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro")
648+
649+
/// Continuously check an expression until it matches the given PollingBehavior
650+
///
651+
/// - Parameters:
652+
/// - until: The desired PollingBehavior to check for.
653+
/// - timeout: How long to run poll the expression until stopping.
654+
/// - comment: A comment describing the expectation.
655+
/// - sourceLocation: The source location to which the recorded expectations
656+
/// and issues should be attributed.
657+
/// - expression: The expression to be evaluated.
658+
///
659+
/// Use this overload of `#require()` when you wish to poll whether a value
660+
/// changes as the result of activity in another task/queue/thread.
661+
@_spi(Experimental)
662+
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
663+
@freestanding(expression) public macro require(
664+
until pollingBehavior: PollingBehavior,
665+
timeout: Duration = .seconds(60),
666+
_ comment: @autoclosure () -> Comment? = nil,
667+
sourceLocation: SourceLocation = #_sourceLocation,
668+
expression: @Sendable () async throws -> Bool
669+
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")
670+
671+
/// Continuously check an expression until it matches the given PollingBehavior
672+
///
673+
/// - Parameters:
674+
/// - until: The desired PollingBehavior to check for.
675+
/// - timeout: How long to run poll the expression until stopping.
676+
/// - comment: A comment describing the expectation.
677+
/// - sourceLocation: The source location to which the recorded expectations
678+
/// and issues should be attributed.
679+
/// - expression: The expression to be evaluated.
680+
///
681+
/// Use this overload of `#require()` when you wish to poll whether a value
682+
/// changes as the result of activity in another task/queue/thread.
683+
@_spi(Experimental)
684+
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
685+
@freestanding(expression) public macro require<R>(
686+
until pollingBehavior: PollingBehavior,
687+
timeout: Duration = .seconds(60),
688+
_ comment: @autoclosure () -> Comment? = nil,
689+
sourceLocation: SourceLocation = #_sourceLocation,
690+
expression: @Sendable () async throws -> R?
691+
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")
692+
where R: Sendable
693+
694+
/// Continuously check an expression until it matches the given PollingBehavior
695+
///
696+
/// - Parameters:
697+
/// - until: The desired PollingBehavior to check for.
698+
/// - throws: The error the expression should throw
699+
/// - timeout: How long to run poll the expression until stopping.
700+
/// - comment: A comment describing the expectation.
701+
/// - sourceLocation: The source location to which the recorded expectations
702+
/// and issues should be attributed.
703+
/// - expression: The expression to be evaluated.
704+
///
705+
/// Use this overload of `#require()` when you wish to poll whether a value
706+
/// changes as the result of activity in another task/queue/thread.
707+
@_spi(Experimental)
708+
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
709+
@freestanding(expression) public macro require<E>(
710+
until pollingBehavior: PollingBehavior,
711+
throws error: E,
712+
timeout: Duration = .seconds(60),
713+
_ comment: @autoclosure () -> Comment? = nil,
714+
sourceLocation: SourceLocation = #_sourceLocation,
715+
expression: @Sendable () async throws -> Bool
716+
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")
717+
where E: Error & Equatable
718+
719+
/// Continuously check an expression until it matches the given PollingBehavior
720+
///
721+
/// - Parameters:
722+
/// - until: The desired PollingBehavior to check for.
723+
/// - timeout: How long to run poll the expression until stopping.
724+
/// - comment: A comment describing the expectation.
725+
/// - sourceLocation: The source location to which the recorded expectations
726+
/// and issues should be attributed.
727+
/// - expression: The expression to be evaluated.
728+
/// - throws: A closure to confirm if the expression throws the expected error.
729+
///
730+
/// Use this overload of `#require()` when you wish to poll whether a value
731+
/// changes as the result of activity in another task/queue/thread.
732+
@_spi(Experimental)
733+
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
734+
@freestanding(expression) public macro require<E>(
735+
until pollingBehavior: PollingBehavior,
736+
timeout: Duration = .seconds(60),
737+
_ comment: @autoclosure () -> Comment? = nil,
738+
sourceLocation: SourceLocation = #_sourceLocation,
739+
expression: @Sendable () async throws -> Bool,
740+
throws errorMatcher: @Sendable (any Error) async throws -> Bool
741+
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")

Sources/Testing/Expectations/ExpectationChecking+Macro.swift

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,102 @@ public func __checkClosureCall<each T>(
12011201
}
12021202
#endif
12031203

1204+
// MARK: - Polling
1205+
1206+
@_spi(Experimental)
1207+
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
1208+
public func __checkClosureCall(
1209+
until behavior: PollingBehavior,
1210+
timeout: Duration = .seconds(60),
1211+
performing closure: @escaping @Sendable () async throws -> Bool,
1212+
expression: __Expression,
1213+
comments: @autoclosure () -> [Comment],
1214+
isRequired: Bool,
1215+
sourceLocation: SourceLocation
1216+
) async -> Result<Void, any Error> {
1217+
await callPolling(
1218+
behavior: behavior,
1219+
timeout: timeout,
1220+
closure: closure,
1221+
expression: expression,
1222+
comments: comments(),
1223+
isRequired: isRequired,
1224+
sourceLocation: sourceLocation
1225+
)
1226+
}
1227+
1228+
@_spi(Experimental)
1229+
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
1230+
public func __checkClosureCall<E>(
1231+
until behavior: PollingBehavior,
1232+
throws error: E,
1233+
timeout: Duration = .seconds(60),
1234+
performing closure: @escaping @Sendable () async throws -> Bool,
1235+
expression: __Expression,
1236+
comments: @autoclosure () -> [Comment],
1237+
isRequired: Bool,
1238+
sourceLocation: SourceLocation
1239+
) async -> Result<Void, any Error> where E: Error & Equatable {
1240+
await callPolling(
1241+
behavior: behavior,
1242+
throws: error,
1243+
timeout: timeout,
1244+
closure: closure,
1245+
expression: expression,
1246+
comments: comments(),
1247+
isRequired: isRequired,
1248+
sourceLocation: sourceLocation
1249+
)
1250+
}
1251+
1252+
@_spi(Experimental)
1253+
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
1254+
public func __checkClosureCall(
1255+
until behavior: PollingBehavior,
1256+
timeout: Duration = .seconds(60),
1257+
performing closure: @escaping @Sendable () async throws -> Bool,
1258+
throws errorMatcher: @escaping @Sendable (any Error) async throws -> Bool,
1259+
expression: __Expression,
1260+
comments: @autoclosure () -> [Comment],
1261+
isRequired: Bool,
1262+
sourceLocation: SourceLocation
1263+
) async -> Result<Void, any Error> {
1264+
await callPolling(
1265+
behavior: behavior,
1266+
timeout: timeout,
1267+
closure: closure,
1268+
errorMatcher: errorMatcher,
1269+
expression: expression,
1270+
comments: comments(),
1271+
isRequired: isRequired,
1272+
sourceLocation: sourceLocation
1273+
)
1274+
}
1275+
1276+
@_spi(Experimental)
1277+
@available(macOS 13, iOS 17, watchOS 9, tvOS 17, visionOS 1, *)
1278+
public func __checkClosureCall<R>(
1279+
until behavior: PollingBehavior,
1280+
timeout: Duration = .seconds(60),
1281+
performing closure: @escaping @Sendable () async throws -> R,
1282+
throws errorMatcher: @escaping @Sendable (any Error) async throws -> Bool,
1283+
expression: __Expression,
1284+
comments: @autoclosure () -> [Comment],
1285+
isRequired: Bool,
1286+
sourceLocation: SourceLocation
1287+
) async -> Result<R, any Error> where R: Sendable {
1288+
await callPolling(
1289+
behavior: behavior,
1290+
timeout: timeout,
1291+
closure: closure,
1292+
errorMatcher: errorMatcher,
1293+
expression: expression,
1294+
comments: comments(),
1295+
isRequired: isRequired,
1296+
sourceLocation: sourceLocation
1297+
)
1298+
}
1299+
12041300
// MARK: -
12051301

12061302
/// Generate a description of an error that includes its type name if not

0 commit comments

Comments
 (0)