Skip to content

Commit a586fba

Browse files
authored
Fix flaky TransactionTests.testResponseStreamFails() test (#582)
1 parent 3725095 commit a586fba

File tree

1 file changed

+58
-17
lines changed

1 file changed

+58
-17
lines changed

Tests/AsyncHTTPClientTests/TransactionTests.swift

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ final class TransactionTests: XCTestCase {
4141
guard let preparedRequest = maybePreparedRequest else {
4242
return XCTFail("Expected to have a request here.")
4343
}
44-
let (transaction, responseTask) = Transaction.makeWithResultTask(
44+
let (transaction, responseTask) = await Transaction.makeWithResultTask(
4545
request: preparedRequest,
4646
preferredEventLoop: embeddedEventLoop
4747
)
@@ -78,7 +78,7 @@ final class TransactionTests: XCTestCase {
7878
guard let preparedRequest = maybePreparedRequest else {
7979
return
8080
}
81-
let (transaction, responseTask) = Transaction.makeWithResultTask(
81+
let (transaction, responseTask) = await Transaction.makeWithResultTask(
8282
request: preparedRequest,
8383
preferredEventLoop: embeddedEventLoop
8484
)
@@ -141,7 +141,7 @@ final class TransactionTests: XCTestCase {
141141
guard let preparedRequest = maybePreparedRequest else {
142142
return
143143
}
144-
var tuple: (Transaction, Task<HTTPClientResponse, Error>)! = Transaction.makeWithResultTask(
144+
var tuple: (Transaction, Task<HTTPClientResponse, Error>)! = await Transaction.makeWithResultTask(
145145
request: preparedRequest,
146146
preferredEventLoop: embeddedEventLoop
147147
)
@@ -196,7 +196,7 @@ final class TransactionTests: XCTestCase {
196196
guard let preparedRequest = maybePreparedRequest else {
197197
return XCTFail("Expected to have a request here.")
198198
}
199-
let (transaction, responseTask) = Transaction.makeWithResultTask(
199+
let (transaction, responseTask) = await Transaction.makeWithResultTask(
200200
request: preparedRequest,
201201
preferredEventLoop: embeddedEventLoop
202202
)
@@ -282,7 +282,7 @@ final class TransactionTests: XCTestCase {
282282
guard let preparedRequest = maybePreparedRequest else {
283283
return XCTFail("Expected to have a request here.")
284284
}
285-
let (transaction, responseTask) = Transaction.makeWithResultTask(
285+
let (transaction, responseTask) = await Transaction.makeWithResultTask(
286286
request: preparedRequest,
287287
preferredEventLoop: eventLoopGroup.next()
288288
)
@@ -324,7 +324,7 @@ final class TransactionTests: XCTestCase {
324324
guard let preparedRequest = maybePreparedRequest else {
325325
return XCTFail("Expected to have a request here.")
326326
}
327-
let (transaction, responseTask) = Transaction.makeWithResultTask(
327+
let (transaction, responseTask) = await Transaction.makeWithResultTask(
328328
request: preparedRequest,
329329
preferredEventLoop: embeddedEventLoop
330330
)
@@ -366,7 +366,7 @@ final class TransactionTests: XCTestCase {
366366
guard let preparedRequest = maybePreparedRequest else {
367367
return XCTFail("Expected to have a request here.")
368368
}
369-
let (transaction, responseTask) = Transaction.makeWithResultTask(
369+
let (transaction, responseTask) = await Transaction.makeWithResultTask(
370370
request: preparedRequest,
371371
preferredEventLoop: embeddedEventLoop
372372
)
@@ -397,7 +397,7 @@ final class TransactionTests: XCTestCase {
397397
func testResponseStreamFails() {
398398
#if compiler(>=5.5.2) && canImport(_Concurrency)
399399
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { return }
400-
XCTAsyncTest {
400+
XCTAsyncTest(timeout: 30) {
401401
let embeddedEventLoop = EmbeddedEventLoop()
402402
defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) }
403403

@@ -409,7 +409,7 @@ final class TransactionTests: XCTestCase {
409409
guard let preparedRequest = maybePreparedRequest else {
410410
return
411411
}
412-
let (transaction, responseTask) = Transaction.makeWithResultTask(
412+
let (transaction, responseTask) = await Transaction.makeWithResultTask(
413413
request: preparedRequest,
414414
preferredEventLoop: embeddedEventLoop
415415
)
@@ -427,6 +427,7 @@ final class TransactionTests: XCTestCase {
427427
transaction.receiveResponseHead(responseHead)
428428

429429
let response = try await responseTask.value
430+
430431
XCTAssertEqual(response.status, responseHead.status)
431432
XCTAssertEqual(response.headers, responseHead.headers)
432433
XCTAssertEqual(response.version, responseHead.version)
@@ -438,6 +439,7 @@ final class TransactionTests: XCTestCase {
438439
XCTAssertNoThrow(try executor.receiveResponseDemand())
439440
executor.resetResponseStreamDemandSignal()
440441
transaction.receiveResponseBodyParts([ByteBuffer(integer: 123)])
442+
441443
let result = try await part1
442444
XCTAssertEqual(result, ByteBuffer(integer: 123))
443445

@@ -493,7 +495,7 @@ final class TransactionTests: XCTestCase {
493495
guard let preparedRequest = maybePreparedRequest else {
494496
return
495497
}
496-
let (transaction, responseTask) = Transaction.makeWithResultTask(
498+
let (transaction, responseTask) = await Transaction.makeWithResultTask(
497499
request: preparedRequest,
498500
preferredEventLoop: eventLoopGroup.next()
499501
)
@@ -553,6 +555,45 @@ actor SharedIterator<Iterator: AsyncIteratorProtocol> {
553555
}
554556
}
555557

558+
/// non fail-able promise that only supports one observer
559+
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
560+
fileprivate actor Promise<Value> {
561+
private enum State {
562+
case initialised
563+
case fulfilled(Value)
564+
}
565+
566+
private var state: State = .initialised
567+
568+
private var observer: CheckedContinuation<Value, Never>?
569+
570+
init() {}
571+
572+
func fulfil(_ value: Value) {
573+
switch self.state {
574+
case .initialised:
575+
self.state = .fulfilled(value)
576+
self.observer?.resume(returning: value)
577+
case .fulfilled:
578+
preconditionFailure("\(Self.self) over fulfilled")
579+
}
580+
}
581+
582+
var value: Value {
583+
get async {
584+
switch self.state {
585+
case .initialised:
586+
return await withCheckedContinuation { (continuation: CheckedContinuation<Value, Never>) in
587+
precondition(self.observer == nil, "\(Self.self) supports only one observer")
588+
self.observer = continuation
589+
}
590+
case .fulfilled(let value):
591+
return value
592+
}
593+
}
594+
}
595+
}
596+
556597
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
557598
extension Transaction {
558599
fileprivate static func makeWithResultTask(
@@ -561,9 +602,9 @@ extension Transaction {
561602
logger: Logger = Logger(label: "test"),
562603
connectionDeadline: NIODeadline = .distantFuture,
563604
preferredEventLoop: EventLoop
564-
) -> (Transaction, _Concurrency.Task<HTTPClientResponse, Error>) {
565-
let transactionPromise = preferredEventLoop.makePromise(of: Transaction.self)
566-
let result = Task {
605+
) async -> (Transaction, _Concurrency.Task<HTTPClientResponse, Error>) {
606+
let transactionPromise = Promise<Transaction>()
607+
let task = Task {
567608
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<HTTPClientResponse, Error>) in
568609
let transaction = Transaction(
569610
request: request,
@@ -573,13 +614,13 @@ extension Transaction {
573614
preferredEventLoop: preferredEventLoop,
574615
responseContinuation: continuation
575616
)
576-
transactionPromise.succeed(transaction)
617+
Task {
618+
await transactionPromise.fulfil(transaction)
619+
}
577620
}
578621
}
579-
// the promise can never fail and it is therefore safe to force unwrap
580-
let transaction = try! transactionPromise.futureResult.wait()
581622

582-
return (transaction, result)
623+
return (await transactionPromise.value, task)
583624
}
584625
}
585626
#endif

0 commit comments

Comments
 (0)