From 74886d6c6568028be629defda95512c2c55decc1 Mon Sep 17 00:00:00 2001 From: Rachel Brindle Date: Thu, 10 Oct 2024 22:38:12 -0700 Subject: [PATCH] Allow unwrap and pollUnwrap to take in custom descriptions (#1162) Also allow pollUnwrap to take in custom timeout and polling intervals. Fixes a bug in the async version of withAssertionRecorder where we didn't reset the assertion recorder after tests --- .../Adapters/AssertionRecorder+Async.swift | 6 ++- .../Nimble/Adapters/AssertionRecorder.swift | 2 + Sources/Nimble/DSL+Require.swift | 32 +++++++------- Sources/Nimble/Polling+Require.swift | 28 ++++++------- .../NimbleTests/AsyncAwaitTest+Require.swift | 2 +- Tests/NimbleTests/AsyncAwaitTest.swift | 2 +- Tests/NimbleTests/DSLTest.swift | 42 +++++++++++++++++++ Tests/NimbleTests/Helpers/AsyncHelpers.swift | 2 +- .../Matchers/AsyncAllPassTest.swift | 11 +++++ Tests/NimbleTests/Matchers/NegationTest.swift | 2 +- .../Matchers/SatisfyAllOfTest.swift | 4 +- .../Matchers/SatisfyAnyOfTest.swift | 4 +- Tests/NimbleTests/PollingTest+Require.swift | 32 ++++++++++++++ 13 files changed, 129 insertions(+), 40 deletions(-) diff --git a/Sources/Nimble/Adapters/AssertionRecorder+Async.swift b/Sources/Nimble/Adapters/AssertionRecorder+Async.swift index f6d3281be..286be5f2d 100644 --- a/Sources/Nimble/Adapters/AssertionRecorder+Async.swift +++ b/Sources/Nimble/Adapters/AssertionRecorder+Async.swift @@ -15,13 +15,15 @@ public func withAssertionHandler(_ tempAssertionHandler: AssertionHandler, closure: () async throws -> Void) async { let environment = NimbleEnvironment.activeInstance let oldRecorder = environment.assertionHandler - _ = NMBExceptionCapture(handler: nil, finally: ({ + defer { environment.assertionHandler = oldRecorder - })) + } environment.assertionHandler = tempAssertionHandler do { try await closure() + } catch is RequireError { + // ignore this } catch { let failureMessage = FailureMessage() failureMessage.stringValue = "unexpected error thrown: <\(error)>" diff --git a/Sources/Nimble/Adapters/AssertionRecorder.swift b/Sources/Nimble/Adapters/AssertionRecorder.swift index adb869132..0ee397219 100644 --- a/Sources/Nimble/Adapters/AssertionRecorder.swift +++ b/Sources/Nimble/Adapters/AssertionRecorder.swift @@ -79,6 +79,8 @@ public func withAssertionHandler(_ tempAssertionHandler: AssertionHandler, try capturer.tryBlockThrows { try closure() } + } catch is RequireError { + // specifically ignore RequireError, will be caught by the assertion handler. } catch { let failureMessage = FailureMessage() failureMessage.stringValue = "unexpected error thrown: <\(error)>" diff --git a/Sources/Nimble/DSL+Require.swift b/Sources/Nimble/DSL+Require.swift index 189d23ef7..4df5c5b4d 100644 --- a/Sources/Nimble/DSL+Require.swift +++ b/Sources/Nimble/DSL+Require.swift @@ -216,8 +216,8 @@ public func requirea(fileID: String = #fileID, file: FileString = #filePath, /// `unwrap` will return the result of the expression if it is non-nil, and throw an error if the value is nil. /// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown. @discardableResult -public func unwrap(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure @escaping () throws -> T?) throws -> T { - try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil()) +public func unwrap(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, description: String? = nil, _ expression: @autoclosure @escaping () throws -> T?) throws -> T { + try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) } /// Makes sure that the expression evaluates to a non-nil value, otherwise throw an error. @@ -226,8 +226,8 @@ public func unwrap(fileID: String = #fileID, file: FileString = #filePath, li /// `unwrap` will return the result of the expression if it is non-nil, and throw an error if the value is nil. /// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown. @discardableResult -public func unwrap(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure () -> (() throws -> T?)) throws -> T { - try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil()) +public func unwrap(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, description: String? = nil, _ expression: @autoclosure () -> (() throws -> T?)) throws -> T { + try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) } /// Makes sure that the expression evaluates to a non-nil value, otherwise throw an error. @@ -236,8 +236,8 @@ public func unwrap(fileID: String = #fileID, file: FileString = #filePath, li /// `unwraps` will return the result of the expression if it is non-nil, and throw an error if the value is nil. /// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown. @discardableResult -public func unwraps(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure @escaping () throws -> T?) throws -> T { - try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil()) +public func unwraps(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, description: String? = nil, _ expression: @autoclosure @escaping () throws -> T?) throws -> T { + try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) } /// Makes sure that the expression evaluates to a non-nil value, otherwise throw an error. @@ -246,8 +246,8 @@ public func unwraps(fileID: String = #fileID, file: FileString = #filePath, l /// `unwraps` will return the result of the expression if it is non-nil, and throw an error if the value is nil. /// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown. @discardableResult -public func unwraps(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure () -> (() throws -> T?)) throws -> T { - try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil()) +public func unwraps(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, description: String? = nil, _ expression: @autoclosure () -> (() throws -> T?)) throws -> T { + try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) } /// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error. @@ -256,8 +256,8 @@ public func unwraps(fileID: String = #fileID, file: FileString = #filePath, l /// `unwrap` will return the result of the expression if it is non-nil, and throw an error if the value is nil. /// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown. @discardableResult -public func unwrap(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @escaping () async throws -> T?) async throws -> T { - try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, try await expression()).toNot(beNil()) +public func unwrap(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, description: String? = nil, _ expression: @escaping () async throws -> T?) async throws -> T { + try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, try await expression()).toNot(beNil(), description: description) } /// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error. @@ -266,8 +266,8 @@ public func unwrap(fileID: String = #fileID, file: FileString = #filePath, li /// `unwrap` will return the result of the expression if it is non-nil, and throw an error if the value is nil. /// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown. @discardableResult -public func unwrap(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: () -> (() async throws -> T?)) async throws -> T { - try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil()) +public func unwrap(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, description: String? = nil, _ expression: () -> (() async throws -> T?)) async throws -> T { + try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) } /// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error. @@ -276,8 +276,8 @@ public func unwrap(fileID: String = #fileID, file: FileString = #filePath, li /// `unwrapa` will return the result of the expression if it is non-nil, and throw an error if the value is nil. /// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown. @discardableResult -public func unwrapa(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure @escaping () async throws -> T?) async throws -> T { - try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, try await expression()).toNot(beNil()) +public func unwrapa(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, description: String? = nil, _ expression: @autoclosure @escaping () async throws -> T?) async throws -> T { + try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, try await expression()).toNot(beNil(), description: description) } /// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error. @@ -286,6 +286,6 @@ public func unwrapa(fileID: String = #fileID, file: FileString = #filePath, l /// `unwrapa` will return the result of the expression if it is non-nil, and throw an error if the value is nil. /// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown. @discardableResult -public func unwrapa(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure () -> (() async throws -> T?)) async throws -> T { - try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil()) +public func unwrapa(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, description: String? = nil, _ expression: @autoclosure () -> (() async throws -> T?)) async throws -> T { + try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) } diff --git a/Sources/Nimble/Polling+Require.swift b/Sources/Nimble/Polling+Require.swift index 7f9c9268d..673ce70b9 100644 --- a/Sources/Nimble/Polling+Require.swift +++ b/Sources/Nimble/Polling+Require.swift @@ -713,50 +713,50 @@ public func pollUnwrap(file: FileString = #file, line: UInt = #line, _ expres /// Makes sure that the expression evaluates to a non-nil value, otherwise throw an error. /// As you can tell, this is a much less verbose equivalent to `require(expression).toEventuallyNot(beNil())` @discardableResult -public func pollUnwrap(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure () -> (() throws -> T?)) throws -> T { - try require(file: file, line: line, expression()).toEventuallyNot(beNil()) +public func pollUnwrap(file: FileString = #file, line: UInt = #line, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (() throws -> T?)) throws -> T { + try require(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) } /// Makes sure that the expression evaluates to a non-nil value, otherwise throw an error. /// As you can tell, this is a much less verbose equivalent to `require(expression).toEventuallyNot(beNil())` @discardableResult -public func pollUnwraps(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure @escaping () throws -> T?) throws -> T { - try require(file: file, line: line, expression()).toEventuallyNot(beNil()) +public func pollUnwraps(file: FileString = #file, line: UInt = #line, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure @escaping () throws -> T?) throws -> T { + try require(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) } /// Makes sure that the expression evaluates to a non-nil value, otherwise throw an error. /// As you can tell, this is a much less verbose equivalent to `require(expression).toEventuallyNot(beNil())` @discardableResult -public func pollUnwraps(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure () -> (() throws -> T?)) throws -> T { - try require(file: file, line: line, expression()).toEventuallyNot(beNil()) +public func pollUnwraps(file: FileString = #file, line: UInt = #line, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (() throws -> T?)) throws -> T { + try require(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) } /// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error. /// As you can tell, this is a much less verbose equivalent to `requirea(expression).toEventuallyNot(beNil())` @discardableResult -public func pollUnwrap(file: FileString = #file, line: UInt = #line, _ expression: @escaping () async throws -> T?) async throws -> T { - try await requirea(file: file, line: line, try await expression()).toEventuallyNot(beNil()) +public func pollUnwrap(file: FileString = #file, line: UInt = #line, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @escaping () async throws -> T?) async throws -> T { + try await requirea(file: file, line: line, try await expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) } /// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error. /// As you can tell, this is a much less verbose equivalent to `requirea(expression).toEventuallyNot(beNil())` @discardableResult -public func pollUnwrap(file: FileString = #file, line: UInt = #line, _ expression: () -> (() async throws -> T?)) async throws -> T { - try await requirea(file: file, line: line, expression()).toEventuallyNot(beNil()) +public func pollUnwrap(file: FileString = #file, line: UInt = #line, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: () -> (() async throws -> T?)) async throws -> T { + try await requirea(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) } /// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error. /// As you can tell, this is a much less verbose equivalent to `requirea(expression).toEventuallyNot(beNil())` @discardableResult -public func pollUnwrapa(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure @escaping () async throws -> T?) async throws -> T { - try await requirea(file: file, line: line, try await expression()).toEventuallyNot(beNil()) +public func pollUnwrapa(file: FileString = #file, line: UInt = #line, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure @escaping () async throws -> T?) async throws -> T { + try await requirea(file: file, line: line, try await expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) } /// Makes sure that the async expression evaluates to a non-nil value, otherwise throw an error. /// As you can tell, this is a much less verbose equivalent to `requirea(expression).toEventuallyNot(beNil())` @discardableResult -public func pollUnwrapa(file: FileString = #file, line: UInt = #line, _ expression: @autoclosure () -> (() async throws -> T?)) async throws -> T { - try await requirea(file: file, line: line, expression()).toEventuallyNot(beNil()) +public func pollUnwrapa(file: FileString = #file, line: UInt = #line, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (() async throws -> T?)) async throws -> T { + try await requirea(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) } #endif // #if !os(WASI) diff --git a/Tests/NimbleTests/AsyncAwaitTest+Require.swift b/Tests/NimbleTests/AsyncAwaitTest+Require.swift index 7925cc36d..f6dfd712d 100644 --- a/Tests/NimbleTests/AsyncAwaitTest+Require.swift +++ b/Tests/NimbleTests/AsyncAwaitTest+Require.swift @@ -59,7 +59,7 @@ final class AsyncAwaitRequireTest: XCTestCase { // swiftlint:disable:this type_b } func testPollUnwrapNegativeCase() async { - await failsWithErrorMessage("expected to eventually not be nil, got nil") { + await failsWithErrorMessage("expected to eventually not be nil, got ") { try await pollUnwrap { nil as Int? } } await failsWithErrorMessage("unexpected error thrown: <\(errorToThrow)>") { diff --git a/Tests/NimbleTests/AsyncAwaitTest.swift b/Tests/NimbleTests/AsyncAwaitTest.swift index 8fcbcbc3e..d88d4b9a3 100644 --- a/Tests/NimbleTests/AsyncAwaitTest.swift +++ b/Tests/NimbleTests/AsyncAwaitTest.swift @@ -233,7 +233,7 @@ final class AsyncAwaitTest: XCTestCase { // swiftlint:disable:this type_body_len } func testWaitUntilDetectsStalledMainThreadActivity() async { - let msg = "-waitUntil() timed out but was unable to run the timeout handler because the main thread is unresponsive (0.5 seconds is allow after the wait times out). Conditions that may cause this include processing blocking IO on the main thread, calls to sleep(), deadlocks, and synchronous IPC. Nimble forcefully stopped run loop which may cause future failures in test run." + let msg = "Waited more than 1.0 second" await failsWithErrorMessage(msg) { await waitUntil(timeout: .seconds(1)) { done in Thread.sleep(forTimeInterval: 3.0) diff --git a/Tests/NimbleTests/DSLTest.swift b/Tests/NimbleTests/DSLTest.swift index d164226c2..1c828686e 100644 --- a/Tests/NimbleTests/DSLTest.swift +++ b/Tests/NimbleTests/DSLTest.swift @@ -1,5 +1,8 @@ import XCTest import Nimble +#if SWIFT_PACKAGE +import NimbleSharedTestHelpers +#endif private func nonThrowingInt() -> Int { return 1 @@ -177,4 +180,43 @@ final class DSLTest: XCTestCase { expect(records.first?.success).to(beFalse()) expect(records.last?.success).to(beTrue()) } + + func testUnwrap() { + expect { try unwrap(Optional.some(1)) }.to(equal(1)) + + failsWithErrorMessage("expected to not be nil, got ") { + try unwrap(nil as Int?) + } + failsWithErrorMessage("expected to not be nil, got ") { + try unwraps(nil as Int?) + } + failsWithErrorMessage("Custom User Message\nexpected to not be nil, got ") { + try unwrap(description: "Custom User Message", nil as Int?) + } + failsWithErrorMessage("Custom User Message 2\nexpected to not be nil, got ") { + try unwraps(description: "Custom User Message 2", nil as Int?) + } + } + + func testUnwrapAsync() async { + @Sendable func asyncOptional(_ value: Int?) async -> Int? { + value + } + + await expect { try await unwrap { await asyncOptional(1) } }.to(equal(1)) + + await failsWithErrorMessage("expected to not be nil, got ") { + try await unwrap { await asyncOptional(nil) } + } + await failsWithErrorMessage("expected to not be nil, got ") { + try await unwrapa(await asyncOptional(nil)) + } + + await failsWithErrorMessage("Some Message\nexpected to not be nil, got ") { + try await unwrap(description: "Some Message") { await asyncOptional(nil) } + } + await failsWithErrorMessage("Other Message\nexpected to not be nil, got ") { + try await unwrapa(description: "Other Message", await asyncOptional(nil)) + } + } } diff --git a/Tests/NimbleTests/Helpers/AsyncHelpers.swift b/Tests/NimbleTests/Helpers/AsyncHelpers.swift index 2770e359f..5b2068218 100644 --- a/Tests/NimbleTests/Helpers/AsyncHelpers.swift +++ b/Tests/NimbleTests/Helpers/AsyncHelpers.swift @@ -53,7 +53,7 @@ final class AsyncMatchersTest: XCTestCase { await expect(1).to(asyncEqual(1)) await expect(2).toNot(asyncEqual(1)) - await failsWithErrorMessage("expected to equal 1, got 2") { + await failsWithErrorMessage("expected to equal 1, got <2>") { await expect(2).to(asyncEqual(1)) } } diff --git a/Tests/NimbleTests/Matchers/AsyncAllPassTest.swift b/Tests/NimbleTests/Matchers/AsyncAllPassTest.swift index e6d22f42f..5c4ec502e 100644 --- a/Tests/NimbleTests/Matchers/AsyncAllPassTest.swift +++ b/Tests/NimbleTests/Matchers/AsyncAllPassTest.swift @@ -26,9 +26,20 @@ private func asyncBeGreaterThan(_ expectedValue: T?) -> AsyncMatc } } +private protocol _OptionalProtocol { + var isNil: Bool { get } +} + +extension Optional: _OptionalProtocol { + var isNil: Bool { self == nil } +} + private func asyncBeNil() -> AsyncMatcher { return AsyncMatcher.simpleNilable("be nil") { actualExpression in let actualValue = try await actualExpression.evaluate() + if let actual = actualValue, let nestedOptionl = actual as? _OptionalProtocol { + return MatcherStatus(bool: nestedOptionl.isNil) + } return MatcherStatus(bool: actualValue == nil) } } diff --git a/Tests/NimbleTests/Matchers/NegationTest.swift b/Tests/NimbleTests/Matchers/NegationTest.swift index a05ea7fbb..45d35f39a 100644 --- a/Tests/NimbleTests/Matchers/NegationTest.swift +++ b/Tests/NimbleTests/Matchers/NegationTest.swift @@ -36,7 +36,7 @@ final class NegationTest: XCTestCase { func testAsyncNonNil() async { await expect(1).to(not(asyncEqual(2))) - await failsWithErrorMessage("expected to not equal <2>, got <2>") { + await failsWithErrorMessage("expected to not equal 2, got <2>") { await expect(2).to(not(asyncEqual(2))) } } diff --git a/Tests/NimbleTests/Matchers/SatisfyAllOfTest.swift b/Tests/NimbleTests/Matchers/SatisfyAllOfTest.swift index 73bebac0d..e2563581a 100644 --- a/Tests/NimbleTests/Matchers/SatisfyAllOfTest.swift +++ b/Tests/NimbleTests/Matchers/SatisfyAllOfTest.swift @@ -83,11 +83,11 @@ final class SatisfyAllOfTest: XCTestCase { await expect(true).toNot(satisfyAllOf(beTruthy(), beFalsy(), asyncEqual(true))) await failsWithErrorMessage( - "expected to match all of: {equal <3>}, and {equal <4>}, and {equal <5>}, got 2") { + "expected to match all of: {equal 3}, and {equal 4}, and {equal 5}, got 2") { await expect(2).to(satisfyAllOf(asyncEqual(3), asyncEqual(4), asyncEqual(5))) } await failsWithErrorMessage( - "expected to match all of: {all be less than 4, but failed first at element <5> in <[5, 6, 7]>}, and {equal <[5, 6, 7]>}, got [5, 6, 7]") { + "expected to match all of: {all be less than 4, but failed first at element <5> in <[5, 6, 7]>}, and {equal [5, 6, 7]}, got [5, 6, 7]") { await expect([5, 6, 7]).to(satisfyAllOf(allPass("be less than 4", {$0 < 4}), asyncEqual([5, 6, 7]))) } await failsWithErrorMessage( diff --git a/Tests/NimbleTests/Matchers/SatisfyAnyOfTest.swift b/Tests/NimbleTests/Matchers/SatisfyAnyOfTest.swift index f908d1b3b..f02c35206 100644 --- a/Tests/NimbleTests/Matchers/SatisfyAnyOfTest.swift +++ b/Tests/NimbleTests/Matchers/SatisfyAnyOfTest.swift @@ -83,11 +83,11 @@ final class SatisfyAnyOfTest: XCTestCase { await expect(true).to(satisfyAnyOf(beTruthy(), beFalsy(), asyncEqual(false), asyncEqual(true))) await failsWithErrorMessage( - "expected to match one of: {equal <3>}, or {equal <4>}, or {equal <5>}, got 2") { + "expected to match one of: {equal 3}, or {equal 4}, or {equal 5}, got 2") { await expect(2).to(satisfyAnyOf(asyncEqual(3), asyncEqual(4), asyncEqual(5))) } await failsWithErrorMessage( - "expected to match one of: {all be less than 4, but failed first at element <5> in <[5, 6, 7]>}, or {equal <[1, 2, 3, 4]>}, got [5, 6, 7]") { + "expected to match one of: {all be less than 4, but failed first at element <5> in <[5, 6, 7]>}, or {equal [1, 2, 3, 4]}, got [5, 6, 7]") { await expect([5, 6, 7]).to(satisfyAnyOf(allPass("be less than 4", {$0 < 4}), asyncEqual([1, 2, 3, 4]))) } await failsWithErrorMessage( diff --git a/Tests/NimbleTests/PollingTest+Require.swift b/Tests/NimbleTests/PollingTest+Require.swift index c91f2c590..7bdc48464 100644 --- a/Tests/NimbleTests/PollingTest+Require.swift +++ b/Tests/NimbleTests/PollingTest+Require.swift @@ -214,6 +214,38 @@ final class PollingRequireTest: XCTestCase { try require(nil).toAlways(equal(1)) } } + + func testPollUnwrapMessage() { + failsWithErrorMessage("expected to eventually not be nil, got ") { + try pollUnwrap(timeout: .milliseconds(100)) { nil as Int? } + } + + failsWithErrorMessage("Custom Message\nexpected to eventually not be nil, got ") { + try pollUnwrap(timeout: .milliseconds(100), description: "Custom Message") { nil as Int? } + } + + failsWithErrorMessage("Custom Message 2\nexpected to eventually not be nil, got ") { + try pollUnwraps(timeout: .milliseconds(100), description: "Custom Message 2") { nil as Int? } + } + } + + func testPollUnwrapMessageAsync() async { + @Sendable func asyncOptional(_ value: Int?) async -> Int? { + value + } + + await failsWithErrorMessage("expected to eventually not be nil, got ") { + try await pollUnwrap(timeout: .milliseconds(100)) { await asyncOptional(nil) as Int? } + } + + await failsWithErrorMessage("Custom Message\nexpected to eventually not be nil, got ") { + try await pollUnwrap(timeout: .milliseconds(100), description: "Custom Message") { await asyncOptional(nil) as Int? } + } + + await failsWithErrorMessage("Custom Message 2\nexpected to eventually not be nil, got ") { + try await pollUnwrapa(timeout: .milliseconds(100), description: "Custom Message 2") { await asyncOptional(nil) as Int? } + } + } } #endif // #if !os(WASI)