MultipartFormDataTests.swift 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963
  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\"\(CRLF)\(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", mimeType: "text/plain")
  118. multipartFormData.appendBodyPart(data: emoji, name: "emoji", mimeType: "text/plain")
  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\"\(CRLF)\(CRLF)" +
  127. "français" +
  128. BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) +
  129. "Content-Disposition: form-data; name=\"japanese\"\(CRLF)" +
  130. "Content-Type: text/plain\(CRLF)\(CRLF)" +
  131. "日本語" +
  132. BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) +
  133. "Content-Disposition: form-data; name=\"emoji\"\(CRLF)" +
  134. "Content-Type: text/plain\(CRLF)\(CRLF)" +
  135. "😃👍🏻🍻🎉" +
  136. BoundaryGenerator.boundary(boundaryType: .Final, boundaryKey: boundary)
  137. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  138. XCTAssertEqual(data, expectedData, "data should match expected data")
  139. case .Failure:
  140. XCTFail("encoding result should not be .Failure")
  141. }
  142. }
  143. func testEncodingFileBodyPart() {
  144. // Given
  145. let multipartFormData = MultipartFormData()
  146. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  147. // When
  148. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  149. let encodingResult = multipartFormData.encode()
  150. // Then
  151. switch encodingResult {
  152. case .Success(let data):
  153. let boundary = multipartFormData.boundary
  154. let expectedData = NSMutableData()
  155. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  156. expectedData.appendData((
  157. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" +
  158. "Content-Type: image/png\(CRLF)\(CRLF)"
  159. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  160. )
  161. expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  162. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  163. XCTAssertEqual(data, expectedData, "data should match expected data")
  164. case .Failure:
  165. XCTFail("encoding result should not be .Failure")
  166. }
  167. }
  168. func testEncodingMultipleFileBodyParts() {
  169. // Given
  170. let multipartFormData = MultipartFormData()
  171. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  172. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  173. // When
  174. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  175. multipartFormData.appendBodyPart(fileURL: rainbowImageURL, name: "rainbow")
  176. let encodingResult = multipartFormData.encode()
  177. // Then
  178. switch encodingResult {
  179. case .Success(let data):
  180. let boundary = multipartFormData.boundary
  181. let expectedData = NSMutableData()
  182. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  183. expectedData.appendData((
  184. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" +
  185. "Content-Type: image/png\(CRLF)\(CRLF)"
  186. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  187. )
  188. expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  189. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  190. expectedData.appendData((
  191. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" +
  192. "Content-Type: image/jpeg\(CRLF)\(CRLF)"
  193. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  194. )
  195. expectedData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  196. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  197. XCTAssertEqual(data, expectedData, "data should match expected data")
  198. case .Failure:
  199. XCTFail("encoding result should not be .Failure")
  200. }
  201. }
  202. func testEncodingStreamBodyPart() {
  203. // Given
  204. let multipartFormData = MultipartFormData()
  205. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  206. let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length)
  207. let unicornStream = NSInputStream(URL: unicornImageURL)!
  208. // When
  209. multipartFormData.appendBodyPart(
  210. stream: unicornStream,
  211. length: unicornDataLength,
  212. name: "unicorn",
  213. fileName: "unicorn.png",
  214. mimeType: "image/png"
  215. )
  216. let encodingResult = multipartFormData.encode()
  217. // Then
  218. switch encodingResult {
  219. case .Success(let data):
  220. let boundary = multipartFormData.boundary
  221. let expectedData = NSMutableData()
  222. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  223. expectedData.appendData((
  224. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" +
  225. "Content-Type: image/png\(CRLF)\(CRLF)"
  226. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  227. )
  228. expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  229. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  230. XCTAssertEqual(data, expectedData, "data should match expected data")
  231. case .Failure:
  232. XCTFail("encoding result should not be .Failure")
  233. }
  234. }
  235. func testEncodingMultipleStreamBodyParts() {
  236. // Given
  237. let multipartFormData = MultipartFormData()
  238. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  239. let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length)
  240. let unicornStream = NSInputStream(URL: unicornImageURL)!
  241. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  242. let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length)
  243. let rainbowStream = NSInputStream(URL: rainbowImageURL)!
  244. // When
  245. multipartFormData.appendBodyPart(
  246. stream: unicornStream,
  247. length: unicornDataLength,
  248. name: "unicorn",
  249. fileName: "unicorn.png",
  250. mimeType: "image/png"
  251. )
  252. multipartFormData.appendBodyPart(
  253. stream: rainbowStream,
  254. length: rainbowDataLength,
  255. name: "rainbow",
  256. fileName: "rainbow.jpg",
  257. mimeType: "image/jpeg"
  258. )
  259. let encodingResult = multipartFormData.encode()
  260. // Then
  261. switch encodingResult {
  262. case .Success(let data):
  263. let boundary = multipartFormData.boundary
  264. let expectedData = NSMutableData()
  265. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  266. expectedData.appendData((
  267. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" +
  268. "Content-Type: image/png\(CRLF)\(CRLF)"
  269. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  270. )
  271. expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  272. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  273. expectedData.appendData((
  274. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" +
  275. "Content-Type: image/jpeg\(CRLF)\(CRLF)"
  276. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  277. )
  278. expectedData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  279. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  280. XCTAssertEqual(data, expectedData, "data should match expected data")
  281. case .Failure:
  282. XCTFail("encoding result should not be .Failure")
  283. }
  284. }
  285. func testEncodingMultipleBodyPartsWithVaryingTypes() {
  286. // Given
  287. let multipartFormData = MultipartFormData()
  288. let loremData = "Lorem ipsum.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  289. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  290. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  291. let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length)
  292. let rainbowStream = NSInputStream(URL: rainbowImageURL)!
  293. // When
  294. multipartFormData.appendBodyPart(data: loremData, name: "lorem")
  295. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  296. multipartFormData.appendBodyPart(
  297. stream: rainbowStream,
  298. length: rainbowDataLength,
  299. name: "rainbow",
  300. fileName: "rainbow.jpg",
  301. mimeType: "image/jpeg"
  302. )
  303. let encodingResult = multipartFormData.encode()
  304. // Then
  305. switch encodingResult {
  306. case .Success(let data):
  307. let boundary = multipartFormData.boundary
  308. let expectedData = NSMutableData()
  309. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  310. expectedData.appendData((
  311. "Content-Disposition: form-data; name=\"lorem\"\(CRLF)\(CRLF)"
  312. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  313. )
  314. expectedData.appendData(loremData)
  315. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  316. expectedData.appendData((
  317. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" +
  318. "Content-Type: image/png\(CRLF)\(CRLF)"
  319. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  320. )
  321. expectedData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  322. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  323. expectedData.appendData((
  324. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" +
  325. "Content-Type: image/jpeg\(CRLF)\(CRLF)"
  326. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  327. )
  328. expectedData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  329. expectedData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  330. XCTAssertEqual(data, expectedData, "data should match expected data")
  331. case .Failure:
  332. XCTFail("encoding result should not be .Failure")
  333. }
  334. }
  335. }
  336. // MARK: -
  337. class MultipartFormDataWriteEncodedDataToDiskTestCase: BaseTestCase {
  338. let CRLF = EncodingCharacters.CRLF
  339. func testWritingEncodedDataBodyPartToDisk() {
  340. // Given
  341. let expectation = expectationWithDescription("multipart form data should be written to disk")
  342. let fileURL = temporaryFileURL()
  343. let multipartFormData = MultipartFormData()
  344. let data = "Lorem ipsum dolor sit amet.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  345. var encodingError: NSError?
  346. // When
  347. multipartFormData.appendBodyPart(data: data, name: "data")
  348. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  349. encodingError = error
  350. expectation.fulfill()
  351. }
  352. waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
  353. // Then
  354. XCTAssertNil(encodingError, "encoding error should be nil")
  355. if let fileData = NSData(contentsOfURL: fileURL) {
  356. let boundary = multipartFormData.boundary
  357. let expectedFileData = (
  358. BoundaryGenerator.boundary(boundaryType: .Initial, boundaryKey: boundary) +
  359. "Content-Disposition: form-data; name=\"data\"\(CRLF)\(CRLF)" +
  360. "Lorem ipsum dolor sit amet." +
  361. BoundaryGenerator.boundary(boundaryType: .Final, boundaryKey: boundary)
  362. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  363. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  364. } else {
  365. XCTFail("file data should not be nil")
  366. }
  367. }
  368. func testWritingMultipleEncodedDataBodyPartsToDisk() {
  369. // Given
  370. let expectation = expectationWithDescription("multipart form data should be written to disk")
  371. let fileURL = temporaryFileURL()
  372. let multipartFormData = MultipartFormData()
  373. let french = "français".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  374. let japanese = "日本語".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  375. let emoji = "😃👍🏻🍻🎉".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  376. var encodingError: NSError?
  377. // When
  378. multipartFormData.appendBodyPart(data: french, name: "french")
  379. multipartFormData.appendBodyPart(data: japanese, name: "japanese")
  380. multipartFormData.appendBodyPart(data: emoji, name: "emoji")
  381. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  382. encodingError = error
  383. expectation.fulfill()
  384. }
  385. waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
  386. // Then
  387. XCTAssertNil(encodingError, "encoding error should be nil")
  388. if let fileData = NSData(contentsOfURL: fileURL) {
  389. let boundary = multipartFormData.boundary
  390. let expectedFileData = (
  391. BoundaryGenerator.boundary(boundaryType: .Initial, boundaryKey: boundary) +
  392. "Content-Disposition: form-data; name=\"french\"\(CRLF)\(CRLF)" +
  393. "français" +
  394. BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) +
  395. "Content-Disposition: form-data; name=\"japanese\"\(CRLF)\(CRLF)" +
  396. "日本語" +
  397. BoundaryGenerator.boundary(boundaryType: .Encapsulated, boundaryKey: boundary) +
  398. "Content-Disposition: form-data; name=\"emoji\"\(CRLF)\(CRLF)" +
  399. "😃👍🏻🍻🎉" +
  400. BoundaryGenerator.boundary(boundaryType: .Final, boundaryKey: boundary)
  401. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  402. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  403. } else {
  404. XCTFail("file data should not be nil")
  405. }
  406. }
  407. func testWritingEncodedFileBodyPartToDisk() {
  408. // Given
  409. let expectation = expectationWithDescription("multipart form data should be written to disk")
  410. let fileURL = temporaryFileURL()
  411. let multipartFormData = MultipartFormData()
  412. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  413. var encodingError: NSError?
  414. // When
  415. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  416. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  417. encodingError = error
  418. expectation.fulfill()
  419. }
  420. waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
  421. // Then
  422. XCTAssertNil(encodingError, "encoding error should be nil")
  423. if let fileData = NSData(contentsOfURL: fileURL) {
  424. let boundary = multipartFormData.boundary
  425. let expectedFileData = NSMutableData()
  426. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  427. expectedFileData.appendData((
  428. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" +
  429. "Content-Type: image/png\(CRLF)\(CRLF)"
  430. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  431. )
  432. expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  433. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  434. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  435. } else {
  436. XCTFail("file data should not be nil")
  437. }
  438. }
  439. func testWritingMultipleEncodedFileBodyPartsToDisk() {
  440. // Given
  441. let expectation = expectationWithDescription("multipart form data should be written to disk")
  442. let fileURL = temporaryFileURL()
  443. let multipartFormData = MultipartFormData()
  444. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  445. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  446. var encodingError: NSError?
  447. // When
  448. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  449. multipartFormData.appendBodyPart(fileURL: rainbowImageURL, name: "rainbow")
  450. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  451. encodingError = error
  452. expectation.fulfill()
  453. }
  454. waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
  455. // Then
  456. XCTAssertNil(encodingError, "encoding error should be nil")
  457. if let fileData = NSData(contentsOfURL: fileURL) {
  458. let boundary = multipartFormData.boundary
  459. let expectedFileData = NSMutableData()
  460. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  461. expectedFileData.appendData((
  462. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" +
  463. "Content-Type: image/png\(CRLF)\(CRLF)"
  464. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  465. )
  466. expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  467. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  468. expectedFileData.appendData((
  469. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" +
  470. "Content-Type: image/jpeg\(CRLF)\(CRLF)"
  471. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  472. )
  473. expectedFileData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  474. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  475. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  476. } else {
  477. XCTFail("file data should not be nil")
  478. }
  479. }
  480. func testWritingEncodedStreamBodyPartToDisk() {
  481. // Given
  482. let expectation = expectationWithDescription("multipart form data should be written to disk")
  483. let fileURL = temporaryFileURL()
  484. let multipartFormData = MultipartFormData()
  485. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  486. let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length)
  487. let unicornStream = NSInputStream(URL: unicornImageURL)!
  488. var encodingError: NSError?
  489. // When
  490. multipartFormData.appendBodyPart(
  491. stream: unicornStream,
  492. length: unicornDataLength,
  493. name: "unicorn",
  494. fileName: "unicorn.png",
  495. mimeType: "image/png"
  496. )
  497. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  498. encodingError = error
  499. expectation.fulfill()
  500. }
  501. waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
  502. // Then
  503. XCTAssertNil(encodingError, "encoding error should be nil")
  504. if let fileData = NSData(contentsOfURL: fileURL) {
  505. let boundary = multipartFormData.boundary
  506. let expectedFileData = NSMutableData()
  507. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  508. expectedFileData.appendData((
  509. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" +
  510. "Content-Type: image/png\(CRLF)\(CRLF)"
  511. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  512. )
  513. expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  514. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  515. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  516. } else {
  517. XCTFail("file data should not be nil")
  518. }
  519. }
  520. func testWritingMultipleEncodedStreamBodyPartsToDisk() {
  521. // Given
  522. let expectation = expectationWithDescription("multipart form data should be written to disk")
  523. let fileURL = temporaryFileURL()
  524. let multipartFormData = MultipartFormData()
  525. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  526. let unicornDataLength = UInt64(NSData(contentsOfURL: unicornImageURL)!.length)
  527. let unicornStream = NSInputStream(URL: unicornImageURL)!
  528. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  529. let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length)
  530. let rainbowStream = NSInputStream(URL: rainbowImageURL)!
  531. var encodingError: NSError?
  532. // When
  533. multipartFormData.appendBodyPart(
  534. stream: unicornStream,
  535. length: unicornDataLength,
  536. name: "unicorn",
  537. fileName: "unicorn.png",
  538. mimeType: "image/png"
  539. )
  540. multipartFormData.appendBodyPart(
  541. stream: rainbowStream,
  542. length: rainbowDataLength,
  543. name: "rainbow",
  544. fileName: "rainbow.jpg",
  545. mimeType: "image/jpeg"
  546. )
  547. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  548. encodingError = error
  549. expectation.fulfill()
  550. }
  551. waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
  552. // Then
  553. XCTAssertNil(encodingError, "encoding error should be nil")
  554. if let fileData = NSData(contentsOfURL: fileURL) {
  555. let boundary = multipartFormData.boundary
  556. let expectedFileData = NSMutableData()
  557. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  558. expectedFileData.appendData((
  559. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" +
  560. "Content-Type: image/png\(CRLF)\(CRLF)"
  561. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  562. )
  563. expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  564. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  565. expectedFileData.appendData((
  566. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" +
  567. "Content-Type: image/jpeg\(CRLF)\(CRLF)"
  568. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  569. )
  570. expectedFileData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  571. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  572. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  573. } else {
  574. XCTFail("file data should not be nil")
  575. }
  576. }
  577. func testWritingMultipleEncodedBodyPartsWithVaryingTypesToDisk() {
  578. // Given
  579. let expectation = expectationWithDescription("multipart form data should be written to disk")
  580. let fileURL = temporaryFileURL()
  581. let multipartFormData = MultipartFormData()
  582. let loremData = "Lorem ipsum.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  583. let unicornImageURL = URLForResource("unicorn", withExtension: "png")
  584. let rainbowImageURL = URLForResource("rainbow", withExtension: "jpg")
  585. let rainbowDataLength = UInt64(NSData(contentsOfURL: rainbowImageURL)!.length)
  586. let rainbowStream = NSInputStream(URL: rainbowImageURL)!
  587. var encodingError: NSError?
  588. // When
  589. multipartFormData.appendBodyPart(data: loremData, name: "lorem")
  590. multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn")
  591. multipartFormData.appendBodyPart(
  592. stream: rainbowStream,
  593. length: rainbowDataLength,
  594. name: "rainbow",
  595. fileName: "rainbow.jpg",
  596. mimeType: "image/jpeg"
  597. )
  598. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  599. encodingError = error
  600. expectation.fulfill()
  601. }
  602. waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
  603. // Then
  604. XCTAssertNil(encodingError, "encoding error should be nil")
  605. if let fileData = NSData(contentsOfURL: fileURL) {
  606. let boundary = multipartFormData.boundary
  607. let expectedFileData = NSMutableData()
  608. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Initial, boundaryKey: boundary))
  609. expectedFileData.appendData((
  610. "Content-Disposition: form-data; name=\"lorem\"\(CRLF)\(CRLF)"
  611. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  612. )
  613. expectedFileData.appendData(loremData)
  614. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  615. expectedFileData.appendData((
  616. "Content-Disposition: form-data; name=\"unicorn\"; filename=\"unicorn.png\"\(CRLF)" +
  617. "Content-Type: image/png\(CRLF)\(CRLF)"
  618. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  619. )
  620. expectedFileData.appendData(NSData(contentsOfURL: unicornImageURL)!)
  621. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundaryKey: boundary))
  622. expectedFileData.appendData((
  623. "Content-Disposition: form-data; name=\"rainbow\"; filename=\"rainbow.jpg\"\(CRLF)" +
  624. "Content-Type: image/jpeg\(CRLF)\(CRLF)"
  625. ).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  626. )
  627. expectedFileData.appendData(NSData(contentsOfURL: rainbowImageURL)!)
  628. expectedFileData.appendData(BoundaryGenerator.boundaryData(boundaryType: .Final, boundaryKey: boundary))
  629. XCTAssertEqual(fileData, expectedFileData, "file data should match expected file data")
  630. } else {
  631. XCTFail("file data should not be nil")
  632. }
  633. }
  634. }
  635. // MARK: -
  636. class MultipartFormDataFailureTestCase: BaseTestCase {
  637. func testThatAppendingFileBodyPartWithInvalidLastPathComponentReturnsError() {
  638. // Given
  639. let fileURL = NSURL(string: "")!
  640. let multipartFormData = MultipartFormData()
  641. var error: NSError?
  642. // When
  643. multipartFormData.appendBodyPart(fileURL: fileURL, name: "empty_data")
  644. switch multipartFormData.encode() {
  645. case .Failure(let encodingError):
  646. error = encodingError
  647. default:
  648. break
  649. }
  650. // Then
  651. XCTAssertNotNil(error, "error should not be nil")
  652. if let error = error {
  653. XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value")
  654. XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value")
  655. if let failureReason = error.userInfo?[NSLocalizedFailureReasonErrorKey] as? String {
  656. let expectedFailureReason = "Failed to extract the fileName of the provided URL: \(fileURL)"
  657. XCTAssertEqual(failureReason, expectedFailureReason, "error failure reason does not match expected value")
  658. } else {
  659. XCTFail("failure reason should not be nil")
  660. }
  661. }
  662. }
  663. func testThatAppendingFileBodyPartThatIsNotFileURLReturnsError() {
  664. // Given
  665. let fileURL = NSURL(string: "http://example.com/image.jpg")!
  666. let multipartFormData = MultipartFormData()
  667. var error: NSError?
  668. // When
  669. multipartFormData.appendBodyPart(fileURL: fileURL, name: "empty_data")
  670. switch multipartFormData.encode() {
  671. case .Failure(let encodingError):
  672. error = encodingError
  673. default:
  674. break
  675. }
  676. // Then
  677. XCTAssertNotNil(error, "error should not be nil")
  678. if let error = error {
  679. XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value")
  680. XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value")
  681. if let failureReason = error.userInfo?[NSLocalizedFailureReasonErrorKey] as? String {
  682. let expectedFailureReason = "The URL does not point to a file URL: \(fileURL)"
  683. XCTAssertEqual(failureReason, expectedFailureReason, "error failure reason does not match expected value")
  684. } else {
  685. XCTFail("failure reason should not be nil")
  686. }
  687. }
  688. }
  689. func testThatAppendingFileBodyPartThatIsNotReachableReturnsError() {
  690. // Given
  691. let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory().stringByAppendingPathComponent("does_not_exist.jpg"))!
  692. let multipartFormData = MultipartFormData()
  693. var error: NSError?
  694. // When
  695. multipartFormData.appendBodyPart(fileURL: fileURL, name: "empty_data")
  696. switch multipartFormData.encode() {
  697. case .Failure(let encodingError):
  698. error = encodingError
  699. default:
  700. break
  701. }
  702. // Then
  703. XCTAssertNotNil(error, "error should not be nil")
  704. if let error = error {
  705. XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value")
  706. XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value")
  707. if let failureReason = error.userInfo?[NSLocalizedFailureReasonErrorKey] as? String {
  708. let expectedFailureReason = "The URL is not reachable: \(fileURL)"
  709. XCTAssertEqual(failureReason, expectedFailureReason, "error failure reason does not match expected value")
  710. } else {
  711. XCTFail("failure reason should not be nil")
  712. }
  713. }
  714. }
  715. func testThatAppendingFileBodyPartThatIsDirectoryReturnsError() {
  716. // Given
  717. let directoryURL = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)!
  718. let multipartFormData = MultipartFormData()
  719. var error: NSError?
  720. // When
  721. multipartFormData.appendBodyPart(fileURL: directoryURL, name: "empty_data")
  722. switch multipartFormData.encode() {
  723. case .Failure(let encodingError):
  724. error = encodingError
  725. default:
  726. break
  727. }
  728. // Then
  729. XCTAssertNotNil(error, "error should not be nil")
  730. if let error = error {
  731. XCTAssertEqual(error.domain, "com.alamofire.error", "error domain does not match expected value")
  732. XCTAssertEqual(error.code, NSURLErrorBadURL, "error code does not match expected value")
  733. if let failureReason = error.userInfo?[NSLocalizedFailureReasonErrorKey] as? String {
  734. let expectedFailureReason = "The URL is a directory, not a file: \(directoryURL)"
  735. XCTAssertEqual(failureReason, expectedFailureReason, "error failure reason does not match expected value")
  736. } else {
  737. XCTFail("failure reason should not be nil")
  738. }
  739. }
  740. }
  741. func testThatWritingEncodedDataToExistingFileURLFails() {
  742. // Given
  743. let expectation = expectationWithDescription("multipart form data should fail when writing to disk")
  744. let fileURL = temporaryFileURL()
  745. "dummy data".writeToURL(fileURL, atomically: true, encoding: NSUTF8StringEncoding, error: nil)
  746. let multipartFormData = MultipartFormData()
  747. let data = "Lorem ipsum dolor sit amet.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  748. var encodingError: NSError?
  749. // When
  750. multipartFormData.appendBodyPart(data: data, name: "data")
  751. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  752. encodingError = error
  753. expectation.fulfill()
  754. }
  755. waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
  756. // Then
  757. XCTAssertNotNil(encodingError, "encoding error should not be nil")
  758. if let encodingError = encodingError {
  759. XCTAssertEqual(encodingError.domain, "com.alamofire.error", "encoding error domain does not match expected value")
  760. XCTAssertEqual(encodingError.code, NSURLErrorBadURL, "encoding error code does not match expected value")
  761. }
  762. }
  763. func testThatWritingEncodedDataToBadURLFails() {
  764. // Given
  765. let expectation = expectationWithDescription("multipart form data should fail when writing to disk")
  766. let fileURL = NSURL(string: "/this/is/not/a/valid/url")!
  767. let multipartFormData = MultipartFormData()
  768. let data = "Lorem ipsum dolor sit amet.".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
  769. var encodingError: NSError?
  770. // When
  771. multipartFormData.appendBodyPart(data: data, name: "data")
  772. multipartFormData.writeEncodedDataToDisk(fileURL) { error in
  773. encodingError = error
  774. expectation.fulfill()
  775. }
  776. waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
  777. // Then
  778. XCTAssertNotNil(encodingError, "encoding error should not be nil")
  779. if let encodingError = encodingError {
  780. XCTAssertEqual(encodingError.domain, "com.alamofire.error", "encoding error domain does not match expected value")
  781. XCTAssertEqual(encodingError.code, NSURLErrorBadURL, "encoding error code does not match expected value")
  782. }
  783. }
  784. }