diff --git a/Sources/Nimble/Adapters/AssertionRecorder+Async.swift b/Sources/Nimble/Adapters/AssertionRecorder+Async.swift index 286be5f2d..441c54dae 100644 --- a/Sources/Nimble/Adapters/AssertionRecorder+Async.swift +++ b/Sources/Nimble/Adapters/AssertionRecorder+Async.swift @@ -8,10 +8,7 @@ /// /// @see AssertionHandler public func withAssertionHandler(_ tempAssertionHandler: AssertionHandler, - fileID: String = #fileID, - file: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + location: SourceLocation = SourceLocation(), closure: () async throws -> Void) async { let environment = NimbleEnvironment.activeInstance let oldRecorder = environment.assertionHandler @@ -27,7 +24,6 @@ public func withAssertionHandler(_ tempAssertionHandler: AssertionHandler, } catch { let failureMessage = FailureMessage() failureMessage.stringValue = "unexpected error thrown: <\(error)>" - let location = SourceLocation(fileID: fileID, filePath: file, line: line, column: column) tempAssertionHandler.assert(false, message: failureMessage, location: location) } } diff --git a/Sources/Nimble/Adapters/AssertionRecorder.swift b/Sources/Nimble/Adapters/AssertionRecorder.swift index 4a3df689b..e49988032 100644 --- a/Sources/Nimble/Adapters/AssertionRecorder.swift +++ b/Sources/Nimble/Adapters/AssertionRecorder.swift @@ -71,10 +71,7 @@ extension NMBExceptionCapture { /// /// @see AssertionHandler public func withAssertionHandler(_ tempAssertionHandler: AssertionHandler, - fileID: String = #fileID, - file: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + location: SourceLocation = SourceLocation(), closure: () throws -> Void) { let environment = NimbleEnvironment.activeInstance let oldRecorder = environment.assertionHandler @@ -92,11 +89,6 @@ public func withAssertionHandler(_ tempAssertionHandler: AssertionHandler, } catch { let failureMessage = FailureMessage() failureMessage.stringValue = "unexpected error thrown: <\(error)>" - let location = SourceLocation( - fileID: fileID, - filePath: file, - line: line, column: column - ) tempAssertionHandler.assert(false, message: failureMessage, location: location) } } diff --git a/Sources/Nimble/Adapters/NMBExpectation.swift b/Sources/Nimble/Adapters/NMBExpectation.swift index be1768d58..52548a6fc 100644 --- a/Sources/Nimble/Adapters/NMBExpectation.swift +++ b/Sources/Nimble/Adapters/NMBExpectation.swift @@ -37,7 +37,7 @@ public final class NMBExpectation: NSObject, Sendable { } private var expectValue: SyncExpectation { - return expect(file: _file, line: _line, self._actualBlock() as NSObject?) + return expect(location: SourceLocation(fileID: "unknown/\(_file)", filePath: _file, line: _line, column: 0), self._actualBlock() as NSObject?) } @objc public var withTimeout: (TimeInterval) -> NMBExpectation { diff --git a/Sources/Nimble/DSL+AsyncAwait.swift b/Sources/Nimble/DSL+AsyncAwait.swift index c7deecc91..8a704ce66 100644 --- a/Sources/Nimble/DSL+AsyncAwait.swift +++ b/Sources/Nimble/DSL+AsyncAwait.swift @@ -3,78 +3,78 @@ 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(location: SourceLocation = SourceLocation(), _ expression: @escaping @Sendable () async throws -> T?) -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression, - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// 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(location: SourceLocation = SourceLocation(), _ expression: @Sendable () -> (@Sendable () async throws -> T)) -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// 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(location: SourceLocation = SourceLocation(), _ expression: @Sendable () -> (@Sendable () async throws -> T?)) -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// 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(location: SourceLocation = SourceLocation(), _ expression: @Sendable () -> (@Sendable () async throws -> Void)) -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// 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(location: SourceLocation = SourceLocation(), _ expression: @autoclosure @escaping @Sendable () async throws -> T?) async -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression, - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// 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(location: SourceLocation = SourceLocation(), _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> T)) async -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// 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(location: SourceLocation = SourceLocation(), _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> T?)) async -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// 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(location: SourceLocation = SourceLocation(), _ expression: @autoclosure @Sendable () -> (@Sendable () async throws -> Void)) async -> AsyncExpectation { return AsyncExpectation( expression: AsyncExpression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } @@ -89,15 +89,12 @@ public func expecta(fileID: String = #fileID, file: FileString = #filePath, line /// Unlike the synchronous version of this call, this does not support catching Objective-C exceptions. public func waitUntil( timeout: NimbleTimeInterval = PollingDefaults.timeout, - fileID: String = #fileID, - file: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + location: SourceLocation = SourceLocation(), action: @escaping @Sendable (@escaping @Sendable () -> Void) async -> Void ) async { await throwableUntil( timeout: timeout, - sourceLocation: SourceLocation(fileID: fileID, filePath: file, line: line, column: column) + sourceLocation: location ) { done in await action(done) } @@ -112,15 +109,12 @@ public func waitUntil( /// Unlike the synchronous version of this call, this does not support catching Objective-C exceptions. public func waitUntil( timeout: NimbleTimeInterval = PollingDefaults.timeout, - fileID: String = #fileID, - file: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + location: SourceLocation = SourceLocation(), action: @escaping @Sendable (@escaping @Sendable () -> Void) -> Void ) async { await throwableUntil( timeout: timeout, - sourceLocation: SourceLocation(fileID: fileID, filePath: file, line: line, column: column) + sourceLocation: location ) { done in action(done) } @@ -154,34 +148,22 @@ private func throwableUntil( case .blockedRunLoop: fail( blockedRunLoopErrorMessageFor("-waitUntil()", leeway: leeway), - fileID: sourceLocation.fileID, - file: sourceLocation.filePath, - line: sourceLocation.line, - column: sourceLocation.column + location: sourceLocation ) case .timedOut: fail( "Waited more than \(timeout.description)", - fileID: sourceLocation.fileID, - file: sourceLocation.filePath, - line: sourceLocation.line, - column: sourceLocation.column + location: sourceLocation ) case let .errorThrown(error): fail( "Unexpected error thrown: \(error)", - fileID: sourceLocation.fileID, - file: sourceLocation.filePath, - line: sourceLocation.line, - column: sourceLocation.column + location: sourceLocation ) case .completed(.error(let error)): fail( "Unexpected error thrown: \(error)", - fileID: sourceLocation.fileID, - file: sourceLocation.filePath, - line: sourceLocation.line, - column: sourceLocation.column + location: sourceLocation ) case .completed(.none): // success break diff --git a/Sources/Nimble/DSL+Require.swift b/Sources/Nimble/DSL+Require.swift index b0b729851..211735bd7 100644 --- a/Sources/Nimble/DSL+Require.swift +++ b/Sources/Nimble/DSL+Require.swift @@ -3,11 +3,11 @@ /// `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: @autoclosure @escaping @Sendable () throws -> T?) -> SyncRequirement { +public func require(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @autoclosure @escaping @Sendable () throws -> T?) -> SyncRequirement { return SyncRequirement( expression: Expression( expression: expression, - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -17,11 +17,11 @@ public func require(fileID: String = #fileID, file: FileString = #filePath, l /// `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: @autoclosure () -> (@Sendable () throws -> T)) -> SyncRequirement { +public func require(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T)) -> SyncRequirement { return SyncRequirement( expression: Expression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -31,11 +31,11 @@ public func require(fileID: String = #fileID, file: FileString = #filePath, l /// `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: @autoclosure () -> (@Sendable () throws -> T?)) -> SyncRequirement { +public func require(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) -> SyncRequirement { return SyncRequirement( expression: Expression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -45,11 +45,11 @@ public func require(fileID: String = #fileID, file: FileString = #filePath, l /// `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: @autoclosure () -> (@Sendable () throws -> Void)) -> SyncRequirement { +public func require(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> Void)) -> SyncRequirement { return SyncRequirement( expression: Expression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -61,11 +61,11 @@ public func require(fileID: String = #fileID, file: FileString = #filePath, line /// /// This is provided as an alternative to ``require``, for when you want to be specific about whether you're using ``SyncRequirement`` or ``AsyncRequirement``. @discardableResult -public func requires(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure @escaping @Sendable () throws -> T?) -> SyncRequirement { +public func requires(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @autoclosure @escaping @Sendable () throws -> T?) -> SyncRequirement { return SyncRequirement( expression: Expression( expression: expression, - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -77,11 +77,11 @@ public func requires(fileID: String = #fileID, file: FileString = #filePath, /// /// This is provided as an alternative to ``require``, for when you want to be specific about whether you're using ``SyncRequirement`` or ``AsyncRequirement``. @discardableResult -public func requires(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T)) -> SyncRequirement { +public func requires(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T)) -> SyncRequirement { return SyncRequirement( expression: Expression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -93,11 +93,11 @@ public func requires(fileID: String = #fileID, file: FileString = #filePath, /// /// This is provided as an alternative to ``require``, for when you want to be specific about whether you're using ``SyncRequirement`` or ``AsyncRequirement``. @discardableResult -public func requires(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) -> SyncRequirement { +public func requires(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) -> SyncRequirement { return SyncRequirement( expression: Expression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -109,11 +109,11 @@ public func requires(fileID: String = #fileID, file: FileString = #filePath, /// /// This is provided as an alternative to ``require``, for when you want to be specific about whether you're using ``SyncRequirement`` or ``AsyncRequirement``. @discardableResult -public func requires(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> Void)) -> SyncRequirement { +public func requires(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> Void)) -> SyncRequirement { return SyncRequirement( expression: Expression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -123,11 +123,11 @@ 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 @Sendable () async throws -> T?) -> AsyncRequirement { +public func require(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @escaping @Sendable () async throws -> T?) -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression, - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -137,11 +137,11 @@ 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: () -> (@Sendable () async throws -> T)) -> AsyncRequirement { +public func require(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: () -> (@Sendable () async throws -> T)) -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -151,11 +151,11 @@ 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: () -> (@Sendable () async throws -> T?)) -> AsyncRequirement { +public func require(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: () -> (@Sendable () async throws -> T?)) -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -167,11 +167,11 @@ 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 @Sendable () async throws -> T?) async -> AsyncRequirement { +public func requirea(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @autoclosure @escaping @Sendable () async throws -> T?) async -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression, - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -183,11 +183,11 @@ 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 () -> (@Sendable () async throws -> T)) async -> AsyncRequirement { +public func requirea(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () async throws -> T)) async -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -199,11 +199,11 @@ 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 () -> (@Sendable () async throws -> T?)) async -> AsyncRequirement { +public func requirea(location: SourceLocation = SourceLocation(), customError: Error? = nil, _ expression: @autoclosure () -> (@Sendable () async throws -> T?)) async -> AsyncRequirement { return AsyncRequirement( expression: AsyncExpression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true), customError: customError) } @@ -216,8 +216,8 @@ public func requirea(fileID: String = #fileID, file: FileString = # /// `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: @autoclosure @escaping @Sendable () throws -> T?) throws -> T { - try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) +public func unwrap(location: SourceLocation = SourceLocation(), customError: Error? = nil, description: String? = nil, _ expression: @autoclosure @escaping @Sendable () throws -> T?) throws -> T { + try requires(location: location, 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, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) throws -> T { - try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) +public func unwrap(location: SourceLocation = SourceLocation(), customError: Error? = nil, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) throws -> T { + try requires(location: location, 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, description: String? = nil, _ expression: @autoclosure @escaping @Sendable () throws -> T?) throws -> T { - try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) +public func unwraps(location: SourceLocation = SourceLocation(), customError: Error? = nil, description: String? = nil, _ expression: @autoclosure @escaping @Sendable () throws -> T?) throws -> T { + try requires(location: location, 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, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) throws -> T { - try requires(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) +public func unwraps(location: SourceLocation = SourceLocation(), customError: Error? = nil, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) throws -> T { + try requires(location: location, 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, 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) +public func unwrap(location: SourceLocation = SourceLocation(), customError: Error? = nil, description: String? = nil, _ expression: @escaping () async throws -> T?) async throws -> T { + try await requirea(location: location, 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 = #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: () -> (@Sendable () async throws -> T?)) async throws -> T { - try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) +public func unwrap(location: SourceLocation = SourceLocation(), customError: Error? = nil, description: String? = nil, _ expression: () -> (@Sendable () async throws -> T?)) async throws -> T { + try await requirea(location: location, 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 = #fi /// `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 @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) +public func unwrapa(location: SourceLocation = SourceLocation(), customError: Error? = nil, description: String? = nil, _ expression: @autoclosure @escaping () async throws -> T?) async throws -> T { + try await requirea(location: location, 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,16 +286,15 @@ 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 () -> (@Sendable () async throws -> T?)) async throws -> T { - try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description) +public func unwrapa(location: SourceLocation = SourceLocation(), customError: Error? = nil, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () async throws -> T?)) async throws -> T { + try await requirea(location: location, customError: customError, expression()).toNot(beNil(), description: description) } /// Always fails the test and throw an error to prevent further test execution. /// /// - Parameter message: A custom message to use in place of the default one. /// - Parameter customError: A custom error to throw in place of a ``RequireError``. -public func requireFail(_ message: String? = nil, customError: Error? = nil, fileID: String = #fileID, filePath: FileString = #filePath, line: UInt = #line, column: UInt = #column) throws { - let location = SourceLocation(fileID: fileID, filePath: filePath, line: line, column: column) +public func requireFail(_ message: String? = nil, customError: Error? = nil, location: SourceLocation = SourceLocation()) throws { let handler = NimbleEnvironment.activeInstance.assertionHandler let msg = message ?? "requireFail() always fails" diff --git a/Sources/Nimble/DSL+Wait.swift b/Sources/Nimble/DSL+Wait.swift index 9ad863904..bef8e77a4 100644 --- a/Sources/Nimble/DSL+Wait.swift +++ b/Sources/Nimble/DSL+Wait.swift @@ -19,24 +19,18 @@ public class NMBWait: NSObject { @objc public class func until( timeout: TimeInterval, - fileID: String = #fileID, - file: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + location: SourceLocation = SourceLocation(), action: @escaping @Sendable (@escaping @Sendable () -> Void) -> Void) { // Convert TimeInterval to NimbleTimeInterval - until(timeout: timeout.nimbleInterval, file: file, line: line, action: action) + until(timeout: timeout.nimbleInterval, location: location, action: action) } #endif public class func until( timeout: NimbleTimeInterval, - fileID: String = #fileID, - file: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + location: SourceLocation = SourceLocation(), action: @escaping @Sendable (@escaping @Sendable () -> Void) -> Void) { - return throwableUntil(timeout: timeout, file: file, line: line) { done in + return throwableUntil(timeout: timeout, location: location) { done in action(done) } } @@ -44,15 +38,11 @@ public class NMBWait: NSObject { // Using a throwable closure makes this method not objc compatible. public class func throwableUntil( timeout: NimbleTimeInterval, - fileID: String = #fileID, - file: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + location: SourceLocation = SourceLocation(), action: @escaping @Sendable (@escaping @Sendable () -> Void) throws -> Void) { let awaiter = NimbleEnvironment.activeInstance.awaiter let leeway = timeout.divided - let location = SourceLocation(fileID: fileID, filePath: file, line: line, column: column) - let result = awaiter.performBlock(file: file, line: line) { (done: @escaping @Sendable (ErrorResult) -> Void) throws -> Void in + let result = awaiter.performBlock(location: location) { (done: @escaping @Sendable (ErrorResult) -> Void) throws -> Void in DispatchQueue.main.async { let capture = NMBExceptionCapture( handler: ({ exception in @@ -81,25 +71,25 @@ public class NMBWait: NSObject { case .incomplete: internalError("Reached .incomplete state for waitUntil(...).") case .blockedRunLoop: fail(blockedRunLoopErrorMessageFor("-waitUntil()", leeway: leeway), - fileID: fileID, file: file, line: line, column: column) + location: location) case .timedOut: fail("Waited more than \(timeout.description)", - fileID: fileID, file: file, line: line, column: column) + location: location) case let .raisedException(exception): fail("Unexpected exception raised: \(exception)", - fileID: fileID, file: file, line: line, column: column + location: location ) case let .errorThrown(error): fail("Unexpected error thrown: \(error)", - fileID: fileID, file: file, line: line, column: column + location: location ) case .completed(.exception(let exception)): fail("Unexpected exception raised: \(exception)", - fileID: fileID, file: file, line: line, column: column + location: location ) case .completed(.error(let error)): fail("Unexpected error thrown: \(error)", - fileID: fileID, file: file, line: line, column: column + location: location ) case .completed(.none): // success break @@ -107,23 +97,17 @@ public class NMBWait: NSObject { } #if canImport(Darwin) - @objc(untilFileID:file:line:column:action:) + @objc(untilLocation:action:) public class func until( - _ fileID: String = #fileID, - file: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + _ location: SourceLocation = SourceLocation(), action: @escaping @Sendable (@escaping @Sendable () -> Void) -> Void) { - until(timeout: .seconds(1), fileID: fileID, file: file, line: line, column: column, action: action) + until(timeout: .seconds(1), location: location, action: action) } #else public class func until( - _ fileID: String = #fileID, - file: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + _ location: SourceLocation = SourceLocation(), action: @escaping (@escaping () -> Void) -> Void) { - until(timeout: .seconds(1), fileID: fileID, file: file, line: line, column: column, action: action) + until(timeout: .seconds(1), location: location, action: action) } #endif } @@ -143,13 +127,10 @@ internal func blockedRunLoopErrorMessageFor(_ fnName: String, leeway: NimbleTime @available(*, noasync, message: "the sync variant of `waitUntil` does not work in async contexts. Use the async variant as a drop-in replacement") public func waitUntil( timeout: NimbleTimeInterval = PollingDefaults.timeout, - fileID: String = #fileID, - file: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + location: SourceLocation = SourceLocation(), action: @escaping @Sendable (@escaping @Sendable () -> Void) -> Void ) { - NMBWait.until(timeout: timeout, fileID: fileID, file: file, line: line, column: column, action: action) + NMBWait.until(timeout: timeout, location: location, action: action) } #endif // #if !os(WASI) diff --git a/Sources/Nimble/DSL.swift b/Sources/Nimble/DSL.swift index 7595522e6..ff20967a0 100644 --- a/Sources/Nimble/DSL.swift +++ b/Sources/Nimble/DSL.swift @@ -1,96 +1,91 @@ /// Make a ``SyncExpectation`` 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: @autoclosure @escaping @Sendable () throws -> T?) -> SyncExpectation { +public func expect(location: SourceLocation = SourceLocation(), _ expression: @autoclosure @escaping @Sendable () throws -> T?) -> SyncExpectation { return SyncExpectation( expression: Expression( expression: expression, - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// Make a ``SyncExpectation`` 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: @autoclosure () -> (@Sendable () throws -> T)) -> SyncExpectation { +public func expect(location: SourceLocation = SourceLocation(), _ expression: @autoclosure () -> (@Sendable () throws -> T)) -> SyncExpectation { return SyncExpectation( expression: Expression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// Make a ``SyncExpectation`` 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: @autoclosure () -> (@Sendable () throws -> T?)) -> SyncExpectation { +public func expect(location: SourceLocation = SourceLocation(), _ expression: @autoclosure () -> (@Sendable () throws -> T?)) -> SyncExpectation { return SyncExpectation( expression: Expression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// Make a ``SyncExpectation`` 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: @autoclosure () -> (@Sendable () throws -> Void)) -> SyncExpectation { +public func expect(location: SourceLocation = SourceLocation(), _ expression: @autoclosure () -> (@Sendable () throws -> Void)) -> SyncExpectation { return SyncExpectation( expression: Expression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// Make a ``SyncExpectation`` on a given actual value. The value given is lazily evaluated. /// 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 @escaping @Sendable () throws -> T?) -> SyncExpectation { +public func expects(location: SourceLocation = SourceLocation(), _ expression: @autoclosure @escaping @Sendable () throws -> T?) -> SyncExpectation { return SyncExpectation( expression: Expression( expression: expression, - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// 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 -> T)) -> SyncExpectation { +public func expects(location: SourceLocation = SourceLocation(), _ expression: @autoclosure () -> (@Sendable () throws -> T)) -> SyncExpectation { return SyncExpectation( expression: Expression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// 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 -> T?)) -> SyncExpectation { +public func expects(location: SourceLocation = SourceLocation(), _ expression: @autoclosure () -> (@Sendable () throws -> T?)) -> SyncExpectation { return SyncExpectation( expression: Expression( expression: expression(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// 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(location: SourceLocation = SourceLocation(), _ expression: @autoclosure () -> (@Sendable () throws -> 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(), - location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column), + location: location, isClosure: true)) } /// Always fails the test with a message and a specified location. -public func fail(_ message: String, location: SourceLocation) { +public func fail(_ message: String, location: SourceLocation = SourceLocation()) { let handler = NimbleEnvironment.activeInstance.assertionHandler handler.assert(false, message: FailureMessage(stringValue: message), location: location) } -/// Always fails the test with a message. -public func fail(_ message: String, fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column) { - fail(message, location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column)) -} - /// Always fails the test. -public func fail(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column) { - fail("fail() always fails", location: SourceLocation(fileID: fileID, filePath: file, line: line, column: column)) +public func fail(location: SourceLocation = SourceLocation()) { + fail("fail() always fails", location: location) } /// Like Swift's precondition(), but raises NSExceptions instead of sigaborts diff --git a/Sources/Nimble/Polling+Require.swift b/Sources/Nimble/Polling+Require.swift index 17a3fffac..1977c59b9 100644 --- a/Sources/Nimble/Polling+Require.swift +++ b/Sources/Nimble/Polling+Require.swift @@ -708,57 +708,57 @@ extension AsyncRequirement { /// 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 @escaping @Sendable () throws -> T?) throws -> T { - try require(file: file, line: line, expression()).toEventuallyNot(beNil()) +public func pollUnwrap(location: SourceLocation = SourceLocation(), _ expression: @autoclosure @escaping @Sendable () throws -> T?) throws -> T { + try require(location: location, expression()).toEventuallyNot(beNil()) } /// 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 { - try require(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) +public func pollUnwrap(location: SourceLocation = SourceLocation(), timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) throws -> T { + try require(location: location, 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, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure @escaping @Sendable () throws -> T?) throws -> T { - try require(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) +public func pollUnwraps(location: SourceLocation = SourceLocation(), timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure @escaping @Sendable () throws -> T?) throws -> T { + try require(location: location, 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, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) throws -> T { - try require(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) +public func pollUnwraps(location: SourceLocation = SourceLocation(), timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () throws -> T?)) throws -> T { + try require(location: location, 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, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @escaping @Sendable () async throws -> T?) async throws -> T { - try await requirea(file: file, line: line, try await expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) +public func pollUnwrap(location: SourceLocation = SourceLocation(), timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @escaping @Sendable () async throws -> T?) async throws -> T { + try await requirea(location: location, 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, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: () -> (@Sendable () async throws -> T?)) async throws -> T { - try await requirea(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) +public func pollUnwrap(location: SourceLocation = SourceLocation(), timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: () -> (@Sendable () async throws -> T?)) async throws -> T { + try await requirea(location: location, 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, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure @escaping @Sendable () async throws -> T?) async throws -> T { - try await requirea(file: file, line: line, try await expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) +public func pollUnwrapa(location: SourceLocation = SourceLocation(), timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure @escaping @Sendable () async throws -> T?) async throws -> T { + try await requirea(location: location, 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, timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () async throws -> T?)) async throws -> T { - try await requirea(file: file, line: line, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) +public func pollUnwrapa(location: SourceLocation = SourceLocation(), timeout: NimbleTimeInterval = PollingDefaults.timeout, pollInterval: NimbleTimeInterval = PollingDefaults.pollInterval, description: String? = nil, _ expression: @autoclosure () -> (@Sendable () async throws -> T?)) async throws -> T { + try await requirea(location: location, expression()).toEventuallyNot(beNil(), timeout: timeout, pollInterval: pollInterval, description: description) } #endif // #if !os(WASI) diff --git a/Sources/Nimble/Utils/AsyncAwait.swift b/Sources/Nimble/Utils/AsyncAwait.swift index 36429791c..3225bcc0c 100644 --- a/Sources/Nimble/Utils/AsyncAwait.swift +++ b/Sources/Nimble/Utils/AsyncAwait.swift @@ -286,10 +286,7 @@ private func runAwaitTrigger( } else { fail( "waitUntil(..) expects its completion closure to be only called once", - fileID: sourceLocation.fileID, - file: sourceLocation.filePath, - line: sourceLocation.line, - column: sourceLocation.column + location: sourceLocation ) } } diff --git a/Sources/Nimble/Utils/PollAwait.swift b/Sources/Nimble/Utils/PollAwait.swift index 5f5913460..9003e93f1 100644 --- a/Sources/Nimble/Utils/PollAwait.swift +++ b/Sources/Nimble/Utils/PollAwait.swift @@ -314,8 +314,7 @@ internal class Awaiter { } func performBlock( - file: FileString, - line: UInt, + location: SourceLocation, _ closure: @escaping (@escaping @Sendable (T) -> Void) throws -> Void ) -> AwaitPromiseBuilder { let promise = AwaitPromise() @@ -344,7 +343,7 @@ internal class Awaiter { } } else { fail("waitUntil(..) expects its completion closure to be only called once", - file: file, line: line) + location: location) } } } diff --git a/Sources/Nimble/Utils/SourceLocation.swift b/Sources/Nimble/Utils/SourceLocation.swift index 557e11219..e3981031f 100644 --- a/Sources/Nimble/Utils/SourceLocation.swift +++ b/Sources/Nimble/Utils/SourceLocation.swift @@ -11,6 +11,7 @@ public typealias FileString = StaticString public typealias FileString = String #endif +@objc public final class SourceLocation: NSObject, Sendable { public let fileID: String @available(*, deprecated, renamed: "filePath") @@ -19,14 +20,8 @@ public final class SourceLocation: NSObject, Sendable { public let line: UInt public let column: UInt - override init() { - fileID = "Unknown/File" - filePath = "Unknown File" - line = 0 - column = 0 - } - - init(fileID: String, filePath: FileString, line: UInt, column: UInt) { + @objc + public init(fileID: String = #fileID, filePath: FileString = #filePath, line: UInt = #line, column: UInt = #column) { self.fileID = fileID self.filePath = filePath self.line = line diff --git a/Sources/NimbleObjectiveC/DSL.m b/Sources/NimbleObjectiveC/DSL.m index 1aad14e36..68e69852f 100644 --- a/Sources/NimbleObjectiveC/DSL.m +++ b/Sources/NimbleObjectiveC/DSL.m @@ -155,22 +155,22 @@ NIMBLE_EXPORT void NMB_failWithMessage(NSString *msg, NSString *file, NSUInteger NIMBLE_EXPORT NMBWaitUntilTimeoutBlock NMB_waitUntilTimeoutBuilder(NSString *file, NSUInteger line) { return ^(NSTimeInterval timeout, void (^ _Nonnull action)(void (^ _Nonnull)(void))) { [NMBWait untilTimeout:timeout - fileID:[NSString stringWithFormat:@"Unknown/%@", file] - file:file - line:line - column:0 + location: [[SourceLocation alloc] initWithFileID:[NSString stringWithFormat:@"Unknown/%@", file] + filePath:file + line:line + column:0] action:action]; }; } NIMBLE_EXPORT NMBWaitUntilBlock NMB_waitUntilBuilder(NSString *file, NSUInteger line) { - return ^(void (^ _Nonnull action)(void (^ _Nonnull)(void))) { - [NMBWait untilFileID:[NSString stringWithFormat:@"Unknown/%@", file] - file:file - line:line - column:0 - action:action]; - }; + return ^(void (^ _Nonnull action)(void (^ _Nonnull)(void))) { + [NMBWait untilLocation:[[SourceLocation alloc] initWithFileID:[NSString stringWithFormat:@"Unknown/%@", file] + filePath:file + line:line + column:0] + action:action]; + }; } NS_ASSUME_NONNULL_END diff --git a/Sources/NimbleSharedTestHelpers/utils.swift b/Sources/NimbleSharedTestHelpers/utils.swift index 1d94b91ac..987678c75 100644 --- a/Sources/NimbleSharedTestHelpers/utils.swift +++ b/Sources/NimbleSharedTestHelpers/utils.swift @@ -10,17 +10,13 @@ import XCTest public func failsWithErrorMessage( _ messages: [String], - fileID: String = #fileID, - filePath: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + location: SourceLocation = SourceLocation(), preferOriginalSourceLocation: Bool = false, closure: () throws -> Void ) { - var location = SourceLocation(fileID: fileID, filePath: filePath, line: line, column: column) - + var location = location let recorder = AssertionRecorder() - withAssertionHandler(recorder, fileID: fileID, file: filePath, line: line, column: column, closure: closure) + withAssertionHandler(recorder, location: location, closure: closure) for msg in messages { var lastFailure: AssertionRecord? @@ -64,14 +60,11 @@ public func failsWithErrorMessage( // Verifies that the error message matches the given regex. public func failsWithErrorRegex( _ regex: String, - fileID: String = #fileID, - filePath: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + location: SourceLocation = SourceLocation(), closure: () throws -> Void ) { let recorder = AssertionRecorder() - withAssertionHandler(recorder, fileID: fileID, file: filePath, line: line, column: column, closure: closure) + withAssertionHandler(recorder, location: location, closure: closure) for assertion in recorder.assertions where assertion.message.stringValue.range(of: regex, options: .regularExpression) != nil && !assertion.success { return @@ -85,48 +78,46 @@ public func failsWithErrorRegex( Assertions Received: \(recorder.assertions) """ - NimbleAssertionHandler.assert(false, - message: FailureMessage(stringValue: message), - location: SourceLocation(fileID: fileID, filePath: filePath, line: line, column: column)) + NimbleAssertionHandler.assert( + false, + message: FailureMessage(stringValue: message), + location: location + ) } public func failsWithErrorMessage( _ message: String, - fileID: String = #fileID, - filePath: FileString = #filePath, - line: UInt = #line, - column: UInt = #column, + location: SourceLocation = SourceLocation(), preferOriginalSourceLocation: Bool = false, closure: () throws -> Void ) { failsWithErrorMessage( [message], - fileID: fileID, - filePath: filePath, - line: line, - column: column, + location: location, preferOriginalSourceLocation: preferOriginalSourceLocation, closure: closure ) } -public func failsWithErrorMessageForNil(_ message: String, fileID: String = #fileID, filePath: FileString = #filePath, line: UInt = #line, column: UInt = #column, preferOriginalSourceLocation: Bool = false, closure: () throws -> Void) { +public func failsWithErrorMessageForNil( + _ message: String, + location: SourceLocation = SourceLocation(), + preferOriginalSourceLocation: Bool = false, + closure: () throws -> Void +) { failsWithErrorMessage( "\(message) (use beNil() to match nils)", - fileID: fileID, - filePath: filePath, - line: line, - column: column, + location: location, preferOriginalSourceLocation: preferOriginalSourceLocation, closure: closure ) } -public func failsWithErrorMessage(_ messages: [String], fileID: String = #fileID, filePath: FileString = #filePath, line: UInt = #line, column: UInt = #column, preferOriginalSourceLocation: Bool = false, closure: () async throws -> Void) async { - var sourceLocation = SourceLocation(fileID: fileID, filePath: filePath, line: line, column: column) +public func failsWithErrorMessage(_ messages: [String], location: SourceLocation = SourceLocation(), preferOriginalSourceLocation: Bool = false, closure: () async throws -> Void) async { + var sourceLocation = location let recorder = AssertionRecorder() - await withAssertionHandler(recorder, fileID: fileID, file: filePath, line: line, column: column, closure: closure) + await withAssertionHandler(recorder, location: location, closure: closure) for msg in messages { var lastFailure: AssertionRecord? @@ -167,25 +158,29 @@ public func failsWithErrorMessage(_ messages: [String], fileID: String = #fileID } } -public func failsWithErrorMessage(_ message: String, fileID: String = #fileID, filePath: FileString = #filePath, line: UInt = #line, column: UInt = #column, preferOriginalSourceLocation: Bool = false, closure: () async throws -> Void) async { +public func failsWithErrorMessage( + _ message: String, + location: SourceLocation = SourceLocation(), + preferOriginalSourceLocation: Bool = false, + closure: () async throws -> Void +) async { await failsWithErrorMessage( [message], - fileID: fileID, - filePath: filePath, - line: line, - column: column, + location: location, preferOriginalSourceLocation: preferOriginalSourceLocation, closure: closure ) } -public func failsWithErrorMessageForNil(_ message: String, fileID: String = #fileID, filePath: FileString = #filePath, line: UInt = #line, column: UInt = #column, preferOriginalSourceLocation: Bool = false, closure: () async throws -> Void) async { +public func failsWithErrorMessageForNil( + _ message: String, + location: SourceLocation = SourceLocation(), + preferOriginalSourceLocation: Bool = false, + closure: () async throws -> Void +) async { await failsWithErrorMessage( "\(message) (use beNil() to match nils)", - fileID: fileID, - filePath: filePath, - line: line, - column: column, + location: location, preferOriginalSourceLocation: preferOriginalSourceLocation, closure: closure ) @@ -201,16 +196,20 @@ public func suppressErrors(closure: () -> T) -> T { return output! } -public func producesStatus(_ status: ExpectationStatus, fileID: String = #fileID, filePath: FileString = #filePath, line: UInt = #line, column: UInt = #column, closure: () -> SyncExpectation) { +public func producesStatus( + _ status: ExpectationStatus, + location: SourceLocation = SourceLocation(), + closure: () -> SyncExpectation +) { let expectation = suppressErrors(closure: closure) - expect(fileID: fileID, file: filePath, line: line, column: column, expectation.status).to(equal(status)) + expect(location: location, expectation.status).to(equal(status)) } -public func producesStatus(_ status: ExpectationStatus, fileID: String = #fileID, filePath: FileString = #filePath, line: UInt = #line, column: UInt = #column, closure: () -> AsyncExpectation) { +public func producesStatus(_ status: ExpectationStatus, location: SourceLocation = SourceLocation(), closure: () -> AsyncExpectation) { let expectation = suppressErrors(closure: closure) - expect(fileID: fileID, file: filePath, line: line, column: column, expectation.status).to(equal(status)) + expect(location: location, expectation.status).to(equal(status)) } #if !os(WASI) @@ -227,10 +226,12 @@ public class NimbleHelper: NSObject { @objc public class func expectFailureMessage(_ message: NSString, block: () -> Void, file: FileString, line: UInt) { failsWithErrorMessage( String(describing: message), - fileID: "Unknown/\(file)", - filePath: file, - line: line, - column: 0, + location: SourceLocation( + fileID: "Unknown/\(file)", + filePath: file, + line: line, + column: 0 + ), preferOriginalSourceLocation: true, closure: block ) @@ -239,10 +240,12 @@ public class NimbleHelper: NSObject { @objc public class func expectFailureMessages(_ messages: [NSString], block: () -> Void, file: FileString, line: UInt) { failsWithErrorMessage( messages.map({String(describing: $0)}), - fileID: "Unknown/\(file)", - filePath: file, - line: line, - column: 0, + location: SourceLocation( + fileID: "Unknown/\(file)", + filePath: file, + line: line, + column: 0 + ), preferOriginalSourceLocation: true, closure: block ) @@ -251,10 +254,12 @@ public class NimbleHelper: NSObject { @objc public class func expectFailureMessageForNil(_ message: NSString, block: () -> Void, file: FileString, line: UInt) { failsWithErrorMessageForNil( String(describing: message), - fileID: "Unknown/\(file)", - filePath: file, - line: line, - column: 0, + location: SourceLocation( + fileID: "Unknown/\(file)", + filePath: file, + line: line, + column: 0 + ), preferOriginalSourceLocation: true, closure: block ) @@ -263,10 +268,12 @@ public class NimbleHelper: NSObject { @objc public class func expectFailureMessageRegex(_ regex: NSString, block: () -> Void, file: FileString, line: UInt) { failsWithErrorRegex( String(describing: regex), - fileID: "Unknown/\(file)", - filePath: file, - line: line, - column: 0, + location: SourceLocation( + fileID: "Unknown/\(file)", + filePath: file, + line: line, + column: 0 + ), closure: block ) } diff --git a/Tests/NimbleTests/AsyncAwaitTest.swift b/Tests/NimbleTests/AsyncAwaitTest.swift index 7ae5c7a63..44d2cd8ea 100644 --- a/Tests/NimbleTests/AsyncAwaitTest.swift +++ b/Tests/NimbleTests/AsyncAwaitTest.swift @@ -284,7 +284,7 @@ final class AsyncAwaitTest: XCTestCase { // swiftlint:disable:this type_body_len for index in 0..<100 { if failed { break } - await waitUntil(line: UInt(index)) { done in + await waitUntil(location: SourceLocation(column: UInt(index))) { done in DispatchQueue(label: "Nimble.waitUntilTest.\(index)").async { done() } diff --git a/Tests/NimbleTests/PollingTest.swift b/Tests/NimbleTests/PollingTest.swift index 20f6fcfc8..4f0b315f6 100644 --- a/Tests/NimbleTests/PollingTest.swift +++ b/Tests/NimbleTests/PollingTest.swift @@ -202,7 +202,7 @@ final class PollingTest: XCTestCase { for index in 0..<100 { if failed { break } - waitUntil(line: UInt(index)) { done in + waitUntil(location: SourceLocation(column: UInt(index))) { done in DispatchQueue(label: "Nimble.waitUntilTest.\(index)").async { done() }