mirror of
https://github.com/swift-server/async-http-client.git
synced 2026-06-02 07:37:34 +00:00
### Motivation With Xcode 13.2, and therefore Swift 5.5.2, Swift Concurrecy is supported on older Apple OSs. async/await suport will no longer be available on Swift before `5.5.2` but this isn't a breaking change because we have not yet made anything of it public. ### Changes - replace all `#if compiler(>=5.5) && canImport(_Concurrency)` with `#if compiler(>=5.5.2) && canImport(_Concurrency)` - replace all `available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)` with `available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)`
209 lines
8.5 KiB
Swift
209 lines
8.5 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the AsyncHTTPClient open source project
|
|
//
|
|
// Copyright (c) 2021 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 NIOCore
|
|
import NIOEmbedded
|
|
import NIOHTTP1
|
|
import XCTest
|
|
|
|
final class Transaction_StateMachineTests: XCTestCase {
|
|
func testRequestWasQueuedAfterWillExecuteRequestWasCalled() {
|
|
#if compiler(>=5.5.2) && canImport(_Concurrency)
|
|
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { return }
|
|
let eventLoop = EmbeddedEventLoop()
|
|
XCTAsyncTest {
|
|
func workaround(_ continuation: CheckedContinuation<HTTPClientResponse, Error>) {
|
|
var state = Transaction.StateMachine(continuation)
|
|
let executor = MockRequestExecutor(eventLoop: eventLoop)
|
|
let queuer = MockTaskQueuer()
|
|
|
|
XCTAssertEqual(state.willExecuteRequest(executor), .none)
|
|
state.requestWasQueued(queuer)
|
|
|
|
let failAction = state.fail(HTTPClientError.cancelled)
|
|
guard case .failResponseHead(_, let error, let scheduler, let rexecutor, let bodyStreamContinuation) = failAction else {
|
|
return XCTFail("Unexpected fail action: \(failAction)")
|
|
}
|
|
XCTAssertEqual(error as? HTTPClientError, .cancelled)
|
|
XCTAssertNil(scheduler)
|
|
XCTAssertNil(bodyStreamContinuation)
|
|
XCTAssert((rexecutor as? MockRequestExecutor) === executor)
|
|
|
|
continuation.resume(throwing: HTTPClientError.cancelled)
|
|
}
|
|
|
|
await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround))
|
|
}
|
|
#endif
|
|
}
|
|
|
|
func testRequestBodyStreamWasPaused() {
|
|
#if compiler(>=5.5.2) && canImport(_Concurrency)
|
|
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { return }
|
|
let eventLoop = EmbeddedEventLoop()
|
|
XCTAsyncTest {
|
|
func workaround(_ continuation: CheckedContinuation<HTTPClientResponse, Error>) {
|
|
var state = Transaction.StateMachine(continuation)
|
|
let executor = MockRequestExecutor(eventLoop: eventLoop)
|
|
|
|
XCTAssertEqual(state.willExecuteRequest(executor), .none)
|
|
XCTAssertEqual(state.resumeRequestBodyStream(), .startStream(ByteBufferAllocator()))
|
|
XCTAssertEqual(state.writeNextRequestPart(), .writeAndContinue(executor))
|
|
state.pauseRequestBodyStream()
|
|
XCTAssertEqual(state.writeNextRequestPart(), .writeAndWait(executor))
|
|
|
|
continuation.resume(throwing: HTTPClientError.cancelled)
|
|
}
|
|
|
|
await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround))
|
|
}
|
|
#endif
|
|
}
|
|
|
|
func testQueuedRequestGetsRemovedWhenDeadlineExceeded() {
|
|
#if compiler(>=5.5.2) && canImport(_Concurrency)
|
|
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { return }
|
|
XCTAsyncTest {
|
|
func workaround(_ continuation: CheckedContinuation<HTTPClientResponse, Error>) {
|
|
var state = Transaction.StateMachine(continuation)
|
|
let queuer = MockTaskQueuer()
|
|
|
|
state.requestWasQueued(queuer)
|
|
|
|
let failAction = state.deadlineExceeded()
|
|
guard case .cancel(let continuation, let scheduler, nil, nil) = failAction else {
|
|
return XCTFail("Unexpected fail action: \(failAction)")
|
|
}
|
|
XCTAssertIdentical(scheduler as? MockTaskQueuer, queuer)
|
|
|
|
continuation.resume(throwing: HTTPClientError.deadlineExceeded)
|
|
}
|
|
|
|
await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround))
|
|
}
|
|
#endif
|
|
}
|
|
|
|
func testScheduledRequestGetsRemovedWhenDeadlineExceeded() {
|
|
#if compiler(>=5.5.2) && canImport(_Concurrency)
|
|
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { return }
|
|
let eventLoop = EmbeddedEventLoop()
|
|
XCTAsyncTest {
|
|
func workaround(_ continuation: CheckedContinuation<HTTPClientResponse, Error>) {
|
|
var state = Transaction.StateMachine(continuation)
|
|
let executor = MockRequestExecutor(eventLoop: eventLoop)
|
|
let queuer = MockTaskQueuer()
|
|
|
|
XCTAssertEqual(state.willExecuteRequest(executor), .none)
|
|
state.requestWasQueued(queuer)
|
|
|
|
let failAction = state.deadlineExceeded()
|
|
guard case .cancel(let continuation, nil, let rexecutor, nil) = failAction else {
|
|
return XCTFail("Unexpected fail action: \(failAction)")
|
|
}
|
|
XCTAssertIdentical(rexecutor as? MockRequestExecutor, executor)
|
|
|
|
continuation.resume(throwing: HTTPClientError.deadlineExceeded)
|
|
}
|
|
|
|
await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround))
|
|
}
|
|
#endif
|
|
}
|
|
|
|
func testRequestWithHeadReceivedGetNotCancelledWhenDeadlineExceeded() {
|
|
#if compiler(>=5.5.2) && canImport(_Concurrency)
|
|
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { return }
|
|
let eventLoop = EmbeddedEventLoop()
|
|
XCTAsyncTest {
|
|
func workaround(_ continuation: CheckedContinuation<HTTPClientResponse, Error>) {
|
|
var state = Transaction.StateMachine(continuation)
|
|
let executor = MockRequestExecutor(eventLoop: eventLoop)
|
|
let queuer = MockTaskQueuer()
|
|
|
|
XCTAssertEqual(state.willExecuteRequest(executor), .none)
|
|
state.requestWasQueued(queuer)
|
|
let head = HTTPResponseHead(version: .http1_1, status: .ok)
|
|
let receiveResponseHeadAction = state.receiveResponseHead(head)
|
|
guard case .succeedResponseHead(head, let continuation) = receiveResponseHeadAction else {
|
|
return XCTFail("Unexpected action: \(receiveResponseHeadAction)")
|
|
}
|
|
|
|
let failAction = state.deadlineExceeded()
|
|
guard case .none = failAction else {
|
|
return XCTFail("Unexpected fail action: \(failAction)")
|
|
}
|
|
continuation.resume(throwing: HTTPClientError.deadlineExceeded)
|
|
}
|
|
|
|
await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround))
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if compiler(>=5.5.2) && canImport(_Concurrency)
|
|
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
|
extension Transaction.StateMachine.StartExecutionAction: Equatable {
|
|
public static func == (lhs: Self, rhs: Self) -> Bool {
|
|
switch (lhs, rhs) {
|
|
case (.none, .none):
|
|
return true
|
|
case (.cancel(let lhsEx), .cancel(let rhsEx)):
|
|
if let lhsMock = lhsEx as? MockRequestExecutor, let rhsMock = rhsEx as? MockRequestExecutor {
|
|
return lhsMock === rhsMock
|
|
}
|
|
return false
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
|
extension Transaction.StateMachine.ResumeProducingAction: Equatable {
|
|
public static func == (lhs: Self, rhs: Self) -> Bool {
|
|
switch (lhs, rhs) {
|
|
case (.none, .none):
|
|
return true
|
|
case (.resumeStream, .resumeStream):
|
|
return true
|
|
case (.startStream, .startStream):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
|
extension Transaction.StateMachine.NextWriteAction: Equatable {
|
|
public static func == (lhs: Self, rhs: Self) -> Bool {
|
|
switch (lhs, rhs) {
|
|
case (.writeAndWait(let lhsEx), .writeAndWait(let rhsEx)),
|
|
(.writeAndContinue(let lhsEx), .writeAndContinue(let rhsEx)):
|
|
if let lhsMock = lhsEx as? MockRequestExecutor, let rhsMock = rhsEx as? MockRequestExecutor {
|
|
return lhsMock === rhsMock
|
|
}
|
|
return false
|
|
case (.fail, .fail):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
#endif
|