From 2ac7d821c8786feccf6ff182422f900575cc4b42 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 24 Sep 2025 15:49:54 -0400 Subject: [PATCH 1/2] Adopt typed throws in the implementation of `#expect()`/`#require()`. This PR adopts typed throws (`throws(E)` instead of `throws`) in the `#expect()` and `#require()` macros' declarations and in their implementations. In particular, the return type of the implementation functions changes from `Result` to `Result` and all implementation functions that take a closure have been updated with an additional generic `E` parameter. `#expect(throws:)` and `#require(throws:)` take two generic error types, `EExpected` and `EActual`. The former is the error type specified by the test author, while the latter is the actual error type inferred from the body closure. In common usage, both will resolve to `any Error`. The deprecated `#expect {} throws: {}` and `#require {} throws: {}` variants are marked `@_unavailableInEmbedded` and continue to use `any Error`. --- Sources/Testing/ExitTests/ExitTest.swift | 2 +- .../Expectations/Expectation+Macro.swift | 165 +++++------ .../ExpectationChecking+Macro.swift | 263 +++++++++++------- .../Support/Additions/ResultAdditions.swift | 4 +- Sources/Testing/Testing.docc/Expectations.md | 8 +- .../testing-for-errors-in-swift-code.md | 8 +- Tests/TestingTests/IssueTests.swift | 26 ++ 7 files changed, 282 insertions(+), 194 deletions(-) diff --git a/Sources/Testing/ExitTests/ExitTest.swift b/Sources/Testing/ExitTests/ExitTest.swift index ef37c4bd9..2e61b63eb 100644 --- a/Sources/Testing/ExitTests/ExitTest.swift +++ b/Sources/Testing/ExitTests/ExitTest.swift @@ -466,7 +466,7 @@ func callExitTest( isRequired: Bool, isolation: isolated (any Actor)? = #isolation, sourceLocation: SourceLocation -) async -> Result { +) async -> Result { guard let configuration = Configuration.current ?? Configuration.all.first else { preconditionFailure("A test must be running on the current task to use #expect(processExitsWith:).") } diff --git a/Sources/Testing/Expectations/Expectation+Macro.swift b/Sources/Testing/Expectations/Expectation+Macro.swift index 17127d058..d99229056 100644 --- a/Sources/Testing/Expectations/Expectation+Macro.swift +++ b/Sources/Testing/Expectations/Expectation+Macro.swift @@ -163,8 +163,8 @@ public macro require( /// - Note: If you use this macro with a Swift compiler version lower than 6.1, /// it doesn't return a value. /// -/// If the thrown error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error), -/// use ``expect(throws:_:sourceLocation:performing:)-7du1h`` instead. +/// If you need to compare the thrown error to another instance of [`Error`](https://developer.apple.com/documentation/swift/error), +/// use ``expect(throws:_:sourceLocation:performing:)-3y3oo`` instead. /// /// ## Expressions that should never throw /// @@ -190,12 +190,12 @@ public macro require( @discardableResult @freestanding(expression) @_documentation(visibility: private) -public macro expect( - throws errorType: E.Type, +public macro expect( + throws errorType: EExpected.Type, _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () throws -> R -) -> E? = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where E: Error + performing expression: () throws(EActual) -> R +) -> EExpected? = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where EExpected: Error /// Check that an expression always throws an error of a given type. /// @@ -229,8 +229,8 @@ public macro expect( /// - Note: If you use this macro with a Swift compiler version lower than 6.1, /// it doesn't return a value. /// -/// If the thrown error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error), -/// use ``expect(throws:_:sourceLocation:performing:)-7du1h`` instead. +/// If you need to compare the thrown error to another instance of [`Error`](https://developer.apple.com/documentation/swift/error), +/// use ``expect(throws:_:sourceLocation:performing:)-3y3oo`` instead. /// /// ## Expressions that should never throw /// @@ -254,12 +254,12 @@ public macro expect( /// check that an error is _not_ thrown by it, do not use this macro. Instead, /// simply call the code in question and allow it to throw an error naturally. @discardableResult -@freestanding(expression) public macro expect( - throws errorType: E.Type, +@freestanding(expression) public macro expect( + throws errorType: EExpected.Type, _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () async throws -> R -) -> E? = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where E: Error + performing expression: () async throws(EActual) -> R +) -> EExpected? = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where EExpected: Error /// Check that an expression always throws an error of a given type, and throw /// an error if it does not. @@ -296,20 +296,20 @@ public macro expect( /// - Note: If you use this macro with a Swift compiler version lower than 6.1, /// it doesn't return a value. /// -/// If the thrown error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error), -/// use ``require(throws:_:sourceLocation:performing:)-4djuw`` instead. +/// If you need to compare the thrown error to another instance of [`Error`](https://developer.apple.com/documentation/swift/error), +/// use ``require(throws:_:sourceLocation:performing:)-5qhyv`` instead. /// /// If `expression` should _never_ throw, simply invoke the code without using /// this macro. The test will then fail if an error is thrown. @discardableResult @freestanding(expression) @_documentation(visibility: private) -public macro require( - throws errorType: E.Type, +public macro require( + throws errorType: EExpected.Type, _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () throws -> R -) -> E = #externalMacro(module: "TestingMacros", type: "RequireThrowsMacro") where E: Error + performing expression: () throws(EActual) -> R +) -> EExpected = #externalMacro(module: "TestingMacros", type: "RequireThrowsMacro") where EExpected: Error /// Check that an expression always throws an error of a given type, and throw /// an error if it does not. @@ -346,18 +346,18 @@ public macro require( /// - Note: If you use this macro with a Swift compiler version lower than 6.1, /// it doesn't return a value. /// -/// If the thrown error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error), -/// use ``require(throws:_:sourceLocation:performing:)-4djuw`` instead. +/// If you need to compare the thrown error to another instance of [`Error`](https://developer.apple.com/documentation/swift/error), +/// use ``require(throws:_:sourceLocation:performing:)-5qhyv`` instead. /// /// If `expression` should _never_ throw, simply invoke the code without using /// this macro. The test will then fail if an error is thrown. @discardableResult -@freestanding(expression) public macro require( - throws errorType: E.Type, +@freestanding(expression) public macro require( + throws errorType: EExpected.Type, _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () async throws -> R -) -> E = #externalMacro(module: "TestingMacros", type: "RequireThrowsMacro") where E: Error + performing expression: () async throws(EActual) -> R +) -> EExpected = #externalMacro(module: "TestingMacros", type: "RequireThrowsMacro") where EExpected: Error /// Check that an expression never throws an error, and throw an error if it /// does. @@ -372,11 +372,11 @@ public macro require( /// any error. The error thrown by `expression` is not rethrown. @freestanding(expression) @_documentation(visibility: private) -public macro require( +public macro require( throws _: Never.Type, _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () throws -> R + performing expression: () throws(E) -> R ) = #externalMacro(module: "TestingMacros", type: "RequireThrowsNeverMacro") /// Check that an expression never throws an error, and throw an error if it @@ -392,11 +392,11 @@ public macro require( /// any error. The error thrown by `expression` is not rethrown. @freestanding(expression) @_documentation(visibility: private) -public macro require( +public macro require( throws _: Never.Type, _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () async throws -> R + performing expression: () async throws(E) -> R ) = #externalMacro(module: "TestingMacros", type: "RequireThrowsNeverMacro") // MARK: - Matching instances of equatable errors @@ -432,16 +432,16 @@ public macro require( /// it doesn't return a value. /// /// If the thrown error need only be an instance of a particular type, use -/// ``expect(throws:_:sourceLocation:performing:)-1hfms`` instead. +/// ``expect(throws:_:sourceLocation:performing:)-7p3ic`` instead. @discardableResult @freestanding(expression) @_documentation(visibility: private) -public macro expect( - throws error: E, +public macro expect( + throws error: EExpected, _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () throws -> R -) -> E? = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where E: Error & Equatable + performing expression: () throws(EActual) -> R +) -> EExpected? = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where EExpected: Error & Equatable /// Check that an expression always throws a specific error. /// @@ -474,14 +474,14 @@ public macro expect( /// it doesn't return a value. /// /// If the thrown error need only be an instance of a particular type, use -/// ``expect(throws:_:sourceLocation:performing:)-1hfms`` instead. +/// ``expect(throws:_:sourceLocation:performing:)-7p3ic`` instead. @discardableResult -@freestanding(expression) public macro expect( - throws error: E, +@freestanding(expression) public macro expect( + throws error: EExpected, _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () async throws -> R -) -> E? = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where E: Error & Equatable + performing expression: () async throws(EActual) -> R +) -> EExpected? = #externalMacro(module: "TestingMacros", type: "ExpectMacro") where EExpected: Error & Equatable /// Check that an expression always throws a specific error, and throw an error /// if it does not. @@ -518,16 +518,16 @@ public macro expect( /// it doesn't return a value. /// /// If the thrown error need only be an instance of a particular type, use -/// ``require(throws:_:sourceLocation:performing:)-7n34r`` instead. +/// ``require(throws:_:sourceLocation:performing:)-1zgzw`` instead. @discardableResult @freestanding(expression) @_documentation(visibility: private) -public macro require( - throws error: E, +public macro require( + throws error: EExpected, _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () throws -> R -) -> E = #externalMacro(module: "TestingMacros", type: "RequireMacro") where E: Error & Equatable + performing expression: () throws(EActual) -> R +) -> EExpected = #externalMacro(module: "TestingMacros", type: "RequireMacro") where EExpected: Error & Equatable /// Check that an expression always throws a specific error, and throw an error /// if it does not. @@ -564,14 +564,14 @@ public macro require( /// it doesn't return a value. /// /// If the thrown error need only be an instance of a particular type, use -/// ``require(throws:_:sourceLocation:performing:)-7n34r`` instead. +/// ``require(throws:_:sourceLocation:performing:)-1zgzw`` instead. @discardableResult -@freestanding(expression) public macro require( - throws error: E, +@freestanding(expression) public macro require( + throws error: EExpected, _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () async throws -> R -) -> E = #externalMacro(module: "TestingMacros", type: "RequireMacro") where E: Error & Equatable + performing expression: () async throws(EActual) -> R +) -> EExpected = #externalMacro(module: "TestingMacros", type: "RequireMacro") where EExpected: Error & Equatable // MARK: - Arbitrary error matching @@ -608,9 +608,9 @@ public macro require( /// discarded. /// /// If the thrown error need only be an instance of a particular type, use -/// ``expect(throws:_:sourceLocation:performing:)-1hfms`` instead. If the thrown -/// error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error), -/// use ``expect(throws:_:sourceLocation:performing:)-7du1h`` instead. +/// ``expect(throws:_:sourceLocation:performing:)-7p3ic`` instead. If you need +/// to compare the thrown error to another instance of [`Error`](https://developer.apple.com/documentation/swift/error), +/// use ``expect(throws:_:sourceLocation:performing:)-3y3oo`` instead. /// /// @Metadata { /// @Available(Swift, introduced: 6.0) @@ -618,8 +618,8 @@ public macro require( /// } /// /// @DeprecationSummary { -/// Examine the result of ``expect(throws:_:sourceLocation:performing:)-7du1h`` -/// or ``expect(throws:_:sourceLocation:performing:)-1hfms`` instead: +/// Examine the result of ``expect(throws:_:sourceLocation:performing:)-3y3oo`` +/// or ``expect(throws:_:sourceLocation:performing:)-7p3ic`` instead: /// /// ```swift /// let error = #expect(throws: FoodTruckError.self) { @@ -628,6 +628,7 @@ public macro require( /// #expect(error?.napkinCount == 0) /// ``` /// } +@_unavailableInEmbedded @available(swift, deprecated: 100000.0, message: "Examine the result of '#expect(throws:)' instead.") @discardableResult @freestanding(expression) @@ -635,8 +636,8 @@ public macro require( public macro expect( _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () throws -> R, - throws errorMatcher: (any Error) throws -> Bool + performing expression: () throws(any Error) -> R, + throws errorMatcher: (any Error) throws(any Error) -> Bool ) -> (any Error)? = #externalMacro(module: "TestingMacros", type: "ExpectMacro") /// Check that an expression always throws an error matching some condition. @@ -672,9 +673,9 @@ public macro expect( /// discarded. /// /// If the thrown error need only be an instance of a particular type, use -/// ``expect(throws:_:sourceLocation:performing:)-1hfms`` instead. If the thrown -/// error need only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error), -/// use ``expect(throws:_:sourceLocation:performing:)-7du1h`` instead. +/// ``expect(throws:_:sourceLocation:performing:)-7p3ic`` instead. If you need +/// to compare the thrown error to another instance of [`Error`](https://developer.apple.com/documentation/swift/error), +/// use ``expect(throws:_:sourceLocation:performing:)-3y3oo`` instead. /// /// @Metadata { /// @Available(Swift, introduced: 6.0) @@ -682,8 +683,8 @@ public macro expect( /// } /// /// @DeprecationSummary { -/// Examine the result of ``expect(throws:_:sourceLocation:performing:)-7du1h`` -/// or ``expect(throws:_:sourceLocation:performing:)-1hfms`` instead: +/// Examine the result of ``expect(throws:_:sourceLocation:performing:)-3y3oo`` +/// or ``expect(throws:_:sourceLocation:performing:)-7p3ic`` instead: /// /// ```swift /// let error = #expect(throws: FoodTruckError.self) { @@ -692,13 +693,14 @@ public macro expect( /// #expect(error?.napkinCount == 0) /// ``` /// } +@_unavailableInEmbedded @available(swift, deprecated: 100000.0, message: "Examine the result of '#expect(throws:)' instead.") @discardableResult @freestanding(expression) public macro expect( _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () async throws -> R, - throws errorMatcher: (any Error) async throws -> Bool + performing expression: () async throws(any Error) -> R, + throws errorMatcher: (any Error) async throws(any Error) -> Bool ) -> (any Error)? = #externalMacro(module: "TestingMacros", type: "ExpectMacro") /// Check that an expression always throws an error matching some condition, and @@ -738,9 +740,10 @@ public macro expect( /// discarded. /// /// If the thrown error need only be an instance of a particular type, use -/// ``require(throws:_:sourceLocation:performing:)-7n34r`` instead. If the thrown error need -/// only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error), -/// use ``require(throws:_:sourceLocation:performing:)-4djuw`` instead. +/// ``require(throws:_:sourceLocation:performing:)-1zgzw`` instead. If you need +/// to compare the thrown error to another instance of [`Error`](https://developer.apple.com/documentation/swift/error), +/// use ``require(throws:_:sourceLocation:performing:)-5qhyv`` instead. + /// /// If `expression` should _never_ throw, simply invoke the code without using /// this macro. The test will then fail if an error is thrown. @@ -751,8 +754,8 @@ public macro expect( /// } /// /// @DeprecationSummary { -/// Examine the result of ``expect(throws:_:sourceLocation:performing:)-7du1h`` -/// or ``expect(throws:_:sourceLocation:performing:)-1hfms`` instead: +/// Examine the result of ``require(throws:_:sourceLocation:performing:)-5qhyv`` +/// or ``require(throws:_:sourceLocation:performing:)-1zgzw`` instead: /// /// ```swift /// let error = try #require(throws: FoodTruckError.self) { @@ -761,6 +764,7 @@ public macro expect( /// #expect(error.napkinCount == 0) /// ``` /// } +@_unavailableInEmbedded @available(swift, deprecated: 100000.0, message: "Examine the result of '#require(throws:)' instead.") @discardableResult @freestanding(expression) @@ -768,8 +772,8 @@ public macro expect( public macro require( _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () throws -> R, - throws errorMatcher: (any Error) throws -> Bool + performing expression: () throws(any Error) -> R, + throws errorMatcher: (any Error) throws(any Error) -> Bool ) -> any Error = #externalMacro(module: "TestingMacros", type: "RequireMacro") /// Check that an expression always throws an error matching some condition, and @@ -809,9 +813,9 @@ public macro require( /// discarded. /// /// If the thrown error need only be an instance of a particular type, use -/// ``require(throws:_:sourceLocation:performing:)-7n34r`` instead. If the thrown error need -/// only equal another instance of [`Error`](https://developer.apple.com/documentation/swift/error), -/// use ``require(throws:_:sourceLocation:performing:)-4djuw`` instead. +/// ``require(throws:_:sourceLocation:performing:)-1zgzw`` instead. If you need +/// to compare the thrown error to another instance of [`Error`](https://developer.apple.com/documentation/swift/error), +/// use ``require(throws:_:sourceLocation:performing:)-5qhyv`` instead. /// /// If `expression` should _never_ throw, simply invoke the code without using /// this macro. The test will then fail if an error is thrown. @@ -822,8 +826,8 @@ public macro require( /// } /// /// @DeprecationSummary { -/// Examine the result of ``expect(throws:_:sourceLocation:performing:)-7du1h`` -/// or ``expect(throws:_:sourceLocation:performing:)-1hfms`` instead: +/// Examine the result of ``require(throws:_:sourceLocation:performing:)-5qhyv`` +/// or ``require(throws:_:sourceLocation:performing:)-1zgzw`` instead: /// /// ```swift /// let error = try #require(throws: FoodTruckError.self) { @@ -832,13 +836,14 @@ public macro require( /// #expect(error.napkinCount == 0) /// ``` /// } +@_unavailableInEmbedded @available(swift, deprecated: 100000.0, message: "Examine the result of '#require(throws:)' instead.") @discardableResult @freestanding(expression) public macro require( _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: () async throws -> R, - throws errorMatcher: (any Error) async throws -> Bool + performing expression: () async throws(any Error) -> R, + throws errorMatcher: (any Error) async throws(any Error) -> Bool ) -> any Error = #externalMacro(module: "TestingMacros", type: "RequireMacro") // MARK: - Exit tests @@ -879,12 +884,12 @@ public macro require( #if SWT_NO_EXIT_TESTS @available(*, unavailable, message: "Exit tests are not available on this platform.") #endif -public macro expect( +public macro expect( processExitsWith expectedExitCondition: ExitTest.Condition, observing observedValues: [any PartialKeyPath & Sendable] = [], _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: @escaping @Sendable () async throws -> Void + performing expression: @escaping @Sendable () async throws(E) -> Void ) -> ExitTest.Result? = #externalMacro(module: "TestingMacros", type: "ExitTestExpectMacro") /// Check that an expression causes the process to terminate in a given fashion @@ -926,12 +931,12 @@ public macro expect( #if SWT_NO_EXIT_TESTS @available(*, unavailable, message: "Exit tests are not available on this platform.") #endif -public macro require( +public macro require( processExitsWith expectedExitCondition: ExitTest.Condition, observing observedValues: [any PartialKeyPath & Sendable] = [], _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: @escaping @Sendable () async throws -> Void + performing expression: @escaping @Sendable () async throws(E) -> Void ) -> ExitTest.Result = #externalMacro(module: "TestingMacros", type: "ExitTestRequireMacro") /// Capture a sendable and codable value to pass to an exit test. diff --git a/Sources/Testing/Expectations/ExpectationChecking+Macro.swift b/Sources/Testing/Expectations/ExpectationChecking+Macro.swift index b47dbd910..4582eb7cb 100644 --- a/Sources/Testing/Expectations/ExpectationChecking+Macro.swift +++ b/Sources/Testing/Expectations/ExpectationChecking+Macro.swift @@ -27,9 +27,9 @@ /// failure. /// - sourceLocation: The source location of the expectation. /// -/// - Returns: A `Result`. If `condition` is `true`, the result -/// is `.success`. If `condition` is `false`, the result is an instance of -/// ``ExpectationFailedError`` describing the failure. +/// - Returns: A `Result`. If `condition` is +/// `true`, the result is `.success`. If `condition` is `false`, the result is +/// an instance of ``ExpectationFailedError`` describing the failure. /// /// If the condition evaluates to `false`, an ``Issue`` is recorded for the test /// that is running in the current task. @@ -68,7 +68,7 @@ public func __checkValue( comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result { +) -> Result { // If the expression being evaluated is a negation (!x instead of x), flip // the condition here so that we evaluate it in the correct sense. We loop // in case of multiple prefix operators (!!(a == b), for example.) @@ -173,7 +173,7 @@ private func _callBinaryOperator( comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result { +) -> Result { let (condition, rhs) = _callBinaryOperator(lhs, op, rhs) return __checkValue( condition, @@ -198,13 +198,13 @@ private func _callBinaryOperator( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkFunctionCall( - _ lhs: T, calling functionCall: (T, repeat each U) throws -> Bool, _ arguments: repeat each U, +public func __checkFunctionCall( + _ lhs: T, calling functionCall: (T, repeat each U) throws(E) -> Bool, _ arguments: repeat each U, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let condition = try functionCall(lhs, repeat each arguments) return __checkValue( condition, @@ -226,13 +226,13 @@ public func __checkFunctionCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkFunctionCall( - _ lhs: T, calling functionCall: (T, Arg0) throws -> Bool, _ argument0: Arg0, +public func __checkFunctionCall( + _ lhs: T, calling functionCall: (T, Arg0) throws(E) -> Bool, _ argument0: Arg0, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let condition = try functionCall(lhs, argument0) return __checkValue( condition, @@ -253,13 +253,13 @@ public func __checkFunctionCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkFunctionCall( - _ lhs: T, calling functionCall: (T, Arg0, Arg1) throws -> Bool, _ argument0: Arg0, _ argument1: Arg1, +public func __checkFunctionCall( + _ lhs: T, calling functionCall: (T, Arg0, Arg1) throws(E) -> Bool, _ argument0: Arg0, _ argument1: Arg1, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let condition = try functionCall(lhs, argument0, argument1) return __checkValue( condition, @@ -280,13 +280,13 @@ public func __checkFunctionCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkFunctionCall( - _ lhs: T, calling functionCall: (T, Arg0, Arg1, Arg2) throws -> Bool, _ argument0: Arg0, _ argument1: Arg1, _ argument2: Arg2, +public func __checkFunctionCall( + _ lhs: T, calling functionCall: (T, Arg0, Arg1, Arg2) throws(E) -> Bool, _ argument0: Arg0, _ argument1: Arg1, _ argument2: Arg2, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let condition = try functionCall(lhs, argument0, argument1, argument2) return __checkValue( condition, @@ -307,13 +307,13 @@ public func __checkFunctionCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkFunctionCall( - _ lhs: T, calling functionCall: (T, Arg0, Arg1, Arg2, Arg3) throws -> Bool, _ argument0: Arg0, _ argument1: Arg1, _ argument2: Arg2, _ argument3: Arg3, +public func __checkFunctionCall( + _ lhs: T, calling functionCall: (T, Arg0, Arg1, Arg2, Arg3) throws(E) -> Bool, _ argument0: Arg0, _ argument1: Arg1, _ argument2: Arg2, _ argument3: Arg3, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let condition = try functionCall(lhs, argument0, argument1, argument2, argument3) return __checkValue( condition, @@ -337,13 +337,13 @@ public func __checkFunctionCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkInoutFunctionCall( - _ lhs: T, calling functionCall: (T, inout /*repeat each*/ U) throws -> Bool, _ arguments: inout /*repeat each*/ U, +public func __checkInoutFunctionCall( + _ lhs: T, calling functionCall: (T, inout /*repeat each*/ U) throws(E) -> Bool, _ arguments: inout /*repeat each*/ U, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let condition = try functionCall(lhs, /*repeat each*/ &arguments) return __checkValue( condition, @@ -367,13 +367,13 @@ public func __checkInoutFunctionCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkFunctionCall( - _ lhs: T, calling functionCall: (T, repeat each U) throws -> R?, _ arguments: repeat each U, +public func __checkFunctionCall( + _ lhs: T, calling functionCall: (T, repeat each U) throws(E) -> R?, _ arguments: repeat each U, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let optionalValue = try functionCall(lhs, repeat each arguments) return __checkValue( optionalValue, @@ -395,13 +395,13 @@ public func __checkFunctionCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkFunctionCall( - _ lhs: T, calling functionCall: (T, Arg0) throws -> R?, _ argument0: Arg0, +public func __checkFunctionCall( + _ lhs: T, calling functionCall: (T, Arg0) throws(E) -> R?, _ argument0: Arg0, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let optionalValue = try functionCall(lhs, argument0) return __checkValue( optionalValue, @@ -422,13 +422,13 @@ public func __checkFunctionCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkFunctionCall( - _ lhs: T, calling functionCall: (T, Arg0, Arg1) throws -> R?, _ argument0: Arg0, _ argument1: Arg1, +public func __checkFunctionCall( + _ lhs: T, calling functionCall: (T, Arg0, Arg1) throws(E) -> R?, _ argument0: Arg0, _ argument1: Arg1, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let optionalValue = try functionCall(lhs, argument0, argument1) return __checkValue( optionalValue, @@ -449,13 +449,13 @@ public func __checkFunctionCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkFunctionCall( - _ lhs: T, calling functionCall: (T, Arg0, Arg1, Arg2) throws -> R?, _ argument0: Arg0, _ argument1: Arg1, _ argument2: Arg2, +public func __checkFunctionCall( + _ lhs: T, calling functionCall: (T, Arg0, Arg1, Arg2) throws(E) -> R?, _ argument0: Arg0, _ argument1: Arg1, _ argument2: Arg2, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let optionalValue = try functionCall(lhs, argument0, argument1, argument2) return __checkValue( optionalValue, @@ -476,13 +476,13 @@ public func __checkFunctionCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkFunctionCall( - _ lhs: T, calling functionCall: (T, Arg0, Arg1, Arg2, Arg3) throws -> R?, _ argument0: Arg0, _ argument1: Arg1, _ argument2: Arg2, _ argument3: Arg3, +public func __checkFunctionCall( + _ lhs: T, calling functionCall: (T, Arg0, Arg1, Arg2, Arg3) throws(E) -> R?, _ argument0: Arg0, _ argument1: Arg1, _ argument2: Arg2, _ argument3: Arg3, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let optionalValue = try functionCall(lhs, argument0, argument1, argument2, argument3) return __checkValue( optionalValue, @@ -507,13 +507,13 @@ public func __checkFunctionCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkInoutFunctionCall( - _ lhs: T, calling functionCall: (T, inout /*repeat each*/ U) throws -> R?, _ arguments: inout /*repeat each*/ U, +public func __checkInoutFunctionCall( + _ lhs: T, calling functionCall: (T, inout /*repeat each*/ U) throws(E) -> R?, _ arguments: inout /*repeat each*/ U, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) rethrows -> Result { +) rethrows -> Result { let optionalValue = try functionCall(lhs, /*repeat each*/ &arguments) return __checkValue( optionalValue, @@ -544,7 +544,7 @@ public func __checkPropertyAccess( comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result { +) -> Result { let condition = memberAccess(lhs) return __checkValue( condition, @@ -574,7 +574,7 @@ public func __checkPropertyAccess( comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result { +) -> Result { let optionalValue = memberAccess(lhs) return __checkValue( optionalValue, @@ -602,7 +602,7 @@ public func __checkPropertyAccess( comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result where T: BidirectionalCollection, T.Element: Equatable { +) -> Result where T: BidirectionalCollection, T.Element: Equatable { let (condition, rhs) = _callBinaryOperator(lhs, op, rhs) func difference() -> String? { guard let rhs else { @@ -649,7 +649,7 @@ public func __checkBinaryOperation( comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result { +) -> Result { let (condition, rhs) = _callBinaryOperator(lhs, op, rhs) return __checkValue( condition, @@ -677,7 +677,7 @@ public func __checkBinaryOperation( comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result where T: RangeExpression, U: RangeExpression { +) -> Result where T: RangeExpression, U: RangeExpression { let (condition, rhs) = _callBinaryOperator(lhs, op, rhs) return __checkValue( condition, @@ -704,7 +704,7 @@ public func __checkCast( comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result { +) -> Result { return __checkValue( value is T, expression: expression, @@ -737,7 +737,7 @@ public func __checkValue( comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result { +) -> Result { // The double-optional below is because capturingRuntimeValue() takes optional // values and interprets nil as "no value available". Rather, if optionalValue // is `nil`, we want to actually store `nil` as the expression's evaluated @@ -776,7 +776,7 @@ public func __checkValue( comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result { +) -> Result { let (optionalValue, rhs) = _callBinaryOperator(lhs, op, rhs) return __checkValue( optionalValue, @@ -802,7 +802,7 @@ public func __checkCast( comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result { +) -> Result { // NOTE: this call to __checkValue() does not go through the optional // bottleneck because we do not want to capture the nil value on failure (it // looks odd in test output.) @@ -829,14 +829,14 @@ public func __checkCast( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkClosureCall( - throws errorType: E.Type, - performing body: () throws -> some Any, +public func __checkClosureCall( + throws errorType: EExpected.Type, + performing body: () throws(EActual) -> some Any, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result where E: Error { +) -> Result where EExpected: Error { if errorType == Never.self { __checkClosureCall( throws: Never.self, @@ -847,15 +847,17 @@ public func __checkClosureCall( sourceLocation: sourceLocation ).map { _ in nil } } else { - __checkClosureCall( + _check( performing: body, - throws: { $0 is E }, + throws: { (actualError: EActual) throws(EActual) in + actualError is EExpected + }, mismatchExplanation: { "expected error of type \(errorType), but \(_description(of: $0)) was thrown instead" }, expression: expression, comments: comments(), isRequired: isRequired, sourceLocation: sourceLocation - ).map { $0 as? E } + ).map { $0 as? EExpected } } } @@ -867,15 +869,15 @@ public func __checkClosureCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkClosureCall( - throws errorType: E.Type, - performing body: () async throws -> sending some Any, +public func __checkClosureCall( + throws errorType: EExpected.Type, + performing body: () async throws(EActual) -> sending some Any, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, isolation: isolated (any Actor)? = #isolation, sourceLocation: SourceLocation -) async -> Result where E: Error { +) async -> Result where EExpected: Error { if errorType == Never.self { await __checkClosureCall( throws: Never.self, @@ -887,16 +889,18 @@ public func __checkClosureCall( sourceLocation: sourceLocation ).map { _ in nil } } else { - await __checkClosureCall( + await _check( performing: body, - throws: { $0 is E }, + throws: { (actualError: EActual) throws(EActual) in + actualError is EExpected + }, mismatchExplanation: { "expected error of type \(errorType), but \(_description(of: $0)) was thrown instead" }, expression: expression, comments: comments(), isRequired: isRequired, isolation: isolation, sourceLocation: sourceLocation - ).map { $0 as? E } + ).map { $0 as? EExpected } } } @@ -910,14 +914,14 @@ public func __checkClosureCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkClosureCall( +public func __checkClosureCall( throws _: Never.Type, - performing body: () throws -> some Any, + performing body: () throws(E) -> some Any, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result { +) -> Result { var success = true var mismatchExplanationValue: String? = nil do { @@ -946,15 +950,15 @@ public func __checkClosureCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkClosureCall( +public func __checkClosureCall( throws _: Never.Type, - performing body: () async throws -> sending some Any, + performing body: () async throws(E) -> sending some Any, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, isolation: isolated (any Actor)? = #isolation, sourceLocation: SourceLocation -) async -> Result { +) async -> Result { var success = true var mismatchExplanationValue: String? = nil do { @@ -983,23 +987,25 @@ public func __checkClosureCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkClosureCall( - throws error: E, - performing body: () throws -> some Any, +public func __checkClosureCall( + throws error: EExpected, + performing body: () throws(EActual) -> some Any, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, sourceLocation: SourceLocation -) -> Result where E: Error & Equatable { - __checkClosureCall( +) -> Result where EExpected: Error & Equatable { + _check( performing: body, - throws: { true == (($0 as? E) == error) }, + throws: { (actualError: EActual) throws(EActual) in + true == ((actualError as? EExpected) == error) + }, mismatchExplanation: { "expected error \(_description(of: error)), but \(_description(of: $0)) was thrown instead" }, expression: expression, comments: comments(), isRequired: isRequired, sourceLocation: sourceLocation - ).map { $0 as? E } + ).map { $0 as? EExpected } } /// Check that an expression always throws an error. @@ -1009,28 +1015,31 @@ public func __checkClosureCall( /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. -public func __checkClosureCall( - throws error: E, - performing body: () async throws -> sending some Any, +public func __checkClosureCall( + throws error: EExpected, + performing body: () async throws(EActual) -> sending some Any, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, isolation: isolated (any Actor)? = #isolation, sourceLocation: SourceLocation -) async -> Result where E: Error & Equatable { - await __checkClosureCall( +) async -> Result where EExpected: Error & Equatable { + await _check( performing: body, - throws: { true == (($0 as? E) == error) }, + throws: { (actualError: EActual) throws(EActual) in + true == ((actualError as? EExpected) == error) + }, mismatchExplanation: { "expected error \(_description(of: error)), but \(_description(of: $0)) was thrown instead" }, expression: expression, comments: comments(), isRequired: isRequired, isolation: isolation, sourceLocation: sourceLocation - ).map { $0 as? E } + ).map { $0 as? EExpected } } -// MARK: - Arbitrary error matching +#if !hasFeature(Embedded) +// MARK: - Arbitrary error matching (deprecated) /// Check that an expression always throws an error. /// @@ -1039,18 +1048,70 @@ public func __checkClosureCall( /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. public func __checkClosureCall( - performing body: () throws -> R, - throws errorMatcher: (any Error) throws -> Bool, + performing body: () throws(any Error) -> R, + throws errorMatcher: (any Error) throws(any Error) -> Bool, + mismatchExplanation: ((any Error) -> String)? = nil, + expression: __Expression, + comments: @autoclosure () -> [Comment], + isRequired: Bool, + sourceLocation: SourceLocation +) -> Result<(any Error)?, ExpectationFailedError> { + _check( + performing: body, + throws: errorMatcher, + mismatchExplanation: mismatchExplanation, + expression: expression, + comments: comments(), + isRequired: isRequired, + sourceLocation: sourceLocation + ) +} + +/// Check that an expression always throws an error. +/// +/// This overload is used for `await #expect { } throws: { }` invocations. +/// +/// - Warning: This function is used to implement the `#expect()` and +/// `#require()` macros. Do not call it directly. +public func __checkClosureCall( + performing body: () async throws(any Error) -> sending R, + throws errorMatcher: (any Error) async throws(any Error) -> Bool, mismatchExplanation: ((any Error) -> String)? = nil, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, + isolation: isolated (any Actor)? = #isolation, sourceLocation: SourceLocation -) -> Result<(any Error)?, any Error> { +) async -> Result<(any Error)?, ExpectationFailedError> { + await _check( + performing: body, + throws: errorMatcher, + mismatchExplanation: mismatchExplanation, + expression: expression, + comments: comments(), + isRequired: isRequired, + isolation: isolation, + sourceLocation: sourceLocation + ) +} +#endif + +// MARK: - Error matching (common implementation) + +/// The common implementation for `#expect(throws)` and `#require(throws:)`. +func _check( + performing body: () throws(E) -> R, + throws errorMatcher: (E) throws(E) -> Bool, + mismatchExplanation: ((E) -> String)? = nil, + expression: __Expression, + comments: @autoclosure () -> [Comment], + isRequired: Bool, + sourceLocation: SourceLocation +) -> Result { var errorMatches = false var mismatchExplanationValue: String? = nil var expression = expression - var caughtError: (any Error)? + var caughtError: E? do { let result = try body() @@ -1082,26 +1143,22 @@ public func __checkClosureCall( ).map { caughtError } } -/// Check that an expression always throws an error. -/// -/// This overload is used for `await #expect { } throws: { }` invocations. -/// -/// - Warning: This function is used to implement the `#expect()` and -/// `#require()` macros. Do not call it directly. -public func __checkClosureCall( - performing body: () async throws -> sending R, - throws errorMatcher: (any Error) async throws -> Bool, - mismatchExplanation: ((any Error) -> String)? = nil, +/// The common implementation for `await #expect(throws)` and +/// `await #require(throws:)`. +func _check( + performing body: () async throws(E) -> sending R, + throws errorMatcher: (E) async throws(E) -> Bool, + mismatchExplanation: ((E) -> String)? = nil, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, isolation: isolated (any Actor)? = #isolation, sourceLocation: SourceLocation -) async -> Result<(any Error)?, any Error> { +) async -> Result { var errorMatches = false var mismatchExplanationValue: String? = nil var expression = expression - var caughtError: (any Error)? + var caughtError: E? do { let result = try await body() @@ -1154,7 +1211,7 @@ public func __checkClosureCall( isRequired: Bool, isolation: isolated (any Actor)? = #isolation, sourceLocation: SourceLocation -) async -> Result { +) async -> Result { await callExitTest( identifiedBy: exitTestID, encodingCapturedValues: [], @@ -1186,7 +1243,7 @@ public func __checkClosureCall( isRequired: Bool, isolation: isolated (any Actor)? = #isolation, sourceLocation: SourceLocation -) async -> Result where repeat each T: Codable & Sendable { +) async -> Result where repeat each T: Codable & Sendable { await callExitTest( identifiedBy: exitTestID, encodingCapturedValues: Array(repeat each capturedValues), diff --git a/Sources/Testing/Support/Additions/ResultAdditions.swift b/Sources/Testing/Support/Additions/ResultAdditions.swift index 9a2e6ea5a..30f00ec15 100644 --- a/Sources/Testing/Support/Additions/ResultAdditions.swift +++ b/Sources/Testing/Support/Additions/ResultAdditions.swift @@ -19,7 +19,7 @@ extension Result { /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. - @inlinable public func __required() throws -> Success { + @inlinable public func __required() throws(Failure) -> Success { try get() } } @@ -48,7 +48,7 @@ extension Result { /// /// - Warning: This function is used to implement the `#expect()` and /// `#require()` macros. Do not call it directly. - @discardableResult public func __required() throws -> T where Success == T? { + @discardableResult public func __required() throws(any Error) -> T where Success == T? { guard let result = try get() else { throw APIMisuseError(description: "Could not unwrap 'nil' value of type Optional<\(T.self)>. Consider using #expect() instead of #require() here.") } diff --git a/Sources/Testing/Testing.docc/Expectations.md b/Sources/Testing/Testing.docc/Expectations.md index e55ff3a1e..b152becb9 100644 --- a/Sources/Testing/Testing.docc/Expectations.md +++ b/Sources/Testing/Testing.docc/Expectations.md @@ -65,11 +65,11 @@ the test when the code doesn't satisfy a requirement, use ### Checking that errors are thrown - -- ``expect(throws:_:sourceLocation:performing:)-1hfms`` -- ``expect(throws:_:sourceLocation:performing:)-7du1h`` +- ``expect(throws:_:sourceLocation:performing:)-7p3ic`` +- ``expect(throws:_:sourceLocation:performing:)-3y3oo`` - ``expect(_:sourceLocation:performing:throws:)`` -- ``require(throws:_:sourceLocation:performing:)-7n34r`` -- ``require(throws:_:sourceLocation:performing:)-4djuw`` +- ``require(throws:_:sourceLocation:performing:)-1zgzw`` +- ``require(throws:_:sourceLocation:performing:)-5qhyv`` - ``require(_:sourceLocation:performing:throws:)`` ### Checking how processes exit diff --git a/Sources/Testing/Testing.docc/testing-for-errors-in-swift-code.md b/Sources/Testing/Testing.docc/testing-for-errors-in-swift-code.md index 7053d0fa5..9904a5290 100644 --- a/Sources/Testing/Testing.docc/testing-for-errors-in-swift-code.md +++ b/Sources/Testing/Testing.docc/testing-for-errors-in-swift-code.md @@ -26,7 +26,7 @@ If the code throws an error, then your test fails. To check that the code under test throws a specific error, or to continue a longer test function after the code throws an error, pass that error as the -first argument of ``expect(throws:_:sourceLocation:performing:)-7du1h``, and +first argument of ``expect(throws:_:sourceLocation:performing:)-3y3oo``, and pass a closure that calls the code under test: ```swift @@ -48,8 +48,8 @@ running your test if the code doesn't throw the expected error. To check that the code under test throws an error of any type, pass `(any Error).self` as the first argument to either -``expect(throws:_:sourceLocation:performing:)-1hfms`` or -``require(throws:_:sourceLocation:performing:)-7n34r``: +``expect(throws:_:sourceLocation:performing:)-7p3ic`` or +``require(throws:_:sourceLocation:performing:)-1zgzw``: ```swift @Test func cannotAddToppingToPizzaBeforeStartOfList() { @@ -81,7 +81,7 @@ the error to `Never`: If the closure throws _any_ error, the testing library records an issue. If you need the test to stop when the code throws an error, include the code inline in the test function instead of wrapping it in a call to -``expect(throws:_:sourceLocation:performing:)-7du1h``. +``expect(throws:_:sourceLocation:performing:)-3y3oo``. ## Inspect an error thrown by your code diff --git a/Tests/TestingTests/IssueTests.swift b/Tests/TestingTests/IssueTests.swift index 6abd384fe..164fff3bc 100644 --- a/Tests/TestingTests/IssueTests.swift +++ b/Tests/TestingTests/IssueTests.swift @@ -511,12 +511,14 @@ final class IssueTests: XCTestCase { let randomNumber = Int.random(in: 0 ... .max) await Test { +#if !hasFeature(Embedded) #expect { asyncNotRequired() throw MyError() } throws: { $0 is MyError } +#endif #expect(throws: MyError.self) { asyncNotRequired() throw MyError() @@ -541,7 +543,11 @@ final class IssueTests: XCTestCase { func testErrorCheckingWithExpect_Mismatching() async throws { let expectationFailed = expectation(description: "Expectation failed") +#if !hasFeature(Embedded) expectationFailed.expectedFulfillmentCount = 13 +#else + expectationFailed.expectedFulfillmentCount = 10 +#endif var configuration = Configuration() configuration.eventHandler = { event, _ in @@ -559,11 +565,13 @@ final class IssueTests: XCTestCase { let randomNumber = Int.random(in: 0 ... .max) await Test { +#if !hasFeature(Embedded) #expect { asyncNotRequired() } throws: { $0 is MyError } +#endif #expect(throws: MyError.self) { asyncNotRequired() } @@ -582,11 +590,13 @@ final class IssueTests: XCTestCase { #expect(throws: MyError()) { throw MyDescriptiveError(description: "something wrong") } +#if !hasFeature(Embedded) #expect { throw MyDescriptiveError(description: "something wrong") } throws: { _ in false } +#endif #expect(throws: Never.self) { throw MyError() } @@ -600,12 +610,14 @@ final class IssueTests: XCTestCase { #expect(throws: MyError.self) { try nonVoidReturning() } +#if !hasFeature(Embedded) #expect { throw MyError() } throws: { error in let parameterizedError = try #require(error as? MyParameterizedError) return parameterizedError.index == 123 } +#endif }.run(configuration: configuration) await fulfillment(of: [expectationFailed], timeout: 0.0) @@ -681,11 +693,13 @@ final class IssueTests: XCTestCase { let randomNumber = Int.random(in: 0 ... .max) await Test { +#if !hasFeature(Embedded) await #expect { () async throws in throw MyError() } throws: { $0 is MyError } +#endif await #expect(throws: MyError.self) { () async throws in throw MyError() } @@ -708,7 +722,11 @@ final class IssueTests: XCTestCase { func testErrorCheckingWithExpectAsync_Mismatching() async throws { let expectationFailed = expectation(description: "Expectation failed") +#if !hasFeature(Embedded) expectationFailed.expectedFulfillmentCount = 13 +#else + expectationFailed.expectedFulfillmentCount = 10 +#endif var configuration = Configuration() configuration.eventHandler = { event, _ in @@ -724,9 +742,11 @@ final class IssueTests: XCTestCase { let randomNumber = Int.random(in: 0 ... .max) await Test { +#if !hasFeature(Embedded) await #expect { () async in } throws: { $0 is MyError } +#endif await #expect(throws: MyError.self) { () async in } await #expect(throws: MyParameterizedError(index: randomNumber)) { () async in } await #expect(throws: MyError.self) { () async throws in @@ -741,11 +761,13 @@ final class IssueTests: XCTestCase { await #expect(throws: MyError()) { () async throws in throw MyDescriptiveError(description: "something wrong") } +#if !hasFeature(Embedded) await #expect { () async throws in throw MyDescriptiveError(description: "something wrong") } throws: { _ in false } +#endif await #expect(throws: Never.self) { () async throws in throw MyError() } @@ -759,12 +781,14 @@ final class IssueTests: XCTestCase { await #expect(throws: MyError.self) { try await nonVoidReturning() } +#if !hasFeature(Embedded) await #expect { () async throws in throw MyError() } throws: { error in let parameterizedError = try #require(error as? MyParameterizedError) return parameterizedError.index == 123 } +#endif }.run(configuration: configuration) await fulfillment(of: [expectationFailed], timeout: 0.0) @@ -822,6 +846,7 @@ final class IssueTests: XCTestCase { await fulfillment(of: [expectationFailed], timeout: 0.0) } +#if !hasFeature(Embedded) func testErrorCheckingWithExpect_ThrowingFromErrorMatcher() async throws { let errorCaught = expectation(description: "Error matcher's error caught") let expectationFailed = expectation(description: "Expectation failed") @@ -931,6 +956,7 @@ final class IssueTests: XCTestCase { await fulfillment(of: [errorCaught, expectationFailed], timeout: 0.0) } +#endif func testErrorCheckingWithExpect_ResultValue() throws { let error = #expect(throws: MyDescriptiveError.self) { From 2c361ab357ff97b0b6a053871fea7cd1b8284dba Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 25 Sep 2025 15:32:14 -0400 Subject: [PATCH 2/2] Remove stray argument --- .../Testing/Expectations/ExpectationChecking+Macro.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/Testing/Expectations/ExpectationChecking+Macro.swift b/Sources/Testing/Expectations/ExpectationChecking+Macro.swift index 4582eb7cb..91102689c 100644 --- a/Sources/Testing/Expectations/ExpectationChecking+Macro.swift +++ b/Sources/Testing/Expectations/ExpectationChecking+Macro.swift @@ -1050,7 +1050,6 @@ public func __checkClosureCall( public func __checkClosureCall( performing body: () throws(any Error) -> R, throws errorMatcher: (any Error) throws(any Error) -> Bool, - mismatchExplanation: ((any Error) -> String)? = nil, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, @@ -1059,7 +1058,7 @@ public func __checkClosureCall( _check( performing: body, throws: errorMatcher, - mismatchExplanation: mismatchExplanation, + mismatchExplanation: nil, expression: expression, comments: comments(), isRequired: isRequired, @@ -1076,7 +1075,6 @@ public func __checkClosureCall( public func __checkClosureCall( performing body: () async throws(any Error) -> sending R, throws errorMatcher: (any Error) async throws(any Error) -> Bool, - mismatchExplanation: ((any Error) -> String)? = nil, expression: __Expression, comments: @autoclosure () -> [Comment], isRequired: Bool, @@ -1086,7 +1084,7 @@ public func __checkClosureCall( await _check( performing: body, throws: errorMatcher, - mismatchExplanation: mismatchExplanation, + mismatchExplanation: nil, expression: expression, comments: comments(), isRequired: isRequired,