diff --git a/Sources/Nimble/DSL+AsyncAwait.swift b/Sources/Nimble/DSL+AsyncAwait.swift index c7deecc9..3fb6e6a1 100644 --- a/Sources/Nimble/DSL+AsyncAwait.swift +++ b/Sources/Nimble/DSL+AsyncAwait.swift @@ -3,7 +3,7 @@ import Dispatch #endif /// Make an ``AsyncExpectation`` on a given actual value. The value given is lazily evaluated. -public func expect(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @escaping @Sendable () async throws -> T?) -> AsyncExpectation { +public func expect(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @escaping @Sendable () async throws -> sending T?) -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression, @@ -12,7 +12,7 @@ public func expect(fileID: String = #fileID, file: FileString = #fi } /// Make an ``AsyncExpectation`` on a given actual value. The closure is lazily invoked. -public func expect(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @Sendable () -> (@Sendable () async throws -> T)) -> AsyncExpectation { +public func expect(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @Sendable () -> (@Sendable () async throws -> sending T)) -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), @@ -21,7 +21,7 @@ public func expect(fileID: String = #fileID, file: FileString = #fi } /// Make an ``AsyncExpectation`` on a given actual value. The closure is lazily invoked. -public func expect(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @Sendable () -> (@Sendable () async throws -> T?)) -> AsyncExpectation { +public func expect(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @Sendable () -> (@Sendable () async throws -> sending T?)) -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), @@ -30,7 +30,7 @@ public func expect(fileID: String = #fileID, file: FileString = #fi } /// Make an ``AsyncExpectation`` on a given actual value. The closure is lazily invoked. -public func expect(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @Sendable () -> (@Sendable () async throws -> Void)) -> AsyncExpectation { +public func expect(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @Sendable () -> (@Sendable () async throws -> sending Void)) -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), @@ -40,7 +40,7 @@ public func expect(fileID: String = #fileID, file: FileString = #filePath, line: /// Make an ``AsyncExpectation`` on a given actual value. The value given is lazily evaluated. /// This is provided to avoid confusion between `expect -> SyncExpectation` and `expect -> AsyncExpectation`. -public func expecta(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @autoclosure @escaping @Sendable () async throws -> T?) async -> AsyncExpectation { +public func expecta(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @autoclosure @escaping @Sendable () async throws -> sending T?) async -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression, @@ -50,7 +50,7 @@ public func expecta(fileID: String = #fileID, file: FileString = #f /// Make an ``AsyncExpectation`` on a given actual value. The closure is lazily invoked. /// This is provided to avoid confusion between `expect -> SyncExpectation` and `expect -> AsyncExpectation` -public func expecta(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> T)) async -> AsyncExpectation { +public func expecta(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> sending T)) async -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), @@ -60,7 +60,7 @@ public func expecta(fileID: String = #fileID, file: FileString = #f /// Make an ``AsyncExpectation`` on a given actual value. The closure is lazily invoked. /// This is provided to avoid confusion between `expect -> SyncExpectation` and `expect -> AsyncExpectation` -public func expecta(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> T?)) async -> AsyncExpectation { +public func expecta(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> sending T?)) async -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), @@ -70,7 +70,7 @@ public func expecta(fileID: String = #fileID, file: FileString = #f /// Make an ``AsyncExpectation`` on a given actual value. The closure is lazily invoked. /// This is provided to avoid confusion between `expect -> SyncExpectation` and `expect -> AsyncExpectation` -public func expecta(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> Void)) async -> AsyncExpectation { +public func expecta(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> sending Void)) async -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), diff --git a/Sources/Nimble/DSL+Require.swift b/Sources/Nimble/DSL+Require.swift index 40832be6..952faddd 100644 --- a/Sources/Nimble/DSL+Require.swift +++ b/Sources/Nimble/DSL+Require.swift @@ -123,7 +123,7 @@ public func requires(fileID: String = #fileID, file: FileString = #filePath, lin /// `require` will return the result of the expression if the matcher passes, and throw an error if not. /// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown. @discardableResult -public func require(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @escaping () async throws -> T?) -> AsyncRequirement { +public func require(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @escaping @Sendable () async throws -> sending T?) -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression, @@ -137,7 +137,7 @@ public func require(fileID: String = #fileID, file: FileString = #f /// `require` will return the result of the expression if the matcher passes, and throw an error if not. /// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown. @discardableResult -public func require(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: () -> (() async throws -> T)) -> AsyncRequirement { +public func require(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: () -> (@Sendable () async throws -> sending T)) -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression(), @@ -151,7 +151,7 @@ public func require(fileID: String = #fileID, file: FileString = #f /// `require` will return the result of the expression if the matcher passes, and throw an error if not. /// if a `customError` is given, then that will be thrown. Otherwise, a ``RequireError`` will be thrown. @discardableResult -public func require(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: () -> (() async throws -> T?)) -> AsyncRequirement { +public func require(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: () -> (@Sendable () async throws -> sending T?)) -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression(), @@ -167,7 +167,7 @@ public func require(fileID: String = #fileID, file: FileString = #f /// /// This is provided to avoid confusion between `require -> SyncRequirement` and `require -> AsyncRequirement`. @discardableResult -public func requirea(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure @escaping () async throws -> T?) async -> AsyncRequirement { +public func requirea(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure @escaping @Sendable () async throws -> sending T?) async -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression, @@ -183,7 +183,7 @@ public func requirea(fileID: String = #fileID, file: FileString = # /// /// This is provided to avoid confusion between `require -> SyncRequirement` and `require -> AsyncRequirement` @discardableResult -public func requirea(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure () -> (() async throws -> T)) async -> AsyncRequirement { +public func requirea(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () async throws -> sending T)) async -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression(), @@ -199,7 +199,7 @@ public func requirea(fileID: String = #fileID, file: FileString = # /// /// This is provided to avoid confusion between `require -> SyncRequirement` and `require -> AsyncRequirement` @discardableResult -public func requirea(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure () -> (() async throws -> T?)) async -> AsyncRequirement { +public func requirea(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () async throws -> sending T?)) async -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression(), @@ -266,7 +266,7 @@ public func unwrap(fileID: String = #fileID, file: FileString = #fi /// `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, description: String? = nil, _ expression: () -> (() async throws -> T?)) async throws -> T { +public func unwrap(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, description: String? = nil, _ expression: () -> (@Sendable () async throws -> sending T?)) async throws -> T { try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) } @@ -286,7 +286,7 @@ public func unwrapa(fileID: String = #fileID, file: FileString = #f /// `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, description: String? = nil, _ expression: @autoclosure () -> (() async throws -> T?)) async throws -> T { +public func unwrapa(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () async throws -> sending 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/DSL.swift b/Sources/Nimble/DSL.swift index c6261a5f..c93719ac 100644 --- a/Sources/Nimble/DSL.swift +++ b/Sources/Nimble/DSL.swift @@ -66,7 +66,10 @@ public func expects(fileID: String = #fileID, file: FileString = #filePath, l /// Make a ``SyncExpectation`` on a given actual value. The closure is lazily invoked. /// This is provided as an alternative to `expect` which avoids overloading with `expect -> AsyncExpectation`. -public func expects(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @autoclosure () -> (@Sendable () throws -> Void)) -> SyncExpectation { +public func expects(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, _ expression: @autoclosure () -> (@Sendable () throws -> sending Void)) -> SyncExpectation { + // It would seem like `sending` isn't necessary for the `expression` argument + // because the closure returns void. However, this gets rid of a type + // conversion warning/error. return SyncExpectation( expression: Expression( expression: expression(), diff --git a/Sources/Nimble/Polling+Require.swift b/Sources/Nimble/Polling+Require.swift index 17a3fffa..4f42f442 100644 --- a/Sources/Nimble/Polling+Require.swift +++ b/Sources/Nimble/Polling+Require.swift @@ -715,7 +715,7 @@ 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, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) throws -> T { +public func pollUnwrap(file: FileString = #file, line: UInt = #line, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> sending T?)) throws -> T { try require(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) } @@ -729,7 +729,7 @@ public func pollUnwraps(file: FileString = #file, line: UInt = #line, timeout /// 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, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) throws -> T { +public func pollUnwraps(file: FileString = #file, line: UInt = #line, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> sending T?)) throws -> T { try require(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) } diff --git a/Tests/NimbleTests/Matchers/BeIdenticalToTest.swift b/Tests/NimbleTests/Matchers/BeIdenticalToTest.swift index 8fcf363e..be04bc78 100644 --- a/Tests/NimbleTests/Matchers/BeIdenticalToTest.swift +++ b/Tests/NimbleTests/Matchers/BeIdenticalToTest.swift @@ -26,7 +26,7 @@ final class BeIdenticalToTest: XCTestCase { } func testBeIdenticalToNegativeMessage() { - let value1 = NSArray() + let value1 = 1 as NSNumber let value2 = value1 let message = "expected to not be identical to \(identityAsString(value2)), got \(identityAsString(value1))" failsWithErrorMessage(message) { @@ -46,7 +46,7 @@ final class BeIdenticalToTest: XCTestCase { expect(1 as NSNumber).toNot(be("turtles" as NSString)) expect([1 as NSNumber] as NSArray).toNot(be([1 as NSNumber] as NSArray)) - let value1 = NSArray() + let value1 = 1 as NSNumber let value2 = value1 let message = "expected to not be identical to \(identityAsString(value1)), got \(identityAsString(value2))" failsWithErrorMessage(message) { diff --git a/Tests/NimbleTests/Matchers/EqualTest.swift b/Tests/NimbleTests/Matchers/EqualTest.swift index c0ccf764..0aa7c1d5 100644 --- a/Tests/NimbleTests/Matchers/EqualTest.swift +++ b/Tests/NimbleTests/Matchers/EqualTest.swift @@ -315,13 +315,24 @@ final class EqualTest: XCTestCase { // swiftlint:disable:this type_body_length expect(originalArray) != expectedArray.reversed() expect(originalArray) != [] - let originalArrayAsync = { @Sendable () async in originalArray } - await expect(originalArrayAsync).toEventually(equal(expectedArray)) - await expect(originalArrayAsync).toEventuallyNot(equal(expectedArray.reversed())) - await expect(originalArrayAsync).toEventuallyNot(equal([])) - await expect(originalArrayAsync) == expectedArray - await expect(originalArrayAsync) != expectedArray.reversed() - await expect(originalArrayAsync) != [] + await expect { @Sendable () async in + originalArray + }.toEventually(equal(expectedArray)) + await expect { @Sendable () async in + originalArray + }.toEventuallyNot(equal(expectedArray.reversed())) + await expect { @Sendable () async in + originalArray + }.toEventuallyNot(equal([])) + await expect { @Sendable () async in + originalArray + } == expectedArray + await expect { @Sendable () async in + originalArray + } != expectedArray.reversed() + await expect { @Sendable () async in + originalArray + } != [] } func testTuple3Array() async { @@ -348,13 +359,24 @@ final class EqualTest: XCTestCase { // swiftlint:disable:this type_body_length expect(originalArray) != expectedArray.reversed() expect(originalArray) != [] - let originalArrayAsync = { @Sendable () async in originalArray } - await expect(originalArrayAsync).toEventually(equal(expectedArray)) - await expect(originalArrayAsync).toEventuallyNot(equal(expectedArray.reversed())) - await expect(originalArrayAsync).toEventuallyNot(equal([])) - await expect(originalArrayAsync) == expectedArray - await expect(originalArrayAsync) != expectedArray.reversed() - await expect(originalArrayAsync) != [] + await expect { @Sendable () async in + originalArray + }.toEventually(equal(expectedArray)) + await expect { @Sendable () async in + originalArray + }.toEventuallyNot(equal(expectedArray.reversed())) + await expect { @Sendable () async in + originalArray + }.toEventuallyNot(equal([])) + await expect { @Sendable () async in + originalArray + } == expectedArray + await expect { @Sendable () async in + originalArray + } != expectedArray.reversed() + await expect { @Sendable () async in + originalArray + } != [] } func testTuple4Array() async { @@ -381,13 +403,24 @@ final class EqualTest: XCTestCase { // swiftlint:disable:this type_body_length expect(originalArray) != expectedArray.reversed() expect(originalArray) != [] - let originalArrayAsync = { @Sendable () async in originalArray } - await expect(originalArrayAsync).toEventually(equal(expectedArray)) - await expect(originalArrayAsync).toEventuallyNot(equal(expectedArray.reversed())) - await expect(originalArrayAsync).toEventuallyNot(equal([])) - await expect(originalArrayAsync) == expectedArray - await expect(originalArrayAsync) != expectedArray.reversed() - await expect(originalArrayAsync) != [] + await expect { @Sendable () async in + originalArray + }.toEventually(equal(expectedArray)) + await expect { @Sendable () async in + originalArray + }.toEventuallyNot(equal(expectedArray.reversed())) + await expect { @Sendable () async in + originalArray + }.toEventuallyNot(equal([])) + await expect { @Sendable () async in + originalArray + } == expectedArray + await expect { @Sendable () async in + originalArray + } != expectedArray.reversed() + await expect { @Sendable () async in + originalArray + } != [] } func testTuple5Array() async { @@ -414,13 +447,24 @@ final class EqualTest: XCTestCase { // swiftlint:disable:this type_body_length expect(originalArray) != expectedArray.reversed() expect(originalArray) != [] - let originalArrayAsync = { @Sendable () async in originalArray } - await expect(originalArrayAsync).toEventually(equal(expectedArray)) - await expect(originalArrayAsync).toEventuallyNot(equal(expectedArray.reversed())) - await expect(originalArrayAsync).toEventuallyNot(equal([])) - await expect(originalArrayAsync) == expectedArray - await expect(originalArrayAsync) != expectedArray.reversed() - await expect(originalArrayAsync) != [] + await expect { @Sendable () async in + originalArray + }.toEventually(equal(expectedArray)) + await expect { @Sendable () async in + originalArray + }.toEventuallyNot(equal(expectedArray.reversed())) + await expect { @Sendable () async in + originalArray + }.toEventuallyNot(equal([])) + await expect { @Sendable () async in + originalArray + } == expectedArray + await expect { @Sendable () async in + originalArray + } != expectedArray.reversed() + await expect { @Sendable () async in + originalArray + } != [] } func testTuple6Array() async { @@ -447,13 +491,24 @@ final class EqualTest: XCTestCase { // swiftlint:disable:this type_body_length expect(originalArray) != expectedArray.reversed() expect(originalArray) != [] - let originalArrayAsync = { @Sendable () async in originalArray } - await expect(originalArrayAsync).toEventually(equal(expectedArray)) - await expect(originalArrayAsync).toEventuallyNot(equal(expectedArray.reversed())) - await expect(originalArrayAsync).toEventuallyNot(equal([])) - await expect(originalArrayAsync) == expectedArray - await expect(originalArrayAsync) != expectedArray.reversed() - await expect(originalArrayAsync) != [] + await expect { @Sendable () async in + originalArray + }.toEventually(equal(expectedArray)) + await expect { @Sendable () async in + originalArray + }.toEventuallyNot(equal(expectedArray.reversed())) + await expect { @Sendable () async in + originalArray + }.toEventuallyNot(equal([])) + await expect { @Sendable () async in + originalArray + } == expectedArray + await expect { @Sendable () async in + originalArray + } != expectedArray.reversed() + await expect { @Sendable () async in + originalArray + } != [] } // swiftlint:enable large_tuple diff --git a/Tests/NimbleTests/Matchers/NegationTest.swift b/Tests/NimbleTests/Matchers/NegationTest.swift index 45d35f39..4929988b 100644 --- a/Tests/NimbleTests/Matchers/NegationTest.swift +++ b/Tests/NimbleTests/Matchers/NegationTest.swift @@ -22,7 +22,7 @@ final class NegationTest: XCTestCase { } func testAsyncNil() async { - @Sendable func nilFunc() async -> Int? { + @Sendable func nilFunc() async -> sending Int? { nil }