Bootstrap: test that returning futures from foreign ELs are okay (#55)

Motivation:

NIO on BSD Sockets had an issue where it wouldn't tolerate futures from
foreign EventLoops being returned from the channel initializers. NIOTS
should also have a test that tests this situation despite the fact that
it didn't have the same bug.

Modification:

- add a test case
- add more assertions

Result:

Better tests.
This commit is contained in:
Johannes Weiss 2019-08-16 17:16:14 +01:00 committed by Cory Benfield
parent 217f948d9b
commit 720645045c
3 changed files with 115 additions and 4 deletions

View File

@ -173,7 +173,8 @@ public final class NIOTSConnectionBootstrap {
return channelOptions.applyAllChannelOptions(to: conn).flatMap {
initializer(conn)
}.flatMap {
conn.register()
conn.eventLoop.assertInEventLoop()
return conn.register()
}.flatMap {
let connectPromise: EventLoopPromise<Void> = conn.eventLoop.makePromise()
connectAction(conn, connectPromise)

View File

@ -263,7 +263,8 @@ public final class NIOTSListenerBootstrap {
return serverChannelOptions.applyAllChannelOptions(to: serverChannel).flatMap {
serverChannelInit(serverChannel)
}.flatMap {
serverChannel.pipeline.addHandler(AcceptHandler(childChannelInitializer: childChannelInit,
eventLoop.assertInEventLoop()
return serverChannel.pipeline.addHandler(AcceptHandler(childChannelInitializer: childChannelInit,
childChannelOptions: childChannelOptions))
}.flatMap {
serverChannel.register()

View File

@ -0,0 +1,109 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2019 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
// swift-tools-version:4.0
//
// swift-tools-version:4.0
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#if canImport(Network)
import XCTest
import Network
import NIO
import NIOTransportServices
import NIOConcurrencyHelpers
import Foundation
@available(OSX 10.14, iOS 12.0, tvOS 12.0, *)
final class NIOTSBootstrapTests: XCTestCase {
var groupBag: [NIOTSEventLoopGroup]? = nil // protected by `self.lock`
let lock = Lock()
override func setUp() {
self.lock.withLock {
XCTAssertNil(self.groupBag)
self.groupBag = []
}
}
override func tearDown() {
XCTAssertNoThrow(try self.lock.withLock {
guard let groupBag = self.groupBag else {
XCTFail()
return
}
XCTAssertNoThrow(try groupBag.forEach {
XCTAssertNoThrow(try $0.syncShutdownGracefully())
})
self.groupBag = nil
})
}
func freshEventLoop() -> EventLoop {
let group: NIOTSEventLoopGroup = .init(loopCount: 1, defaultQoS: .default)
self.lock.withLock {
self.groupBag!.append(group)
}
return group.next()
}
func testBootstrapsTolerateFuturesFromDifferentEventLoopsReturnedInInitializers() throws {
let childChannelDone = self.freshEventLoop().makePromise(of: Void.self)
let serverChannelDone = self.freshEventLoop().makePromise(of: Void.self)
let serverChannel = try assertNoThrowWithValue(NIOTSListenerBootstrap(group: self.freshEventLoop())
.childChannelInitializer { channel in
channel.eventLoop.preconditionInEventLoop()
defer {
childChannelDone.succeed(())
}
return self.freshEventLoop().makeSucceededFuture(())
}
.serverChannelInitializer { channel in
channel.eventLoop.preconditionInEventLoop()
defer {
serverChannelDone.succeed(())
}
return self.freshEventLoop().makeSucceededFuture(())
}
.bind(host: "127.0.0.1", port: 0)
.wait())
defer {
XCTAssertNoThrow(try serverChannel.close().wait())
}
let client = try assertNoThrowWithValue(NIOTSConnectionBootstrap(group: self.freshEventLoop())
.channelInitializer { channel in
channel.eventLoop.preconditionInEventLoop()
return self.freshEventLoop().makeSucceededFuture(())
}
.connect(to: serverChannel.localAddress!)
.wait())
defer {
XCTAssertNoThrow(try client.syncCloseAcceptingAlreadyClosed())
}
XCTAssertNoThrow(try childChannelDone.futureResult.wait())
XCTAssertNoThrow(try serverChannelDone.futureResult.wait())
}
}
extension Channel {
func syncCloseAcceptingAlreadyClosed() throws {
do {
try self.close().wait()
} catch ChannelError.alreadyClosed {
/* we're happy with this one */
} catch let e {
throw e
}
}
}
#endif