| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618 |
- //
- // ConcurrencyTests.swift
- //
- // Copyright (c) 2021 Alamofire Software Foundation (http://alamofire.org/)
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #if compiler(>=5.6.0) && canImport(_Concurrency)
- import Alamofire
- import XCTest
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- final class DataRequestConcurrencyTests: BaseTestCase {
- func testThatDataTaskSerializesResponseUsingSerializer() async throws {
- // Given
- let session = stored(Session())
- // When
- let value = try await session.request(.get)
- .serializingResponse(using: .data)
- .value
- // Then
- XCTAssertNotNil(value)
- }
- func testThatDataTaskSerializesDecodable() async throws {
- // Given
- let session = stored(Session())
- // When
- let value = try await session.request(.get).serializingDecodable(TestResponse.self).value
- // Then
- XCTAssertNotNil(value)
- }
- func testThatDataTaskSerializesString() async throws {
- // Given
- let session = stored(Session())
- // When
- let value = try await session.request(.get).serializingString().value
- // Then
- XCTAssertNotNil(value)
- }
- func testThatDataTaskSerializesData() async throws {
- // Given
- let session = stored(Session())
- // When
- let value = try await session.request(.get).serializingData().value
- // Then
- XCTAssertNotNil(value)
- }
- func testThatDataTaskProducesResult() async {
- // Given
- let session = stored(Session())
- // When
- let result = await session.request(.get).serializingDecodable(TestResponse.self).result
- // Then
- XCTAssertNotNil(result.success)
- }
- func testThatDataTaskProducesValue() async throws {
- // Given
- let session = stored(Session())
- // When
- let value = try await session.request(.get).serializingDecodable(TestResponse.self).value
- // Then
- XCTAssertNotNil(value)
- }
- func testThatDataTaskProperlySupportsConcurrentRequests() async {
- // Given
- let session = stored(Session())
- // When
- async let first = session.request(.get).serializingDecodable(TestResponse.self).response
- async let second = session.request(.get).serializingDecodable(TestResponse.self).response
- async let third = session.request(.get).serializingDecodable(TestResponse.self).response
- // Then
- let responses = await [first, second, third]
- XCTAssertEqual(responses.count, 3)
- XCTAssertTrue(responses.allSatisfy(\.result.isSuccess))
- }
- func testThatDataTaskCancellationCancelsRequest() async {
- // Given
- let session = stored(Session())
- let request = session.request(.get)
- let task = request.serializingDecodable(TestResponse.self)
- // When
- task.cancel()
- let response = await task.response
- // Then
- XCTAssertTrue(response.error?.isExplicitlyCancelledError == true)
- XCTAssertTrue(request.isCancelled, "Underlying DataRequest should be cancelled.")
- }
- func testThatDataTaskIsAutomaticallyCancelledInTask() async {
- // Given
- let session = stored(Session())
- let request = session.request(.get)
- // When
- let task = Task {
- await request.serializingDecodable(TestResponse.self).result
- }
- task.cancel()
- let result = await task.value
- // Then
- XCTAssertTrue(result.failure?.isExplicitlyCancelledError == true)
- XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
- XCTAssertTrue(request.isCancelled, "Underlying DataRequest should be cancelled.")
- }
- func testThatDataTaskIsNotAutomaticallyCancelledInTaskWhenDisabled() async {
- // Given
- let session = stored(Session())
- let request = session.request(.get)
- // When
- let task = Task {
- await request.serializingDecodable(TestResponse.self, automaticallyCancelling: false).result
- }
- task.cancel()
- let result = await task.value
- // Then
- XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
- XCTAssertFalse(request.isCancelled, "Underlying DataRequest should not be cancelled.")
- XCTAssertTrue(result.isSuccess, "DataRequest should succeed.")
- }
- func testThatDataTaskIsAutomaticallyCancelledInTaskGroup() async {
- // Given
- let session = stored(Session())
- let request = session.request(.get)
- // When
- let task = Task {
- await withTaskGroup(of: Result<TestResponse, AFError>.self) { group -> Result<TestResponse, AFError> in
- group.addTask {
- await request.serializingDecodable(TestResponse.self).result
- }
- return await group.first(where: { _ in true })!
- }
- }
- task.cancel()
- let result = await task.value
- // Then
- XCTAssertTrue(result.failure?.isExplicitlyCancelledError == true)
- XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
- XCTAssertTrue(request.isCancelled, "Underlying DataRequest should be cancelled.")
- }
- func testThatDataTaskIsNotAutomaticallyCancelledInTaskGroupWhenDisabled() async {
- // Given
- let session = stored(Session())
- let request = session.request(.get)
- // When
- let task = Task {
- await withTaskGroup(of: Result<TestResponse, AFError>.self) { group -> Result<TestResponse, AFError> in
- group.addTask {
- await request.serializingDecodable(TestResponse.self, automaticallyCancelling: false).result
- }
- return await group.first(where: { _ in true })!
- }
- }
- task.cancel()
- let result = await task.value
- // Then
- XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
- XCTAssertFalse(request.isCancelled, "Underlying DataRequest should not be cancelled.")
- XCTAssertTrue(result.isSuccess, "DataRequest should succeed.")
- }
- }
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- final class DownloadConcurrencyTests: BaseTestCase {
- func testThatDownloadTaskSerializesResponseFromSerializer() async throws {
- // Given
- let session = stored(Session())
- // When
- let value = try await session.download(.get)
- .serializingDownload(using: .data)
- .value
- // Then
- XCTAssertNotNil(value)
- }
- func testThatDownloadTaskSerializesDecodable() async throws {
- // Given
- let session = stored(Session())
- // When
- let value = try await session.download(.get).serializingDecodable(TestResponse.self).value
- // Then
- XCTAssertNotNil(value)
- }
- func testThatDownloadTaskSerializesString() async throws {
- // Given
- let session = stored(Session())
- // When
- let value = try await session.download(.get).serializingString().value
- // Then
- XCTAssertNotNil(value)
- }
- func testThatDownloadTaskSerializesData() async throws {
- // Given
- let session = stored(Session())
- // When
- let value = try await session.download(.get).serializingData().value
- // Then
- XCTAssertNotNil(value)
- }
- func testThatDownloadTaskSerializesURL() async throws {
- // Given
- let session = stored(Session())
- // When
- let value = try await session.download(.get).serializingDownloadedFileURL().value
- // Then
- XCTAssertNotNil(value)
- }
- func testThatDownloadTaskProducesResult() async {
- // Given
- let session = stored(Session())
- // When
- let result = await session.download(.get).serializingDecodable(TestResponse.self).result
- // Then
- XCTAssertNotNil(result.success)
- }
- func testThatDownloadTaskProducesValue() async throws {
- // Given
- let session = stored(Session())
- // When
- let value = try await session.download(.get).serializingDecodable(TestResponse.self).value
- // Then
- XCTAssertNotNil(value)
- }
- func testThatDownloadTaskProperlySupportsConcurrentRequests() async {
- // Given
- let session = stored(Session())
- // When
- async let first = session.download(.get).serializingDecodable(TestResponse.self).response
- async let second = session.download(.get).serializingDecodable(TestResponse.self).response
- async let third = session.download(.get).serializingDecodable(TestResponse.self).response
- // Then
- let responses = await [first, second, third]
- XCTAssertEqual(responses.count, 3)
- XCTAssertTrue(responses.allSatisfy(\.result.isSuccess))
- }
- func testThatDownloadTaskCancelsRequest() async {
- // Given
- let session = stored(Session())
- let request = session.download(.get)
- let task = request.serializingDecodable(TestResponse.self)
- // When
- task.cancel()
- let response = await task.response
- // Then
- XCTAssertTrue(response.error?.isExplicitlyCancelledError == true)
- }
- func testThatDownloadTaskIsAutomaticallyCancelledInTask() async {
- // Given
- let session = stored(Session())
- let request = session.download(.get)
- // When
- let task = Task {
- await request.serializingDecodable(TestResponse.self).result
- }
- task.cancel()
- let result = await task.value
- // Then
- XCTAssertTrue(result.failure?.isExplicitlyCancelledError == true)
- XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
- XCTAssertTrue(request.isCancelled, "Underlying DownloadRequest should be cancelled.")
- }
- func testThatDownloadTaskIsNotAutomaticallyCancelledInTaskWhenDisabled() async {
- // Given
- let session = stored(Session())
- let request = session.download(.get)
- // When
- let task = Task {
- await request.serializingDecodable(TestResponse.self, automaticallyCancelling: false).result
- }
- task.cancel()
- let result = await task.value
- // Then
- XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
- XCTAssertFalse(request.isCancelled, "Underlying DownloadRequest should not be cancelled.")
- XCTAssertTrue(result.isSuccess, "DownloadRequest should succeed.")
- }
- func testThatDownloadTaskIsAutomaticallyCancelledInTaskGroup() async {
- // Given
- let session = stored(Session())
- let request = session.download(.get)
- // When
- let task = Task {
- await withTaskGroup(of: Result<TestResponse, AFError>.self) { group -> Result<TestResponse, AFError> in
- group.addTask {
- await request.serializingDecodable(TestResponse.self).result
- }
- return await group.first(where: { _ in true })!
- }
- }
- task.cancel()
- let result = await task.value
- // Then
- XCTAssertTrue(result.failure?.isExplicitlyCancelledError == true)
- XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
- XCTAssertTrue(request.isCancelled, "Underlying DownloadRequest should be cancelled.")
- }
- func testThatDownloadTaskIsNotAutomaticallyCancelledInTaskGroupWhenDisabled() async {
- // Given
- let session = stored(Session())
- let request = session.download(.get)
- // When
- let task = Task {
- await withTaskGroup(of: Result<TestResponse, AFError>.self) { group -> Result<TestResponse, AFError> in
- group.addTask {
- await request.serializingDecodable(TestResponse.self, automaticallyCancelling: false).result
- }
- return await group.first(where: { _ in true })!
- }
- }
- task.cancel()
- let result = await task.value
- // Then
- XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
- XCTAssertFalse(request.isCancelled, "Underlying DownloadRequest should not be cancelled.")
- XCTAssertTrue(result.isSuccess, "DownloadRequest should succeed.")
- }
- }
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- final class DataStreamConcurrencyTests: BaseTestCase {
- func testThatDataStreamTaskCanStreamData() async {
- // Given
- let session = stored(Session())
- // When
- let task = session.streamRequest(.payloads(2)).streamTask()
- var datas: [Data] = []
- for await data in task.streamingData().compactMap(\.value) {
- datas.append(data)
- }
- // Then
- XCTAssertEqual(datas.count, 2)
- }
- func testThatDataStreamTaskCanStreamStrings() async {
- // Given
- let session = stored(Session())
- // When
- let task = session.streamRequest(.payloads(2)).streamTask()
- var strings: [String] = []
- for await string in task.streamingStrings().compactMap(\.value) {
- strings.append(string)
- }
- // Then
- XCTAssertEqual(strings.count, 2)
- }
- func testThatDataStreamTaskCanStreamDecodable() async {
- // Given
- let session = stored(Session())
- // When
- let task = session.streamRequest(.payloads(2)).streamTask()
- let stream = task.streamingResponses(serializedUsing: DecodableStreamSerializer<TestResponse>())
- var responses: [TestResponse] = []
- for await response in stream.compactMap(\.value) {
- responses.append(response)
- }
- // Then
- XCTAssertEqual(responses.count, 2)
- }
- func testThatDataStreamTaskCanBeDirectlyCancelled() async {
- // Given
- let session = stored(Session())
- // When
- let expectedPayloads = 10
- let request = session.streamRequest(.payloads(expectedPayloads))
- let task = request.streamTask()
- var datas: [Data] = []
- for await data in task.streamingData().compactMap(\.value) {
- datas.append(data)
- if datas.count == 1 {
- task.cancel()
- }
- }
- // Then
- XCTAssertTrue(request.isCancelled)
- XCTAssertTrue(datas.count == 1)
- }
- func testThatDataStreamTaskIsCancelledByCancellingIteration() async {
- // Given
- let session = stored(Session())
- // When
- let expectedPayloads = 10
- let request = session.streamRequest(.payloads(expectedPayloads))
- let task = request.streamTask()
- var datas: [Data] = []
- for await data in task.streamingData().compactMap(\.value) {
- datas.append(data)
- if datas.count == 1 {
- break
- }
- }
- // Then
- XCTAssertTrue(request.isCancelled)
- XCTAssertTrue(datas.count == 1)
- }
- func testThatDataStreamTaskCanBeImplicitlyCancelled() async {
- // Given
- let session = stored(Session())
- // When
- let expectedPayloads = 10
- let request = session.streamRequest(.payloads(expectedPayloads))
- let task = Task<[Data], Never> {
- var datas: [Data] = []
- for await data in request.streamTask().streamingData().compactMap(\.value) {
- datas.append(data)
- }
- return datas
- }
- task.cancel()
- let datas: [Data] = await task.value
- // Then
- XCTAssertTrue(request.isCancelled)
- XCTAssertTrue(datas.isEmpty)
- }
- func testThatDataStreamTaskCanBeCancelledAfterStreamTurnsOffAutomaticCancellation() async {
- // Given
- let session = stored(Session())
- // When
- let expectedPayloads = 10
- let request = session.streamRequest(.payloads(expectedPayloads))
- let task = Task<[Data], Never> {
- var datas: [Data] = []
- let streamTask = request.streamTask()
- for await data in streamTask.streamingData(automaticallyCancelling: false).compactMap(\.value) {
- datas.append(data)
- break
- }
- for await data in streamTask.streamingData().compactMap(\.value) {
- datas.append(data)
- break
- }
- return datas
- }
- let datas: [Data] = await task.value
- // Then
- XCTAssertTrue(request.isCancelled)
- XCTAssertTrue(datas.count == 2)
- }
- }
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- final class ClosureAPIConcurrencyTests: BaseTestCase {
- func testThatDownloadProgressStreamReturnsProgress() async {
- // Given
- let session = stored(Session())
- // When
- let request = session.request(.get)
- async let uploadProgress = request.uploadProgress().collect()
- async let downloadProgress = request.downloadProgress().collect()
- async let requests = request.urlRequests().collect()
- async let tasks = request.urlSessionTasks().collect()
- async let descriptions = request.cURLDescriptions().collect()
- async let response = request.serializingDecodable(TestResponse.self).response
- let values: (uploadProgresses: [Progress],
- downloadProgresses: [Progress],
- requests: [URLRequest],
- tasks: [URLSessionTask],
- descriptions: [String],
- response: AFDataResponse<TestResponse>)
- #if swift(>=5.10)
- values = try! await (uploadProgress, downloadProgress, requests, tasks, descriptions, response)
- #else
- values = await (uploadProgress, downloadProgress, requests, tasks, descriptions, response)
- #endif
- // Then
- XCTAssertTrue(values.uploadProgresses.isEmpty, "uploadProgresses should be empty")
- XCTAssertNotNil(values.downloadProgresses.last, "downloadProgresses should not be empty")
- XCTAssertTrue(values.downloadProgresses.last?.isFinished == true, "last download progression should be finished")
- XCTAssertNotNil(values.requests.last, "requests should not be empty")
- XCTAssertNotNil(values.tasks.last, "tasks should not be empty")
- XCTAssertNotNil(values.descriptions.last, "descriptions should not be empty")
- XCTAssertTrue(values.response.result.isSuccess, "request should succeed")
- }
- }
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- extension AsyncSequence {
- func collect() async rethrows -> [Element] {
- var elements: [Element] = []
- for try await element in self {
- elements.append(element)
- }
- return elements
- }
- }
- #endif
|