|
12 | 12 | //
|
13 | 13 | //===----------------------------------------------------------------------===//
|
14 | 14 |
|
15 |
| -import NIO |
| 15 | +import NIOCore |
| 16 | +import NIOPosix |
| 17 | +import NIOEmbedded |
| 18 | +import Atomics |
16 | 19 | @testable import RediStack
|
17 | 20 | import XCTest
|
18 | 21 |
|
@@ -43,6 +46,83 @@ final class RedisCommandHandlerTests: XCTestCase {
|
43 | 46 | XCTAssertEqual(error, .connectionClosed)
|
44 | 47 | }
|
45 | 48 | }
|
| 49 | + |
| 50 | + func testCloseIsTriggeredOnceCommandQueueIsEmpty() { |
| 51 | + let loop = EmbeddedEventLoop() |
| 52 | + let channel = EmbeddedChannel(handler: RedisCommandHandler(), loop: loop) |
| 53 | + |
| 54 | + XCTAssertNoThrow(try channel.connect(to: .init(unixDomainSocketPath: "/foo")).wait()) |
| 55 | + XCTAssertTrue(channel.isActive) |
| 56 | + |
| 57 | + let getFoo = RESPValue.array([.bulkString(.init(string: "GET")), .bulkString(.init(string: "foo"))]) |
| 58 | + let promiseFoo = loop.makePromise(of: RESPValue.self) |
| 59 | + let commandFoo = (message: getFoo, responsePromise: promiseFoo) |
| 60 | + XCTAssertNoThrow(try channel.writeOutbound(commandFoo)) |
| 61 | + XCTAssertEqual(try channel.readOutbound(as: RESPValue.self), getFoo) |
| 62 | + |
| 63 | + let getBar = RESPValue.array([.bulkString(.init(string: "GET")), .bulkString(.init(string: "bar"))]) |
| 64 | + let promiseBar = loop.makePromise(of: RESPValue.self) |
| 65 | + let commandBar = (message: getBar, responsePromise: promiseBar) |
| 66 | + XCTAssertNoThrow(try channel.writeOutbound(commandBar)) |
| 67 | + XCTAssertEqual(try channel.readOutbound(as: RESPValue.self), getBar) |
| 68 | + |
| 69 | + let getBaz = RESPValue.array([.bulkString(.init(string: "GET")), .bulkString(.init(string: "baz"))]) |
| 70 | + let promiseBaz = loop.makePromise(of: RESPValue.self) |
| 71 | + let commandBaz = (message: getBaz, responsePromise: promiseBaz) |
| 72 | + XCTAssertNoThrow(try channel.writeOutbound(commandBaz)) |
| 73 | + XCTAssertEqual(try channel.readOutbound(as: RESPValue.self), getBaz) |
| 74 | + |
| 75 | + let gracefulClosePromise = loop.makePromise(of: Void.self) |
| 76 | + let channelCloseHitCounter = ManagedAtomic<Int>(0) |
| 77 | + gracefulClosePromise.futureResult.whenComplete { _ in |
| 78 | + channelCloseHitCounter.wrappingIncrement(ordering: .relaxed) |
| 79 | + } |
| 80 | + channel.triggerUserOutboundEvent(RedisGracefulConnectionCloseEvent(), promise: gracefulClosePromise) |
| 81 | + XCTAssertEqual(channelCloseHitCounter.load(ordering: .relaxed), 0) |
| 82 | + |
| 83 | + let fooResponse = RESPValue.simpleString(.init(string: "fooresult")) |
| 84 | + XCTAssertNoThrow(try channel.writeInbound(fooResponse)) |
| 85 | + XCTAssertTrue(channel.isActive) |
| 86 | + XCTAssertEqual(channelCloseHitCounter.load(ordering: .relaxed), 0) |
| 87 | + XCTAssertEqual(try promiseFoo.futureResult.wait(), fooResponse) |
| 88 | + |
| 89 | + let barResponse = RESPValue.simpleString(.init(string: "barresult")) |
| 90 | + XCTAssertNoThrow(try channel.writeInbound(barResponse)) |
| 91 | + XCTAssertTrue(channel.isActive) |
| 92 | + XCTAssertEqual(channelCloseHitCounter.load(ordering: .relaxed), 0) |
| 93 | + XCTAssertEqual(try promiseBar.futureResult.wait(), barResponse) |
| 94 | + |
| 95 | + let bazResponse = RESPValue.simpleString(.init(string: "bazresult")) |
| 96 | + XCTAssertNoThrow(try channel.writeInbound(bazResponse)) |
| 97 | + XCTAssertEqual(try promiseBaz.futureResult.wait(), bazResponse) |
| 98 | + XCTAssertFalse(channel.isActive) |
| 99 | + XCTAssertEqual(channelCloseHitCounter.load(ordering: .relaxed), 1) |
| 100 | + XCTAssertNoThrow(try gracefulClosePromise.futureResult.wait()) |
| 101 | + } |
| 102 | + |
| 103 | + func testCloseIsTriggeredRightAwayIfCommandQueueIsEmpty() { |
| 104 | + let loop = EmbeddedEventLoop() |
| 105 | + let channel = EmbeddedChannel(handler: RedisCommandHandler(), loop: loop) |
| 106 | + XCTAssertNoThrow(try channel.connect(to: .init(unixDomainSocketPath: "/foo")).wait()) |
| 107 | + XCTAssertTrue(channel.isActive) |
| 108 | + |
| 109 | + let gracefulClosePromise = loop.makePromise(of: Void.self) |
| 110 | + let gracefulCloseHitCounter = ManagedAtomic<Int>(0) |
| 111 | + gracefulClosePromise.futureResult.whenComplete { _ in |
| 112 | + gracefulCloseHitCounter.wrappingIncrement(ordering: .relaxed) |
| 113 | + } |
| 114 | + channel.triggerUserOutboundEvent(RedisGracefulConnectionCloseEvent(), promise: gracefulClosePromise) |
| 115 | + XCTAssertFalse(channel.isActive) |
| 116 | + XCTAssertEqual(gracefulCloseHitCounter.load(ordering: .relaxed), 1) |
| 117 | + |
| 118 | + let getBar = RESPValue.array([.bulkString(.init(string: "GET")), .bulkString(.init(string: "bar"))]) |
| 119 | + let promiseBar = loop.makePromise(of: RESPValue.self) |
| 120 | + let commandBar = (message: getBar, responsePromise: promiseBar) |
| 121 | + channel.write(commandBar, promise: nil) |
| 122 | + XCTAssertThrowsError(try promiseBar.futureResult.wait()) { |
| 123 | + XCTAssertEqual($0 as? RedisClientError, .connectionClosed) |
| 124 | + } |
| 125 | + } |
46 | 126 | }
|
47 | 127 |
|
48 | 128 | private final class RemoteCloseHandler: ChannelInboundHandler {
|
|
0 commit comments