Skip to content
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
2 changes: 1 addition & 1 deletion Sources/Testing/ExitTests/ExitTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ func callExitTest(
isRequired: Bool,
isolation: isolated (any Actor)? = #isolation,
sourceLocation: SourceLocation
) async -> Result<ExitTest.Result?, any Error> {
) async -> Result<ExitTest.Result?, ExpectationFailedError> {
guard let configuration = Configuration.current ?? Configuration.all.first else {
preconditionFailure("A test must be running on the current task to use #expect(processExitsWith:).")
}
Expand Down
165 changes: 85 additions & 80 deletions Sources/Testing/Expectations/Expectation+Macro.swift

Large diffs are not rendered by default.

263 changes: 159 additions & 104 deletions Sources/Testing/Expectations/ExpectationChecking+Macro.swift

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Sources/Testing/Support/Additions/ResultAdditions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Expand Down Expand Up @@ -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<T>() throws -> T where Success == T? {
@discardableResult public func __required<T>() throws(any Error) -> T where Success == T? {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't use throws(some Error) unfortunately.

(We'll likely need to rewrite this function for Embedded Swift to throw some public-but-opaque error type AKA AnyTestingLibraryError.)

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.")
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/Testing/Testing.docc/Expectations.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ the test when the code doesn't satisfy a requirement, use
### Checking that errors are thrown

- <doc:testing-for-errors-in-swift-code>
- ``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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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() {
Expand Down Expand Up @@ -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

Expand Down
26 changes: 26 additions & 0 deletions Tests/TestingTests/IssueTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand All @@ -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()
}
Expand All @@ -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()
}
Expand All @@ -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)
Expand Down Expand Up @@ -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()
}
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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()
}
Expand All @@ -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)
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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) {
Expand Down