UploadTests.swift 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  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, withMethod: .post)
  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?.urlString ?? "", 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, withMethod: .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?.urlString ?? "", 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, withMethod: .post)
  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?.urlString ?? "", 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, withMethod: .post, 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?.urlString ?? "", 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, withMethod: .post)
  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?.urlString ?? "", 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, withMethod: .post, 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?.urlString ?? "", 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: String.Encoding.utf8, allowLossyConversion: false)!
  122. let expectation = self.expectation(description: "Upload request should succeed: \(urlString)")
  123. var request: URLRequest?
  124. var response: HTTPURLResponse?
  125. var error: Error?
  126. // When
  127. Alamofire.upload(data, to: urlString, withMethod: .post)
  128. .response { responseRequest, responseResponse, _, responseError in
  129. request = responseRequest
  130. response = responseResponse
  131. error = responseError
  132. expectation.fulfill()
  133. }
  134. waitForExpectations(timeout: timeout, handler: nil)
  135. // Then
  136. XCTAssertNotNil(request, "request should not be nil")
  137. XCTAssertNotNil(response, "response should not be nil")
  138. XCTAssertNil(error, "error should be nil")
  139. }
  140. func testUploadDataRequestWithProgress() {
  141. // Given
  142. let urlString = "https://httpbin.org/post"
  143. let data: Data = {
  144. var text = ""
  145. for _ in 1...3_000 {
  146. text += "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
  147. }
  148. return text.data(using: String.Encoding.utf8, allowLossyConversion: false)!
  149. }()
  150. let expectation = self.expectation(description: "Bytes upload progress should be reported: \(urlString)")
  151. var uploadByteValues: [(bytes: Int64, totalBytes: Int64, totalBytesExpected: Int64)] = []
  152. var uploadProgressValues: [(completedUnitCount: Int64, totalUnitCount: Int64)] = []
  153. var downloadByteValues: [(bytes: Int64, totalBytes: Int64, totalBytesExpected: Int64)] = []
  154. var downloadProgressValues: [(completedUnitCount: Int64, totalUnitCount: Int64)] = []
  155. var responseRequest: URLRequest?
  156. var responseResponse: HTTPURLResponse?
  157. var responseData: Data?
  158. var responseError: Error?
  159. // When
  160. Alamofire.upload(data, to: urlString, withMethod: .post)
  161. .uploadProgress { progress in
  162. uploadProgressValues.append((progress.completedUnitCount, progress.totalUnitCount))
  163. }
  164. .uploadProgress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
  165. let bytes = (bytes: bytesRead, totalBytes: totalBytesRead, totalBytesExpected: totalBytesExpectedToRead)
  166. uploadByteValues.append(bytes)
  167. }
  168. .downloadProgress { progress in
  169. downloadProgressValues.append((progress.completedUnitCount, progress.totalUnitCount))
  170. }
  171. .downloadProgress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
  172. let bytes = (bytes: bytesRead, totalBytes: totalBytesRead, totalBytesExpected: totalBytesExpectedToRead)
  173. downloadByteValues.append(bytes)
  174. }
  175. .response { request, response, data, error in
  176. responseRequest = request
  177. responseResponse = response
  178. responseData = data
  179. responseError = error
  180. expectation.fulfill()
  181. }
  182. waitForExpectations(timeout: timeout, handler: nil)
  183. // Then
  184. XCTAssertNotNil(responseRequest)
  185. XCTAssertNotNil(responseResponse)
  186. XCTAssertNotNil(responseData)
  187. XCTAssertNil(responseError)
  188. XCTAssertEqual(uploadByteValues.count, uploadProgressValues.count)
  189. XCTAssertEqual(downloadByteValues.count, downloadProgressValues.count)
  190. if uploadByteValues.count == uploadProgressValues.count {
  191. for (byteValue, progressValue) in zip(uploadByteValues, uploadProgressValues) {
  192. XCTAssertGreaterThan(byteValue.bytes, 0)
  193. XCTAssertEqual(byteValue.totalBytes, progressValue.completedUnitCount)
  194. XCTAssertEqual(byteValue.totalBytesExpected, progressValue.totalUnitCount)
  195. }
  196. }
  197. if let lastUploadByteValue = uploadByteValues.last, let lastUploadProgressValue = uploadProgressValues.last {
  198. let byteValueFractionalCompletion = Double(lastUploadByteValue.totalBytes) / Double(lastUploadByteValue.totalBytesExpected)
  199. let progressValueFractionalCompletion = Double(lastUploadProgressValue.0) / Double(lastUploadProgressValue.1)
  200. XCTAssertEqual(byteValueFractionalCompletion, 1.0)
  201. XCTAssertEqual(progressValueFractionalCompletion, 1.0)
  202. } else {
  203. XCTFail("last item in uploadByteValues and uploadProgressValues should not be nil")
  204. }
  205. if downloadByteValues.count == downloadProgressValues.count {
  206. for (byteValue, progressValue) in zip(downloadByteValues, downloadProgressValues) {
  207. XCTAssertGreaterThan(byteValue.bytes, 0)
  208. XCTAssertEqual(byteValue.totalBytes, progressValue.completedUnitCount)
  209. XCTAssertEqual(byteValue.totalBytesExpected, progressValue.totalUnitCount)
  210. }
  211. }
  212. if let lastDownloadByteValue = downloadByteValues.last, let lastDownloadProgressValue = downloadProgressValues.last {
  213. let byteValueFractionalCompletion = Double(lastDownloadByteValue.totalBytes) / Double(lastDownloadByteValue.totalBytesExpected)
  214. let progressValueFractionalCompletion = Double(lastDownloadProgressValue.0) / Double(lastDownloadProgressValue.1)
  215. XCTAssertEqual(byteValueFractionalCompletion, 1.0)
  216. XCTAssertEqual(progressValueFractionalCompletion, 1.0)
  217. } else {
  218. XCTFail("last item in downloadByteValues and downloadProgressValues should not be nil")
  219. }
  220. }
  221. }
  222. // MARK: -
  223. class UploadMultipartFormDataTestCase: BaseTestCase {
  224. // MARK: Tests
  225. func testThatUploadingMultipartFormDataSetsContentTypeHeader() {
  226. // Given
  227. let urlString = "https://httpbin.org/post"
  228. let uploadData = "upload_data".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  229. let expectation = self.expectation(description: "multipart form data upload should succeed")
  230. var formData: MultipartFormData?
  231. var request: URLRequest?
  232. var response: HTTPURLResponse?
  233. var data: Data?
  234. var error: Error?
  235. // When
  236. Alamofire.upload(
  237. multipartFormData: { multipartFormData in
  238. multipartFormData.append(uploadData, withName: "upload_data")
  239. formData = multipartFormData
  240. },
  241. to: urlString,
  242. withMethod: .post,
  243. encodingCompletion: { result in
  244. switch result {
  245. case .success(let upload, _, _):
  246. upload.response { responseRequest, responseResponse, responseData, responseError in
  247. request = responseRequest
  248. response = responseResponse
  249. data = responseData
  250. error = responseError
  251. expectation.fulfill()
  252. }
  253. case .failure:
  254. expectation.fulfill()
  255. }
  256. }
  257. )
  258. waitForExpectations(timeout: timeout, handler: nil)
  259. // Then
  260. XCTAssertNotNil(request, "request should not be nil")
  261. XCTAssertNotNil(response, "response should not be nil")
  262. XCTAssertNotNil(data, "data should not be nil")
  263. XCTAssertNil(error, "error should be nil")
  264. if
  265. let request = request,
  266. let multipartFormData = formData,
  267. let contentType = request.value(forHTTPHeaderField: "Content-Type")
  268. {
  269. XCTAssertEqual(contentType, multipartFormData.contentType, "Content-Type header value should match")
  270. } else {
  271. XCTFail("Content-Type header value should not be nil")
  272. }
  273. }
  274. func testThatUploadingMultipartFormDataSucceedsWithDefaultParameters() {
  275. // Given
  276. let urlString = "https://httpbin.org/post"
  277. let frenchData = "français".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  278. let japaneseData = "日本語".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  279. let expectation = self.expectation(description: "multipart form data upload should succeed")
  280. var request: URLRequest?
  281. var response: HTTPURLResponse?
  282. var data: Data?
  283. var error: Error?
  284. // When
  285. Alamofire.upload(
  286. multipartFormData: { multipartFormData in
  287. multipartFormData.append(frenchData, withName: "french")
  288. multipartFormData.append(japaneseData, withName: "japanese")
  289. },
  290. to: urlString,
  291. withMethod: .post,
  292. encodingCompletion: { result in
  293. switch result {
  294. case .success(let upload, _, _):
  295. upload.response { responseRequest, responseResponse, responseData, responseError in
  296. request = responseRequest
  297. response = responseResponse
  298. data = responseData
  299. error = responseError
  300. expectation.fulfill()
  301. }
  302. case .failure:
  303. expectation.fulfill()
  304. }
  305. }
  306. )
  307. waitForExpectations(timeout: timeout, handler: nil)
  308. // Then
  309. XCTAssertNotNil(request, "request should not be nil")
  310. XCTAssertNotNil(response, "response should not be nil")
  311. XCTAssertNotNil(data, "data should not be nil")
  312. XCTAssertNil(error, "error should be nil")
  313. }
  314. func testThatUploadingMultipartFormDataWhileStreamingFromMemoryMonitorsProgress() {
  315. executeMultipartFormDataUploadRequestWithProgress(streamFromDisk: false)
  316. }
  317. func testThatUploadingMultipartFormDataWhileStreamingFromDiskMonitorsProgress() {
  318. executeMultipartFormDataUploadRequestWithProgress(streamFromDisk: true)
  319. }
  320. func testThatUploadingMultipartFormDataBelowMemoryThresholdStreamsFromMemory() {
  321. // Given
  322. let urlString = "https://httpbin.org/post"
  323. let frenchData = "français".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  324. let japaneseData = "日本語".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  325. let expectation = self.expectation(description: "multipart form data upload should succeed")
  326. var streamingFromDisk: Bool?
  327. var streamFileURL: URL?
  328. // When
  329. Alamofire.upload(
  330. multipartFormData: { multipartFormData in
  331. multipartFormData.append(frenchData, withName: "french")
  332. multipartFormData.append(japaneseData, withName: "japanese")
  333. },
  334. to: urlString,
  335. withMethod: .post,
  336. encodingCompletion: { result in
  337. switch result {
  338. case let .success(upload, uploadStreamingFromDisk, uploadStreamFileURL):
  339. streamingFromDisk = uploadStreamingFromDisk
  340. streamFileURL = uploadStreamFileURL
  341. upload.response { _, _, _, _ in
  342. expectation.fulfill()
  343. }
  344. case .failure:
  345. expectation.fulfill()
  346. }
  347. }
  348. )
  349. waitForExpectations(timeout: timeout, handler: nil)
  350. // Then
  351. XCTAssertNotNil(streamingFromDisk, "streaming from disk should not be nil")
  352. XCTAssertNil(streamFileURL, "stream file URL should be nil")
  353. if let streamingFromDisk = streamingFromDisk {
  354. XCTAssertFalse(streamingFromDisk, "streaming from disk should be false")
  355. }
  356. }
  357. func testThatUploadingMultipartFormDataBelowMemoryThresholdSetsContentTypeHeader() {
  358. // Given
  359. let urlString = "https://httpbin.org/post"
  360. let uploadData = "upload data".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  361. let expectation = self.expectation(description: "multipart form data upload should succeed")
  362. var formData: MultipartFormData?
  363. var request: URLRequest?
  364. var streamingFromDisk: Bool?
  365. // When
  366. Alamofire.upload(
  367. multipartFormData: { multipartFormData in
  368. multipartFormData.append(uploadData, withName: "upload_data")
  369. formData = multipartFormData
  370. },
  371. to: urlString,
  372. withMethod: .post,
  373. encodingCompletion: { result in
  374. switch result {
  375. case let .success(upload, uploadStreamingFromDisk, _):
  376. streamingFromDisk = uploadStreamingFromDisk
  377. upload.response { responseRequest, _, _, _ in
  378. request = responseRequest
  379. expectation.fulfill()
  380. }
  381. case .failure:
  382. expectation.fulfill()
  383. }
  384. }
  385. )
  386. waitForExpectations(timeout: timeout, handler: nil)
  387. // Then
  388. XCTAssertNotNil(streamingFromDisk, "streaming from disk should not be nil")
  389. if let streamingFromDisk = streamingFromDisk {
  390. XCTAssertFalse(streamingFromDisk, "streaming from disk should be false")
  391. }
  392. if
  393. let request = request,
  394. let multipartFormData = formData,
  395. let contentType = request.value(forHTTPHeaderField: "Content-Type")
  396. {
  397. XCTAssertEqual(contentType, multipartFormData.contentType, "Content-Type header value should match")
  398. } else {
  399. XCTFail("Content-Type header value should not be nil")
  400. }
  401. }
  402. func testThatUploadingMultipartFormDataAboveMemoryThresholdStreamsFromDisk() {
  403. // Given
  404. let urlString = "https://httpbin.org/post"
  405. let frenchData = "français".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  406. let japaneseData = "日本語".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  407. let expectation = self.expectation(description: "multipart form data upload should succeed")
  408. var streamingFromDisk: Bool?
  409. var streamFileURL: URL?
  410. // When
  411. Alamofire.upload(
  412. multipartFormData: { multipartFormData in
  413. multipartFormData.append(frenchData, withName: "french")
  414. multipartFormData.append(japaneseData, withName: "japanese")
  415. },
  416. usingThreshold: 0,
  417. to: urlString,
  418. withMethod: .post,
  419. encodingCompletion: { result in
  420. switch result {
  421. case let .success(upload, uploadStreamingFromDisk, uploadStreamFileURL):
  422. streamingFromDisk = uploadStreamingFromDisk
  423. streamFileURL = uploadStreamFileURL
  424. upload.response { _, _, _, _ in
  425. expectation.fulfill()
  426. }
  427. case .failure:
  428. expectation.fulfill()
  429. }
  430. }
  431. )
  432. waitForExpectations(timeout: timeout, handler: nil)
  433. // Then
  434. XCTAssertNotNil(streamingFromDisk, "streaming from disk should not be nil")
  435. XCTAssertNotNil(streamFileURL, "stream file URL should not be nil")
  436. if let streamingFromDisk = streamingFromDisk, let streamFilePath = streamFileURL?.path {
  437. XCTAssertTrue(streamingFromDisk, "streaming from disk should be true")
  438. XCTAssertTrue(
  439. FileManager.default.fileExists(atPath: streamFilePath),
  440. "stream file path should exist"
  441. )
  442. }
  443. }
  444. func testThatUploadingMultipartFormDataAboveMemoryThresholdSetsContentTypeHeader() {
  445. // Given
  446. let urlString = "https://httpbin.org/post"
  447. let uploadData = "upload data".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  448. let expectation = self.expectation(description: "multipart form data upload should succeed")
  449. var formData: MultipartFormData?
  450. var request: URLRequest?
  451. var streamingFromDisk: Bool?
  452. // When
  453. Alamofire.upload(
  454. multipartFormData: { multipartFormData in
  455. multipartFormData.append(uploadData, withName: "upload_data")
  456. formData = multipartFormData
  457. },
  458. usingThreshold: 0,
  459. to: urlString,
  460. withMethod: .post,
  461. encodingCompletion: { result in
  462. switch result {
  463. case let .success(upload, uploadStreamingFromDisk, _):
  464. streamingFromDisk = uploadStreamingFromDisk
  465. upload.response { responseRequest, _, _, _ in
  466. request = responseRequest
  467. expectation.fulfill()
  468. }
  469. case .failure:
  470. expectation.fulfill()
  471. }
  472. }
  473. )
  474. waitForExpectations(timeout: timeout, handler: nil)
  475. // Then
  476. XCTAssertNotNil(streamingFromDisk, "streaming from disk should not be nil")
  477. if let streamingFromDisk = streamingFromDisk {
  478. XCTAssertTrue(streamingFromDisk, "streaming from disk should be true")
  479. }
  480. if
  481. let request = request,
  482. let multipartFormData = formData,
  483. let contentType = request.value(forHTTPHeaderField: "Content-Type")
  484. {
  485. XCTAssertEqual(contentType, multipartFormData.contentType, "Content-Type header value should match")
  486. } else {
  487. XCTFail("Content-Type header value should not be nil")
  488. }
  489. }
  490. // ⚠️ This test has been removed as a result of rdar://26870455 in Xcode 8 Seed 1
  491. // func testThatUploadingMultipartFormDataOnBackgroundSessionWritesDataToFileToAvoidCrash() {
  492. // // Given
  493. // let manager: SessionManager = {
  494. // let identifier = "org.alamofire.uploadtests.\(UUID().uuidString)"
  495. // let configuration = URLSessionConfiguration.background(withIdentifier: identifier)
  496. //
  497. // return SessionManager(configuration: configuration, serverTrustPolicyManager: nil)
  498. // }()
  499. //
  500. // let urlString = "https://httpbin.org/post"
  501. // let french = "français".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  502. // let japanese = "日本語".data(using: String.Encoding.utf8, allowLossyConversion: false)!
  503. //
  504. // let expectation = self.expectation(description: "multipart form data upload should succeed")
  505. //
  506. // var request: URLRequest?
  507. // var response: HTTPURLResponse?
  508. // var data: Data?
  509. // var error: Error?
  510. // var streamingFromDisk: Bool?
  511. //
  512. // // When
  513. // manager.upload(
  514. // multipartFormData: { multipartFormData in
  515. // multipartFormData.append(french, withName: "french")
  516. // multipartFormData.append(japanese, withName: "japanese")
  517. // },
  518. // to: urlString,
  519. // withMethod: .post,
  520. // encodingCompletion: { result in
  521. // switch result {
  522. // case let .success(upload, uploadStreamingFromDisk, _):
  523. // streamingFromDisk = uploadStreamingFromDisk
  524. //
  525. // upload.response { responseRequest, responseResponse, responseData, responseError in
  526. // request = responseRequest
  527. // response = responseResponse
  528. // data = responseData
  529. // error = responseError
  530. //
  531. // expectation.fulfill()
  532. // }
  533. // case .failure:
  534. // expectation.fulfill()
  535. // }
  536. // }
  537. // )
  538. //
  539. // waitForExpectations(timeout: timeout, handler: nil)
  540. //
  541. // // Then
  542. // XCTAssertNotNil(request, "request should not be nil")
  543. // XCTAssertNotNil(response, "response should not be nil")
  544. // XCTAssertNotNil(data, "data should not be nil")
  545. // XCTAssertNil(error, "error should be nil")
  546. //
  547. // if let streamingFromDisk = streamingFromDisk {
  548. // XCTAssertTrue(streamingFromDisk, "streaming from disk should be true")
  549. // } else {
  550. // XCTFail("streaming from disk should not be nil")
  551. // }
  552. // }
  553. // MARK: Combined Test Execution
  554. private func executeMultipartFormDataUploadRequestWithProgress(streamFromDisk: Bool) {
  555. // Given
  556. let urlString = "https://httpbin.org/post"
  557. let loremData1: Data = {
  558. var loremValues: [String] = []
  559. for _ in 1...1_500 {
  560. loremValues.append("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
  561. }
  562. return loremValues.joined(separator: " ").data(using: String.Encoding.utf8, allowLossyConversion: false)!
  563. }()
  564. let loremData2: Data = {
  565. var loremValues: [String] = []
  566. for _ in 1...1_500 {
  567. loremValues.append("Lorem ipsum dolor sit amet, nam no graeco recusabo appellantur.")
  568. }
  569. return loremValues.joined(separator: " ").data(using: String.Encoding.utf8, allowLossyConversion: false)!
  570. }()
  571. let expectation = self.expectation(description: "multipart form data upload should succeed")
  572. var uploadByteValues: [(bytes: Int64, totalBytes: Int64, totalBytesExpected: Int64)] = []
  573. var uploadProgressValues: [(completedUnitCount: Int64, totalUnitCount: Int64)] = []
  574. var downloadByteValues: [(bytes: Int64, totalBytes: Int64, totalBytesExpected: Int64)] = []
  575. var downloadProgressValues: [(completedUnitCount: Int64, totalUnitCount: Int64)] = []
  576. var request: URLRequest?
  577. var response: HTTPURLResponse?
  578. var data: Data?
  579. var error: Error?
  580. // When
  581. Alamofire.upload(
  582. multipartFormData: { multipartFormData in
  583. multipartFormData.append(loremData1, withName: "lorem1")
  584. multipartFormData.append(loremData2, withName: "lorem2")
  585. },
  586. usingThreshold: streamFromDisk ? 0 : 100_000_000,
  587. to: urlString,
  588. withMethod: .post,
  589. encodingCompletion: { result in
  590. switch result {
  591. case .success(let upload, _, _):
  592. upload
  593. .uploadProgress { progress in
  594. uploadProgressValues.append((progress.completedUnitCount, progress.totalUnitCount))
  595. }
  596. .uploadProgress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
  597. let bytes = (bytes: bytesRead, totalBytes: totalBytesRead, totalBytesExpected: totalBytesExpectedToRead)
  598. uploadByteValues.append(bytes)
  599. }
  600. .downloadProgress { progress in
  601. downloadProgressValues.append((progress.completedUnitCount, progress.totalUnitCount))
  602. }
  603. .downloadProgress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
  604. let bytes = (bytes: bytesRead, totalBytes: totalBytesRead, totalBytesExpected: totalBytesExpectedToRead)
  605. downloadByteValues.append(bytes)
  606. }
  607. .response { responseRequest, responseResponse, responseData, responseError in
  608. request = responseRequest
  609. response = responseResponse
  610. data = responseData
  611. error = responseError
  612. expectation.fulfill()
  613. }
  614. case .failure:
  615. expectation.fulfill()
  616. }
  617. }
  618. )
  619. waitForExpectations(timeout: timeout, handler: nil)
  620. // Then
  621. XCTAssertNotNil(request)
  622. XCTAssertNotNil(response)
  623. XCTAssertNotNil(data)
  624. XCTAssertNil(error)
  625. XCTAssertEqual(uploadByteValues.count, uploadProgressValues.count)
  626. XCTAssertEqual(downloadByteValues.count, downloadProgressValues.count)
  627. if uploadByteValues.count == uploadProgressValues.count {
  628. for (byteValue, progressValue) in zip(uploadByteValues, uploadProgressValues) {
  629. XCTAssertGreaterThan(byteValue.bytes, 0)
  630. XCTAssertEqual(byteValue.totalBytes, progressValue.completedUnitCount)
  631. XCTAssertEqual(byteValue.totalBytesExpected, progressValue.totalUnitCount)
  632. }
  633. }
  634. if let lastUploadByteValue = uploadByteValues.last, let lastUploadProgressValue = uploadProgressValues.last {
  635. let byteValueFractionalCompletion = Double(lastUploadByteValue.totalBytes) / Double(lastUploadByteValue.totalBytesExpected)
  636. let progressValueFractionalCompletion = Double(lastUploadProgressValue.0) / Double(lastUploadProgressValue.1)
  637. XCTAssertEqual(byteValueFractionalCompletion, 1.0)
  638. XCTAssertEqual(progressValueFractionalCompletion, 1.0)
  639. } else {
  640. XCTFail("last item in uploadByteValues and uploadProgressValues should not be nil")
  641. }
  642. if downloadByteValues.count == downloadProgressValues.count {
  643. for (byteValue, progressValue) in zip(downloadByteValues, downloadProgressValues) {
  644. XCTAssertGreaterThan(byteValue.bytes, 0)
  645. XCTAssertEqual(byteValue.totalBytes, progressValue.completedUnitCount)
  646. XCTAssertEqual(byteValue.totalBytesExpected, progressValue.totalUnitCount)
  647. }
  648. }
  649. if let lastDownloadByteValue = downloadByteValues.last, let lastDownloadProgressValue = downloadProgressValues.last {
  650. let byteValueFractionalCompletion = Double(lastDownloadByteValue.totalBytes) / Double(lastDownloadByteValue.totalBytesExpected)
  651. let progressValueFractionalCompletion = Double(lastDownloadProgressValue.0) / Double(lastDownloadProgressValue.1)
  652. XCTAssertEqual(byteValueFractionalCompletion, 1.0)
  653. XCTAssertEqual(progressValueFractionalCompletion, 1.0)
  654. } else {
  655. XCTFail("last item in downloadByteValues and downloadProgressValues should not be nil")
  656. }
  657. }
  658. }