StorageIntegration.swift 32 KB

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