ConcurrencyTests.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. //
  2. // ConcurrencyTests.swift
  3. //
  4. // Copyright (c) 2021 Alamofire Software Foundation (http://alamofire.org/)
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. //
  24. #if compiler(>=5.6.0) && canImport(_Concurrency)
  25. import Alamofire
  26. import XCTest
  27. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  28. final class DataRequestConcurrencyTests: BaseTestCase {
  29. func testThatDataTaskSerializesResponseUsingSerializer() async throws {
  30. // Given
  31. let session = stored(Session())
  32. // When
  33. let value = try await session.request(.get)
  34. .serializingResponse(using: .data)
  35. .value
  36. // Then
  37. XCTAssertNotNil(value)
  38. }
  39. func testThatDataTaskSerializesDecodable() async throws {
  40. // Given
  41. let session = stored(Session())
  42. // When
  43. let value = try await session.request(.get).serializingDecodable(TestResponse.self).value
  44. // Then
  45. XCTAssertNotNil(value)
  46. }
  47. func testThatDataTaskSerializesString() async throws {
  48. // Given
  49. let session = stored(Session())
  50. // When
  51. let value = try await session.request(.get).serializingString().value
  52. // Then
  53. XCTAssertNotNil(value)
  54. }
  55. func testThatDataTaskSerializesData() async throws {
  56. // Given
  57. let session = stored(Session())
  58. // When
  59. let value = try await session.request(.get).serializingData().value
  60. // Then
  61. XCTAssertNotNil(value)
  62. }
  63. func testThatDataTaskProducesResult() async {
  64. // Given
  65. let session = stored(Session())
  66. // When
  67. let result = await session.request(.get).serializingDecodable(TestResponse.self).result
  68. // Then
  69. XCTAssertNotNil(result.success)
  70. }
  71. func testThatDataTaskProducesValue() async throws {
  72. // Given
  73. let session = stored(Session())
  74. // When
  75. let value = try await session.request(.get).serializingDecodable(TestResponse.self).value
  76. // Then
  77. XCTAssertNotNil(value)
  78. }
  79. func testThatDataTaskProperlySupportsConcurrentRequests() async {
  80. // Given
  81. let session = stored(Session())
  82. // When
  83. async let first = session.request(.get).serializingDecodable(TestResponse.self).response
  84. async let second = session.request(.get).serializingDecodable(TestResponse.self).response
  85. async let third = session.request(.get).serializingDecodable(TestResponse.self).response
  86. // Then
  87. let responses = await [first, second, third]
  88. XCTAssertEqual(responses.count, 3)
  89. XCTAssertTrue(responses.allSatisfy(\.result.isSuccess))
  90. }
  91. func testThatDataTaskCancellationCancelsRequest() async {
  92. // Given
  93. let session = stored(Session())
  94. let request = session.request(.get)
  95. let task = request.serializingDecodable(TestResponse.self)
  96. // When
  97. task.cancel()
  98. let response = await task.response
  99. // Then
  100. XCTAssertTrue(response.error?.isExplicitlyCancelledError == true)
  101. XCTAssertTrue(request.isCancelled, "Underlying DataRequest should be cancelled.")
  102. }
  103. func testThatDataTaskIsAutomaticallyCancelledInTaskWhenEnabled() async {
  104. // Given
  105. let session = stored(Session())
  106. let request = session.request(.get)
  107. // When
  108. let task = Task {
  109. await request.serializingDecodable(TestResponse.self, automaticallyCancelling: true).result
  110. }
  111. task.cancel()
  112. let result = await task.value
  113. // Then
  114. XCTAssertTrue(result.failure?.isExplicitlyCancelledError == true)
  115. XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
  116. XCTAssertTrue(request.isCancelled, "Underlying DataRequest should be cancelled.")
  117. }
  118. func testThatDataTaskIsAutomaticallyCancelledInTaskGroupWhenEnabled() async {
  119. // Given
  120. let session = stored(Session())
  121. let request = session.request(.get)
  122. // When
  123. let task = Task {
  124. await withTaskGroup(of: Result<TestResponse, AFError>.self) { group -> Result<TestResponse, AFError> in
  125. group.addTask {
  126. await request.serializingDecodable(TestResponse.self, automaticallyCancelling: true).result
  127. }
  128. return await group.first(where: { _ in true })!
  129. }
  130. }
  131. task.cancel()
  132. let result = await task.value
  133. // Then
  134. XCTAssertTrue(result.failure?.isExplicitlyCancelledError == true)
  135. XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
  136. XCTAssertTrue(request.isCancelled, "Underlying DataRequest should be cancelled.")
  137. }
  138. }
  139. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  140. final class DownloadConcurrencyTests: BaseTestCase {
  141. func testThatDownloadTaskSerializesResponseFromSerializer() async throws {
  142. // Given
  143. let session = stored(Session())
  144. // When
  145. let value = try await session.download(.get)
  146. .serializingDownload(using: .data)
  147. .value
  148. // Then
  149. XCTAssertNotNil(value)
  150. }
  151. func testThatDownloadTaskSerializesDecodable() async throws {
  152. // Given
  153. let session = stored(Session())
  154. // When
  155. let value = try await session.download(.get).serializingDecodable(TestResponse.self).value
  156. // Then
  157. XCTAssertNotNil(value)
  158. }
  159. func testThatDownloadTaskSerializesString() async throws {
  160. // Given
  161. let session = stored(Session())
  162. // When
  163. let value = try await session.download(.get).serializingString().value
  164. // Then
  165. XCTAssertNotNil(value)
  166. }
  167. func testThatDownloadTaskSerializesData() async throws {
  168. // Given
  169. let session = stored(Session())
  170. // When
  171. let value = try await session.download(.get).serializingData().value
  172. // Then
  173. XCTAssertNotNil(value)
  174. }
  175. func testThatDownloadTaskSerializesURL() async throws {
  176. // Given
  177. let session = stored(Session())
  178. // When
  179. let value = try await session.download(.get).serializingDownloadedFileURL().value
  180. // Then
  181. XCTAssertNotNil(value)
  182. }
  183. func testThatDownloadTaskProducesResult() async {
  184. // Given
  185. let session = stored(Session())
  186. // When
  187. let result = await session.download(.get).serializingDecodable(TestResponse.self).result
  188. // Then
  189. XCTAssertNotNil(result.success)
  190. }
  191. func testThatDownloadTaskProducesValue() async throws {
  192. // Given
  193. let session = stored(Session())
  194. // When
  195. let value = try await session.download(.get).serializingDecodable(TestResponse.self).value
  196. // Then
  197. XCTAssertNotNil(value)
  198. }
  199. func testThatDownloadTaskProperlySupportsConcurrentRequests() async {
  200. // Given
  201. let session = stored(Session())
  202. // When
  203. async let first = session.download(.get).serializingDecodable(TestResponse.self).response
  204. async let second = session.download(.get).serializingDecodable(TestResponse.self).response
  205. async let third = session.download(.get).serializingDecodable(TestResponse.self).response
  206. // Then
  207. let responses = await [first, second, third]
  208. XCTAssertEqual(responses.count, 3)
  209. XCTAssertTrue(responses.allSatisfy(\.result.isSuccess))
  210. }
  211. func testThatDownloadTaskCancelsRequest() async {
  212. // Given
  213. let session = stored(Session())
  214. let request = session.download(.get)
  215. let task = request.serializingDecodable(TestResponse.self)
  216. // When
  217. task.cancel()
  218. let response = await task.response
  219. // Then
  220. XCTAssertTrue(response.error?.isExplicitlyCancelledError == true)
  221. }
  222. func testThatDownloadTaskIsAutomaticallyCancelledInTaskWhenEnabled() async {
  223. // Given
  224. let session = stored(Session())
  225. let request = session.download(.get)
  226. // When
  227. let task = Task {
  228. await request.serializingDecodable(TestResponse.self, automaticallyCancelling: true).result
  229. }
  230. task.cancel()
  231. let result = await task.value
  232. // Then
  233. XCTAssertTrue(result.failure?.isExplicitlyCancelledError == true)
  234. XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
  235. XCTAssertTrue(request.isCancelled, "Underlying DownloadRequest should be cancelled.")
  236. }
  237. func testThatDownloadTaskIsAutomaticallyCancelledInTaskGroupWhenEnabled() async {
  238. // Given
  239. let session = stored(Session())
  240. let request = session.download(.get)
  241. // When
  242. let task = Task {
  243. await withTaskGroup(of: Result<TestResponse, AFError>.self) { group -> Result<TestResponse, AFError> in
  244. group.addTask {
  245. await request.serializingDecodable(TestResponse.self, automaticallyCancelling: true).result
  246. }
  247. return await group.first(where: { _ in true })!
  248. }
  249. }
  250. task.cancel()
  251. let result = await task.value
  252. // Then
  253. XCTAssertTrue(result.failure?.isExplicitlyCancelledError == true)
  254. XCTAssertTrue(task.isCancelled, "Task should be cancelled.")
  255. XCTAssertTrue(request.isCancelled, "Underlying DownloadRequest should be cancelled.")
  256. }
  257. }
  258. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  259. final class DataStreamConcurrencyTests: BaseTestCase {
  260. func testThatDataStreamTaskCanStreamData() async {
  261. // Given
  262. let session = stored(Session())
  263. // When
  264. let task = session.streamRequest(.payloads(2)).streamTask()
  265. var datas: [Data] = []
  266. for await data in task.streamingData().compactMap(\.value) {
  267. datas.append(data)
  268. }
  269. // Then
  270. XCTAssertEqual(datas.count, 2)
  271. }
  272. func testThatDataStreamTaskCanStreamStrings() async {
  273. // Given
  274. let session = stored(Session())
  275. // When
  276. let task = session.streamRequest(.payloads(2)).streamTask()
  277. var strings: [String] = []
  278. for await string in task.streamingStrings().compactMap(\.value) {
  279. strings.append(string)
  280. }
  281. // Then
  282. XCTAssertEqual(strings.count, 2)
  283. }
  284. func testThatDataStreamTaskCanStreamDecodable() async {
  285. // Given
  286. let session = stored(Session())
  287. // When
  288. let task = session.streamRequest(.payloads(2)).streamTask()
  289. let stream = task.streamingResponses(serializedUsing: DecodableStreamSerializer<TestResponse>())
  290. var responses: [TestResponse] = []
  291. for await response in stream.compactMap(\.value) {
  292. responses.append(response)
  293. }
  294. // Then
  295. XCTAssertEqual(responses.count, 2)
  296. }
  297. func testThatDataStreamTaskCanBeDirectlyCancelled() async {
  298. // Given
  299. let session = stored(Session())
  300. // When
  301. let expectedPayloads = 10
  302. let request = session.streamRequest(.payloads(expectedPayloads))
  303. let task = request.streamTask()
  304. var datas: [Data] = []
  305. for await data in task.streamingData().compactMap(\.value) {
  306. datas.append(data)
  307. if datas.count == 1 {
  308. task.cancel()
  309. }
  310. }
  311. // Then
  312. XCTAssertTrue(request.isCancelled)
  313. XCTAssertTrue(datas.count == 1)
  314. }
  315. func testThatDataStreamTaskIsCancelledByCancellingIteration() async {
  316. // Given
  317. let session = stored(Session())
  318. // When
  319. let expectedPayloads = 10
  320. let request = session.streamRequest(.payloads(expectedPayloads))
  321. let task = request.streamTask()
  322. var datas: [Data] = []
  323. for await data in task.streamingData().compactMap(\.value) {
  324. datas.append(data)
  325. if datas.count == 1 {
  326. break
  327. }
  328. }
  329. // Then
  330. XCTAssertTrue(request.isCancelled)
  331. XCTAssertTrue(datas.count == 1)
  332. }
  333. func testThatDataStreamTaskCanBeImplicitlyCancelled() async {
  334. // Given
  335. let session = stored(Session())
  336. // When
  337. let expectedPayloads = 10
  338. let request = session.streamRequest(.payloads(expectedPayloads))
  339. let task = Task<[Data], Never> {
  340. var datas: [Data] = []
  341. for await data in request.streamTask().streamingData().compactMap(\.value) {
  342. datas.append(data)
  343. }
  344. return datas
  345. }
  346. task.cancel()
  347. let datas: [Data] = await task.value
  348. // Then
  349. XCTAssertTrue(request.isCancelled)
  350. XCTAssertTrue(datas.isEmpty)
  351. }
  352. func testThatDataStreamTaskCanBeCancelledAfterStreamTurnsOffAutomaticCancellation() async {
  353. // Given
  354. let session = stored(Session())
  355. // When
  356. let expectedPayloads = 10
  357. let request = session.streamRequest(.payloads(expectedPayloads))
  358. let task = Task<[Data], Never> {
  359. var datas: [Data] = []
  360. let streamTask = request.streamTask()
  361. for await data in streamTask.streamingData(automaticallyCancelling: false).compactMap(\.value) {
  362. datas.append(data)
  363. break
  364. }
  365. for await data in streamTask.streamingData().compactMap(\.value) {
  366. datas.append(data)
  367. break
  368. }
  369. return datas
  370. }
  371. let datas: [Data] = await task.value
  372. // Then
  373. XCTAssertTrue(request.isCancelled)
  374. XCTAssertTrue(datas.count == 2)
  375. }
  376. }
  377. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  378. final class ClosureAPIConcurrencyTests: BaseTestCase {
  379. func testThatDownloadProgressStreamReturnsProgress() async {
  380. // Given
  381. let session = stored(Session())
  382. // When
  383. let request = session.request(.get)
  384. async let uploadProgress = request.uploadProgress().collect()
  385. async let downloadProgress = request.downloadProgress().collect()
  386. async let requests = request.urlRequests().collect()
  387. async let tasks = request.urlSessionTasks().collect()
  388. async let descriptions = request.cURLDescriptions().collect()
  389. async let response = request.serializingDecodable(TestResponse.self).response
  390. let values: (uploadProgresses: [Progress],
  391. downloadProgresses: [Progress],
  392. requests: [URLRequest],
  393. tasks: [URLSessionTask],
  394. descriptions: [String],
  395. response: AFDataResponse<TestResponse>)
  396. #if swift(>=5.8)
  397. values = try! await (uploadProgress, downloadProgress, requests, tasks, descriptions, response)
  398. #else
  399. values = await (uploadProgress, downloadProgress, requests, tasks, descriptions, response)
  400. #endif
  401. // Then
  402. XCTAssertTrue(values.uploadProgresses.isEmpty)
  403. XCTAssertNotNil(values.downloadProgresses.last)
  404. XCTAssertTrue(values.downloadProgresses.last?.isFinished == true)
  405. XCTAssertNotNil(values.requests.last)
  406. XCTAssertNotNil(values.tasks.last)
  407. XCTAssertNotNil(values.descriptions.last)
  408. XCTAssertTrue(values.response.result.isSuccess)
  409. }
  410. }
  411. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  412. extension AsyncSequence {
  413. func collect() async rethrows -> [Element] {
  414. var elements: [Element] = []
  415. for try await element in self {
  416. elements.append(element)
  417. }
  418. return elements
  419. }
  420. }
  421. #endif