Skip to content
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
49 changes: 27 additions & 22 deletions Sources/Testing/Expectations/Expectation+Macro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,16 @@
///
/// If `expression` does not throw an error, or if it throws an error that is
/// not an instance of `errorType`, an ``Issue`` is recorded for the test that
/// is running in the current task.
/// is running in the current task. Any value returned by `expression` is
/// discarded.
///
/// If the thrown error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error),
/// use ``expect(throws:_:performing:)-1s3lx`` instead. If `expression` should
/// _never_ throw any error, use ``expect(throws:_:performing:)-jtjw`` instead.
@freestanding(expression) public macro expect<E>(
@freestanding(expression) public macro expect<E, R>(
throws errorType: E.Type,
_ comment: @autoclosure () -> Comment? = nil,
performing expression: () async throws -> Void
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where E: Error

/// Check that an expression never throws an error.
Expand All @@ -111,7 +112,8 @@
/// ```
///
/// If `expression` throws an error, an ``Issue`` is recorded for the test that
/// is running in the current task.
/// is running in the current task. Any value returned by `expression` is
/// discarded.
///
/// Test functions can be annotated with `throws` and can throw errors which are
/// then recorded as [issues](doc:Issues) when the test runs. If the intent is
Expand All @@ -124,10 +126,10 @@
/// ``expect(throws:_:performing:)-2j0od`` instead. If the thrown error need
/// only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error),
/// use ``expect(throws:_:performing:)-1s3lx`` instead.
@freestanding(expression) public macro expect(
@freestanding(expression) public macro expect<R>(
throws _: Never.Type,
_ comment: @autoclosure () -> Comment? = nil,
performing expression: () async throws -> Void
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro")

/// Check that an expression always throws an error of a given type, and throw
Expand Down Expand Up @@ -156,17 +158,17 @@
/// If `expression` does not throw an error, or if it throws an error that is
/// not an instance of `errorType`, an ``Issue`` is recorded for the test that
/// is running in the current task and an instance of ``ExpectationFailedError``
/// is thrown.
/// is thrown. Any value returned by `expression` is discarded.
///
/// If the thrown error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error),
/// use ``require(throws:_:performing:)-84jir`` instead.
///
/// If `expression` should _never_ throw, simply invoke the code without using
/// this macro. The test will then fail if an error is thrown.
@freestanding(expression) public macro require<E>(
@freestanding(expression) public macro require<E, R>(
throws errorType: E.Type,
_ comment: @autoclosure () -> Comment? = nil,
performing expression: () async throws -> Void
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "RequireMacro") where E: Error

/// Check that an expression never throws an error, and throw an error if it
Expand All @@ -179,10 +181,10 @@
/// - Throws: An instance of ``ExpectationFailedError`` if `expression` throws
/// any error. The error thrown by `expression` is not rethrown.
@available(*, deprecated, message: "try #require(throws: Never.self) is redundant. Invoke non-throwing test code directly instead.")
@freestanding(expression) public macro require(
@freestanding(expression) public macro require<R>(
throws _: Never.Type,
_ comment: @autoclosure () -> Comment? = nil,
performing expression: () async throws -> Void
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")

// MARK: - Matching instances of equatable errors
Expand All @@ -206,15 +208,15 @@
///
/// If `expression` does not throw an error, or if it throws an error that is
/// not equal to `error`, an ``Issue`` is recorded for the test that is running
/// in the current task.
/// in the current task. Any value returned by `expression` is discarded.
///
/// If the thrown error need only be an instance of a particular type, use
/// ``expect(throws:_:performing:)-2j0od`` instead. If `expression` should
/// _never_ throw any error, use ``expect(throws:_:performing:)-jtjw`` instead.
@freestanding(expression) public macro expect<E>(
@freestanding(expression) public macro expect<E, R>(
throws error: E,
_ comment: @autoclosure () -> Comment? = nil,
performing expression: () async throws -> Void
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where E: Error & Equatable

/// Check that an expression always throws a specific error, and throw an error
Expand All @@ -241,13 +243,14 @@
/// If `expression` does not throw an error, or if it throws an error that is
/// not equal to `error`, an ``Issue`` is recorded for the test that is running
/// in the current task and an instance of ``ExpectationFailedError`` is thrown.
/// Any value returned by `expression` is discarded.
///
/// If the thrown error need only be an instance of a particular type, use
/// ``require(throws:_:performing:)-8762f`` instead.
@freestanding(expression) public macro require<E>(
@freestanding(expression) public macro require<E, R>(
throws error: E,
_ comment: @autoclosure () -> Comment? = nil,
performing expression: () async throws -> Void
performing expression: () async throws -> R
) = #externalMacro(module: "TestingMacros", type: "RequireMacro") where E: Error & Equatable

// MARK: - Arbitrary error matching
Expand Down Expand Up @@ -276,16 +279,17 @@
/// If `expression` does not throw an error, if it throws an error that is
/// not matched by `errorMatcher`, or if `errorMatcher` throws an error
/// (including the error passed to it), an ``Issue`` is recorded for the test
/// that is running in the current task.
/// that is running in the current task. Any value returned by `expression` is
/// discarded.
///
/// If the thrown error need only be an instance of a particular type, use
/// ``expect(throws:_:performing:)-2j0od`` instead. If the thrown error need
/// only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error),
/// use ``expect(throws:_:performing:)-1s3lx`` instead. If an error should
/// _never_ be thrown, use ``expect(throws:_:performing:)-jtjw`` instead.
@freestanding(expression) public macro expect(
@freestanding(expression) public macro expect<R>(
_ comment: @autoclosure () -> Comment? = nil,
performing expression: () async throws -> Void,
performing expression: () async throws -> R,
throws errorMatcher: (any Error) async throws -> Bool
) = #externalMacro(module: "TestingMacros", type: "ExpectMacro")

Expand Down Expand Up @@ -318,7 +322,8 @@
/// not matched by `errorMatcher`, or if `errorMatcher` throws an error
/// (including the error passed to it), an ``Issue`` is recorded for the test
/// that is running in the current task and an instance of
/// ``ExpectationFailedError`` is thrown.
/// ``ExpectationFailedError`` is thrown. Any value returned by `expression` is
/// discarded.
///
/// If the thrown error need only be an instance of a particular type, use
/// ``require(throws:_:performing:)-8762f`` instead. If the thrown error need
Expand All @@ -327,8 +332,8 @@
///
/// If `expression` should _never_ throw, simply invoke the code without using
/// this macro. The test will then fail if an error is thrown.
@freestanding(expression) public macro require(
@freestanding(expression) public macro require<R>(
_ comment: @autoclosure () -> Comment? = nil,
performing expression: () async throws -> Void,
performing expression: () async throws -> R,
throws errorMatcher: (any Error) async throws -> Bool
) = #externalMacro(module: "TestingMacros", type: "RequireMacro")
42 changes: 26 additions & 16 deletions Sources/Testing/Expectations/ExpectationChecking+Macro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ public func __checkCast<V, T>(
/// `#require()` macros. Do not call it directly.
public func __checkClosureCall<E>(
throws errorType: E.Type,
performing expression: () throws -> Void,
performing expression: () throws -> some Any,
sourceCode: SourceCode,
comments: @autoclosure () -> [Comment],
isRequired: Bool,
Expand Down Expand Up @@ -521,7 +521,7 @@ public func __checkClosureCall<E>(
/// `#require()` macros. Do not call it directly.
public func __checkClosureCall<E>(
throws errorType: E.Type,
performing expression: () async throws -> Void,
performing expression: () async throws -> some Any,
sourceCode: SourceCode,
comments: @autoclosure () -> [Comment],
isRequired: Bool,
Expand Down Expand Up @@ -561,7 +561,7 @@ public func __checkClosureCall<E>(
/// `#require()` macros. Do not call it directly.
public func __checkClosureCall(
throws _: Never.Type,
performing expression: () throws -> Void,
performing expression: () throws -> some Any,
sourceCode: SourceCode,
comments: @autoclosure () -> [Comment],
isRequired: Bool,
Expand All @@ -570,7 +570,7 @@ public func __checkClosureCall(
var success = true
var mismatchExplanationValue: String? = nil
do {
try expression()
_ = try expression()
} catch {
success = false
mismatchExplanationValue = "an error was thrown when none was expected: \(_description(of: error))"
Expand All @@ -597,7 +597,7 @@ public func __checkClosureCall(
/// `#require()` macros. Do not call it directly.
public func __checkClosureCall(
throws _: Never.Type,
performing expression: () async throws -> Void,
performing expression: () async throws -> some Any,
sourceCode: SourceCode,
comments: @autoclosure () -> [Comment],
isRequired: Bool,
Expand All @@ -606,7 +606,7 @@ public func __checkClosureCall(
var success = true
var mismatchExplanationValue: String? = nil
do {
try await expression()
_ = try await expression()
} catch {
success = false
mismatchExplanationValue = "an error was thrown when none was expected: \(_description(of: error))"
Expand All @@ -633,7 +633,7 @@ public func __checkClosureCall(
/// `#require()` macros. Do not call it directly.
public func __checkClosureCall<E>(
throws error: E,
performing expression: () throws -> Void,
performing expression: () throws -> some Any,
sourceCode: SourceCode,
comments: @autoclosure () -> [Comment],
isRequired: Bool,
Expand All @@ -659,7 +659,7 @@ public func __checkClosureCall<E>(
/// `#require()` macros. Do not call it directly.
public func __checkClosureCall<E>(
throws error: E,
performing expression: () async throws -> Void,
performing expression: () async throws -> some Any,
sourceCode: SourceCode,
comments: @autoclosure () -> [Comment],
isRequired: Bool,
Expand All @@ -684,8 +684,8 @@ public func __checkClosureCall<E>(
///
/// - Warning: This function is used to implement the `#expect()` and
/// `#require()` macros. Do not call it directly.
public func __checkClosureCall(
performing expression: () throws -> Void,
public func __checkClosureCall<R>(
performing expression: () throws -> R,
throws errorMatcher: (any Error) throws -> Bool,
mismatchExplanation: ((any Error) -> String)? = nil,
sourceCode: SourceCode,
Expand All @@ -696,8 +696,13 @@ public func __checkClosureCall(
var errorMatches = false
var mismatchExplanationValue: String? = nil
do {
try expression()
mismatchExplanationValue = "an error was expected but none was thrown"
let result = try expression()

var explanation = "an error was expected but none was thrown"
if R.self != Void.self {
explanation += " and \"\(result)\" was returned"
}
mismatchExplanationValue = explanation
} catch {
do {
errorMatches = try errorMatcher(error)
Expand Down Expand Up @@ -726,8 +731,8 @@ public func __checkClosureCall(
///
/// - Warning: This function is used to implement the `#expect()` and
/// `#require()` macros. Do not call it directly.
public func __checkClosureCall(
performing expression: () async throws -> Void,
public func __checkClosureCall<R>(
performing expression: () async throws -> R,
throws errorMatcher: (any Error) async throws -> Bool,
mismatchExplanation: ((any Error) -> String)? = nil,
sourceCode: SourceCode,
Expand All @@ -738,8 +743,13 @@ public func __checkClosureCall(
var errorMatches = false
var mismatchExplanationValue: String? = nil
do {
try await expression()
mismatchExplanationValue = "an error was expected but none was thrown"
let result = try await expression()

var explanation = "an error was expected but none was thrown"
if R.self != Void.self {
explanation += " and \"\(result)\" was returned"
}
mismatchExplanationValue = explanation
} catch {
do {
errorMatches = try await errorMatcher(error)
Expand Down
Loading