Files
async-http-client/Tests/AsyncHTTPClientTests/ConnectionPoolTestsSupport.swift
T
Ilya Teterin e401a2801c Fixes #234 by removing setter on internal ConnectionsState so modification (#311)
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.
2020-10-02 14:31:25 +01:00

132 lines
5.1 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
import NIO
import XCTest
class ConnectionForTests: PoolManageableConnection {
var eventLoop: EventLoop
var isActiveEstimation: Bool
init(eventLoop: EventLoop) {
self.eventLoop = eventLoop
self.isActiveEstimation = true
}
func cancel() -> EventLoopFuture<Void> {
return self.eventLoop.makeSucceededFuture(())
}
}
extension ConnectionPoolTests {
func buildState(count: Int, release: Bool = true, eventLoop: EventLoop? = nil) -> (HTTP1ConnectionProvider.ConnectionsState<ConnectionForTests>, [ConnectionForTests]) {
let eventLoop = eventLoop ?? self.eventLoop!
var state = HTTP1ConnectionProvider.ConnectionsState<ConnectionForTests>(eventLoop: eventLoop)
var items: [ConnectionForTests] = []
if count == 0 {
return (state, items)
}
for _ in 1...count {
// Set up connection pool to have one available connection
do {
let connection = ConnectionForTests(eventLoop: eventLoop)
items.append(connection)
// First, we ask the empty pool for a connection, triggering connection creation
XCTAssertTrue(state.enqueue())
let action = state.acquire(waiter: .init(promise: eventLoop.makePromise(), setupComplete: eventLoop.makeSucceededFuture(()), preference: .indifferent))
switch action {
case .create(let waiter):
waiter.promise.succeed(connection)
default:
XCTFail("Unexpected action: \(action)")
}
// We offer the connection to the pool so that it can be tracked
_ = state.offer(connection: connection)
}
}
if release {
for item in items {
// No we release the connection, making it available for the next caller
_ = state.release(connection: item, closing: false)
}
}
return (state, items)
}
}
func XCTAssertState<ConnectionType>(_ state: HTTP1ConnectionProvider.ConnectionsState<ConnectionType>, available: Int, leased: Int, waiters: Int, pending: Int, opened: Int) {
let snapshot = state.testsOnly_getInternalState()
XCTAssertEqual(available, snapshot.availableConnections.count)
XCTAssertEqual(leased, snapshot.leasedConnections.count)
XCTAssertEqual(waiters, snapshot.waiters.count)
XCTAssertEqual(pending, snapshot.pending)
XCTAssertEqual(opened, snapshot.openedConnectionsCount)
}
func XCTAssertState<ConnectionType>(_ state: HTTP1ConnectionProvider.ConnectionsState<ConnectionType>, available: Int, leased: Int, waiters: Int, pending: Int, opened: Int, isLeased connection: ConnectionType) {
let snapshot = state.testsOnly_getInternalState()
XCTAssertEqual(available, snapshot.availableConnections.count)
XCTAssertEqual(leased, snapshot.leasedConnections.count)
XCTAssertEqual(waiters, snapshot.waiters.count)
XCTAssertEqual(pending, snapshot.pending)
XCTAssertEqual(opened, snapshot.openedConnectionsCount)
XCTAssertTrue(snapshot.leasedConnections.contains(ConnectionKey(connection)))
}
func XCTAssertState<ConnectionType>(_ state: HTTP1ConnectionProvider.ConnectionsState<ConnectionType>, available: Int, leased: Int, waiters: Int, pending: Int, opened: Int, isNotLeased connection: ConnectionType) {
let snapshot = state.testsOnly_getInternalState()
XCTAssertEqual(available, snapshot.availableConnections.count)
XCTAssertEqual(leased, snapshot.leasedConnections.count)
XCTAssertEqual(waiters, snapshot.waiters.count)
XCTAssertEqual(pending, snapshot.pending)
XCTAssertEqual(opened, snapshot.openedConnectionsCount)
XCTAssertFalse(snapshot.leasedConnections.contains(ConnectionKey(connection)))
}
struct XCTEmptyError: Error {}
func XCTUnwrap<T>(_ value: T?) throws -> T {
if let unwrapped = value {
return unwrapped
}
throw XCTEmptyError()
}
struct TempError: Error {}
func XCTAssertStateClose<ConnectionType>(_ state: HTTP1ConnectionProvider.ConnectionsState<ConnectionType>, available: Int, leased: Int, waiters: Int, clean: Bool) throws {
var state = state
let (foundWaiters, foundAvailable, foundLeased, foundClean) = try XCTUnwrap(state.close())
XCTAssertEqual(waiters, foundWaiters.count)
XCTAssertEqual(available, foundAvailable.count)
XCTAssertEqual(leased, foundLeased.count)
XCTAssertEqual(clean, foundClean)
for waiter in foundWaiters {
waiter.promise.fail(TempError())
}
for lease in foundLeased {
try lease.cancel().wait()
}
}