MultipartFormDataTests.swift 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  1. // MultipartFormDataTests.swift
  2. //
  3. // Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. import Alamofire
  23. import Foundation
  24. import XCTest
  25. struct EncodingCharacters {
  26. static let CRLF = "\r\n"
  27. }
  28. struct BoundaryGenerator {
  29. enum BoundaryType {
  30. case Initial, Encapsulated, Final
  31. }
  32. static func boundary(#boundaryType: BoundaryType, boundaryKey: String) -> String {
  33. let boundary: String
  34. switch boundaryType {
  35. case .Initial:
  36. boundary = "--\(boundaryKey)\(EncodingCharacters.CRLF)"
  37. case .Encapsulated:
  38. boundary = "\(EncodingCharacters.CRLF)--\(boundaryKey)\(EncodingCharacters.CRLF)"
  39. case .Final:
  40. boundary = "\(EncodingCharacters.CRLF)--\(boundaryKey)--\(EncodingCharacters.CRLF)"
  41. }
  42. return boundary
  43. }
  44. static func boundaryData(#boundaryType: BoundaryType, boundaryKey: String) -> NSData {
  45. return BoundaryGenerator.boundary(
  46. boundaryType: boundaryType,
  47. boundaryKey: boundaryKey
  48. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  49. }
  50. }
  51. private func temporaryFileURL() -> NSURL {
  52. let tempDirectoryURL = NSURL(fileURLWithPath: NSTemporaryDirectory())!
  53. let directoryURL = tempDirectoryURL.URLByAppendingPathComponent("com.alamofire.test/multipart.form.data")
  54. let fileManager = NSFileManager.defaultManager()
  55. fileManager.createDirectoryAtURL(directoryURL, withIntermediateDirectories: true, attributes: nil, error: nil)
  56. let fileName = NSUUID().UUIDString
  57. let fileURL = directoryURL.URLByAppendingPathComponent(fileName)
  58. return fileURL
  59. }
  60. // MARK: -
  61. class MultipartFormDataPropertiesTestCase: BaseTestCase {
  62. func testThatContentTypeContainsBoundary() {
  63. // Given
  64. let multipartFormData = MultipartFormData()
  65. // When
  66. let boundary = multipartFormData.boundary
  67. // Then
  68. let expectedContentType = "multipart/form-data; boundary=\(boundary)"
  69. XCTAssertEqual(multipartFormData.contentType, expectedContentType, "contentType should match expected value")
  70. }
  71. func testThatContentLengthMatchesTotalBodyPartSize() {
  72. // Given
  73. let multipartFormData = MultipartFormData()
  74. let data1 = "Lorem ipsum dolor sit amet.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  75. let data2 = "Vim at integre alterum.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  76. // When
  77. multipartFormData.appendBodyPart(data: data1, name: "data1")
  78. multipartFormData.appendBodyPart(data: data2, name: "data2")
  79. // Then
  80. let expectedContentLength = UInt64(data1.length + data2.length)
  81. XCTAssertEqual(multipartFormData.contentLength, expectedContentLength, "content length should match expected value")
  82. }
  83. }
  84. // MARK: -
  85. class MultipartFormDataEncodingTestCase: BaseTestCase {
  86. let CRLF = EncodingCharacters.CRLF
  87. func testEncodingDataBodyPart() {
  88. // Given
  89. let multipartFormData = MultipartFormData()
  90. let data = "Lorem ipsum dolor sit amet.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  91. // When
  92. multipartFormData.appendBodyPart(data: data, name: "data")
  93. let encodingResult = multipartFormData.encode()
  94. // Then
  95. switch encodingResult {
  96. case .Success(let data):
  97. let boundary = multipartFormData.boundary
  98. let expectedData = (
  99. BoundaryGenerator.boundary(boundaryType: .Initial, boundaryKey: boundary) +
  100. "Content-Disposition: form-data; name=\"data\"\(self.CRLF)\(self.CRLF)" +
  101. "Lorem ipsum dolor sit amet." +
  102. BoundaryGenerator.boundary(boundaryType: .Final, boundaryKey: boundary)
  103. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  104. XCTAssertEqual(data, expectedData, "data should match expected data")
  105. case .Failure:
  106. XCTFail("encoding result should not be .Failure")
  107. }
  108. }
  109. func testEncodingMultipleDataBodyParts() {
  110. // Given
  111. let multipartFormData = MultipartFormData()
  112. let french = "français".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  113. let japanese = "日本語".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  114. let emoji = "😃👍🏻🍻🎉".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  115. // When
  116. multipartFormData.appendBodyPart(data: french, name: "french")
  117. multipartFormData.appendBodyPart(data: japanese, name: "japanese")
  118. multipartFormData.appendBodyPart(data: emoji, name: "emoji")
  119. let encodingResult = multipartFormData.encode()
  120. // Then
  121. switch encodingResult {
  122. case .Success(let data):
  123. let boundary = multipartFormData.boundary
  124. let expectedData = (
  125. BoundaryGenerator.boundary(boundaryType: .Initial, boundaryKey: boundary) +
  126. "Content-Disposition: form-data; name=\"french\"\(self.CRLF)\(self.CRLF)" +
  127. "français" +
  128. BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) +
  129. "Content-Disposition: form-data; name=\"japanese\"\(self.CRLF)\(self.CRLF)" +
  130. "日本語" +
  131. BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) +
  132. "Content-Disposition: form-data; name=\"emoji\"\(self.CRLF)\(self.CRLF)" +
  133. "😃👍🏻🍻🎉" +
  134. BoundaryGenerator.boundary(boundaryType: .Final, boundaryKey: boundary)
  135. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  136. XCTAssertEqual(data, expectedData, "data should match expected data")
  137. case .Failure:
  138. XCTFail("encoding result should not be .Failure")
  139. }
  140. }
  141. func testEncodingFileBodyPart() {
  142. // Given
  143. let multipartFormData = MultipartFormData()
  144. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  145. // When
  146. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  147. let encodingResult = multipartFormData.encode()
  148. // Then
  149. switch encodingResult {
  150. case .Success(let data):
  151. let boundary = multipartFormData.boundary
  152. let expectedData = NSMutableData()
  153. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  154. expectedData.appendData((
  155. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(self.CRLF)" +
  156. "Content-Type: image/png\(self.CRLF)\(self.CRLF)"
  157. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  158. )
  159. expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  160. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  161. XCTAssertEqual(data, expectedData, "data should match expected data")
  162. case .Failure:
  163. XCTFail("encoding result should not be .Failure")
  164. }
  165. }
  166. func testEncodingMultipleFileBodyParts() {
  167. // Given
  168. let multipartFormData = MultipartFormData()
  169. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  170. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  171. // When
  172. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  173. multipartFormData.appendBodyPart(fileURL: rainbowImageURL, name: "rainbow")
  174. let encodingResult = multipartFormData.encode()
  175. // Then
  176. switch encodingResult {
  177. case .Success(let data):
  178. let boundary = multipartFormData.boundary
  179. let expectedData = NSMutableData()
  180. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  181. expectedData.appendData((
  182. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(self.CRLF)" +
  183. "Content-Type: image/png\(self.CRLF)\(self.CRLF)"
  184. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  185. )
  186. expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  187. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  188. expectedData.appendData((
  189. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(self.CRLF)" +
  190. "Content-Type: image/jpeg\(self.CRLF)\(self.CRLF)"
  191. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  192. )
  193. expectedData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  194. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  195. XCTAssertEqual(data, expectedData, "data should match expected data")
  196. case .Failure:
  197. XCTFail("encoding result should not be .Failure")
  198. }
  199. }
  200. func testEncodingStreamBodyPart() {
  201. // Given
  202. let multipartFormData = MultipartFormData()
  203. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  204. let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length)
  205. let unicornStream = NSInputStream(URL: unicornImageURL)!
  206. // When
  207. multipartFormData.appendBodyPart(
  208. stream: unicornStream,
  209. name: "unicorn",
  210. fileName: "unicorn.png",
  211. length: unicornDataLength,
  212. mimeType: "image/png"
  213. )
  214. let encodingResult = multipartFormData.encode()
  215. // Then
  216. switch encodingResult {
  217. case .Success(let data):
  218. let boundary = multipartFormData.boundary
  219. let expectedData = NSMutableData()
  220. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  221. expectedData.appendData((
  222. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(self.CRLF)" +
  223. "Content-Type: image/png\(self.CRLF)\(self.CRLF)"
  224. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  225. )
  226. expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  227. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  228. XCTAssertEqual(data, expectedData, "data should match expected data")
  229. case .Failure:
  230. XCTFail("encoding result should not be .Failure")
  231. }
  232. }
  233. func testEncodingMultipleStreamBodyParts() {
  234. // Given
  235. let multipartFormData = MultipartFormData()
  236. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  237. let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length)
  238. let unicornStream = NSInputStream(URL: unicornImageURL)!
  239. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  240. let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length)
  241. let rainbowStream = NSInputStream(URL: rainbowImageURL)!
  242. // When
  243. multipartFormData.appendBodyPart(
  244. stream: unicornStream,
  245. name: "unicorn",
  246. fileName: "unicorn.png",
  247. length: unicornDataLength,
  248. mimeType: "image/png"
  249. )
  250. multipartFormData.appendBodyPart(
  251. stream: rainbowStream,
  252. name: "rainbow",
  253. fileName: "rainbow.jpg",
  254. length: rainbowDataLength,
  255. mimeType: "image/jpeg"
  256. )
  257. let encodingResult = multipartFormData.encode()
  258. // Then
  259. switch encodingResult {
  260. case .Success(let data):
  261. let boundary = multipartFormData.boundary
  262. let expectedData = NSMutableData()
  263. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  264. expectedData.appendData((
  265. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(self.CRLF)" +
  266. "Content-Type: image/png\(self.CRLF)\(self.CRLF)"
  267. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  268. )
  269. expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  270. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  271. expectedData.appendData((
  272. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(self.CRLF)" +
  273. "Content-Type: image/jpeg\(self.CRLF)\(self.CRLF)"
  274. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  275. )
  276. expectedData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  277. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  278. XCTAssertEqual(data, expectedData, "data should match expected data")
  279. case .Failure:
  280. XCTFail("encoding result should not be .Failure")
  281. }
  282. }
  283. func testEncodingMultipleBodyPartsWithVaryingTypes() {
  284. // Given
  285. let multipartFormData = MultipartFormData()
  286. let loremData = "Lorem ipsum.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  287. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  288. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  289. let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length)
  290. let rainbowStream = NSInputStream(URL: rainbowImageURL)!
  291. // When
  292. multipartFormData.appendBodyPart(data: loremData, name: "lorem")
  293. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  294. multipartFormData.appendBodyPart(
  295. stream: rainbowStream,
  296. name: "rainbow",
  297. fileName: "rainbow.jpg",
  298. length: rainbowDataLength,
  299. mimeType: "image/jpeg"
  300. )
  301. let encodingResult = multipartFormData.encode()
  302. // Then
  303. switch encodingResult {
  304. case .Success(let data):
  305. let boundary = multipartFormData.boundary
  306. let expectedData = NSMutableData()
  307. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  308. expectedData.appendData((
  309. "Content-Disposition: form-data; name=\"lorem\"\(self.CRLF)\(self.CRLF)"
  310. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  311. )
  312. expectedData.appendData(loremData)
  313. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  314. expectedData.appendData((
  315. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(self.CRLF)" +
  316. "Content-Type: image/png\(self.CRLF)\(self.CRLF)"
  317. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  318. )
  319. expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  320. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  321. expectedData.appendData((
  322. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(self.CRLF)" +
  323. "Content-Type: image/jpeg\(self.CRLF)\(self.CRLF)"
  324. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  325. )
  326. expectedData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  327. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  328. XCTAssertEqual(data, expectedData, "data should match expected data")
  329. case .Failure:
  330. XCTFail("encoding result should not be .Failure")
  331. }
  332. }
  333. }
  334. // MARK: -
  335. class MultipartFormDataWriteEncodedDataToDiskTestCase: BaseTestCase {
  336. let CRLF = EncodingCharacters.CRLF
  337. func testWritingEncodedDataBodyPartToDisk() {
  338. // Given
  339. let expectation = expectationWithDescription("multipart form data should be written to disk")
  340. let fileURL = temporaryFileURL()
  341. let multipartFormData = MultipartFormData()
  342. let data = "Lorem ipsum dolor sit amet.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  343. var encodingError: NSError?
  344. // When
  345. multipartFormData.appendBodyPart(data: data, name: "data")
  346. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  347. encodingError = error
  348. expectation.fulfill()
  349. }
  350. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  351. // Then
  352. XCTAssertNil(encodingError, "encoding error should be nil")
  353. if let fileData = NSData(contentsOfURL: fileURL) {
  354. let boundary = multipartFormData.boundary
  355. let expectedFileData = (
  356. BoundaryGenerator.boundary(boundaryType: .Initial, boundaryKey: boundary) +
  357. "Content-Disposition: form-data; name=\"data\"\(self.CRLF)\(self.CRLF)" +
  358. "Lorem ipsum dolor sit amet." +
  359. BoundaryGenerator.boundary(boundaryType: .Final, boundaryKey: boundary)
  360. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  361. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  362. } else {
  363. XCTFail("file data should not be nil")
  364. }
  365. }
  366. func testWritingMultipleEncodedDataBodyPartsToDisk() {
  367. // Given
  368. let expectation = expectationWithDescription("multipart form data should be written to disk")
  369. let fileURL = temporaryFileURL()
  370. let multipartFormData = MultipartFormData()
  371. let french = "français".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  372. let japanese = "日本語".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  373. let emoji = "😃👍🏻🍻🎉".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  374. var encodingError: NSError?
  375. // When
  376. multipartFormData.appendBodyPart(data: french, name: "french")
  377. multipartFormData.appendBodyPart(data: japanese, name: "japanese")
  378. multipartFormData.appendBodyPart(data: emoji, name: "emoji")
  379. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  380. encodingError = error
  381. expectation.fulfill()
  382. }
  383. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  384. // Then
  385. XCTAssertNil(encodingError, "encoding error should be nil")
  386. if let fileData = NSData(contentsOfURL: fileURL) {
  387. let boundary = multipartFormData.boundary
  388. let expectedFileData = (
  389. BoundaryGenerator.boundary(boundaryType: .Initial, boundaryKey: boundary) +
  390. "Content-Disposition: form-data; name=\"french\"\(self.CRLF)\(self.CRLF)" +
  391. "français" +
  392. BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) +
  393. "Content-Disposition: form-data; name=\"japanese\"\(self.CRLF)\(self.CRLF)" +
  394. "日本語" +
  395. BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) +
  396. "Content-Disposition: form-data; name=\"emoji\"\(self.CRLF)\(self.CRLF)" +
  397. "😃👍🏻🍻🎉" +
  398. BoundaryGenerator.boundary(boundaryType: .Final, boundaryKey: boundary)
  399. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  400. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  401. } else {
  402. XCTFail("file data should not be nil")
  403. }
  404. }
  405. func testWritingEncodedFileBodyPartToDisk() {
  406. // Given
  407. let expectation = expectationWithDescription("multipart form data should be written to disk")
  408. let fileURL = temporaryFileURL()
  409. let multipartFormData = MultipartFormData()
  410. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  411. var encodingError: NSError?
  412. // When
  413. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  414. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  415. encodingError = error
  416. expectation.fulfill()
  417. }
  418. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  419. // Then
  420. XCTAssertNil(encodingError, "encoding error should be nil")
  421. if let fileData = NSData(contentsOfURL: fileURL) {
  422. let boundary = multipartFormData.boundary
  423. let expectedFileData = NSMutableData()
  424. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  425. expectedFileData.appendData((
  426. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(self.CRLF)" +
  427. "Content-Type: image/png\(self.CRLF)\(self.CRLF)"
  428. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  429. )
  430. expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  431. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  432. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  433. } else {
  434. XCTFail("file data should not be nil")
  435. }
  436. }
  437. func testWritingMultipleEncodedFileBodyPartsToDisk() {
  438. // Given
  439. let expectation = expectationWithDescription("multipart form data should be written to disk")
  440. let fileURL = temporaryFileURL()
  441. let multipartFormData = MultipartFormData()
  442. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  443. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  444. var encodingError: NSError?
  445. // When
  446. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  447. multipartFormData.appendBodyPart(fileURL: rainbowImageURL, name: "rainbow")
  448. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  449. encodingError = error
  450. expectation.fulfill()
  451. }
  452. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  453. // Then
  454. XCTAssertNil(encodingError, "encoding error should be nil")
  455. if let fileData = NSData(contentsOfURL: fileURL) {
  456. let boundary = multipartFormData.boundary
  457. let expectedFileData = NSMutableData()
  458. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  459. expectedFileData.appendData((
  460. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(self.CRLF)" +
  461. "Content-Type: image/png\(self.CRLF)\(self.CRLF)"
  462. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  463. )
  464. expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  465. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  466. expectedFileData.appendData((
  467. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(self.CRLF)" +
  468. "Content-Type: image/jpeg\(self.CRLF)\(self.CRLF)"
  469. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  470. )
  471. expectedFileData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  472. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  473. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  474. } else {
  475. XCTFail("file data should not be nil")
  476. }
  477. }
  478. func testWritingEncodedStreamBodyPartToDisk() {
  479. // Given
  480. let expectation = expectationWithDescription("multipart form data should be written to disk")
  481. let fileURL = temporaryFileURL()
  482. let multipartFormData = MultipartFormData()
  483. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  484. let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length)
  485. let unicornStream = NSInputStream(URL: unicornImageURL)!
  486. var encodingError: NSError?
  487. // When
  488. multipartFormData.appendBodyPart(
  489. stream: unicornStream,
  490. name: "unicorn",
  491. fileName: "unicorn.png",
  492. length: unicornDataLength,
  493. mimeType: "image/png"
  494. )
  495. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  496. encodingError = error
  497. expectation.fulfill()
  498. }
  499. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  500. // Then
  501. XCTAssertNil(encodingError, "encoding error should be nil")
  502. if let fileData = NSData(contentsOfURL: fileURL) {
  503. let boundary = multipartFormData.boundary
  504. let expectedFileData = NSMutableData()
  505. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  506. expectedFileData.appendData((
  507. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(self.CRLF)" +
  508. "Content-Type: image/png\(self.CRLF)\(self.CRLF)"
  509. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  510. )
  511. expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  512. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  513. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  514. } else {
  515. XCTFail("file data should not be nil")
  516. }
  517. }
  518. func testWritingMultipleEncodedStreamBodyPartsToDisk() {
  519. // Given
  520. let expectation = expectationWithDescription("multipart form data should be written to disk")
  521. let fileURL = temporaryFileURL()
  522. let multipartFormData = MultipartFormData()
  523. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  524. let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length)
  525. let unicornStream = NSInputStream(URL: unicornImageURL)!
  526. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  527. let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length)
  528. let rainbowStream = NSInputStream(URL: rainbowImageURL)!
  529. var encodingError: NSError?
  530. // When
  531. multipartFormData.appendBodyPart(
  532. stream: unicornStream,
  533. name: "unicorn",
  534. fileName: "unicorn.png",
  535. length: unicornDataLength,
  536. mimeType: "image/png"
  537. )
  538. multipartFormData.appendBodyPart(
  539. stream: rainbowStream,
  540. name: "rainbow",
  541. fileName: "rainbow.jpg",
  542. length: rainbowDataLength,
  543. mimeType: "image/jpeg"
  544. )
  545. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  546. encodingError = error
  547. expectation.fulfill()
  548. }
  549. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  550. // Then
  551. XCTAssertNil(encodingError, "encoding error should be nil")
  552. if let fileData = NSData(contentsOfURL: fileURL) {
  553. let boundary = multipartFormData.boundary
  554. let expectedFileData = NSMutableData()
  555. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  556. expectedFileData.appendData((
  557. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(self.CRLF)" +
  558. "Content-Type: image/png\(self.CRLF)\(self.CRLF)"
  559. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  560. )
  561. expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  562. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  563. expectedFileData.appendData((
  564. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(self.CRLF)" +
  565. "Content-Type: image/jpeg\(self.CRLF)\(self.CRLF)"
  566. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  567. )
  568. expectedFileData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  569. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  570. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  571. } else {
  572. XCTFail("file data should not be nil")
  573. }
  574. }
  575. func testWritingMultipleEncodedBodyPartsWithVaryingTypesToDisk() {
  576. // Given
  577. let expectation = expectationWithDescription("multipart form data should be written to disk")
  578. let fileURL = temporaryFileURL()
  579. let multipartFormData = MultipartFormData()
  580. let loremData = "Lorem ipsum.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  581. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  582. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  583. let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length)
  584. let rainbowStream = NSInputStream(URL: rainbowImageURL)!
  585. var encodingError: NSError?
  586. // When
  587. multipartFormData.appendBodyPart(data: loremData, name: "lorem")
  588. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  589. multipartFormData.appendBodyPart(
  590. stream: rainbowStream,
  591. name: "rainbow",
  592. fileName: "rainbow.jpg",
  593. length: rainbowDataLength,
  594. mimeType: "image/jpeg"
  595. )
  596. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  597. encodingError = error
  598. expectation.fulfill()
  599. }
  600. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  601. // Then
  602. XCTAssertNil(encodingError, "encoding error should be nil")
  603. if let fileData = NSData(contentsOfURL: fileURL) {
  604. let boundary = multipartFormData.boundary
  605. let expectedFileData = NSMutableData()
  606. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  607. expectedFileData.appendData((
  608. "Content-Disposition: form-data; name=\"lorem\"\(self.CRLF)\(self.CRLF)"
  609. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  610. )
  611. expectedFileData.appendData(loremData)
  612. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  613. expectedFileData.appendData((
  614. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(self.CRLF)" +
  615. "Content-Type: image/png\(self.CRLF)\(self.CRLF)"
  616. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  617. )
  618. expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  619. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  620. expectedFileData.appendData((
  621. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(self.CRLF)" +
  622. "Content-Type: image/jpeg\(self.CRLF)\(self.CRLF)"
  623. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  624. )
  625. expectedFileData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  626. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  627. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  628. } else {
  629. XCTFail("file data should not be nil")
  630. }
  631. }
  632. }
  633. // MARK: -
  634. class MultipartFormDataFailureTestCase: BaseTestCase {
  635. func testThatAppendingFileBodyPartWithInvalidLastPathComponentReturnsError() {
  636. // Given
  637. let fileURL = NSURL(string: "")!
  638. let multipartFormData = MultipartFormData()
  639. // When
  640. let error = multipartFormData.appendBodyPart(fileURL: fileURL, name: "empty_data")
  641. // Then
  642. XCTAssertNotNil(error, "error should not be nil")
  643. if let error = error {
  644. XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value")
  645. XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value")
  646. if let failureReason = error.userInfo?[NSLocalizedFailureReasonErrorKey] as? String {
  647. let expectedFailureReason = "Failed to extract the fileName of the provided URL: \(fileURL)"
  648. XCTAssertEqual(failureReason, expectedFailureReason, "error failure reason does not match expected value")
  649. } else {
  650. XCTFail("failure reason should not be nil")
  651. }
  652. }
  653. }
  654. func testThatAppendingFileBodyPartThatIsNotFileURLReturnsError() {
  655. // Given
  656. let fileURL = NSURL(string: "http://example.com/image.jpg")!
  657. let multipartFormData = MultipartFormData()
  658. // When
  659. let error = multipartFormData.appendBodyPart(fileURL: fileURL, name: "empty_data")
  660. // Then
  661. XCTAssertNotNil(error, "error should not be nil")
  662. if let error = error {
  663. XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value")
  664. XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value")
  665. if let failureReason = error.userInfo?[NSLocalizedFailureReasonErrorKey] as? String {
  666. let expectedFailureReason = "The URL does not point to a file URL: \(fileURL)"
  667. XCTAssertEqual(failureReason, expectedFailureReason, "error failure reason does not match expected value")
  668. } else {
  669. XCTFail("failure reason should not be nil")
  670. }
  671. }
  672. }
  673. func testThatAppendingFileBodyPartThatIsNotReachableReturnsError() {
  674. // Given
  675. let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory().stringByAppendingPathComponent("does_not_exist.jpg"))!
  676. let multipartFormData = MultipartFormData()
  677. // When
  678. let error = multipartFormData.appendBodyPart(fileURL: fileURL, name: "empty_data")
  679. // Then
  680. XCTAssertNotNil(error, "error should not be nil")
  681. if let error = error {
  682. XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value")
  683. XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value")
  684. if let failureReason = error.userInfo?[NSLocalizedFailureReasonErrorKey] as? String {
  685. let expectedFailureReason = "The URL is not reachable: \(fileURL)"
  686. XCTAssertEqual(failureReason, expectedFailureReason, "error failure reason does not match expected value")
  687. } else {
  688. XCTFail("failure reason should not be nil")
  689. }
  690. }
  691. }
  692. func testThatAppendingFileBodyPartThatIsDirectoryReturnsError() {
  693. // Given
  694. let directoryURL = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)!
  695. let multipartFormData = MultipartFormData()
  696. // When
  697. let error = multipartFormData.appendBodyPart(fileURL: directoryURL, name: "empty_data")
  698. // Then
  699. XCTAssertNotNil(error, "error should not be nil")
  700. if let error = error {
  701. XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value")
  702. XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value")
  703. if let failureReason = error.userInfo?[NSLocalizedFailureReasonErrorKey] as? String {
  704. let expectedFailureReason = "The URL is a directory, not a file: \(directoryURL)"
  705. XCTAssertEqual(failureReason, expectedFailureReason, "error failure reason does not match expected value")
  706. } else {
  707. XCTFail("failure reason should not be nil")
  708. }
  709. }
  710. }
  711. func testThatWritingEncodedDataToExistingFileURLFails() {
  712. // Given
  713. let expectation = expectationWithDescription("multipart form data should fail when writing to disk")
  714. let fileURL = temporaryFileURL()
  715. "dummy data".writeToURL(fileURL, atomically: true, encoding: NSUTF8StringEncoding, error: nil)
  716. let multipartFormData = MultipartFormData()
  717. let data = "Lorem ipsum dolor sit amet.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  718. var encodingError: NSError?
  719. // When
  720. multipartFormData.appendBodyPart(data: data, name: "data")
  721. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  722. encodingError = error
  723. expectation.fulfill()
  724. }
  725. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  726. // Then
  727. XCTAssertNotNil(encodingError, "encoding error should not be nil")
  728. if let encodingError = encodingError {
  729. XCTAssertEqual(encodingError.domain, "com.alamofire.error", "encoding error domain does not match expected value")
  730. XCTAssertEqual(encodingError.code, NSURLErrorBadURL, "encoding error code does not match expected value")
  731. }
  732. }
  733. func testThatWritingEncodedDataToBadURLFails() {
  734. // Given
  735. let expectation = expectationWithDescription("multipart form data should fail when writing to disk")
  736. let fileURL = NSURL(string: "/this/is/not/a/valid/url")!
  737. let multipartFormData = MultipartFormData()
  738. let data = "Lorem ipsum dolor sit amet.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  739. var encodingError: NSError?
  740. // When
  741. multipartFormData.appendBodyPart(data: data, name: "data")
  742. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  743. encodingError = error
  744. expectation.fulfill()
  745. }
  746. waitForExpectationsWithTimeout(self.defaultTimeout, handler: nil)
  747. // Then
  748. XCTAssertNotNil(encodingError, "encoding error should not be nil")
  749. if let encodingError = encodingError {
  750. XCTAssertEqual(encodingError.domain, "com.alamofire.error", "encoding error domain does not match expected value")
  751. XCTAssertEqual(encodingError.code, NSURLErrorBadURL, "encoding error code does not match expected value")
  752. }
  753. }
  754. }