Skip to content

Commit 59c0215

Browse files
authored
Move ConnectionPool test-utils into separate target (#544)
1 parent 5d817be commit 59c0215

14 files changed

+95
-73
lines changed

Package.swift

+8
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ let package = Package(
5757
path: "Sources/ConnectionPoolModule",
5858
swiftSettings: swiftSettings
5959
),
60+
.target(
61+
name: "ConnectionPoolTestUtils",
62+
dependencies: [
63+
"_ConnectionPoolModule",
64+
.product(name: "NIOConcurrencyHelpers", package: "swift-nio"),
65+
]
66+
),
6067
.testTarget(
6168
name: "PostgresNIOTests",
6269
dependencies: [
@@ -70,6 +77,7 @@ let package = Package(
7077
name: "ConnectionPoolModuleTests",
7178
dependencies: [
7279
.target(name: "_ConnectionPoolModule"),
80+
.target(name: "ConnectionPoolTestUtils"),
7381
.product(name: "DequeModule", package: "swift-collections"),
7482
.product(name: "NIOCore", package: "swift-nio"),
7583
.product(name: "NIOConcurrencyHelpers", package: "swift-nio"),

Tests/ConnectionPoolModuleTests/Mocks/MockClock.swift renamed to Sources/ConnectionPoolTestUtils/MockClock.swift

+18-15
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
1-
@testable import _ConnectionPoolModule
1+
import _ConnectionPoolModule
22
import Atomics
33
import DequeModule
4+
import NIOConcurrencyHelpers
45

56
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
6-
final class MockClock: Clock {
7-
struct Instant: InstantProtocol, Comparable {
8-
typealias Duration = Swift.Duration
7+
public final class MockClock: Clock {
8+
public struct Instant: InstantProtocol, Comparable {
9+
public typealias Duration = Swift.Duration
910

10-
func advanced(by duration: Self.Duration) -> Self {
11+
public func advanced(by duration: Self.Duration) -> Self {
1112
.init(self.base + duration)
1213
}
1314

14-
func duration(to other: Self) -> Self.Duration {
15+
public func duration(to other: Self) -> Self.Duration {
1516
self.base - other.base
1617
}
1718

1819
private var base: Swift.Duration
1920

20-
init(_ base: Duration) {
21+
public init(_ base: Duration) {
2122
self.base = base
2223
}
2324

24-
static func < (lhs: Self, rhs: Self) -> Bool {
25+
public static func < (lhs: Self, rhs: Self) -> Bool {
2526
lhs.base < rhs.base
2627
}
2728

28-
static func == (lhs: Self, rhs: Self) -> Bool {
29+
public static func == (lhs: Self, rhs: Self) -> Bool {
2930
lhs.base == rhs.base
3031
}
3132
}
@@ -58,16 +59,18 @@ final class MockClock: Clock {
5859
var continuation: CheckedContinuation<Void, any Error>
5960
}
6061

61-
typealias Duration = Swift.Duration
62+
public typealias Duration = Swift.Duration
6263

63-
var minimumResolution: Duration { .nanoseconds(1) }
64+
public var minimumResolution: Duration { .nanoseconds(1) }
6465

65-
var now: Instant { self.stateBox.withLockedValue { $0.now } }
66+
public var now: Instant { self.stateBox.withLockedValue { $0.now } }
6667

6768
private let stateBox = NIOLockedValueBox(State())
6869
private let waiterIDGenerator = ManagedAtomic(0)
6970

70-
func sleep(until deadline: Instant, tolerance: Duration?) async throws {
71+
public init() {}
72+
73+
public func sleep(until deadline: Instant, tolerance: Duration?) async throws {
7174
let waiterID = self.waiterIDGenerator.loadThenWrappingIncrement(ordering: .relaxed)
7275

7376
return try await withTaskCancellationHandler {
@@ -131,7 +134,7 @@ final class MockClock: Clock {
131134
}
132135

133136
@discardableResult
134-
func nextTimerScheduled() async -> Instant {
137+
public func nextTimerScheduled() async -> Instant {
135138
await withCheckedContinuation { (continuation: CheckedContinuation<Instant, Never>) in
136139
let instant = self.stateBox.withLockedValue { state -> Instant? in
137140
if let scheduled = state.nextDeadlines.popFirst() {
@@ -149,7 +152,7 @@ final class MockClock: Clock {
149152
}
150153
}
151154

152-
func advance(to deadline: Instant) {
155+
public func advance(to deadline: Instant) {
153156
let waiters = self.stateBox.withLockedValue { state -> ArraySlice<Sleeper> in
154157
precondition(deadline > state.now, "Time can only move forward")
155158
state.now = deadline

Tests/ConnectionPoolModuleTests/Mocks/MockConnection.swift renamed to Sources/ConnectionPoolTestUtils/MockConnection.swift

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
import _ConnectionPoolModule
12
import DequeModule
2-
@testable import _ConnectionPoolModule
3+
import NIOConcurrencyHelpers
34

4-
// Sendability enforced through the lock
5-
final class MockConnection: PooledConnection, Sendable {
6-
typealias ID = Int
5+
public final class MockConnection: PooledConnection, Sendable {
6+
public typealias ID = Int
77

8-
let id: ID
8+
public let id: ID
99

1010
private enum State {
1111
case running([CheckedContinuation<Void, any Error>], [@Sendable ((any Error)?) -> ()])
@@ -15,11 +15,11 @@ final class MockConnection: PooledConnection, Sendable {
1515

1616
private let lock: NIOLockedValueBox<State> = NIOLockedValueBox(.running([], []))
1717

18-
init(id: Int) {
18+
public init(id: Int) {
1919
self.id = id
2020
}
2121

22-
var signalToClose: Void {
22+
public var signalToClose: Void {
2323
get async throws {
2424
try await withCheckedThrowingContinuation { continuation in
2525
let runRightAway = self.lock.withLockedValue { state -> Bool in
@@ -41,7 +41,7 @@ final class MockConnection: PooledConnection, Sendable {
4141
}
4242
}
4343

44-
func onClose(_ closure: @escaping @Sendable ((any Error)?) -> ()) {
44+
public func onClose(_ closure: @escaping @Sendable ((any Error)?) -> ()) {
4545
let enqueued = self.lock.withLockedValue { state -> Bool in
4646
switch state {
4747
case .closed:
@@ -64,7 +64,7 @@ final class MockConnection: PooledConnection, Sendable {
6464
}
6565
}
6666

67-
func close() {
67+
public func close() {
6868
let continuations = self.lock.withLockedValue { state -> [CheckedContinuation<Void, any Error>] in
6969
switch state {
7070
case .running(let continuations, let callbacks):
@@ -81,7 +81,7 @@ final class MockConnection: PooledConnection, Sendable {
8181
}
8282
}
8383

84-
func closeIfClosing() {
84+
public func closeIfClosing() {
8585
let callbacks = self.lock.withLockedValue { state -> [@Sendable ((any Error)?) -> ()] in
8686
switch state {
8787
case .running, .closed:
@@ -100,7 +100,7 @@ final class MockConnection: PooledConnection, Sendable {
100100
}
101101

102102
extension MockConnection: CustomStringConvertible {
103-
var description: String {
103+
public var description: String {
104104
let state = self.lock.withLockedValue { $0 }
105105
return "MockConnection(id: \(self.id), state: \(state))"
106106
}

Tests/ConnectionPoolModuleTests/Mocks/MockConnectionFactory.swift renamed to Sources/ConnectionPoolTestUtils/MockConnectionFactory.swift

+15-12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
@testable import _ConnectionPoolModule
1+
import _ConnectionPoolModule
22
import DequeModule
3+
import NIOConcurrencyHelpers
34

45
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
5-
final class MockConnectionFactory<Clock: _Concurrency.Clock>: Sendable where Clock.Duration == Duration {
6-
typealias ConnectionIDGenerator = _ConnectionPoolModule.ConnectionIDGenerator
7-
typealias Request = ConnectionRequest<MockConnection>
8-
typealias KeepAliveBehavior = MockPingPongBehavior
9-
typealias MetricsDelegate = NoOpConnectionPoolMetrics<Int>
10-
typealias ConnectionID = Int
11-
typealias Connection = MockConnection
6+
public final class MockConnectionFactory<Clock: _Concurrency.Clock>: Sendable where Clock.Duration == Duration {
7+
public typealias ConnectionIDGenerator = _ConnectionPoolModule.ConnectionIDGenerator
8+
public typealias Request = ConnectionRequest<MockConnection>
9+
public typealias KeepAliveBehavior = MockPingPongBehavior
10+
public typealias MetricsDelegate = NoOpConnectionPoolMetrics<Int>
11+
public typealias ConnectionID = Int
12+
public typealias Connection = MockConnection
1213

1314
let stateBox = NIOLockedValueBox(State())
1415

@@ -20,15 +21,17 @@ final class MockConnectionFactory<Clock: _Concurrency.Clock>: Sendable where Clo
2021
var runningConnections = [ConnectionID: Connection]()
2122
}
2223

23-
var pendingConnectionAttemptsCount: Int {
24+
public init() {}
25+
26+
public var pendingConnectionAttemptsCount: Int {
2427
self.stateBox.withLockedValue { $0.attempts.count }
2528
}
2629

27-
var runningConnections: [Connection] {
30+
public var runningConnections: [Connection] {
2831
self.stateBox.withLockedValue { Array($0.runningConnections.values) }
2932
}
3033

31-
func makeConnection(
34+
public func makeConnection(
3235
id: Int,
3336
for pool: ConnectionPool<MockConnection, Int, ConnectionIDGenerator, some ConnectionRequestProtocol, Int, MockPingPongBehavior<MockConnection>, NoOpConnectionPoolMetrics<Int>, Clock>
3437
) async throws -> ConnectionAndMetadata<MockConnection> {
@@ -52,7 +55,7 @@ final class MockConnectionFactory<Clock: _Concurrency.Clock>: Sendable where Clo
5255
}
5356

5457
@discardableResult
55-
func nextConnectAttempt(_ closure: (ConnectionID) async throws -> UInt16) async rethrows -> Connection {
58+
public func nextConnectAttempt(_ closure: (ConnectionID) async throws -> UInt16) async rethrows -> Connection {
5659
let (connectionID, continuation) = await withCheckedContinuation { (continuation: CheckedContinuation<(ConnectionID, CheckedContinuation<(MockConnection, UInt16), any Error>), Never>) in
5760
let attempt = self.stateBox.withLockedValue { state -> (ConnectionID, CheckedContinuation<(MockConnection, UInt16), any Error>)? in
5861
if let attempt = state.attempts.popFirst() {

Tests/ConnectionPoolModuleTests/Mocks/MockPingPongBehaviour.swift renamed to Sources/ConnectionPoolTestUtils/MockPingPongBehaviour.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import DequeModule
33

44
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
5-
final class MockPingPongBehavior<Connection: PooledConnection>: ConnectionKeepAliveBehavior {
6-
let keepAliveFrequency: Duration?
5+
public final class MockPingPongBehavior<Connection: PooledConnection>: ConnectionKeepAliveBehavior {
6+
public let keepAliveFrequency: Duration?
77

88
let stateBox = NIOLockedValueBox(State())
99

@@ -13,11 +13,11 @@ final class MockPingPongBehavior<Connection: PooledConnection>: ConnectionKeepAl
1313
var waiter = Deque<CheckedContinuation<(Connection, CheckedContinuation<Bool, any Error>), Never>>()
1414
}
1515

16-
init(keepAliveFrequency: Duration?, connectionType: Connection.Type) {
16+
public init(keepAliveFrequency: Duration?, connectionType: Connection.Type) {
1717
self.keepAliveFrequency = keepAliveFrequency
1818
}
1919

20-
func runKeepAlive(for connection: Connection) async throws {
20+
public func runKeepAlive(for connection: Connection) async throws {
2121
precondition(self.keepAliveFrequency != nil)
2222

2323
// we currently don't support cancellation when creating a connection
@@ -40,7 +40,7 @@ final class MockPingPongBehavior<Connection: PooledConnection>: ConnectionKeepAl
4040
}
4141

4242
@discardableResult
43-
func nextKeepAlive(_ closure: (Connection) async throws -> Bool) async rethrows -> Connection {
43+
public func nextKeepAlive(_ closure: (Connection) async throws -> Bool) async rethrows -> Connection {
4444
let (connection, continuation) = await withCheckedContinuation { (continuation: CheckedContinuation<(Connection, CheckedContinuation<Bool, any Error>), Never>) in
4545
let run = self.stateBox.withLockedValue { state -> (Connection, CheckedContinuation<Bool, any Error>)? in
4646
if let run = state.runs.popFirst() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import _ConnectionPoolModule
2+
3+
public final class MockRequest: ConnectionRequestProtocol, Hashable, Sendable {
4+
public typealias Connection = MockConnection
5+
6+
public struct ID: Hashable {
7+
var objectID: ObjectIdentifier
8+
9+
init(_ request: MockRequest) {
10+
self.objectID = ObjectIdentifier(request)
11+
}
12+
}
13+
14+
public init() {}
15+
16+
public var id: ID { ID(self) }
17+
18+
public static func ==(lhs: MockRequest, rhs: MockRequest) -> Bool {
19+
lhs.id == rhs.id
20+
}
21+
22+
public func hash(into hasher: inout Hasher) {
23+
hasher.combine(self.id)
24+
}
25+
26+
public func complete(with: Result<Connection, ConnectionPoolError>) {
27+
28+
}
29+
}

Tests/ConnectionPoolModuleTests/ConnectionPoolTests.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
@testable import _ConnectionPoolModule
22
import Atomics
3-
import XCTest
3+
import ConnectionPoolTestUtils
44
import NIOEmbedded
5+
import XCTest
56

67
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
78
final class ConnectionPoolTests: XCTestCase {

Tests/ConnectionPoolModuleTests/ConnectionRequestTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@testable import _ConnectionPoolModule
2+
import ConnectionPoolTestUtils
23
import XCTest
34

45
final class ConnectionRequestTests: XCTestCase {

Tests/ConnectionPoolModuleTests/Mocks/MockRequest.swift

-28
This file was deleted.

Tests/ConnectionPoolModuleTests/NoKeepAliveBehaviorTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import _ConnectionPoolModule
2+
import ConnectionPoolTestUtils
23
import XCTest
34

45
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)

Tests/ConnectionPoolModuleTests/PoolStateMachine+ConnectionGroupTests.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import XCTest
21
@testable import _ConnectionPoolModule
2+
import ConnectionPoolTestUtils
3+
import XCTest
34

45
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
56
final class PoolStateMachine_ConnectionGroupTests: XCTestCase {

Tests/ConnectionPoolModuleTests/PoolStateMachine+ConnectionStateTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@testable import _ConnectionPoolModule
2+
import ConnectionPoolTestUtils
23
import XCTest
34

45
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)

Tests/ConnectionPoolModuleTests/PoolStateMachine+RequestQueueTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@testable import _ConnectionPoolModule
2+
import ConnectionPoolTestUtils
23
import XCTest
34

45
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)

Tests/ConnectionPoolModuleTests/PoolStateMachineTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ConnectionPoolTestUtils
12
import XCTest
23
@testable import _ConnectionPoolModule
34

0 commit comments

Comments
 (0)