mirror of
https://github.com/swift-server/async-http-client.git
synced 2026-06-02 07:37:34 +00:00
e401a2801c
allowed only using exposed API. Motivation: Having a setter for internal state of ConnectionsState led to a subset of test testing invalid invariants, for example when we have at the same time an available connecion and a waiter, which is an invalid state of the system. Modifications: * test of ConnectionsState * ConnectionsState * ConnectionPool are modified so the state under tests is achieved only by a sequence of modifications invoked by state API. During modification some tests are eliminated as they were testing artificial state, which can not be achieved by exposed APIs. ConnectionsState is pruned from "replace" as in no valid state we can have a situation when we can "replace" a connection. Invalid invariants and tests are removed. Result: We do not have a way to modify state of the ConnectionsState by direct interaction with private state of the object. Getter on the state is considered harmless and used for tests only.
620 lines
24 KiB
Swift
620 lines
24 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the AsyncHTTPClient open source project
|
|
//
|
|
// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
@testable import AsyncHTTPClient
|
|
@testable import NIO
|
|
import NIOConcurrencyHelpers
|
|
import NIOFoundationCompat
|
|
import NIOHTTP1
|
|
import NIOHTTPCompression
|
|
import NIOSSL
|
|
import NIOTestUtils
|
|
import NIOTransportServices
|
|
import XCTest
|
|
|
|
class ConnectionPoolTests: XCTestCase {
|
|
var eventLoop: EmbeddedEventLoop!
|
|
|
|
func testPending() {
|
|
var state = HTTP1ConnectionProvider.ConnectionsState<ConnectionForTests>(eventLoop: self.eventLoop)
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 0)
|
|
XCTAssertTrue(state.enqueue())
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 1, opened: 0)
|
|
}
|
|
|
|
// MARK: - Acquire Tests
|
|
|
|
func testAcquireWhenEmpty() {
|
|
var state = HTTP1ConnectionProvider.ConnectionsState<ConnectionForTests>(eventLoop: self.eventLoop)
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 0)
|
|
|
|
XCTAssertTrue(state.enqueue())
|
|
let action = state.acquire(waiter: .init(promise: self.eventLoop.makePromise(), setupComplete: self.eventLoop.makeSucceededFuture(()), preference: .indifferent))
|
|
switch action {
|
|
case .create(let waiter):
|
|
waiter.promise.fail(TempError())
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 1)
|
|
}
|
|
|
|
func testAcquireWhenAvailable() throws {
|
|
var (state, _) = self.buildState(count: 1)
|
|
|
|
// Validate that the pool has one available connection and it's internal state is correct
|
|
XCTAssertState(state, available: 1, leased: 0, waiters: 0, pending: 0, opened: 1)
|
|
|
|
XCTAssertTrue(state.enqueue())
|
|
|
|
let action = state.acquire(waiter: .init(promise: self.eventLoop.makePromise(), setupComplete: self.eventLoop.makeSucceededFuture(()), preference: .indifferent))
|
|
switch action {
|
|
case .lease(let connection, let waiter):
|
|
XCTAssertState(state, available: 0, leased: 1, waiters: 0, pending: 0, opened: 1)
|
|
|
|
// cleanup
|
|
waiter.promise.succeed(connection)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
// cleanup
|
|
try XCTAssertStateClose(state, available: 0, leased: 1, waiters: 0, clean: false)
|
|
}
|
|
|
|
func testAcquireWhenUnavailable() throws {
|
|
var (state, _) = self.buildState(count: 8, release: false)
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 0, pending: 0, opened: 8)
|
|
|
|
XCTAssertTrue(state.enqueue())
|
|
let action = state.acquire(waiter: .init(promise: self.eventLoop.makePromise(), setupComplete: self.eventLoop.makeSucceededFuture(()), preference: .indifferent))
|
|
switch action {
|
|
case .none:
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 1, pending: 0, opened: 8)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
// cleanup
|
|
try XCTAssertStateClose(state, available: 0, leased: 8, waiters: 1, clean: false)
|
|
}
|
|
|
|
// MARK: - Acquire on Specific EL Tests
|
|
|
|
func testAcquireWhenEmptySpecificEL() {
|
|
let el: EventLoop = self.eventLoop
|
|
let preference: HTTPClient.EventLoopPreference = .delegateAndChannel(on: el)
|
|
|
|
var state = HTTP1ConnectionProvider.ConnectionsState<ConnectionForTests>(eventLoop: el)
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 0)
|
|
|
|
XCTAssertTrue(state.enqueue())
|
|
let action = state.acquire(waiter: .init(promise: el.makePromise(), setupComplete: el.makeSucceededFuture(()), preference: preference))
|
|
switch action {
|
|
case .create(let waiter):
|
|
waiter.promise.fail(TempError())
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 1)
|
|
}
|
|
|
|
func testAcquireWhenAvailableSpecificEL() throws {
|
|
let el: EventLoop = self.eventLoop
|
|
let preference: HTTPClient.EventLoopPreference = .delegateAndChannel(on: el)
|
|
var (state, _) = self.buildState(count: 1, eventLoop: el)
|
|
XCTAssertState(state, available: 1, leased: 0, waiters: 0, pending: 0, opened: 1)
|
|
|
|
XCTAssertTrue(state.enqueue())
|
|
let action = state.acquire(waiter: .init(promise: el.makePromise(), setupComplete: el.makeSucceededFuture(()), preference: preference))
|
|
switch action {
|
|
case .lease(let connection, let waiter):
|
|
waiter.promise.succeed(connection)
|
|
XCTAssertState(state, available: 0, leased: 1, waiters: 0, pending: 0, opened: 1)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
// cleanup
|
|
try XCTAssertStateClose(state, available: 0, leased: 1, waiters: 0, clean: false)
|
|
}
|
|
|
|
func testAcquireReplace() throws {
|
|
let el: EventLoop = self.eventLoop
|
|
var (state, connections) = self.buildState(count: 8, release: false, eventLoop: el)
|
|
|
|
// release a connection
|
|
_ = state.release(connection: connections.first!, closing: false)
|
|
XCTAssertState(state, available: 1, leased: 7, waiters: 0, pending: 0, opened: 8)
|
|
|
|
// other eventLoop
|
|
let preference: HTTPClient.EventLoopPreference = .delegateAndChannel(on: EmbeddedEventLoop())
|
|
|
|
XCTAssertTrue(state.enqueue())
|
|
let action = state.acquire(waiter: .init(promise: el.makePromise(), setupComplete: el.makeSucceededFuture(()), preference: preference))
|
|
switch action {
|
|
case .replace(let connection, let waiter):
|
|
waiter.promise.fail(TempError())
|
|
XCTAssertState(state, available: 0, leased: 7, waiters: 0, pending: 0, opened: 8, isNotLeased: connection)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
// cleanup
|
|
try XCTAssertStateClose(state, available: 0, leased: 7, waiters: 0, clean: false)
|
|
}
|
|
|
|
func testAcquireWhenUnavailableSpecificEL() throws {
|
|
var (state, _) = self.buildState(count: 8, release: false, eventLoop: self.eventLoop)
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 0, pending: 0, opened: 8)
|
|
|
|
XCTAssertTrue(state.enqueue())
|
|
let action = state.acquire(waiter: .init(promise: self.eventLoop.makePromise(), setupComplete: self.eventLoop.makeSucceededFuture(()), preference: .delegateAndChannel(on: self.eventLoop)))
|
|
switch action {
|
|
case .none:
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 1, pending: 0, opened: 8)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
// cleanup
|
|
try XCTAssertStateClose(state, available: 0, leased: 8, waiters: 1, clean: false)
|
|
}
|
|
|
|
// MARK: - Acquire Errors Tests
|
|
|
|
func testAcquireWhenClosed() {
|
|
var state = HTTP1ConnectionProvider.ConnectionsState<Connection>(eventLoop: self.eventLoop)
|
|
_ = state.close()
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 0)
|
|
|
|
XCTAssertFalse(state.enqueue())
|
|
|
|
let promise = self.eventLoop.makePromise(of: Connection.self)
|
|
let action = state.acquire(waiter: .init(promise: promise, setupComplete: self.eventLoop.makeSucceededFuture(()), preference: .indifferent))
|
|
switch action {
|
|
case .fail(let waiter, let error):
|
|
waiter.promise.fail(error)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
}
|
|
|
|
func testConnectFailedWhenClosed() {
|
|
var state = HTTP1ConnectionProvider.ConnectionsState<Connection>(eventLoop: self.eventLoop)
|
|
_ = state.close()
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 0)
|
|
|
|
let action = state.connectFailed()
|
|
switch action {
|
|
case .none:
|
|
break
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
}
|
|
|
|
// MARK: - Release Tests
|
|
|
|
func testReleaseAliveConnectionEmptyQueue() throws {
|
|
var (state, connections) = self.buildState(count: 1, release: false)
|
|
XCTAssertState(state, available: 0, leased: 1, waiters: 0, pending: 0, opened: 1)
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
let action = state.release(connection: connection, closing: false)
|
|
switch action {
|
|
case .park:
|
|
XCTAssertState(state, available: 1, leased: 0, waiters: 0, pending: 0, opened: 1)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
// cleanup
|
|
try XCTAssertStateClose(state, available: 1, leased: 0, waiters: 0, clean: true)
|
|
}
|
|
|
|
func testReleaseAliveButClosingConnectionEmptyQueue() throws {
|
|
var (state, connections) = self.buildState(count: 1, release: false)
|
|
XCTAssertState(state, available: 0, leased: 1, waiters: 0, pending: 0, opened: 1)
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
// closing should be true to test that we can discard connection that is still active, but caller indicated that it will be closed soon
|
|
let action = state.release(connection: connection, closing: true)
|
|
switch action {
|
|
case .closeProvider:
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 0)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
// cleanup
|
|
XCTAssertNil(state.close())
|
|
}
|
|
|
|
func testReleaseInactiveConnectionEmptyQueue() throws {
|
|
var (state, connections) = self.buildState(count: 1, release: false)
|
|
XCTAssertState(state, available: 0, leased: 1, waiters: 0, pending: 0, opened: 1)
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
connection.isActiveEstimation = false
|
|
// closing should be false to test that we check connection state in order to decided if we need to discard the connection
|
|
let action = state.release(connection: connection, closing: false)
|
|
switch action {
|
|
case .closeProvider:
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 0)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
// cleanup
|
|
XCTAssertNil(state.close())
|
|
}
|
|
|
|
func testReleaseInactiveConnectionEmptyQueueHasConnections() throws {
|
|
var (state, connections) = self.buildState(count: 2, release: false)
|
|
XCTAssertState(state, available: 0, leased: 2, waiters: 0, pending: 0, opened: 2)
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
|
|
// Return a connection to the pool
|
|
_ = state.release(connection: try XCTUnwrap(connections.dropFirst().first), closing: false)
|
|
XCTAssertState(state, available: 1, leased: 1, waiters: 0, pending: 0, opened: 2)
|
|
|
|
let action = state.release(connection: connection, closing: true)
|
|
switch action {
|
|
case .none:
|
|
XCTAssertState(state, available: 1, leased: 0, waiters: 0, pending: 0, opened: 1)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
// cleanup
|
|
try XCTAssertStateClose(state, available: 1, leased: 0, waiters: 0, clean: true)
|
|
}
|
|
|
|
func testReleaseAliveConnectionHasWaiter() throws {
|
|
var (state, connections) = self.buildState(count: 8, release: false)
|
|
|
|
// Add one waiter to the pool
|
|
XCTAssertTrue(state.enqueue())
|
|
_ = state.acquire(waiter: .init(promise: self.eventLoop.makePromise(), setupComplete: self.eventLoop.makeSucceededFuture(()), preference: .indifferent))
|
|
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 1, pending: 0, opened: 8)
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
let action = state.release(connection: connection, closing: false)
|
|
switch action {
|
|
case .lease(let connection, let waiter):
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 0, pending: 0, opened: 8, isLeased: connection)
|
|
// cleanup
|
|
waiter.promise.succeed(connection)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
// cleanup
|
|
try XCTAssertStateClose(state, available: 0, leased: 8, waiters: 0, clean: false)
|
|
}
|
|
|
|
func testReleaseInactiveConnectionHasWaitersNoConnections() throws {
|
|
var (state, connections) = self.buildState(count: 8, release: false)
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 0, pending: 0, opened: 8)
|
|
|
|
// Add one waiter to the pool
|
|
XCTAssertTrue(state.enqueue())
|
|
_ = state.acquire(waiter: .init(promise: self.eventLoop.makePromise(), setupComplete: self.eventLoop.makeSucceededFuture(()), preference: .indifferent))
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 1, pending: 0, opened: 8)
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
let action = state.release(connection: connection, closing: true)
|
|
switch action {
|
|
case .create(let waiter):
|
|
XCTAssertState(state, available: 0, leased: 7, waiters: 0, pending: 0, opened: 8)
|
|
// cleanup
|
|
waiter.promise.fail(TempError())
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
// cleanup
|
|
try XCTAssertStateClose(state, available: 0, leased: 7, waiters: 0, clean: false)
|
|
}
|
|
|
|
// MARK: - Release on Specific EL Tests
|
|
|
|
func testReleaseAliveConnectionSameELHasWaiterSpecificEL() throws {
|
|
var (state, connections) = self.buildState(count: 8, release: false)
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 0, pending: 0, opened: 8)
|
|
|
|
// Add one waiter to the pool
|
|
XCTAssertTrue(state.enqueue())
|
|
_ = state.acquire(waiter: .init(promise: self.eventLoop.makePromise(), setupComplete: self.eventLoop.makeSucceededFuture(()), preference: .delegateAndChannel(on: self.eventLoop)))
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 1, pending: 0, opened: 8)
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
let action = state.release(connection: connection, closing: false)
|
|
switch action {
|
|
case .lease(let connection, let waiter):
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 0, pending: 0, opened: 8, isLeased: connection)
|
|
// cleanup
|
|
waiter.promise.succeed(connection)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
try XCTAssertStateClose(state, available: 0, leased: 8, waiters: 0, clean: false)
|
|
}
|
|
|
|
func testReleaseAliveConnectionDifferentELNoSameELConnectionsOnLimitHasWaiterSpecificEL() throws {
|
|
var (state, connections) = self.buildState(count: 8, release: false)
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 0, pending: 0, opened: 8)
|
|
|
|
let differentEL = EmbeddedEventLoop()
|
|
// Add one waiter to the pool
|
|
XCTAssertTrue(state.enqueue())
|
|
_ = state.acquire(waiter: .init(promise: differentEL.makePromise(), setupComplete: differentEL.makeSucceededFuture(()), preference: .delegateAndChannel(on: differentEL)))
|
|
XCTAssertState(state, available: 0, leased: 8, waiters: 1, pending: 0, opened: 8)
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
let action = state.release(connection: connection, closing: false)
|
|
switch action {
|
|
case .replace(let connection, let waiter):
|
|
XCTAssertState(state, available: 0, leased: 7, waiters: 0, pending: 0, opened: 8, isNotLeased: connection)
|
|
// cleanup
|
|
waiter.promise.fail(TempError())
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
|
|
try XCTAssertStateClose(state, available: 0, leased: 7, waiters: 0, clean: false)
|
|
}
|
|
|
|
// MARK: - Next Waiter Tests
|
|
|
|
func testNextWaiterEmptyQueue() throws {
|
|
var (state, _) = self.buildState(count: 0)
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 0)
|
|
|
|
let action = state.processNextWaiter()
|
|
switch action {
|
|
case .closeProvider:
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 0)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
}
|
|
|
|
func testNextWaiterEmptyQueueHasConnections() throws {
|
|
var (state, _) = self.buildState(count: 1, release: true)
|
|
XCTAssertState(state, available: 1, leased: 0, waiters: 0, pending: 0, opened: 1)
|
|
|
|
let action = state.processNextWaiter()
|
|
switch action {
|
|
case .none:
|
|
XCTAssertState(state, available: 1, leased: 0, waiters: 0, pending: 0, opened: 1)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
}
|
|
|
|
// MARK: - Timeout and Remote Close Tests
|
|
|
|
func testTimeoutLeasedConnection() throws {
|
|
var (state, connections) = self.buildState(count: 1, release: false)
|
|
XCTAssertState(state, available: 0, leased: 1, waiters: 0, pending: 0, opened: 1)
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
let action = state.timeout(connection: connection)
|
|
switch action {
|
|
case .none:
|
|
XCTAssertState(state, available: 0, leased: 1, waiters: 0, pending: 0, opened: 1)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
}
|
|
|
|
func testTimeoutAvailableConnection() throws {
|
|
var (state, connections) = self.buildState(count: 1)
|
|
XCTAssertState(state, available: 1, leased: 0, waiters: 0, pending: 0, opened: 1)
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
let action = state.timeout(connection: connection)
|
|
switch action {
|
|
case .closeAnd(_, let after):
|
|
switch after {
|
|
case .closeProvider:
|
|
break
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 0)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
}
|
|
|
|
func testRemoteClosedLeasedConnection() throws {
|
|
var (state, connections) = self.buildState(count: 1, release: false)
|
|
|
|
XCTAssertState(state, available: 0, leased: 1, waiters: 0, pending: 0, opened: 1)
|
|
|
|
// This can happen when just leased connection is closed before TaskHandler is added to pipeline
|
|
let connection = try XCTUnwrap(connections.first)
|
|
let action = state.remoteClosed(connection: connection)
|
|
switch action {
|
|
case .none:
|
|
XCTAssertState(state, available: 0, leased: 1, waiters: 0, pending: 0, opened: 1)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
}
|
|
|
|
func testRemoteClosedAvailableConnection() throws {
|
|
var (state, connections) = self.buildState(count: 1)
|
|
|
|
XCTAssertState(state, available: 1, leased: 0, waiters: 0, pending: 0, opened: 1)
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
let action = state.remoteClosed(connection: connection)
|
|
switch action {
|
|
case .closeProvider:
|
|
XCTAssertState(state, available: 0, leased: 0, waiters: 0, pending: 0, opened: 0)
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
}
|
|
|
|
// MARK: - Shutdown tests
|
|
|
|
func testShutdownOnPendingAndSuccess() {
|
|
var state = HTTP1ConnectionProvider.ConnectionsState<ConnectionForTests>(eventLoop: self.eventLoop)
|
|
|
|
XCTAssertTrue(state.enqueue())
|
|
|
|
let connectionPromise = self.eventLoop.makePromise(of: ConnectionForTests.self)
|
|
let setupPromise = self.eventLoop.makePromise(of: Void.self)
|
|
let waiter = HTTP1ConnectionProvider.Waiter(promise: connectionPromise, setupComplete: setupPromise.futureResult, preference: .indifferent)
|
|
var action = state.acquire(waiter: waiter)
|
|
|
|
guard case .create = action else {
|
|
XCTFail("unexpected action \(action)")
|
|
return
|
|
}
|
|
|
|
let snapshot = state.testsOnly_getInternalState()
|
|
XCTAssertEqual(snapshot.openedConnectionsCount, 1)
|
|
|
|
if let (waiters, available, leased, clean) = state.close() {
|
|
XCTAssertTrue(waiters.isEmpty)
|
|
XCTAssertTrue(available.isEmpty)
|
|
XCTAssertTrue(leased.isEmpty)
|
|
XCTAssertFalse(clean)
|
|
} else {
|
|
XCTFail("Expecting snapshot")
|
|
}
|
|
|
|
let connection = ConnectionForTests(eventLoop: self.eventLoop)
|
|
|
|
action = state.offer(connection: connection)
|
|
guard case .closeAnd(_, .closeProvider) = action else {
|
|
XCTFail("unexpected action \(action)")
|
|
return
|
|
}
|
|
|
|
connectionPromise.fail(TempError())
|
|
setupPromise.succeed(())
|
|
}
|
|
|
|
func testShutdownOnPendingAndError() {
|
|
var state = HTTP1ConnectionProvider.ConnectionsState<ConnectionForTests>(eventLoop: self.eventLoop)
|
|
|
|
XCTAssertTrue(state.enqueue())
|
|
|
|
let connectionPromise = self.eventLoop.makePromise(of: ConnectionForTests.self)
|
|
let setupPromise = self.eventLoop.makePromise(of: Void.self)
|
|
let waiter = HTTP1ConnectionProvider.Waiter(promise: connectionPromise, setupComplete: setupPromise.futureResult, preference: .indifferent)
|
|
var action = state.acquire(waiter: waiter)
|
|
|
|
guard case .create = action else {
|
|
XCTFail("unexpected action \(action)")
|
|
return
|
|
}
|
|
|
|
let snapshot = state.testsOnly_getInternalState()
|
|
XCTAssertEqual(snapshot.openedConnectionsCount, 1)
|
|
|
|
if let (waiters, available, leased, clean) = state.close() {
|
|
XCTAssertTrue(waiters.isEmpty)
|
|
XCTAssertTrue(available.isEmpty)
|
|
XCTAssertTrue(leased.isEmpty)
|
|
XCTAssertFalse(clean)
|
|
} else {
|
|
XCTFail("Expecting snapshot")
|
|
}
|
|
|
|
action = state.connectFailed()
|
|
guard case .closeProvider = action else {
|
|
XCTFail("unexpected action \(action)")
|
|
return
|
|
}
|
|
|
|
connectionPromise.fail(TempError())
|
|
setupPromise.succeed(())
|
|
}
|
|
|
|
func testShutdownTimeout() throws {
|
|
var (state, connections) = self.buildState(count: 1)
|
|
|
|
if let (waiters, available, leased, clean) = state.close() {
|
|
XCTAssertTrue(waiters.isEmpty)
|
|
XCTAssertFalse(available.isEmpty)
|
|
XCTAssertTrue(leased.isEmpty)
|
|
XCTAssertTrue(clean)
|
|
} else {
|
|
XCTFail("Expecting snapshot")
|
|
}
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
let action = state.timeout(connection: connection)
|
|
switch action {
|
|
case .closeAnd(_, let next):
|
|
switch next {
|
|
case .closeProvider:
|
|
// expected
|
|
break
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
}
|
|
|
|
func testShutdownRemoteClosed() throws {
|
|
var (state, connections) = self.buildState(count: 1)
|
|
|
|
if let (waiters, available, leased, clean) = state.close() {
|
|
XCTAssertTrue(waiters.isEmpty)
|
|
XCTAssertFalse(available.isEmpty)
|
|
XCTAssertTrue(leased.isEmpty)
|
|
XCTAssertTrue(clean)
|
|
} else {
|
|
XCTFail("Expecting snapshot")
|
|
}
|
|
|
|
let connection = try XCTUnwrap(connections.first)
|
|
let action = state.remoteClosed(connection: connection)
|
|
switch action {
|
|
case .closeProvider:
|
|
// expected
|
|
break
|
|
default:
|
|
XCTFail("Unexpected action: \(action)")
|
|
}
|
|
}
|
|
|
|
override func setUp() {
|
|
XCTAssertNil(self.eventLoop)
|
|
self.eventLoop = EmbeddedEventLoop()
|
|
}
|
|
|
|
override func tearDown() {
|
|
XCTAssertNotNil(self.eventLoop)
|
|
XCTAssertNoThrow(try self.eventLoop.syncShutdownGracefully())
|
|
self.eventLoop = nil
|
|
}
|
|
}
|