DownloadTests.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. //
  2. // DownloadTests.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 DownloadInitializationTestCase: BaseTestCase {
  28. let searchPathDirectory: FileManager.SearchPathDirectory = .cachesDirectory
  29. let searchPathDomain: FileManager.SearchPathDomainMask = .userDomainMask
  30. func testDownloadClassMethodWithMethodURLAndDestination() {
  31. // Given
  32. let urlString = "https://httpbin.org/"
  33. // When
  34. let request = Alamofire.download(urlString)
  35. // Then
  36. XCTAssertNotNil(request.request)
  37. XCTAssertEqual(request.request?.httpMethod, "GET")
  38. XCTAssertEqual(request.request?.urlString, urlString)
  39. XCTAssertNil(request.response)
  40. }
  41. func testDownloadClassMethodWithMethodURLHeadersAndDestination() {
  42. // Given
  43. let urlString = "https://httpbin.org/"
  44. let headers = ["Authorization": "123456"]
  45. // When
  46. let request = Alamofire.download(urlString, headers: headers)
  47. // Then
  48. XCTAssertNotNil(request.request)
  49. XCTAssertEqual(request.request?.httpMethod, "GET")
  50. XCTAssertEqual(request.request?.urlString, urlString)
  51. XCTAssertEqual(request.request?.value(forHTTPHeaderField: "Authorization"), "123456")
  52. XCTAssertNil(request.response)
  53. }
  54. }
  55. // MARK: -
  56. class DownloadResponseTestCase: BaseTestCase {
  57. private var randomCachesFileURL: URL {
  58. return FileManager.cachesDirectoryURL.appendingPathComponent("\(UUID().uuidString).json")
  59. }
  60. func testDownloadRequest() {
  61. // Given
  62. let fileURL = randomCachesFileURL
  63. let numberOfLines = 100
  64. let urlString = "https://httpbin.org/stream/\(numberOfLines)"
  65. let destination: DownloadRequest.DownloadFileDestination = { _, _ in (fileURL, []) }
  66. let expectation = self.expectation(description: "Download request should download data to file: \(urlString)")
  67. var response: DefaultDownloadResponse?
  68. // When
  69. Alamofire.download(urlString, to: destination)
  70. .response { resp in
  71. response = resp
  72. expectation.fulfill()
  73. }
  74. waitForExpectations(timeout: timeout, handler: nil)
  75. // Then
  76. XCTAssertNotNil(response?.request)
  77. XCTAssertNotNil(response?.response)
  78. XCTAssertNotNil(response?.destinationURL)
  79. XCTAssertNil(response?.resumeData)
  80. XCTAssertNil(response?.error)
  81. if let destinationURL = response?.destinationURL {
  82. XCTAssertTrue(FileManager.default.fileExists(atPath: destinationURL.path))
  83. if let data = try? Data(contentsOf: destinationURL) {
  84. XCTAssertGreaterThan(data.count, 0)
  85. } else {
  86. XCTFail("data should exist for contents of destinationURL")
  87. }
  88. }
  89. }
  90. func testDownloadRequestWithProgress() {
  91. // Given
  92. let randomBytes = 4 * 1024 * 1024
  93. let urlString = "https://httpbin.org/bytes/\(randomBytes)"
  94. let expectation = self.expectation(description: "Bytes download progress should be reported: \(urlString)")
  95. var byteValues: [(bytes: Int64, totalBytes: Int64, totalBytesExpected: Int64)] = []
  96. var progressValues: [(completedUnitCount: Int64, totalUnitCount: Int64)] = []
  97. var response: DefaultDownloadResponse?
  98. // When
  99. Alamofire.download(urlString)
  100. .downloadProgress { progress in
  101. progressValues.append((progress.completedUnitCount, progress.totalUnitCount))
  102. }
  103. .downloadProgress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
  104. let bytes = (bytes: bytesRead, totalBytes: totalBytesRead, totalBytesExpected: totalBytesExpectedToRead)
  105. byteValues.append(bytes)
  106. }
  107. .response { resp in
  108. response = resp
  109. expectation.fulfill()
  110. }
  111. waitForExpectations(timeout: timeout, handler: nil)
  112. // Then
  113. XCTAssertNotNil(response?.request)
  114. XCTAssertNotNil(response?.response)
  115. XCTAssertNotNil(response?.temporaryURL)
  116. XCTAssertNil(response?.destinationURL)
  117. XCTAssertNil(response?.resumeData)
  118. XCTAssertNil(response?.error)
  119. XCTAssertEqual(byteValues.count, progressValues.count)
  120. if byteValues.count == progressValues.count {
  121. for (byteValue, progressValue) in zip(byteValues, progressValues) {
  122. XCTAssertGreaterThan(byteValue.bytes, 0)
  123. XCTAssertEqual(byteValue.totalBytes, progressValue.completedUnitCount)
  124. XCTAssertEqual(byteValue.totalBytesExpected, progressValue.totalUnitCount)
  125. }
  126. }
  127. if let lastByteValue = byteValues.last, let lastProgressValue = progressValues.last {
  128. let byteValueFractionalCompletion = Double(lastByteValue.totalBytes) / Double(lastByteValue.totalBytesExpected)
  129. let progressValueFractionalCompletion = Double(lastProgressValue.0) / Double(lastProgressValue.1)
  130. XCTAssertEqual(byteValueFractionalCompletion, 1.0)
  131. XCTAssertEqual(progressValueFractionalCompletion, 1.0)
  132. } else {
  133. XCTFail("last item in bytesValues and progressValues should not be nil")
  134. }
  135. }
  136. func testDownloadRequestWithParameters() {
  137. // Given
  138. let urlString = "https://httpbin.org/get"
  139. let parameters = ["foo": "bar"]
  140. let expectation = self.expectation(description: "Download request should download data to file")
  141. var response: DefaultDownloadResponse?
  142. // When
  143. Alamofire.download(urlString, parameters: parameters)
  144. .response { resp in
  145. response = resp
  146. expectation.fulfill()
  147. }
  148. waitForExpectations(timeout: timeout, handler: nil)
  149. // Then
  150. XCTAssertNotNil(response?.request)
  151. XCTAssertNotNil(response?.response)
  152. XCTAssertNotNil(response?.temporaryURL)
  153. XCTAssertNil(response?.destinationURL)
  154. XCTAssertNil(response?.resumeData)
  155. XCTAssertNil(response?.error)
  156. if
  157. let temporaryURL = response?.temporaryURL,
  158. let data = try? Data(contentsOf: temporaryURL),
  159. let jsonObject = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0)),
  160. let json = jsonObject as? [String: Any],
  161. let args = json["args"] as? [String: String]
  162. {
  163. XCTAssertEqual(args["foo"], "bar")
  164. } else {
  165. XCTFail("args parameter in JSON should not be nil")
  166. }
  167. }
  168. func testDownloadRequestWithHeaders() {
  169. // Given
  170. let fileURL = randomCachesFileURL
  171. let urlString = "https://httpbin.org/get"
  172. let headers = ["Authorization": "123456"]
  173. let destination: DownloadRequest.DownloadFileDestination = { _, _ in (fileURL, []) }
  174. let expectation = self.expectation(description: "Download request should download data to file: \(fileURL)")
  175. var response: DefaultDownloadResponse?
  176. // When
  177. Alamofire.download(urlString, headers: headers, to: destination)
  178. .response { resp in
  179. response = resp
  180. expectation.fulfill()
  181. }
  182. waitForExpectations(timeout: timeout, handler: nil)
  183. // Then
  184. XCTAssertNotNil(response?.request)
  185. XCTAssertNotNil(response?.response)
  186. XCTAssertNotNil(response?.destinationURL)
  187. XCTAssertNil(response?.resumeData)
  188. XCTAssertNil(response?.error)
  189. if
  190. let data = try? Data(contentsOf: fileURL),
  191. let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []),
  192. let json = jsonObject as? [String: Any],
  193. let headers = json["headers"] as? [String: String]
  194. {
  195. XCTAssertEqual(headers["Authorization"], "123456")
  196. } else {
  197. XCTFail("headers parameter in JSON should not be nil")
  198. }
  199. }
  200. func testThatDownloadingFileAndMovingToDirectoryThatDoesNotExistThrowsError() {
  201. // Given
  202. let fileURL = FileManager.cachesDirectoryURL.appendingPathComponent("some/random/folder/test_output.json")
  203. let expectation = self.expectation(description: "Download request should download data but fail to move file")
  204. var response: DefaultDownloadResponse?
  205. // When
  206. Alamofire.download("https://httpbin.org/get", to: { _, _ in (fileURL, [])})
  207. .response { resp in
  208. response = resp
  209. expectation.fulfill()
  210. }
  211. waitForExpectations(timeout: timeout, handler: nil)
  212. // Then
  213. XCTAssertNotNil(response?.request)
  214. XCTAssertNotNil(response?.response)
  215. XCTAssertNotNil(response?.temporaryURL)
  216. XCTAssertNotNil(response?.destinationURL)
  217. XCTAssertNil(response?.resumeData)
  218. XCTAssertNotNil(response?.error)
  219. if let error = response?.error as? CocoaError {
  220. XCTAssertEqual(error.code, .fileNoSuchFileError)
  221. } else {
  222. XCTFail("error should not be nil")
  223. }
  224. }
  225. func testThatDownloadOptionsCanCreateIntermediateDirectoriesPriorToMovingFile() {
  226. // Given
  227. let fileURL = FileManager.cachesDirectoryURL.appendingPathComponent("some/random/folder/test_output.json")
  228. let expectation = self.expectation(description: "Download request should download data to file: \(fileURL)")
  229. var response: DefaultDownloadResponse?
  230. // When
  231. Alamofire.download("https://httpbin.org/get", to: { _, _ in (fileURL, [.createIntermediateDirectories])})
  232. .response { resp in
  233. response = resp
  234. expectation.fulfill()
  235. }
  236. waitForExpectations(timeout: timeout, handler: nil)
  237. // Then
  238. XCTAssertNotNil(response?.request)
  239. XCTAssertNotNil(response?.response)
  240. XCTAssertNotNil(response?.temporaryURL)
  241. XCTAssertNotNil(response?.destinationURL)
  242. XCTAssertNil(response?.resumeData)
  243. XCTAssertNil(response?.error)
  244. }
  245. func testThatDownloadingFileAndMovingToDestinationThatIsOccupiedThrowsError() {
  246. do {
  247. // Given
  248. let directoryURL = FileManager.cachesDirectoryURL.appendingPathComponent("some/random/folder")
  249. try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
  250. let fileURL = directoryURL.appendingPathComponent("test_output.json")
  251. try "random_data".write(to: fileURL, atomically: true, encoding: .utf8)
  252. let expectation = self.expectation(description: "Download should complete but fail to move file")
  253. var response: DefaultDownloadResponse?
  254. // When
  255. Alamofire.download("https://httpbin.org/get", to: { _, _ in (fileURL, [])})
  256. .response { resp in
  257. response = resp
  258. expectation.fulfill()
  259. }
  260. waitForExpectations(timeout: timeout, handler: nil)
  261. // Then
  262. XCTAssertNotNil(response?.request)
  263. XCTAssertNotNil(response?.response)
  264. XCTAssertNotNil(response?.temporaryURL)
  265. XCTAssertNotNil(response?.destinationURL)
  266. XCTAssertNil(response?.resumeData)
  267. XCTAssertNotNil(response?.error)
  268. if let error = response?.error as? CocoaError {
  269. XCTAssertEqual(error.code, .fileWriteFileExistsError)
  270. } else {
  271. XCTFail("error should not be nil")
  272. }
  273. } catch {
  274. XCTFail("Test encountered unexpected error: \(error)")
  275. }
  276. }
  277. func testThatDownloadOptionsCanRemovePreviousFilePriorToMovingFile() {
  278. do {
  279. // Given
  280. let directoryURL = FileManager.cachesDirectoryURL.appendingPathComponent("some/random/folder")
  281. try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
  282. let fileURL = directoryURL.appendingPathComponent("test_output.json")
  283. let expectation = self.expectation(description: "Download should complete and move file to URL: \(fileURL)")
  284. var response: DefaultDownloadResponse?
  285. // When
  286. Alamofire.download("https://httpbin.org/get", to: { _, _ in (fileURL, [.removePreviousFile])})
  287. .response { resp in
  288. response = resp
  289. expectation.fulfill()
  290. }
  291. waitForExpectations(timeout: timeout, handler: nil)
  292. // Then
  293. XCTAssertNotNil(response?.request)
  294. XCTAssertNotNil(response?.response)
  295. XCTAssertNotNil(response?.temporaryURL)
  296. XCTAssertNotNil(response?.destinationURL)
  297. XCTAssertNil(response?.resumeData)
  298. XCTAssertNil(response?.error)
  299. } catch {
  300. XCTFail("Test encountered unexpected error: \(error)")
  301. }
  302. }
  303. }
  304. // MARK: -
  305. class DownloadResumeDataTestCase: BaseTestCase {
  306. let urlString = "https://upload.wikimedia.org/wikipedia/commons/6/69/NASA-HS201427a-HubbleUltraDeepField2014-20140603.jpg"
  307. func testThatImmediatelyCancelledDownloadDoesNotHaveResumeDataAvailable() {
  308. // Given
  309. let expectation = self.expectation(description: "Download should be cancelled")
  310. var response: DefaultDownloadResponse?
  311. // When
  312. let download = Alamofire.download(urlString)
  313. .response { resp in
  314. response = resp
  315. expectation.fulfill()
  316. }
  317. download.cancel()
  318. waitForExpectations(timeout: timeout, handler: nil)
  319. // Then
  320. XCTAssertNotNil(response?.request)
  321. XCTAssertNil(response?.response)
  322. XCTAssertNil(response?.destinationURL)
  323. XCTAssertNil(response?.resumeData)
  324. XCTAssertNotNil(response?.error)
  325. XCTAssertNil(download.resumeData)
  326. }
  327. func testThatCancelledDownloadResponseDataMatchesResumeData() {
  328. // Given
  329. let expectation = self.expectation(description: "Download should be cancelled")
  330. var response: DefaultDownloadResponse?
  331. // When
  332. let download = Alamofire.download(urlString)
  333. download.downloadProgress { _, _, _ in
  334. download.cancel()
  335. }
  336. download.response { resp in
  337. response = resp
  338. expectation.fulfill()
  339. }
  340. waitForExpectations(timeout: timeout, handler: nil)
  341. // Then
  342. XCTAssertNotNil(response?.request)
  343. XCTAssertNotNil(response?.response)
  344. XCTAssertNil(response?.destinationURL)
  345. XCTAssertNotNil(response?.resumeData)
  346. XCTAssertNotNil(response?.error)
  347. XCTAssertNotNil(download.resumeData, "resume data should not be nil")
  348. if let responseResumeData = response?.resumeData, let resumeData = download.resumeData {
  349. XCTAssertEqual(responseResumeData, resumeData)
  350. } else {
  351. XCTFail("response resume data or resume data was unexpectedly nil")
  352. }
  353. }
  354. func testThatCancelledDownloadResumeDataIsAvailableWithJSONResponseSerializer() {
  355. // Given
  356. let expectation = self.expectation(description: "Download should be cancelled")
  357. var response: DownloadResponse<Any>?
  358. // When
  359. let download = Alamofire.download(urlString)
  360. download.downloadProgress { _, _, _ in
  361. download.cancel()
  362. }
  363. download.responseJSON { resp in
  364. response = resp
  365. expectation.fulfill()
  366. }
  367. waitForExpectations(timeout: timeout, handler: nil)
  368. // Then
  369. XCTAssertNotNil(response?.request)
  370. XCTAssertNotNil(response?.response)
  371. XCTAssertNil(response?.destinationURL)
  372. XCTAssertNotNil(response?.resumeData)
  373. XCTAssertEqual(response?.result.isFailure, true)
  374. XCTAssertNotNil(response?.result.error)
  375. XCTAssertNotNil(download.resumeData)
  376. }
  377. }