addition of assertSuccess() and assertFailure() on EventLoopFuture (#2417)

* added assertSuccess() and assertFailure()

* test functions for assertSuccess() and assertFailure()

* removed callbacks approach and add precondition variants

* added test for precondition variants

* self.always to handle future w/o creating a promise, added fileID and line for debug

* updated test for asserts on EventLoopFuture
This commit is contained in:
dkz2 2023-05-04 11:25:18 -05:00 committed by GitHub
parent 546eaa261e
commit 7f4d10bd94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 127 additions and 0 deletions

View File

@ -2187,6 +2187,88 @@ extension EventLoopFuture {
}
}
// MARK: assertion
extension EventLoopFuture {
/// Attaches a callback to the `EventLoopFuture` that asserts the original future's success.
///
/// If the original future fails, it triggers an assertion failure, causing a runtime error during development.
/// The assertion failure will include the file and line of the calling site.
///
/// - parameters:
/// - file: The file this function was called in, for debugging purposes.
/// - line: The line this function was called on, for debugging purposes.
@inlinable
public func assertSuccess(file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Value> {
return self.always { result in
switch result {
case .success:
()
case .failure(let error):
assertionFailure("Expected success, but got failure: \(error)", file: file, line: line)
}
}
}
/// Attaches a callback to the `EventLoopFuture` that asserts the original future's failure.
///
/// If the original future succeeds, it triggers an assertion failure, causing a runtime error during development.
/// The assertion failure will include the file and line of the calling site.
///
/// - parameters:
/// - file: The file this function was called in, for debugging purposes.
/// - line: The line this function was called on, for debugging purposes.
@inlinable
public func assertFailure(file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Value> {
return self.always { result in
switch result {
case .success(let value):
assertionFailure("Expected failure, but got success: \(value)", file: file, line: line)
case .failure:
()
}
}
}
/// Attaches a callback to the `EventLoopFuture` that preconditions the original future's success.
///
/// If the original future fails, it triggers a precondition failure, causing a runtime error during development.
/// The precondition failure will include the file and line of the calling site.
///
/// - parameters:
/// - file: The file this function was called in, for debugging purposes.
/// - line: The line this function was called on, for debugging purposes.
@inlinable
public func preconditionSuccess(file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Value> {
return self.always { result in
switch result {
case .success:
()
case .failure(let error):
Swift.preconditionFailure("Expected success, but got failure: \(error)", file: file, line: line)
}
}
}
/// Attaches a callback to the `EventLoopFuture` that preconditions the original future's failure.
///
/// If the original future succeeds, it triggers a precondition failure, causing a runtime error during development.
/// The precondition failure will include the file and line of the calling site.
///
/// - parameters:
/// - file: The file this function was called in, for debugging purposes.
/// - line: The line this function was called on, for debugging purposes.
@inlinable
public func preconditionFailure(file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Value> {
return self.always { result in
switch result {
case .success(let value):
Swift.preconditionFailure("Expected failure, but got success: \(value)", file: file, line: line)
case .failure:
()
}
}
}
}
/// An opaque identifier for a specific `EventLoopFuture`.
///

View File

@ -1496,4 +1496,49 @@ class EventLoopFutureTest : XCTestCase {
XCTAssertEqual((1...10).reduce(0, +), try all.wait())
}
func testAssertSuccess() {
let eventLoop = EmbeddedEventLoop()
let promise = eventLoop.makePromise(of: String.self)
let assertedFuture = promise.futureResult.assertSuccess()
promise.succeed("hello")
XCTAssertNoThrow(try assertedFuture.wait())
}
func testAssertFailure() {
let eventLoop = EmbeddedEventLoop()
let promise = eventLoop.makePromise(of: String.self)
let assertedFuture = promise.futureResult.assertFailure()
promise.fail(EventLoopFutureTestError.example)
XCTAssertThrowsError(try assertedFuture.wait()) { error in
XCTAssertEqual(error as? EventLoopFutureTestError, EventLoopFutureTestError.example)
}
}
func testPreconditionSuccess() {
let eventLoop = EmbeddedEventLoop()
let promise = eventLoop.makePromise(of: String.self)
let preconditionedFuture = promise.futureResult.preconditionSuccess()
promise.succeed("hello")
XCTAssertNoThrow(try preconditionedFuture.wait())
}
func testPreconditionFailure() {
let eventLoop = EmbeddedEventLoop()
let promise = eventLoop.makePromise(of: String.self)
let preconditionedFuture = promise.futureResult.preconditionFailure()
promise.fail(EventLoopFutureTestError.example)
XCTAssertThrowsError(try preconditionedFuture.wait()) { error in
XCTAssertEqual(error as? EventLoopFutureTestError, EventLoopFutureTestError.example)
}
}
}