StorageIntegration.swift 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. // Copyright 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import FirebaseAuth
  15. import FirebaseCore
  16. @testable import FirebaseStorage
  17. import XCTest
  18. /**
  19. * Firebase Storage Integration tests
  20. *
  21. * To run these tests, you need to define the following access rights:
  22. *
  23. rules_version = '2';
  24. service firebase.storage {
  25. match /b/{bucket}/o {
  26. match /{directChild=*} {
  27. allow read: if request.auth != null;
  28. }
  29. match /ios {
  30. match /public/{allPaths=**} {
  31. allow write: if request.auth != null;
  32. allow read: if true;
  33. }
  34. match /private/{allPaths=**} {
  35. allow read, write: if false;
  36. }
  37. }
  38. }
  39. }
  40. */
  41. class StorageResultTests: StorageIntegrationCommon {
  42. func testGetMetadata() {
  43. let expectation = self.expectation(description: "testGetMetadata")
  44. let ref = storage.reference().child("ios/public/1mb")
  45. ref.getMetadata { result in
  46. self.assertResultSuccess(result)
  47. expectation.fulfill()
  48. }
  49. waitForExpectations()
  50. }
  51. func testUpdateMetadata() {
  52. let expectation = self.expectation(description: #function)
  53. let meta = StorageMetadata()
  54. meta.contentType = "lol/custom"
  55. meta.customMetadata = ["lol": "custom metadata is neat",
  56. "ちかてつ": "🚇",
  57. "shinkansen": "新幹線"]
  58. let ref = storage.reference(withPath: "ios/public/1mb")
  59. ref.updateMetadata(meta) { result in
  60. switch result {
  61. case let .success(metadata):
  62. XCTAssertEqual(meta.contentType, metadata.contentType)
  63. XCTAssertEqual(meta.customMetadata!["lol"], metadata.customMetadata!["lol"])
  64. XCTAssertEqual(meta.customMetadata!["ちかてつ"], metadata.customMetadata!["ちかてつ"])
  65. XCTAssertEqual(meta.customMetadata!["shinkansen"],
  66. metadata.customMetadata!["shinkansen"])
  67. case let .failure(error):
  68. XCTFail("Unexpected error \(error) from updateMetadata")
  69. }
  70. expectation.fulfill()
  71. }
  72. waitForExpectations()
  73. }
  74. func testDelete() throws {
  75. let expectation = self.expectation(description: #function)
  76. let ref = storage.reference(withPath: "ios/public/fileToDelete")
  77. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  78. ref.putData(data) { result in
  79. self.assertResultSuccess(result)
  80. ref.delete { error in
  81. XCTAssertNil(error, "Error should be nil")
  82. // Next delete should fail and verify the first delete succeeded.
  83. ref.delete { error in
  84. do {
  85. let nsError = try XCTUnwrap(error as? NSError)
  86. XCTAssertEqual(nsError.code, StorageErrorCode.objectNotFound.rawValue)
  87. XCTAssertEqual(
  88. nsError.localizedDescription,
  89. "Object ios/public/fileToDelete does not exist."
  90. )
  91. let userInfo = try XCTUnwrap(nsError.userInfo)
  92. let object = try XCTUnwrap(userInfo["object"] as? String)
  93. XCTAssertEqual(object, "ios/public/fileToDelete")
  94. let responseErrorCode = try XCTUnwrap(userInfo["ResponseErrorCode"] as? Int)
  95. XCTAssertEqual(responseErrorCode, 404)
  96. let responseErrorDomain = try XCTUnwrap(userInfo["ResponseErrorDomain"] as? String)
  97. XCTAssertEqual(responseErrorDomain, "com.google.HTTPStatus")
  98. let bucket = try XCTUnwrap(userInfo["bucket"] as? String)
  99. XCTAssertEqual(bucket, "ios-opensource-samples.appspot.com")
  100. expectation.fulfill()
  101. } catch {
  102. XCTFail("Unexpected unwrap failure")
  103. }
  104. }
  105. }
  106. }
  107. waitForExpectations()
  108. }
  109. func testDeleteWithNilCompletion() throws {
  110. let expectation = self.expectation(description: #function)
  111. let ref = storage.reference(withPath: "ios/public/fileToDelete")
  112. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  113. ref.putData(data) { result in
  114. self.assertResultSuccess(result)
  115. ref.delete(completion: nil)
  116. expectation.fulfill()
  117. }
  118. waitForExpectations()
  119. }
  120. func testSimplePutData() throws {
  121. let expectation = self.expectation(description: #function)
  122. let ref = storage.reference(withPath: "ios/public/testBytesUpload")
  123. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  124. ref.putData(data) { result in
  125. self.assertResultSuccess(result)
  126. expectation.fulfill()
  127. }
  128. waitForExpectations()
  129. }
  130. func testNoDeadlocks() throws {
  131. let storage2 = Storage.storage(url: "")
  132. let expectation1 = expectation(description: #function)
  133. let expectation2 = expectation(description: #function)
  134. let ref = storage.reference(withPath: "ios/public/testBytesUpload")
  135. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  136. ref.putData(data) { result in
  137. expectation1.fulfill()
  138. let ref2 = storage2.reference(withPath: "ios/public/testBytesUpload")
  139. ref2.putData(data) { result in
  140. expectation2.fulfill()
  141. }
  142. }
  143. waitForExpectations(timeout: 30)
  144. }
  145. func testSimplePutSpecialCharacter() throws {
  146. let expectation = self.expectation(description: #function)
  147. let ref = storage.reference(withPath: "ios/public/-._~!$'()*,=:@&+;")
  148. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  149. ref.putData(data) { result in
  150. self.assertResultSuccess(result)
  151. expectation.fulfill()
  152. }
  153. waitForExpectations()
  154. }
  155. func testSimplePutDataInBackgroundQueue() throws {
  156. let expectation = self.expectation(description: #function)
  157. let ref = storage.reference(withPath: "ios/public/testBytesUpload")
  158. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  159. DispatchQueue.global(qos: .background).async {
  160. ref.putData(data) { result in
  161. self.assertResultSuccess(result)
  162. expectation.fulfill()
  163. }
  164. }
  165. waitForExpectations()
  166. }
  167. func testSimplePutEmptyData() {
  168. let expectation = self.expectation(description: #function)
  169. let ref = storage.reference(withPath: "ios/public/testSimplePutEmptyData")
  170. let data = Data()
  171. ref.putData(data) { result in
  172. self.assertResultSuccess(result)
  173. expectation.fulfill()
  174. }
  175. waitForExpectations()
  176. }
  177. func testSimplePutDataUnauthorized() throws {
  178. let expectation = self.expectation(description: #function)
  179. let file = "ios/private/secretfile.txt"
  180. let ref = storage.reference(withPath: file)
  181. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  182. ref.putData(data) { result in
  183. switch result {
  184. case .success:
  185. XCTFail("Unexpected success from unauthorized putData")
  186. case let .failure(error as StorageError):
  187. switch error {
  188. case let .unauthorized(bucket, object, serverError):
  189. XCTAssertEqual(bucket, "ios-opensource-samples.appspot.com")
  190. XCTAssertEqual(object, file)
  191. XCTAssertNotNil(serverError)
  192. XCTAssertEqual(serverError["ResponseErrorCode"] as? Int, 403)
  193. expectation.fulfill()
  194. default:
  195. XCTFail("Failed with unexpected error: \(error)")
  196. }
  197. case let .failure(error):
  198. XCTFail("Failed with unexpected error: \(error)")
  199. }
  200. }
  201. waitForExpectations()
  202. }
  203. func testSimplePutDataUnauthorizedThrow() throws {
  204. let expectation = self.expectation(description: #function)
  205. let ref = storage.reference(withPath: "ios/private/secretfile.txt")
  206. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  207. ref.putData(data) { result in
  208. do {
  209. try _ = result.get() // .failure will throw
  210. } catch {
  211. expectation.fulfill()
  212. return
  213. }
  214. XCTFail("Unexpected success from unauthorized putData")
  215. expectation.fulfill()
  216. }
  217. waitForExpectations()
  218. }
  219. func testSimplePutFile() throws {
  220. let expectation = self.expectation(description: #function)
  221. let putFileExpectation = self.expectation(description: "putFile")
  222. let ref = storage.reference(withPath: "ios/public/testSimplePutFile")
  223. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  224. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  225. let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
  226. try data.write(to: fileURL, options: .atomicWrite)
  227. let task = ref.putFile(from: fileURL) { result in
  228. self.assertResultSuccess(result)
  229. putFileExpectation.fulfill()
  230. }
  231. task.observe(StorageTaskStatus.success) { snapshot in
  232. XCTAssertEqual(snapshot.description, "<State: Success>")
  233. expectation.fulfill()
  234. }
  235. var uploadedBytes: Int64 = -1
  236. task.observe(StorageTaskStatus.progress) { snapshot in
  237. XCTAssertTrue(snapshot.description.starts(with: "<State: Progress") ||
  238. snapshot.description.starts(with: "<State: Resume"))
  239. guard let progress = snapshot.progress else {
  240. XCTFail("Failed to get snapshot.progress")
  241. return
  242. }
  243. XCTAssertGreaterThanOrEqual(progress.completedUnitCount, uploadedBytes)
  244. uploadedBytes = progress.completedUnitCount
  245. }
  246. waitForExpectations()
  247. }
  248. func testPutFileLimitedChunk() throws {
  249. defer {
  250. // Reset since tests share storage instance.
  251. storage.uploadChunkSizeBytes = Int64.max
  252. }
  253. let expectation = self.expectation(description: #function)
  254. let putFileExpectation = self.expectation(description: "putFile")
  255. let ref = storage.reference(withPath: "ios/public/testPutFilePauseResume")
  256. let bundle = Bundle(for: StorageIntegrationCommon.self)
  257. let filePath = try XCTUnwrap(bundle.path(forResource: "1mb", ofType: "dat"),
  258. "Failed to get filePath")
  259. let data = try XCTUnwrap(Data(contentsOf: URL(fileURLWithPath: filePath)),
  260. "Failed to load file")
  261. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  262. let fileURL = tmpDirURL.appendingPathComponent("LargePutFile.txt")
  263. var progressCount = 0
  264. try data.write(to: fileURL, options: .atomicWrite)
  265. // Limit the upload chunk size
  266. storage.uploadChunkSizeBytes = 256_000
  267. let task = ref.putFile(from: fileURL) { result in
  268. XCTAssertGreaterThanOrEqual(progressCount, 4)
  269. self.assertResultSuccess(result)
  270. putFileExpectation.fulfill()
  271. }
  272. task.observe(StorageTaskStatus.success) { snapshot in
  273. XCTAssertEqual(snapshot.description, "<State: Success>")
  274. expectation.fulfill()
  275. }
  276. var uploadedBytes: Int64 = -1
  277. task.observe(StorageTaskStatus.progress) { snapshot in
  278. XCTAssertTrue(snapshot.description.starts(with: "<State: Progress") ||
  279. snapshot.description.starts(with: "<State: Resume"))
  280. guard let progress = snapshot.progress else {
  281. XCTFail("Failed to get snapshot.progress")
  282. return
  283. }
  284. progressCount = progressCount + 1
  285. XCTAssertGreaterThanOrEqual(progress.completedUnitCount, uploadedBytes)
  286. uploadedBytes = progress.completedUnitCount
  287. }
  288. waitForExpectations()
  289. }
  290. func testPutFileTinyChunk() throws {
  291. defer {
  292. // Reset since tests share storage instance.
  293. storage.uploadChunkSizeBytes = Int64.max
  294. }
  295. let expectation = self.expectation(description: #function)
  296. let putFileExpectation = self.expectation(description: "putFile")
  297. let ref = storage.reference(withPath: "ios/public/testPutFilePauseResume")
  298. let bundle = Bundle(for: StorageIntegrationCommon.self)
  299. let filePath = try XCTUnwrap(bundle.path(forResource: "1mb", ofType: "dat"),
  300. "Failed to get filePath")
  301. let data = try XCTUnwrap(Data(contentsOf: URL(fileURLWithPath: filePath)),
  302. "Failed to load file")
  303. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  304. let fileURL = tmpDirURL.appendingPathComponent("LargePutFile.txt")
  305. var progressCount = 0
  306. try data.write(to: fileURL, options: .atomicWrite)
  307. // Limit the upload chunk size. This should behave exactly like the previous
  308. // test since small chunk sizes are rounded up to 256K.
  309. storage.uploadChunkSizeBytes = 1
  310. let task = ref.putFile(from: fileURL) { result in
  311. XCTAssertGreaterThanOrEqual(progressCount, 4)
  312. XCTAssertLessThanOrEqual(progressCount, 6)
  313. self.assertResultSuccess(result)
  314. putFileExpectation.fulfill()
  315. }
  316. task.observe(StorageTaskStatus.success) { snapshot in
  317. XCTAssertEqual(snapshot.description, "<State: Success>")
  318. expectation.fulfill()
  319. }
  320. var uploadedBytes: Int64 = -1
  321. task.observe(StorageTaskStatus.progress) { snapshot in
  322. XCTAssertTrue(snapshot.description.starts(with: "<State: Progress") ||
  323. snapshot.description.starts(with: "<State: Resume"))
  324. guard let progress = snapshot.progress else {
  325. XCTFail("Failed to get snapshot.progress")
  326. return
  327. }
  328. progressCount = progressCount + 1
  329. XCTAssertGreaterThanOrEqual(progress.completedUnitCount, uploadedBytes)
  330. uploadedBytes = progress.completedUnitCount
  331. }
  332. waitForExpectations()
  333. }
  334. func testAttemptToUploadDirectoryShouldFail() throws {
  335. // This `.numbers` file is actually a directory.
  336. let fileName = "HomeImprovement.numbers"
  337. let expectation = self.expectation(description: #function)
  338. let bundle = Bundle(for: StorageIntegrationCommon.self)
  339. let fileURL = try XCTUnwrap(bundle.url(forResource: fileName, withExtension: ""),
  340. "Failed to get filePath")
  341. let ref = storage.reference(withPath: "ios/public/" + fileName)
  342. ref.putFile(from: fileURL) { result in
  343. self.assertResultFailure(result)
  344. expectation.fulfill()
  345. }
  346. waitForExpectations()
  347. }
  348. func testPutFileWithSpecialCharacters() throws {
  349. let expectation = self.expectation(description: #function)
  350. let fileName = "hello&+@_ .txt"
  351. let ref = storage.reference(withPath: "ios/public/" + fileName)
  352. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  353. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  354. let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
  355. try data.write(to: fileURL, options: .atomicWrite)
  356. ref.putFile(from: fileURL) { result in
  357. switch result {
  358. case let .success(metadata):
  359. XCTAssertEqual(fileName, metadata.name)
  360. ref.getMetadata { result in
  361. self.assertResultSuccess(result)
  362. }
  363. case let .failure(error):
  364. XCTFail("Unexpected error \(error) from putFile")
  365. }
  366. expectation.fulfill()
  367. }
  368. waitForExpectations()
  369. }
  370. func testSimplePutDataNoMetadata() throws {
  371. let expectation = self.expectation(description: #function)
  372. let ref = storage.reference(withPath: "ios/public/testSimplePutDataNoMetadata")
  373. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  374. ref.putData(data) { result in
  375. self.assertResultSuccess(result)
  376. expectation.fulfill()
  377. }
  378. waitForExpectations()
  379. }
  380. func testSimplePutFileNoMetadata() throws {
  381. let expectation = self.expectation(description: #function)
  382. let fileName = "hello&+@_ .txt"
  383. let ref = storage.reference(withPath: "ios/public/" + fileName)
  384. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  385. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  386. let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
  387. try data.write(to: fileURL, options: .atomicWrite)
  388. ref.putFile(from: fileURL) { result in
  389. self.assertResultSuccess(result)
  390. expectation.fulfill()
  391. }
  392. waitForExpectations()
  393. }
  394. func testSimpleGetData() {
  395. let expectation = self.expectation(description: #function)
  396. let ref = storage.reference(withPath: "ios/public/1mb")
  397. ref.getData(maxSize: 1024 * 1024) { result in
  398. self.assertResultSuccess(result)
  399. expectation.fulfill()
  400. }
  401. waitForExpectations()
  402. }
  403. func testSimpleGetDataInBackgroundQueue() {
  404. let expectation = self.expectation(description: #function)
  405. let ref = storage.reference(withPath: "ios/public/1mb")
  406. DispatchQueue.global(qos: .background).async {
  407. ref.getData(maxSize: 1024 * 1024) { result in
  408. self.assertResultSuccess(result)
  409. expectation.fulfill()
  410. }
  411. }
  412. waitForExpectations()
  413. }
  414. func testSimpleGetDataWithCustomCallbackQueue() {
  415. let expectation = self.expectation(description: #function)
  416. let callbackQueueLabel = "customCallbackQueue"
  417. let callbackQueueKey = DispatchSpecificKey<String>()
  418. let callbackQueue = DispatchQueue(label: callbackQueueLabel)
  419. callbackQueue.setSpecific(key: callbackQueueKey, value: callbackQueueLabel)
  420. storage.callbackQueue = callbackQueue
  421. let ref = storage.reference(withPath: "ios/public/1mb")
  422. ref.getData(maxSize: 1024 * 1024) { result in
  423. self.assertResultSuccess(result)
  424. XCTAssertFalse(Thread.isMainThread)
  425. let currentQueueLabel = DispatchQueue.getSpecific(key: callbackQueueKey)
  426. XCTAssertEqual(currentQueueLabel, callbackQueueLabel)
  427. expectation.fulfill()
  428. // Reset the callbackQueue to default (main queue).
  429. self.storage.callbackQueue = DispatchQueue.main
  430. callbackQueue.setSpecific(key: callbackQueueKey, value: nil)
  431. }
  432. waitForExpectations()
  433. }
  434. func testSimpleGetDataTooSmall() {
  435. let expectation = self.expectation(description: #function)
  436. let ref = storage.reference(withPath: "ios/public/1mb")
  437. let maxSize: Int64 = 1024
  438. ref.getData(maxSize: maxSize) { result in
  439. switch result {
  440. case .success:
  441. XCTFail("Unexpected success from getData too small")
  442. case let .failure(error as StorageError):
  443. switch error {
  444. case let .downloadSizeExceeded(total, max):
  445. XCTAssertEqual(total, 1_048_576)
  446. XCTAssertEqual(max, maxSize)
  447. default:
  448. XCTFail("Failed with unexpected error: \(error)")
  449. }
  450. case let .failure(error):
  451. XCTFail("Failed with unexpected error: \(error)")
  452. }
  453. expectation.fulfill()
  454. }
  455. waitForExpectations()
  456. }
  457. func testSimpleGetDownloadURL() {
  458. let expectation = self.expectation(description: #function)
  459. let ref = storage.reference(withPath: "ios/public/1mb")
  460. // Download URL format is
  461. // "https://firebasestorage.googleapis.com:443/v0/b/{bucket}/o/{path}?alt=media&token={token}"
  462. let downloadURLPrefix =
  463. "https://firebasestorage.googleapis.com:443/v0/b/ios-opensource-samples" +
  464. ".appspot.com/o/ios%2Fpublic%2F1mb?alt=media&token"
  465. ref.downloadURL { result in
  466. switch result {
  467. case let .success(downloadURL):
  468. let urlString = downloadURL.absoluteString
  469. XCTAssertTrue(urlString.hasPrefix(downloadURLPrefix))
  470. case let .failure(error):
  471. XCTFail("Unexpected error \(error) from downloadURL")
  472. }
  473. expectation.fulfill()
  474. }
  475. waitForExpectations()
  476. }
  477. func testSimpleGetFile() throws {
  478. let expectation = self.expectation(description: #function)
  479. let ref = storage.reference(withPath: "ios/public/helloworld" + #function)
  480. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  481. let fileURL = tmpDirURL.appendingPathComponent(#function + "hello.txt")
  482. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  483. ref.putData(data) { result in
  484. switch result {
  485. case .success:
  486. let task = ref.write(toFile: fileURL)
  487. task.observe(StorageTaskStatus.success) { snapshot in
  488. do {
  489. let stringData = try String(contentsOf: fileURL, encoding: .utf8)
  490. XCTAssertEqual(stringData, "Hello Swift World")
  491. XCTAssertEqual(snapshot.description, "<State: Success>")
  492. } catch {
  493. XCTFail("Error processing success snapshot")
  494. }
  495. expectation.fulfill()
  496. }
  497. task.observe(StorageTaskStatus.progress) { snapshot in
  498. XCTAssertNil(snapshot.error, "Error should be nil")
  499. guard let progress = snapshot.progress else {
  500. XCTFail("Missing progress")
  501. return
  502. }
  503. print("\(progress.completedUnitCount) of \(progress.totalUnitCount)")
  504. }
  505. task.observe(StorageTaskStatus.failure) { snapshot in
  506. XCTAssertNil(snapshot.error, "Error should be nil")
  507. }
  508. case let .failure(error):
  509. XCTFail("Unexpected error \(error) from putData")
  510. expectation.fulfill()
  511. }
  512. }
  513. waitForExpectations()
  514. }
  515. func testCancelErrorCode() throws {
  516. let expectation = self.expectation(description: #function)
  517. let ref = storage.reference(withPath: "ios/public/helloworld" + #function)
  518. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  519. let fileURL = tmpDirURL.appendingPathComponent(#function + "hello.txt")
  520. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  521. ref.putData(data) { result in
  522. switch result {
  523. case .success:
  524. let task = ref.write(toFile: fileURL)
  525. task.observe(StorageTaskStatus.success) { snapshot in
  526. XCTFail("Error processing success snapshot")
  527. expectation.fulfill()
  528. }
  529. task.observe(StorageTaskStatus.failure) { snapshot in
  530. let expected = "User cancelled the upload/download."
  531. if let error = snapshot.error {
  532. let errorDescription = error.localizedDescription
  533. XCTAssertEqual(errorDescription, expected)
  534. let code = (error as NSError).code
  535. XCTAssertEqual(code, StorageErrorCode.cancelled.rawValue)
  536. }
  537. expectation.fulfill()
  538. }
  539. task.cancel()
  540. case let .failure(error):
  541. XCTFail("Unexpected error \(error) from putData")
  542. expectation.fulfill()
  543. }
  544. }
  545. waitForExpectations()
  546. }
  547. private func assertMetadata(actualMetadata: StorageMetadata,
  548. expectedContentType: String,
  549. expectedCustomMetadata: [String: String]) {
  550. XCTAssertEqual(actualMetadata.cacheControl, "cache-control")
  551. XCTAssertEqual(actualMetadata.contentDisposition, "content-disposition")
  552. XCTAssertEqual(actualMetadata.contentEncoding, "gzip")
  553. XCTAssertEqual(actualMetadata.contentLanguage, "de")
  554. XCTAssertEqual(actualMetadata.contentType, expectedContentType)
  555. XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
  556. for (key, value) in expectedCustomMetadata {
  557. XCTAssertEqual(actualMetadata.customMetadata![key], value)
  558. }
  559. }
  560. private func assertMetadataNil(actualMetadata: StorageMetadata) {
  561. XCTAssertNil(actualMetadata.cacheControl)
  562. XCTAssertNil(actualMetadata.contentDisposition)
  563. XCTAssertEqual(actualMetadata.contentEncoding, "identity")
  564. XCTAssertNil(actualMetadata.contentLanguage)
  565. XCTAssertNil(actualMetadata.contentType)
  566. XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
  567. XCTAssertNil(actualMetadata.customMetadata)
  568. }
  569. func testUpdateMetadata2() {
  570. let expectation = self.expectation(description: #function)
  571. let ref = storage.reference(withPath: "ios/public/1mb")
  572. let metadata = StorageMetadata()
  573. metadata.cacheControl = "cache-control"
  574. metadata.contentDisposition = "content-disposition"
  575. metadata.contentEncoding = "gzip"
  576. metadata.contentLanguage = "de"
  577. metadata.contentType = "content-type-a"
  578. metadata.customMetadata = ["a": "b"]
  579. ref.updateMetadata(metadata) { updatedMetadata, error in
  580. XCTAssertNil(error, "Error should be nil")
  581. guard let updatedMetadata = updatedMetadata else {
  582. XCTFail("Metadata is nil")
  583. expectation.fulfill()
  584. return
  585. }
  586. self.assertMetadata(actualMetadata: updatedMetadata,
  587. expectedContentType: "content-type-a",
  588. expectedCustomMetadata: ["a": "b"])
  589. let metadata = updatedMetadata
  590. metadata.contentType = "content-type-b"
  591. metadata.customMetadata = ["a": "b", "c": "d"]
  592. ref.updateMetadata(metadata) { result in
  593. switch result {
  594. case let .success(updatedMetadata):
  595. self.assertMetadata(actualMetadata: updatedMetadata,
  596. expectedContentType: "content-type-b",
  597. expectedCustomMetadata: ["a": "b", "c": "d"])
  598. metadata.cacheControl = nil
  599. metadata.contentDisposition = nil
  600. metadata.contentEncoding = nil
  601. metadata.contentLanguage = nil
  602. metadata.contentType = nil
  603. metadata.customMetadata = nil
  604. ref.updateMetadata(metadata) { result in
  605. self.assertResultSuccess(result)
  606. expectation.fulfill()
  607. }
  608. case let .failure(error):
  609. XCTFail("Unexpected error \(error) from updateMetadata")
  610. expectation.fulfill()
  611. }
  612. }
  613. }
  614. waitForExpectations()
  615. }
  616. func testResumeGetFile() {
  617. let expectation = self.expectation(description: #function)
  618. let expectationPause = self.expectation(description: "pause")
  619. let expectationResume = self.expectation(description: "resume")
  620. let ref = storage.reference().child("ios/public/1mb")
  621. let fileURL = URL(fileURLWithPath: "\(NSTemporaryDirectory())/hello.txt")
  622. let task = ref.write(toFile: fileURL)
  623. var downloadedBytes: Int64 = 0
  624. var resumeAtBytes = 256 * 1024
  625. task.observe(StorageTaskStatus.success) { snapshot in
  626. XCTAssertEqual(snapshot.description, "<State: Success>")
  627. expectation.fulfill()
  628. }
  629. task.observe(StorageTaskStatus.progress) { snapshot in
  630. let description = snapshot.description
  631. XCTAssertTrue(description.contains("State: Progress") ||
  632. description.contains("State: Resume"))
  633. let progress = snapshot.progress
  634. if let completed = progress?.completedUnitCount {
  635. XCTAssertGreaterThanOrEqual(completed, downloadedBytes)
  636. downloadedBytes = completed
  637. if completed > resumeAtBytes {
  638. task.pause()
  639. expectationPause.fulfill()
  640. resumeAtBytes = Int.max
  641. }
  642. }
  643. }
  644. task.observe(StorageTaskStatus.pause) { snapshot in
  645. XCTAssertEqual(snapshot.description, "<State: Paused>")
  646. task.resume()
  647. expectationResume.fulfill()
  648. }
  649. waitForExpectations()
  650. XCTAssertEqual(resumeAtBytes, Int.max)
  651. }
  652. func testResumeGetFileInBackgroundQueue() {
  653. let expectation = self.expectation(description: #function)
  654. let expectationPause = self.expectation(description: "pause")
  655. let expectationResume = self.expectation(description: "resume")
  656. let ref = storage.reference().child("ios/public/1mb")
  657. let fileURL = URL(fileURLWithPath: "\(NSTemporaryDirectory())/hello.txt")
  658. let task = ref.write(toFile: fileURL)
  659. var downloadedBytes: Int64 = 0
  660. var resumeAtBytes = 256 * 1024
  661. task.observe(StorageTaskStatus.success) { snapshot in
  662. XCTAssertEqual(snapshot.description, "<State: Success>")
  663. expectation.fulfill()
  664. }
  665. task.observe(StorageTaskStatus.progress) { snapshot in
  666. let description = snapshot.description
  667. XCTAssertTrue(description.contains("State: Progress") ||
  668. description.contains("State: Resume"))
  669. let progress = snapshot.progress
  670. if let completed = progress?.completedUnitCount {
  671. XCTAssertGreaterThanOrEqual(completed, downloadedBytes)
  672. downloadedBytes = completed
  673. if completed > resumeAtBytes {
  674. DispatchQueue.global(qos: .background).async {
  675. task.pause()
  676. }
  677. expectationPause.fulfill()
  678. resumeAtBytes = Int.max
  679. }
  680. }
  681. }
  682. task.observe(StorageTaskStatus.pause) { snapshot in
  683. XCTAssertEqual(snapshot.description, "<State: Paused>")
  684. DispatchQueue.global(qos: .background).async {
  685. task.resume()
  686. }
  687. expectationResume.fulfill()
  688. }
  689. waitForExpectations()
  690. XCTAssertEqual(resumeAtBytes, Int.max)
  691. }
  692. func testPagedListFiles() {
  693. let expectation = self.expectation(description: #function)
  694. let ref = storage.reference(withPath: "ios/public/list")
  695. ref.list(maxResults: 2) { result in
  696. switch result {
  697. case let .success(listResult):
  698. XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
  699. XCTAssertEqual(listResult.prefixes, [])
  700. guard let pageToken = listResult.pageToken else {
  701. XCTFail("pageToken should not be nil")
  702. expectation.fulfill()
  703. return
  704. }
  705. ref.list(maxResults: 2, pageToken: pageToken) { result in
  706. switch result {
  707. case let .success(listResult):
  708. XCTAssertEqual(listResult.items, [])
  709. XCTAssertEqual(listResult.prefixes, [ref.child("prefix")])
  710. XCTAssertNil(listResult.pageToken, "pageToken should be nil")
  711. case let .failure(error):
  712. XCTFail("Unexpected error \(error) from list")
  713. }
  714. expectation.fulfill()
  715. }
  716. case let .failure(error):
  717. XCTFail("Unexpected error \(error) from list")
  718. expectation.fulfill()
  719. }
  720. }
  721. waitForExpectations()
  722. }
  723. func testPagedListFilesTooManyError() {
  724. let expectation = self.expectation(description: #function)
  725. let ref = storage.reference(withPath: "ios/public/list")
  726. ref.list(maxResults: 22222) { result in
  727. switch result {
  728. case .success:
  729. XCTFail("Unexpected success from list")
  730. case let .failure(error as StorageError):
  731. switch error {
  732. case let .invalidArgument(message):
  733. XCTAssertEqual(message, "Argument 'maxResults' must be between 1 and 1000 inclusive.")
  734. default:
  735. XCTFail("Failed with unexpected error: \(error)")
  736. }
  737. case let .failure(error):
  738. XCTFail("Failed with unexpected error: \(error)")
  739. }
  740. expectation.fulfill()
  741. }
  742. waitForExpectations()
  743. }
  744. func testListAllFiles() {
  745. let expectation = self.expectation(description: #function)
  746. let ref = storage.reference(withPath: "ios/public/list")
  747. ref.listAll { result in
  748. switch result {
  749. case let .success(listResult):
  750. XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
  751. XCTAssertEqual(listResult.prefixes, [ref.child("prefix")])
  752. XCTAssertNil(listResult.pageToken, "pageToken should be nil")
  753. case let .failure(error):
  754. XCTFail("Unexpected error \(error) from list")
  755. }
  756. expectation.fulfill()
  757. }
  758. waitForExpectations()
  759. }
  760. private func waitForExpectations() {
  761. let kFIRStorageIntegrationTestTimeout = 100.0
  762. waitForExpectations(timeout: kFIRStorageIntegrationTestTimeout,
  763. handler: { error in
  764. if let error {
  765. print(error)
  766. }
  767. })
  768. }
  769. private func assertResultSuccess<T>(_ result: Result<T, Error>,
  770. file: StaticString = #file, line: UInt = #line) {
  771. switch result {
  772. case let .success(value):
  773. XCTAssertNotNil(value, file: file, line: line)
  774. case let .failure(error):
  775. XCTFail("Unexpected error \(error)")
  776. }
  777. }
  778. private func assertResultFailure<T>(_ result: Result<T, Error>,
  779. file: StaticString = #file, line: UInt = #line) {
  780. switch result {
  781. case let .success(value):
  782. XCTFail("Unexpected success with value: \(value)")
  783. case let .failure(error):
  784. XCTAssertNotNil(error, file: file, line: line)
  785. }
  786. }
  787. }