Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add an always method on EventLoopFuture #981

Merged
merged 9 commits into from
May 15, 2019
15 changes: 15 additions & 0 deletions Sources/NIO/EventLoopFuture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1267,3 +1267,18 @@ func executeAndComplete<Value>(_ promise: EventLoopPromise<Value>?, _ body: () t
promise?.fail(e)
}
}


extension EventLoopFuture {
/// Adds an observer callback to this `EventLoopFuture` that is called when the
/// `EventLoopFuture` has any result.
///
/// - parameters:
/// - callback: the callback that is called when the `EventLoopFuture` is fulfilled.
/// - returns: the current `EventLoopFuture`
public func always(_ callback: @escaping (Result<Value, Error>) -> Void) -> EventLoopFuture<Value> {
self.whenComplete { result in callback(result) }
return self
}

}
2 changes: 2 additions & 0 deletions Tests/NIOTests/EventLoopFutureTest+XCTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ extension EventLoopFutureTest {
("testWhenAllCompleteResultsWithFailuresStillSucceed", testWhenAllCompleteResultsWithFailuresStillSucceed),
("testWhenAllCompleteResults", testWhenAllCompleteResults),
("testWhenAllCompleteResolvesAfterFutures", testWhenAllCompleteResolvesAfterFutures),
("testAlways", testAlways),
("testAlwaysWithFailingPromise", testAlwaysWithFailingPromise),
]
}
}
Expand Down
45 changes: 45 additions & 0 deletions Tests/NIOTests/EventLoopFutureTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1015,4 +1015,49 @@ class EventLoopFutureTest : XCTestCase {
let results = try assertNoThrowWithValue(mainFuture.wait().map { try $0.get() })
XCTAssertEqual(results, [0, 1, 2, 3, 4])
}

struct DatabaseError: Error {}
struct Database {
let query: () -> EventLoopFuture<[String]>

var closed = false

init(query: @escaping () -> EventLoopFuture<[String]>) {
self.query = query
}

func runQuery() -> EventLoopFuture<[String]> {
return query()
}

mutating func close() {
self.closed = true
}
}

func testAlways() throws {
let group = EmbeddedEventLoop()
let loop = group.next()
var db = Database { loop.makeSucceededFuture(["Item 1", "Item 2", "Item 3"]) }

XCTAssertFalse(db.closed)
let _ = try assertNoThrowWithValue(db.runQuery().always { result in
assertSuccess(result)
db.close()
}.map { $0.map { $0.uppercased() }}.wait())
XCTAssertTrue(db.closed)
}

func testAlwaysWithFailingPromise() throws {
let group = EmbeddedEventLoop()
let loop = group.next()
var db = Database { loop.makeFailedFuture(DatabaseError()) }

XCTAssertFalse(db.closed)
let _ = try XCTAssertThrowsError(db.runQuery().always { result in
assertFailure(result)
db.close()
}.map { $0.map { $0.uppercased() }}.wait()) { XCTAssertTrue($0 is DatabaseError) }
XCTAssertTrue(db.closed)
}
}
8 changes: 8 additions & 0 deletions Tests/NIOTests/TestUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,11 @@ func getBoolSocketOption<IntType: SignedInteger>(channel: Channel, level: IntTyp
file: file,
line: line).wait() != 0
}

func assertSuccess<Value>(_ result: Result<Value, Error>, file: StaticString = #file, line: UInt = #line) {
guard case .success = result else { return XCTFail("Expected result to be successful", file: file, line: line) }
}

func assertFailure<Value>(_ result: Result<Value, Error>, file: StaticString = #file, line: UInt = #line) {
guard case .failure = result else { return XCTFail("Expected result to be a failure", file: file, line: line) }
}