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.
132 lines
5.1 KiB
Swift
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()
|
|
}
|
|
}
|