UploadTests.swift 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. //
  2. // UploadTests.swift
  3. //
  4. // Copyright (c) 2014-2016 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. import Alamofire
  25. import Foundation
  26. import XCTest
  27. class UploadFileInitializationTestCase: BaseTestCase {
  28. func testUploadClassMethodWithMethodURLAndFile() {
  29. // Given
  30. let urlString = "https://httpbin.org/"
  31. let imageURL = url(forResource: "rainbow", withExtension: "jpg")
  32. // When
  33. let request = Alamofire.upload(imageURL, to: urlString)
  34. // Then
  35. XCTAssertNotNil(request.request, "request should not be nil")
  36. XCTAssertEqual(request.request?.httpMethod ?? "", "POST", "request HTTP method should be POST")
  37. XCTAssertEqual(request.request?.url?.absoluteString, urlString, "request URL string should be equal")
  38. XCTAssertNil(request.response, "response should be nil")
  39. }
  40. func testUploadClassMethodWithMethodURLHeadersAndFile() {
  41. // Given
  42. let urlString = "https://httpbin.org/"
  43. let headers = ["Authorization": "123456"]
  44. let imageURL = url(forResource: "rainbow", withExtension: "jpg")
  45. // When
  46. let request = Alamofire.upload(imageURL, to: urlString, method: .post, headers: headers)
  47. // Then
  48. XCTAssertNotNil(request.request, "request should not be nil")
  49. XCTAssertEqual(request.request?.httpMethod ?? "", "POST", "request HTTP method should be POST")
  50. XCTAssertEqual(request.request?.url?.absoluteString, urlString, "request URL string should be equal")
  51. let authorizationHeader = request.request?.value(forHTTPHeaderField: "Authorization") ?? ""
  52. XCTAssertEqual(authorizationHeader, "123456", "Authorization header is incorrect")
  53. XCTAssertNil(request.response, "response should be nil")
  54. }
  55. }
  56. // MARK: -
  57. class UploadDataInitializationTestCase: BaseTestCase {
  58. func testUploadClassMethodWithMethodURLAndData() {
  59. // Given
  60. let urlString = "https://httpbin.org/"
  61. // When
  62. let request = Alamofire.upload(Data(), to: urlString)
  63. // Then
  64. XCTAssertNotNil(request.request, "request should not be nil")
  65. XCTAssertEqual(request.request?.httpMethod ?? "", "POST", "request HTTP method should be POST")
  66. XCTAssertEqual(request.request?.url?.absoluteString, urlString, "request URL string should be equal")
  67. XCTAssertNil(request.response, "response should be nil")
  68. }
  69. func testUploadClassMethodWithMethodURLHeadersAndData() {
  70. // Given
  71. let urlString = "https://httpbin.org/"
  72. let headers = ["Authorization": "123456"]
  73. // When
  74. let request = Alamofire.upload(Data(), to: urlString, headers: headers)
  75. // Then
  76. XCTAssertNotNil(request.request, "request should not be nil")
  77. XCTAssertEqual(request.request?.httpMethod ?? "", "POST", "request HTTP method should be POST")
  78. XCTAssertEqual(request.request?.url?.absoluteString, urlString, "request URL string should be equal")
  79. let authorizationHeader = request.request?.value(forHTTPHeaderField: "Authorization") ?? ""
  80. XCTAssertEqual(authorizationHeader, "123456", "Authorization header is incorrect")
  81. XCTAssertNil(request.response, "response should be nil")
  82. }
  83. }
  84. // MARK: -
  85. class UploadStreamInitializationTestCase: BaseTestCase {
  86. func testUploadClassMethodWithMethodURLAndStream() {
  87. // Given
  88. let urlString = "https://httpbin.org/"
  89. let imageURL = url(forResource: "rainbow", withExtension: "jpg")
  90. let imageStream = InputStream(url: imageURL)!
  91. // When
  92. let request = Alamofire.upload(imageStream, to: urlString)
  93. // Then
  94. XCTAssertNotNil(request.request, "request should not be nil")
  95. XCTAssertEqual(request.request?.httpMethod ?? "", "POST", "request HTTP method should be POST")
  96. XCTAssertEqual(request.request?.url?.absoluteString, urlString, "request URL string should be equal")
  97. XCTAssertNil(request.response, "response should be nil")
  98. }
  99. func testUploadClassMethodWithMethodURLHeadersAndStream() {
  100. // Given
  101. let urlString = "https://httpbin.org/"
  102. let imageURL = url(forResource: "rainbow", withExtension: "jpg")
  103. let headers = ["Authorization": "123456"]
  104. let imageStream = InputStream(url: imageURL)!
  105. // When
  106. let request = Alamofire.upload(imageStream, to: urlString, headers: headers)
  107. // Then
  108. XCTAssertNotNil(request.request, "request should not be nil")
  109. XCTAssertEqual(request.request?.httpMethod ?? "", "POST", "request HTTP method should be POST")
  110. XCTAssertEqual(request.request?.url?.absoluteString, urlString, "request URL string should be equal")
  111. let authorizationHeader = request.request?.value(forHTTPHeaderField: "Authorization") ?? ""
  112. XCTAssertEqual(authorizationHeader, "123456", "Authorization header is incorrect")
  113. XCTAssertNil(request.response, "response should be nil")
  114. }
  115. }
  116. // MARK: -
  117. class UploadDataTestCase: BaseTestCase {
  118. func testUploadDataRequest() {
  119. // Given
  120. let urlString = "https://httpbin.org/post"
  121. let data = "Lorem ipsum dolor sit amet".data(using: .utf8, allowLossyConversion: false)!
  122. let expectation = self.expectation(description: "Upload request should succeed: \(urlString)")
  123. var response: DefaultDataResponse?
  124. // When
  125. Alamofire.upload(data, to: urlString)
  126. .response { resp in
  127. response = resp
  128. expectation.fulfill()
  129. }
  130. waitForExpectations(timeout: timeout, handler: nil)
  131. // Then
  132. XCTAssertNotNil(response?.request)
  133. XCTAssertNotNil(response?.response)
  134. XCTAssertNil(response?.error)
  135. }
  136. func testUploadDataRequestWithProgress() {
  137. // Given
  138. let urlString = "https://httpbin.org/post"
  139. let data: Data = {
  140. var text = ""
  141. for _ in 1...3_000 {
  142. text += "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
  143. }
  144. return text.data(using: .utf8, allowLossyConversion: false)!
  145. }()
  146. let expectation = self.expectation(description: "Bytes upload progress should be reported: \(urlString)")
  147. var uploadProgressValues: [Double] = []
  148. var downloadProgressValues: [Double] = []
  149. var response: DefaultDataResponse?
  150. // When
  151. Alamofire.upload(data, to: urlString)
  152. .uploadProgress { progress in
  153. uploadProgressValues.append(progress.fractionCompleted)
  154. }
  155. .downloadProgress { progress in
  156. downloadProgressValues.append(progress.fractionCompleted)
  157. }
  158. .response { resp in
  159. response = resp
  160. expectation.fulfill()
  161. }
  162. waitForExpectations(timeout: timeout, handler: nil)
  163. // Then
  164. XCTAssertNotNil(response?.request)
  165. XCTAssertNotNil(response?.response)
  166. XCTAssertNotNil(response?.data)
  167. XCTAssertNil(response?.error)
  168. var previousUploadProgress: Double = uploadProgressValues.first ?? 0.0
  169. for progress in uploadProgressValues {
  170. XCTAssertGreaterThanOrEqual(progress, previousUploadProgress)
  171. previousUploadProgress = progress
  172. }
  173. if let lastProgressValue = uploadProgressValues.last {
  174. XCTAssertEqual(lastProgressValue, 1.0)
  175. } else {
  176. XCTFail("last item in uploadProgressValues should not be nil")
  177. }
  178. var previousDownloadProgress: Double = downloadProgressValues.first ?? 0.0
  179. for progress in downloadProgressValues {
  180. XCTAssertGreaterThanOrEqual(progress, previousDownloadProgress)
  181. previousDownloadProgress = progress
  182. }
  183. if let lastProgressValue = downloadProgressValues.last {
  184. XCTAssertEqual(lastProgressValue, 1.0)
  185. } else {
  186. XCTFail("last item in downloadProgressValues should not be nil")
  187. }
  188. }
  189. }
  190. // MARK: -
  191. class UploadMultipartFormDataTestCase: BaseTestCase {
  192. // MARK: Tests
  193. func testThatUploadingMultipartFormDataSetsContentTypeHeader() {
  194. // Given
  195. let urlString = "https://httpbin.org/post"
  196. let uploadData = "upload_data".data(using: .utf8, allowLossyConversion: false)!
  197. let expectation = self.expectation(description: "multipart form data upload should succeed")
  198. var formData: MultipartFormData?
  199. var response: DefaultDataResponse?
  200. // When
  201. Alamofire.upload(
  202. multipartFormData: { multipartFormData in
  203. multipartFormData.append(uploadData, withName: "upload_data")
  204. formData = multipartFormData
  205. },
  206. to: urlString,
  207. encodingCompletion: { result in
  208. switch result {
  209. case .success(let upload, _, _):
  210. upload.response { resp in
  211. response = resp
  212. expectation.fulfill()
  213. }
  214. case .failure:
  215. expectation.fulfill()
  216. }
  217. }
  218. )
  219. waitForExpectations(timeout: timeout, handler: nil)
  220. // Then
  221. XCTAssertNotNil(response?.request)
  222. XCTAssertNotNil(response?.response)
  223. XCTAssertNotNil(response?.data)
  224. XCTAssertNil(response?.error)
  225. if
  226. let request = response?.request,
  227. let multipartFormData = formData,
  228. let contentType = request.value(forHTTPHeaderField: "Content-Type")
  229. {
  230. XCTAssertEqual(contentType, multipartFormData.contentType)
  231. } else {
  232. XCTFail("Content-Type header value should not be nil")
  233. }
  234. }
  235. func testThatUploadingMultipartFormDataSucceedsWithDefaultParameters() {
  236. // Given
  237. let urlString = "https://httpbin.org/post"
  238. let frenchData = "français".data(using: .utf8, allowLossyConversion: false)!
  239. let japaneseData = "日本語".data(using: .utf8, allowLossyConversion: false)!
  240. let expectation = self.expectation(description: "multipart form data upload should succeed")
  241. var response: DefaultDataResponse?
  242. // When
  243. Alamofire.upload(
  244. multipartFormData: { multipartFormData in
  245. multipartFormData.append(frenchData, withName: "french")
  246. multipartFormData.append(japaneseData, withName: "japanese")
  247. },
  248. to: urlString,
  249. encodingCompletion: { result in
  250. switch result {
  251. case .success(let upload, _, _):
  252. upload.response { resp in
  253. response = resp
  254. expectation.fulfill()
  255. }
  256. case .failure:
  257. expectation.fulfill()
  258. }
  259. }
  260. )
  261. waitForExpectations(timeout: timeout, handler: nil)
  262. // Then
  263. XCTAssertNotNil(response?.request)
  264. XCTAssertNotNil(response?.response)
  265. XCTAssertNotNil(response?.data)
  266. XCTAssertNil(response?.error)
  267. }
  268. func testThatUploadingMultipartFormDataWhileStreamingFromMemoryMonitorsProgress() {
  269. executeMultipartFormDataUploadRequestWithProgress(streamFromDisk: false)
  270. }
  271. func testThatUploadingMultipartFormDataWhileStreamingFromDiskMonitorsProgress() {
  272. executeMultipartFormDataUploadRequestWithProgress(streamFromDisk: true)
  273. }
  274. func testThatUploadingMultipartFormDataBelowMemoryThresholdStreamsFromMemory() {
  275. // Given
  276. let urlString = "https://httpbin.org/post"
  277. let frenchData = "français".data(using: .utf8, allowLossyConversion: false)!
  278. let japaneseData = "日本語".data(using: .utf8, allowLossyConversion: false)!
  279. let expectation = self.expectation(description: "multipart form data upload should succeed")
  280. var streamingFromDisk: Bool?
  281. var streamFileURL: URL?
  282. // When
  283. Alamofire.upload(
  284. multipartFormData: { multipartFormData in
  285. multipartFormData.append(frenchData, withName: "french")
  286. multipartFormData.append(japaneseData, withName: "japanese")
  287. },
  288. to: urlString,
  289. encodingCompletion: { result in
  290. switch result {
  291. case let .success(upload, uploadStreamingFromDisk, uploadStreamFileURL):
  292. streamingFromDisk = uploadStreamingFromDisk
  293. streamFileURL = uploadStreamFileURL
  294. upload.response { _ in
  295. expectation.fulfill()
  296. }
  297. case .failure:
  298. expectation.fulfill()
  299. }
  300. }
  301. )
  302. waitForExpectations(timeout: timeout, handler: nil)
  303. // Then
  304. XCTAssertNotNil(streamingFromDisk, "streaming from disk should not be nil")
  305. XCTAssertNil(streamFileURL, "stream file URL should be nil")
  306. if let streamingFromDisk = streamingFromDisk {
  307. XCTAssertFalse(streamingFromDisk, "streaming from disk should be false")
  308. }
  309. }
  310. func testThatUploadingMultipartFormDataBelowMemoryThresholdSetsContentTypeHeader() {
  311. // Given
  312. let urlString = "https://httpbin.org/post"
  313. let uploadData = "upload data".data(using: .utf8, allowLossyConversion: false)!
  314. let expectation = self.expectation(description: "multipart form data upload should succeed")
  315. var formData: MultipartFormData?
  316. var request: URLRequest?
  317. var streamingFromDisk: Bool?
  318. // When
  319. Alamofire.upload(
  320. multipartFormData: { multipartFormData in
  321. multipartFormData.append(uploadData, withName: "upload_data")
  322. formData = multipartFormData
  323. },
  324. to: urlString,
  325. encodingCompletion: { result in
  326. switch result {
  327. case let .success(upload, uploadStreamingFromDisk, _):
  328. streamingFromDisk = uploadStreamingFromDisk
  329. upload.response { resp in
  330. request = resp.request
  331. expectation.fulfill()
  332. }
  333. case .failure:
  334. expectation.fulfill()
  335. }
  336. }
  337. )
  338. waitForExpectations(timeout: timeout, handler: nil)
  339. // Then
  340. XCTAssertNotNil(streamingFromDisk, "streaming from disk should not be nil")
  341. if let streamingFromDisk = streamingFromDisk {
  342. XCTAssertFalse(streamingFromDisk, "streaming from disk should be false")
  343. }
  344. if
  345. let request = request,
  346. let multipartFormData = formData,
  347. let contentType = request.value(forHTTPHeaderField: "Content-Type")
  348. {
  349. XCTAssertEqual(contentType, multipartFormData.contentType, "Content-Type header value should match")
  350. } else {
  351. XCTFail("Content-Type header value should not be nil")
  352. }
  353. }
  354. func testThatUploadingMultipartFormDataAboveMemoryThresholdStreamsFromDisk() {
  355. // Given
  356. let urlString = "https://httpbin.org/post"
  357. let frenchData = "français".data(using: .utf8, allowLossyConversion: false)!
  358. let japaneseData = "日本語".data(using: .utf8, allowLossyConversion: false)!
  359. let expectation = self.expectation(description: "multipart form data upload should succeed")
  360. var streamingFromDisk: Bool?
  361. var streamFileURL: URL?
  362. // When
  363. Alamofire.upload(
  364. multipartFormData: { multipartFormData in
  365. multipartFormData.append(frenchData, withName: "french")
  366. multipartFormData.append(japaneseData, withName: "japanese")
  367. },
  368. usingThreshold: 0,
  369. to: urlString,
  370. encodingCompletion: { result in
  371. switch result {
  372. case let .success(upload, uploadStreamingFromDisk, uploadStreamFileURL):
  373. streamingFromDisk = uploadStreamingFromDisk
  374. streamFileURL = uploadStreamFileURL
  375. upload.response { _ in
  376. expectation.fulfill()
  377. }
  378. case .failure:
  379. expectation.fulfill()
  380. }
  381. }
  382. )
  383. waitForExpectations(timeout: timeout, handler: nil)
  384. // Then
  385. XCTAssertNotNil(streamingFromDisk, "streaming from disk should not be nil")
  386. XCTAssertNotNil(streamFileURL, "stream file URL should not be nil")
  387. if let streamingFromDisk = streamingFromDisk, let streamFilePath = streamFileURL?.path {
  388. XCTAssertTrue(streamingFromDisk, "streaming from disk should be true")
  389. XCTAssertFalse(FileManager.default.fileExists(atPath: streamFilePath), "stream file path should not exist")
  390. }
  391. }
  392. func testThatUploadingMultipartFormDataAboveMemoryThresholdSetsContentTypeHeader() {
  393. // Given
  394. let urlString = "https://httpbin.org/post"
  395. let uploadData = "upload data".data(using: .utf8, allowLossyConversion: false)!
  396. let expectation = self.expectation(description: "multipart form data upload should succeed")
  397. var formData: MultipartFormData?
  398. var request: URLRequest?
  399. var streamingFromDisk: Bool?
  400. // When
  401. Alamofire.upload(
  402. multipartFormData: { multipartFormData in
  403. multipartFormData.append(uploadData, withName: "upload_data")
  404. formData = multipartFormData
  405. },
  406. usingThreshold: 0,
  407. to: urlString,
  408. encodingCompletion: { result in
  409. switch result {
  410. case let .success(upload, uploadStreamingFromDisk, _):
  411. streamingFromDisk = uploadStreamingFromDisk
  412. upload.response { resp in
  413. request = resp.request
  414. expectation.fulfill()
  415. }
  416. case .failure:
  417. expectation.fulfill()
  418. }
  419. }
  420. )
  421. waitForExpectations(timeout: timeout, handler: nil)
  422. // Then
  423. XCTAssertNotNil(streamingFromDisk, "streaming from disk should not be nil")
  424. if let streamingFromDisk = streamingFromDisk {
  425. XCTAssertTrue(streamingFromDisk, "streaming from disk should be true")
  426. }
  427. if
  428. let request = request,
  429. let multipartFormData = formData,
  430. let contentType = request.value(forHTTPHeaderField: "Content-Type")
  431. {
  432. XCTAssertEqual(contentType, multipartFormData.contentType, "Content-Type header value should match")
  433. } else {
  434. XCTFail("Content-Type header value should not be nil")
  435. }
  436. }
  437. // ⚠️ This test has been removed as a result of rdar://26870455 in Xcode 8 Seed 1
  438. // func testThatUploadingMultipartFormDataOnBackgroundSessionWritesDataToFileToAvoidCrash() {
  439. // // Given
  440. // let manager: SessionManager = {
  441. // let identifier = "org.alamofire.uploadtests.\(UUID().uuidString)"
  442. // let configuration = URLSessionConfiguration.background(withIdentifier: identifier)
  443. //
  444. // return SessionManager(configuration: configuration, serverTrustPolicyManager: nil)
  445. // }()
  446. //
  447. // let urlString = "https://httpbin.org/post"
  448. // let french = "français".data(using: .utf8, allowLossyConversion: false)!
  449. // let japanese = "日本語".data(using: .utf8, allowLossyConversion: false)!
  450. //
  451. // let expectation = self.expectation(description: "multipart form data upload should succeed")
  452. //
  453. // var request: URLRequest?
  454. // var response: HTTPURLResponse?
  455. // var data: Data?
  456. // var error: Error?
  457. // var streamingFromDisk: Bool?
  458. //
  459. // // When
  460. // manager.upload(
  461. // multipartFormData: { multipartFormData in
  462. // multipartFormData.append(french, withName: "french")
  463. // multipartFormData.append(japanese, withName: "japanese")
  464. // },
  465. // to: urlString,
  466. // withMethod: .post,
  467. // encodingCompletion: { result in
  468. // switch result {
  469. // case let .success(upload, uploadStreamingFromDisk, _):
  470. // streamingFromDisk = uploadStreamingFromDisk
  471. //
  472. // upload.response { responseRequest, responseResponse, responseData, responseError in
  473. // request = responseRequest
  474. // response = responseResponse
  475. // data = responseData
  476. // error = responseError
  477. //
  478. // expectation.fulfill()
  479. // }
  480. // case .failure:
  481. // expectation.fulfill()
  482. // }
  483. // }
  484. // )
  485. //
  486. // waitForExpectations(timeout: timeout, handler: nil)
  487. //
  488. // // Then
  489. // XCTAssertNotNil(request, "request should not be nil")
  490. // XCTAssertNotNil(response, "response should not be nil")
  491. // XCTAssertNotNil(data, "data should not be nil")
  492. // XCTAssertNil(error, "error should be nil")
  493. //
  494. // if let streamingFromDisk = streamingFromDisk {
  495. // XCTAssertTrue(streamingFromDisk, "streaming from disk should be true")
  496. // } else {
  497. // XCTFail("streaming from disk should not be nil")
  498. // }
  499. // }
  500. // MARK: Combined Test Execution
  501. private func executeMultipartFormDataUploadRequestWithProgress(streamFromDisk: Bool) {
  502. // Given
  503. let urlString = "https://httpbin.org/post"
  504. let loremData1: Data = {
  505. var loremValues: [String] = []
  506. for _ in 1...1_500 {
  507. loremValues.append("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
  508. }
  509. return loremValues.joined(separator: " ").data(using: .utf8, allowLossyConversion: false)!
  510. }()
  511. let loremData2: Data = {
  512. var loremValues: [String] = []
  513. for _ in 1...1_500 {
  514. loremValues.append("Lorem ipsum dolor sit amet, nam no graeco recusabo appellantur.")
  515. }
  516. return loremValues.joined(separator: " ").data(using: .utf8, allowLossyConversion: false)!
  517. }()
  518. let expectation = self.expectation(description: "multipart form data upload should succeed")
  519. var uploadProgressValues: [Double] = []
  520. var downloadProgressValues: [Double] = []
  521. var response: DefaultDataResponse?
  522. // When
  523. Alamofire.upload(
  524. multipartFormData: { multipartFormData in
  525. multipartFormData.append(loremData1, withName: "lorem1")
  526. multipartFormData.append(loremData2, withName: "lorem2")
  527. },
  528. usingThreshold: streamFromDisk ? 0 : 100_000_000,
  529. to: urlString,
  530. encodingCompletion: { result in
  531. switch result {
  532. case .success(let upload, _, _):
  533. upload
  534. .uploadProgress { progress in
  535. uploadProgressValues.append(progress.fractionCompleted)
  536. }
  537. .downloadProgress { progress in
  538. downloadProgressValues.append(progress.fractionCompleted)
  539. }
  540. .response { resp in
  541. response = resp
  542. expectation.fulfill()
  543. }
  544. case .failure:
  545. expectation.fulfill()
  546. }
  547. }
  548. )
  549. waitForExpectations(timeout: timeout, handler: nil)
  550. // Then
  551. XCTAssertNotNil(response?.request)
  552. XCTAssertNotNil(response?.response)
  553. XCTAssertNotNil(response?.data)
  554. XCTAssertNil(response?.error)
  555. var previousUploadProgress: Double = uploadProgressValues.first ?? 0.0
  556. for progress in uploadProgressValues {
  557. XCTAssertGreaterThanOrEqual(progress, previousUploadProgress)
  558. previousUploadProgress = progress
  559. }
  560. if let lastProgressValue = uploadProgressValues.last {
  561. XCTAssertEqual(lastProgressValue, 1.0)
  562. } else {
  563. XCTFail("last item in uploadProgressValues should not be nil")
  564. }
  565. var previousDownloadProgress: Double = downloadProgressValues.first ?? 0.0
  566. for progress in downloadProgressValues {
  567. XCTAssertGreaterThanOrEqual(progress, previousDownloadProgress)
  568. previousDownloadProgress = progress
  569. }
  570. if let lastProgressValue = downloadProgressValues.last {
  571. XCTAssertEqual(lastProgressValue, 1.0)
  572. } else {
  573. XCTFail("last item in downloadProgressValues should not be nil")
  574. }
  575. }
  576. }