Skip to content

Commit 9195d3b

Browse files
authored
Speedup tests (#639)
* Enable fast failure mode for testing * Split-up HTTPClientTests into multiple subclasses * Move test subclasses into separate files
1 parent d7b69d9 commit 9195d3b

21 files changed

+662
-231
lines changed

Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,8 +497,9 @@ final class AsyncAwaitEndToEndTests: XCTestCase {
497497
defer { XCTAssertNoThrow(try serverChannel.close().wait()) }
498498
let port = serverChannel.localAddress!.port!
499499

500-
var config = HTTPClient.Configuration()
501-
config.timeout.connect = .seconds(3)
500+
let config = HTTPClient.Configuration()
501+
.enableFastFailureModeForTesting()
502+
502503
let localClient = HTTPClient(eventLoopGroupProvider: .createNew, configuration: config)
503504
defer { XCTAssertNoThrow(try localClient.syncShutdown()) }
504505
let request = HTTPClientRequest(url: "https://localhost:\(port)")
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
//
15+
// ConnectionPoolSizeConfigValueIsRespectedTests+XCTest.swift
16+
//
17+
import XCTest
18+
19+
///
20+
/// NOTE: This file was generated by generate_linux_tests.rb
21+
///
22+
/// Do NOT edit this file directly as it will be regenerated automatically when needed.
23+
///
24+
25+
extension ConnectionPoolSizeConfigValueIsRespectedTests {
26+
static var allTests: [(String, (ConnectionPoolSizeConfigValueIsRespectedTests) -> () throws -> Void)] {
27+
return [
28+
("testConnectionPoolSizeConfigValueIsRespected", testConnectionPoolSizeConfigValueIsRespected),
29+
]
30+
}
31+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import AsyncHTTPClient
16+
import Atomics
17+
#if canImport(Network)
18+
import Network
19+
#endif
20+
import Logging
21+
import NIOConcurrencyHelpers
22+
import NIOCore
23+
import NIOFoundationCompat
24+
import NIOHTTP1
25+
import NIOHTTPCompression
26+
import NIOPosix
27+
import NIOSSL
28+
import NIOTestUtils
29+
import NIOTransportServices
30+
import XCTest
31+
32+
final class ConnectionPoolSizeConfigValueIsRespectedTests: XCTestCaseHTTPClientTestsBaseClass {
33+
func testConnectionPoolSizeConfigValueIsRespected() {
34+
let numberOfRequestsPerThread = 1000
35+
let numberOfParallelWorkers = 16
36+
let poolSize = 12
37+
38+
let httpBin = HTTPBin()
39+
defer { XCTAssertNoThrow(try httpBin.shutdown()) }
40+
41+
let group = MultiThreadedEventLoopGroup(numberOfThreads: 4)
42+
defer { XCTAssertNoThrow(try group.syncShutdownGracefully()) }
43+
44+
let configuration = HTTPClient.Configuration(
45+
connectionPool: .init(
46+
idleTimeout: .seconds(30),
47+
concurrentHTTP1ConnectionsPerHostSoftLimit: poolSize
48+
)
49+
)
50+
let client = HTTPClient(eventLoopGroupProvider: .shared(group), configuration: configuration)
51+
defer { XCTAssertNoThrow(try client.syncShutdown()) }
52+
53+
let g = DispatchGroup()
54+
for workerID in 0..<numberOfParallelWorkers {
55+
DispatchQueue(label: "\(#file):\(#line):worker-\(workerID)").async(group: g) {
56+
func makeRequest() {
57+
let url = "http://127.0.0.1:\(httpBin.port)"
58+
XCTAssertNoThrow(try client.get(url: url).wait())
59+
}
60+
for _ in 0..<numberOfRequestsPerThread {
61+
makeRequest()
62+
}
63+
}
64+
}
65+
let timeout = DispatchTime.now() + .seconds(60)
66+
switch g.wait(timeout: timeout) {
67+
case .success:
68+
break
69+
case .timedOut:
70+
XCTFail("Timed out")
71+
}
72+
73+
XCTAssertEqual(httpBin.createdConnections, poolSize)
74+
}
75+
}

Tests/AsyncHTTPClientTests/HTTP2ClientTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ class HTTP2ClientTests: XCTestCase {
287287
)
288288

289289
XCTAssertThrowsError(try task.futureResult.timeout(after: .seconds(2)).wait()) {
290-
XCTAssertEqual($0 as? HTTPClientError, .cancelled)
290+
XCTAssertEqualTypeAndValue($0, HTTPClientError.cancelled)
291291
}
292292
}
293293

Tests/AsyncHTTPClientTests/HTTPClient+SOCKSTests.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ class HTTPClientSOCKSTests: XCTestCase {
7676
func testProxySOCKS() throws {
7777
let socksBin = try MockSOCKSServer(expectedURL: "/socks/test", expectedResponse: "it works!")
7878
let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup),
79-
configuration: .init(proxy: .socksServer(host: "localhost", port: socksBin.port)))
79+
configuration: .init(
80+
proxy: .socksServer(host: "localhost", port: socksBin.port)
81+
).enableFastFailureModeForTesting())
8082

8183
defer {
8284
XCTAssertNoThrow(try localClient.syncShutdown())
@@ -90,8 +92,8 @@ class HTTPClientSOCKSTests: XCTestCase {
9092
}
9193

9294
func testProxySOCKSBogusAddress() throws {
93-
var config = HTTPClient.Configuration(proxy: .socksServer(host: "127.0.."))
94-
config.networkFrameworkWaitForConnectivity = false
95+
let config = HTTPClient.Configuration(proxy: .socksServer(host: "127.0.."))
96+
.enableFastFailureModeForTesting()
9597
let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup),
9698
configuration: config)
9799

@@ -104,8 +106,8 @@ class HTTPClientSOCKSTests: XCTestCase {
104106
// there is no socks server, so we should fail
105107
func testProxySOCKSFailureNoServer() throws {
106108
let localHTTPBin = HTTPBin()
107-
var config = HTTPClient.Configuration(proxy: .socksServer(host: "localhost", port: localHTTPBin.port))
108-
config.networkFrameworkWaitForConnectivity = false
109+
let config = HTTPClient.Configuration(proxy: .socksServer(host: "localhost", port: localHTTPBin.port))
110+
.enableFastFailureModeForTesting()
109111

110112
let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup),
111113
configuration: config)
@@ -118,8 +120,8 @@ class HTTPClientSOCKSTests: XCTestCase {
118120

119121
// speak to a server that doesn't speak SOCKS
120122
func testProxySOCKSFailureInvalidServer() throws {
121-
var config = HTTPClient.Configuration(proxy: .socksServer(host: "localhost"))
122-
config.networkFrameworkWaitForConnectivity = false
123+
let config = HTTPClient.Configuration(proxy: .socksServer(host: "localhost"))
124+
.enableFastFailureModeForTesting()
123125

124126
let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup),
125127
configuration: config)
@@ -132,8 +134,8 @@ class HTTPClientSOCKSTests: XCTestCase {
132134
// test a handshake failure with a misbehaving server
133135
func testProxySOCKSMisbehavingServer() throws {
134136
let socksBin = try MockSOCKSServer(expectedURL: "/socks/test", expectedResponse: "it works!", misbehave: true)
135-
var config = HTTPClient.Configuration(proxy: .socksServer(host: "localhost", port: socksBin.port))
136-
config.networkFrameworkWaitForConnectivity = false
137+
let config = HTTPClient.Configuration(proxy: .socksServer(host: "localhost", port: socksBin.port))
138+
.enableFastFailureModeForTesting()
137139

138140
let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup),
139141
configuration: config)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import AsyncHTTPClient
16+
import Atomics
17+
#if canImport(Network)
18+
import Network
19+
#endif
20+
import Logging
21+
import NIOConcurrencyHelpers
22+
import NIOCore
23+
import NIOFoundationCompat
24+
import NIOHTTP1
25+
import NIOHTTPCompression
26+
import NIOPosix
27+
import NIOSSL
28+
import NIOTestUtils
29+
import NIOTransportServices
30+
import XCTest
31+
32+
class XCTestCaseHTTPClientTestsBaseClass: XCTestCase {
33+
typealias Request = HTTPClient.Request
34+
35+
var clientGroup: EventLoopGroup!
36+
var serverGroup: EventLoopGroup!
37+
var defaultHTTPBin: HTTPBin<HTTPBinHandler>!
38+
var defaultClient: HTTPClient!
39+
var backgroundLogStore: CollectEverythingLogHandler.LogStore!
40+
41+
var defaultHTTPBinURLPrefix: String {
42+
return "http://localhost:\(self.defaultHTTPBin.port)/"
43+
}
44+
45+
override func setUp() {
46+
XCTAssertNil(self.clientGroup)
47+
XCTAssertNil(self.serverGroup)
48+
XCTAssertNil(self.defaultHTTPBin)
49+
XCTAssertNil(self.defaultClient)
50+
XCTAssertNil(self.backgroundLogStore)
51+
52+
self.clientGroup = getDefaultEventLoopGroup(numberOfThreads: 1)
53+
self.serverGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
54+
self.defaultHTTPBin = HTTPBin()
55+
self.backgroundLogStore = CollectEverythingLogHandler.LogStore()
56+
var backgroundLogger = Logger(label: "\(#function)", factory: { _ in
57+
CollectEverythingLogHandler(logStore: self.backgroundLogStore!)
58+
})
59+
backgroundLogger.logLevel = .trace
60+
self.defaultClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup),
61+
configuration: HTTPClient.Configuration().enableFastFailureModeForTesting(),
62+
backgroundActivityLogger: backgroundLogger)
63+
}
64+
65+
override func tearDown() {
66+
if let defaultClient = self.defaultClient {
67+
XCTAssertNoThrow(try defaultClient.syncShutdown())
68+
self.defaultClient = nil
69+
}
70+
71+
XCTAssertNotNil(self.defaultHTTPBin)
72+
XCTAssertNoThrow(try self.defaultHTTPBin.shutdown())
73+
self.defaultHTTPBin = nil
74+
75+
XCTAssertNotNil(self.clientGroup)
76+
XCTAssertNoThrow(try self.clientGroup.syncShutdownGracefully())
77+
self.clientGroup = nil
78+
79+
XCTAssertNotNil(self.serverGroup)
80+
XCTAssertNoThrow(try self.serverGroup.syncShutdownGracefully())
81+
self.serverGroup = nil
82+
83+
XCTAssertNotNil(self.backgroundLogStore)
84+
self.backgroundLogStore = nil
85+
}
86+
}

Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,8 +429,8 @@ class HTTPClientInternalTests: XCTestCase {
429429
let el2 = elg.next()
430430

431431
let httpBin = HTTPBin(.refuse)
432-
var config = HTTPClient.Configuration()
433-
config.networkFrameworkWaitForConnectivity = false
432+
let config = HTTPClient.Configuration()
433+
.enableFastFailureModeForTesting()
434434
let client = HTTPClient(eventLoopGroupProvider: .shared(elg), configuration: config)
435435

436436
defer {
@@ -590,3 +590,12 @@ class HTTPClientInternalTests: XCTestCase {
590590
XCTAssert(threadPools.dropFirst().allSatisfy { $0 === firstThreadPool })
591591
}
592592
}
593+
594+
extension HTTPClient.Configuration {
595+
func enableFastFailureModeForTesting() -> Self {
596+
var copy = self
597+
copy.networkFrameworkWaitForConnectivity = false
598+
copy.connectionPool.retryConnectionEstablishment = false
599+
return copy
600+
}
601+
}

Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,10 @@ extension HTTPClientTests {
7777
("testWorksWhenServerClosesConnectionAfterReceivingRequest", testWorksWhenServerClosesConnectionAfterReceivingRequest),
7878
("testSubsequentRequestsWorkWithServerSendingConnectionClose", testSubsequentRequestsWorkWithServerSendingConnectionClose),
7979
("testSubsequentRequestsWorkWithServerAlternatingBetweenKeepAliveAndClose", testSubsequentRequestsWorkWithServerAlternatingBetweenKeepAliveAndClose),
80-
("testStressGetHttps", testStressGetHttps),
8180
("testStressGetHttpsSSLError", testStressGetHttpsSSLError),
8281
("testSelfSignedCertificateIsRejectedWithCorrectError", testSelfSignedCertificateIsRejectedWithCorrectError),
8382
("testSelfSignedCertificateIsRejectedWithCorrectErrorIfRequestDeadlineIsExceeded", testSelfSignedCertificateIsRejectedWithCorrectErrorIfRequestDeadlineIsExceeded),
8483
("testFailingConnectionIsReleased", testFailingConnectionIsReleased),
85-
("testResponseDelayGet", testResponseDelayGet),
86-
("testIdleTimeoutNoReuse", testIdleTimeoutNoReuse),
8784
("testStressGetClose", testStressGetClose),
8885
("testManyConcurrentRequestsWork", testManyConcurrentRequestsWork),
8986
("testRepeatedRequestsWorkWhenServerAlwaysCloses", testRepeatedRequestsWorkWhenServerAlwaysCloses),
@@ -104,7 +101,6 @@ extension HTTPClientTests {
104101
("testUseExistingConnectionOnDifferentEL", testUseExistingConnectionOnDifferentEL),
105102
("testWeRecoverFromServerThatClosesTheConnectionOnUs", testWeRecoverFromServerThatClosesTheConnectionOnUs),
106103
("testPoolClosesIdleConnections", testPoolClosesIdleConnections),
107-
("testRacePoolIdleConnectionsAndGet", testRacePoolIdleConnectionsAndGet),
108104
("testAvoidLeakingTLSHandshakeCompletionPromise", testAvoidLeakingTLSHandshakeCompletionPromise),
109105
("testAsyncShutdown", testAsyncShutdown),
110106
("testAsyncShutdownDefaultQueue", testAsyncShutdownDefaultQueue),
@@ -126,7 +122,6 @@ extension HTTPClientTests {
126122
("testContentLengthTooLongFails", testContentLengthTooLongFails),
127123
("testContentLengthTooShortFails", testContentLengthTooShortFails),
128124
("testBodyUploadAfterEndFails", testBodyUploadAfterEndFails),
129-
("testNoBytesSentOverBodyLimit", testNoBytesSentOverBodyLimit),
130125
("testDoubleError", testDoubleError),
131126
("testSSLHandshakeErrorPropagation", testSSLHandshakeErrorPropagation),
132127
("testSSLHandshakeErrorPropagationDelayedClose", testSSLHandshakeErrorPropagationDelayedClose),
@@ -145,7 +140,6 @@ extension HTTPClientTests {
145140
("testCloseWhileBackpressureIsExertedIsFine", testCloseWhileBackpressureIsExertedIsFine),
146141
("testErrorAfterCloseWhileBackpressureExerted", testErrorAfterCloseWhileBackpressureExerted),
147142
("testRequestSpecificTLS", testRequestSpecificTLS),
148-
("testConnectionPoolSizeConfigValueIsRespected", testConnectionPoolSizeConfigValueIsRespected),
149143
("testRequestWithHeaderTransferEncodingIdentityDoesNotFail", testRequestWithHeaderTransferEncodingIdentityDoesNotFail),
150144
("testMassiveDownload", testMassiveDownload),
151145
("testShutdownWithFutures", testShutdownWithFutures),

0 commit comments

Comments
 (0)