Prevent spurious test failure in testShuttingDownFailsRegistration (#338)

Motivation:

There were two errors in testShuttingDownFailsRegistration that
could cause spurious test failures.

The first was that the test did not wait for connection establishment
before it began shutting the selector down, due to a change made in

The second was that the server side of the connection was not wedged open,
and so it would be closed by the call to loop.closeGently. That closure
could, if the main thread took long enough, lead to the client side
reading EOF and being forcibly closed.

Modifications:

Added a promise to wait for connection establishment.
Added support for wedging both the server and client side open.

Result:

Fewer test failures.
This commit is contained in:
Cory Benfield 2018-04-23 11:32:34 +01:00 committed by GitHub
parent cbb3f19f62
commit f06a9ccb79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 28 additions and 5 deletions

View File

@ -130,20 +130,31 @@ public class EventLoopTest : XCTestCase {
typealias OutboundIn = Any
typealias OutboundOut = Any
private let promiseRegisterCallback: (EventLoopPromise<Void>) -> Void
var closePromise: EventLoopPromise<Void>? = nil
init(_ promiseRegisterCallback: @escaping (EventLoopPromise<Void>) -> Void) {
self.promiseRegisterCallback = promiseRegisterCallback
}
func close(ctx: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise<Void>?) {
guard self.closePromise == nil else {
XCTFail("Attempted to create duplicate close promise")
return
}
XCTAssertTrue(ctx.channel.isActive)
self.closePromise = ctx.eventLoop.newPromise()
self.closePromise!.futureResult.whenSuccess {
ctx.close(mode: mode, promise: promise)
}
promiseRegisterCallback(self.closePromise!)
}
}
let promiseQueue = DispatchQueue(label: "promiseQueue")
var promises: [EventLoopPromise<Void>] = []
let group = MultiThreadedEventLoopGroup(numThreads: 1)
defer {
do {
@ -156,13 +167,22 @@ public class EventLoopTest : XCTestCase {
}
let loop = group.next() as! SelectableEventLoop
let serverChannel = try ServerBootstrap(group: group).bind(host: "127.0.0.1", port: 0).wait()
let serverChannel = try ServerBootstrap(group: group)
.childChannelInitializer { channel in
channel.pipeline.add(handler: WedgeOpenHandler { promise in
promiseQueue.sync { promises.append(promise) }
})
}
.bind(host: "127.0.0.1", port: 0).wait()
defer {
XCTAssertNoThrow(try serverChannel.syncCloseAcceptingAlreadyClosed())
}
let connectPromise: EventLoopPromise<Void> = loop.newPromise()
// We're going to create and register a channel, but not actually attempt to do anything with it.
let wedgeHandler = WedgeOpenHandler()
let wedgeHandler = WedgeOpenHandler { promise in
promiseQueue.sync { promises.append(promise) }
}
let channel = try SocketChannel(eventLoop: loop, protocolFamily: AF_INET)
_ = try channel.eventLoop.submit {
channel.pipeline.add(handler: wedgeHandler).then {
@ -170,9 +190,12 @@ public class EventLoopTest : XCTestCase {
}.then {
// connecting here to stop epoll from throwing EPOLLHUP at us
channel.connect(to: serverChannel.localAddress!)
}
}.cascade(promise: connectPromise)
}.wait()
// Wait for the connect to complete.
XCTAssertNoThrow(try connectPromise.futureResult.wait())
// Now we're going to start closing the event loop. This should not immediately succeed.
let loopCloseFut = loop.closeGently()
@ -192,8 +215,8 @@ public class EventLoopTest : XCTestCase {
XCTAssertFalse(loopCloseFut.isFulfilled)
// Now let it close.
loop.execute {
wedgeHandler.closePromise!.succeed(result: ())
promiseQueue.sync {
promises.forEach { $0.succeed(result: ()) }
}
XCTAssertNoThrow(try loopCloseFut.wait())
}