StorageIntegration.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. // Copyright 2021 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. //
  15. // Firebase Storage Integration tests
  16. //
  17. // To run these tests, you need to define the following access rights:
  18. // *
  19. // rules_version = '2';
  20. // service firebase.storage {
  21. // match /b/{bucket}/o {
  22. // match /{directChild=*} {
  23. // allow read: if request.auth != null;
  24. // }
  25. // match /ios {
  26. // match /public/{allPaths=**} {
  27. // allow write: if request.auth != null;
  28. // allow read: if true;
  29. // }
  30. // match /private/{allPaths=**} {
  31. // allow read, write: if false;
  32. // }
  33. // }
  34. // }
  35. // }
  36. //
  37. // You also need to enable email/password sign in and add a test user in your
  38. // Firebase Authentication settings. Your account credentials need to match
  39. // the credentials defined in `kTestUser` and `kTestPassword` in Credentials.swift.
  40. //
  41. // You can define these access rights in the Firebase Console of your project.
  42. //
  43. import Combine
  44. import FirebaseAuth
  45. import FirebaseCombineSwift
  46. import FirebaseCore
  47. import FirebaseStorage
  48. import XCTest
  49. class StorageIntegration: XCTestCase {
  50. var app: FirebaseApp!
  51. var auth: Auth!
  52. var storage: Storage!
  53. static var once = false
  54. static var signedIn = false
  55. override class func setUp() {
  56. FirebaseApp.configure()
  57. }
  58. override func setUp() {
  59. super.setUp()
  60. app = FirebaseApp.app()
  61. auth = Auth.auth(app: app)
  62. storage = Storage.storage(app: app!)
  63. if !StorageIntegration.signedIn {
  64. signInAndWait()
  65. }
  66. if !StorageIntegration.once {
  67. StorageIntegration.once = true
  68. let setupExpectation = expectation(description: "setUp")
  69. let largeFiles = ["ios/public/1mb"]
  70. let emptyFiles =
  71. ["ios/public/empty", "ios/public/list/a", "ios/public/list/b", "ios/public/list/prefix/c"]
  72. setupExpectation.expectedFulfillmentCount = largeFiles.count + emptyFiles.count
  73. do {
  74. var cancellables = Set<AnyCancellable>()
  75. let bundle = Bundle(for: StorageIntegration.self)
  76. let filePath = try XCTUnwrap(bundle.path(forResource: "1mb", ofType: "dat"),
  77. "Failed to get filePath")
  78. let data = try XCTUnwrap(Data(contentsOf: URL(fileURLWithPath: filePath)),
  79. "Failed to load file")
  80. for file in largeFiles + emptyFiles {
  81. storage
  82. .reference()
  83. .child(file)
  84. .putData(data)
  85. .assertNoFailure()
  86. .sink { _ in
  87. setupExpectation.fulfill()
  88. }
  89. .store(in: &cancellables)
  90. }
  91. waitForExpectations()
  92. } catch {
  93. XCTFail("Error thrown setting up files in setUp")
  94. }
  95. }
  96. }
  97. override func tearDown() {
  98. app = nil
  99. storage = nil
  100. super.tearDown()
  101. }
  102. func testGetMetadata() {
  103. var cancellables = Set<AnyCancellable>()
  104. let expectation = self.expectation(description: "testGetMetadata")
  105. storage.reference().child("ios/public/1mb")
  106. .getMetadata()
  107. .assertNoFailure()
  108. .sink { metadata in
  109. XCTAssertNotNil(metadata)
  110. expectation.fulfill()
  111. }
  112. .store(in: &cancellables)
  113. waitForExpectations()
  114. }
  115. func testUpdateMetadata() {
  116. var cancellables = Set<AnyCancellable>()
  117. let expectation = self.expectation(description: #function)
  118. let meta = StorageMetadata()
  119. meta.contentType = "lol/custom"
  120. meta.customMetadata = ["lol": "custom metadata is neat",
  121. "ちかてつ": "🚇",
  122. "shinkansen": "新幹線"]
  123. storage.reference(withPath: "ios/public/1mb")
  124. .updateMetadata(meta)
  125. .assertNoFailure()
  126. .sink { metadata in
  127. XCTAssertEqual(meta.contentType, metadata.contentType)
  128. XCTAssertEqual(meta.customMetadata!["lol"], metadata.customMetadata!["lol"])
  129. XCTAssertEqual(meta.customMetadata!["ちかてつ"], metadata.customMetadata!["ちかてつ"])
  130. XCTAssertEqual(meta.customMetadata!["shinkansen"],
  131. metadata.customMetadata!["shinkansen"])
  132. expectation.fulfill()
  133. }
  134. .store(in: &cancellables)
  135. waitForExpectations()
  136. }
  137. func testDelete() throws {
  138. var cancellables = Set<AnyCancellable>()
  139. let expectation = self.expectation(description: #function)
  140. let ref = storage.reference(withPath: "ios/public/fileToDelete")
  141. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  142. ref.putData(data)
  143. .flatMap { _ in ref.delete() }
  144. .assertNoFailure()
  145. .sink { success in
  146. XCTAssertTrue(success)
  147. expectation.fulfill()
  148. }
  149. .store(in: &cancellables)
  150. waitForExpectations()
  151. }
  152. func testDeleteWithNilCompletion() throws {
  153. var cancellables = Set<AnyCancellable>()
  154. let expectation = self.expectation(description: #function)
  155. let ref = storage.reference(withPath: "ios/public/fileToDelete")
  156. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  157. ref.putData(data)
  158. .assertNoFailure()
  159. .sink { metadata in
  160. XCTAssertEqual(metadata.name, "fileToDelete")
  161. ref.delete(completion: nil)
  162. expectation.fulfill()
  163. }
  164. .store(in: &cancellables)
  165. waitForExpectations()
  166. }
  167. func testSimplePutData() throws {
  168. var cancellables = Set<AnyCancellable>()
  169. let expectation = self.expectation(description: #function)
  170. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  171. storage.reference(withPath: "ios/public/testBytesUpload")
  172. .putData(data)
  173. .assertNoFailure()
  174. .sink { metadata in
  175. XCTAssertEqual(metadata.name, "testBytesUpload")
  176. XCTAssertEqual(metadata.contentEncoding, "identity")
  177. expectation.fulfill()
  178. }
  179. .store(in: &cancellables)
  180. waitForExpectations()
  181. }
  182. func testSimplePutSpecialCharacter() throws {
  183. var cancellables = Set<AnyCancellable>()
  184. let expectation = self.expectation(description: #function)
  185. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  186. let path = "ios/public/-._~!$'()*,=:@&+;"
  187. storage.reference(withPath: path)
  188. .putData(data)
  189. .assertNoFailure()
  190. .sink { metadata in
  191. XCTAssertEqual(metadata.contentType, "application/octet-stream")
  192. expectation.fulfill()
  193. }
  194. .store(in: &cancellables)
  195. waitForExpectations()
  196. }
  197. func testSimplePutDataInBackgroundQueue() throws {
  198. var cancellables = Set<AnyCancellable>()
  199. let expectation = self.expectation(description: #function)
  200. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  201. storage.reference(withPath: "ios/public/testBytesUpload")
  202. .putData(data)
  203. .subscribe(on: DispatchQueue.global(qos: .background))
  204. .assertNoFailure()
  205. .sink { _ in
  206. expectation.fulfill()
  207. }
  208. .store(in: &cancellables)
  209. waitForExpectations()
  210. }
  211. func testSimplePutEmptyData() {
  212. var cancellables = Set<AnyCancellable>()
  213. let expectation = self.expectation(description: #function)
  214. storage
  215. .reference(withPath: "ios/public/testSimplePutEmptyData")
  216. .putData(Data())
  217. .assertNoFailure()
  218. .sink { _ in
  219. expectation.fulfill()
  220. }
  221. .store(in: &cancellables)
  222. waitForExpectations()
  223. }
  224. func testSimplePutDataUnauthorized() throws {
  225. var cancellables = Set<AnyCancellable>()
  226. let expectation = self.expectation(description: #function)
  227. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  228. storage
  229. .reference(withPath: "ios/private/secretfile.txt")
  230. .putData(data)
  231. .sink(receiveCompletion: { completion in
  232. switch completion {
  233. case .finished:
  234. XCTFail("Unexpected success return from putData)")
  235. case let .failure(error):
  236. let message = String(describing: error)
  237. XCTAssertTrue(message.contains("unauthorized"))
  238. XCTAssertTrue(message.contains("ios-opensource-samples.appspot.com"))
  239. expectation.fulfill()
  240. }
  241. }, receiveValue: { value in
  242. print("Received value \(value)")
  243. })
  244. .store(in: &cancellables)
  245. waitForExpectations()
  246. }
  247. func testAttemptToUploadDirectoryShouldFail() throws {
  248. let expectation = self.expectation(description: #function)
  249. var cancellables = Set<AnyCancellable>()
  250. // This `.numbers` file is actually a directory.
  251. let fileName = "HomeImprovement.numbers"
  252. let bundle = Bundle(for: StorageIntegration.self)
  253. let fileURL = try XCTUnwrap(bundle.url(forResource: fileName, withExtension: ""),
  254. "Failed to get filePath")
  255. storage
  256. .reference(withPath: "ios/public/" + fileName)
  257. .putFile(from: fileURL)
  258. .sink(receiveCompletion: { completion in
  259. switch completion {
  260. case .finished:
  261. XCTFail("Unexpected success return from putFile)")
  262. case let .failure(error):
  263. XCTAssertTrue(String(describing: error).starts(with: "unknown"))
  264. expectation.fulfill()
  265. }
  266. }, receiveValue: { value in
  267. print("Received value \(value)")
  268. })
  269. .store(in: &cancellables)
  270. waitForExpectations()
  271. }
  272. func testPutFileWithSpecialCharacters() throws {
  273. var cancellables = Set<AnyCancellable>()
  274. let expectation = self.expectation(description: #function)
  275. let fileName = "hello&+@_ .txt"
  276. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  277. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  278. let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
  279. try data.write(to: fileURL, options: .atomicWrite)
  280. let ref = storage.reference(withPath: "ios/public/" + fileName)
  281. ref
  282. .putFile(from: fileURL)
  283. .assertNoFailure()
  284. .sink { _ in
  285. ref
  286. .getMetadata()
  287. .assertNoFailure()
  288. .sink { metadata in
  289. XCTAssertNotNil(metadata)
  290. expectation.fulfill()
  291. }
  292. .store(in: &cancellables)
  293. }
  294. .store(in: &cancellables)
  295. waitForExpectations()
  296. }
  297. func testSimplePutDataNoMetadata() throws {
  298. var cancellables = Set<AnyCancellable>()
  299. let expectation = self.expectation(description: #function)
  300. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  301. storage
  302. .reference(withPath: "ios/public/testSimplePutDataNoMetadata")
  303. .putData(data)
  304. .assertNoFailure()
  305. .sink { metadata in
  306. XCTAssertNotNil(metadata)
  307. expectation.fulfill()
  308. }
  309. .store(in: &cancellables)
  310. waitForExpectations()
  311. }
  312. func testSimplePutFileNoMetadata() throws {
  313. var cancellables = Set<AnyCancellable>()
  314. let expectation = self.expectation(description: #function)
  315. let fileName = "hello&+@_ .txt"
  316. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  317. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  318. let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
  319. try data.write(to: fileURL, options: .atomicWrite)
  320. storage
  321. .reference(withPath: "ios/public/" + fileName)
  322. .putFile(from: fileURL)
  323. .assertNoFailure()
  324. .sink { metadata in
  325. XCTAssertNotNil(metadata)
  326. expectation.fulfill()
  327. }
  328. .store(in: &cancellables)
  329. waitForExpectations()
  330. }
  331. func testSimpleGetData() {
  332. var cancellables = Set<AnyCancellable>()
  333. let expectation = self.expectation(description: #function)
  334. storage
  335. .reference(withPath: "ios/public/1mb")
  336. .getData(maxSize: 1024 * 1024)
  337. .assertNoFailure()
  338. .sink { _ in
  339. expectation.fulfill()
  340. }
  341. .store(in: &cancellables)
  342. waitForExpectations()
  343. }
  344. func testSimpleGetDataInBackgroundQueue() {
  345. var cancellables = Set<AnyCancellable>()
  346. let expectation = self.expectation(description: #function)
  347. storage
  348. .reference(withPath: "ios/public/1mb")
  349. .getData(maxSize: 1024 * 1024)
  350. .subscribe(on: DispatchQueue.global(qos: .background))
  351. .assertNoFailure()
  352. .sink { _ in
  353. expectation.fulfill()
  354. }
  355. .store(in: &cancellables)
  356. waitForExpectations()
  357. }
  358. func testSimpleGetDataWithCustomCallbackQueue() {
  359. var cancellables = Set<AnyCancellable>()
  360. let expectation = self.expectation(description: #function)
  361. let callbackQueueLabel = "customCallbackQueue"
  362. let callbackQueueKey = DispatchSpecificKey<String>()
  363. let callbackQueue = DispatchQueue(label: callbackQueueLabel)
  364. callbackQueue.setSpecific(key: callbackQueueKey, value: callbackQueueLabel)
  365. storage.callbackQueue = callbackQueue
  366. storage
  367. .reference(withPath: "ios/public/1mb")
  368. .getData(maxSize: 1024 * 1024)
  369. .assertNoFailure()
  370. .sink { _ in
  371. XCTAssertFalse(Thread.isMainThread)
  372. let currentQueueLabel = DispatchQueue.getSpecific(key: callbackQueueKey)
  373. XCTAssertEqual(currentQueueLabel, callbackQueueLabel)
  374. expectation.fulfill()
  375. // Reset the callbackQueue to default (main queue).
  376. self.storage.callbackQueue = DispatchQueue.main
  377. callbackQueue.setSpecific(key: callbackQueueKey, value: nil)
  378. }
  379. .store(in: &cancellables)
  380. waitForExpectations()
  381. }
  382. func testSimpleGetDataTooSmall() {
  383. var cancellables = Set<AnyCancellable>()
  384. let expectation = self.expectation(description: #function)
  385. storage
  386. .reference(withPath: "ios/public/1mb")
  387. .getData(maxSize: 1024)
  388. .sink(receiveCompletion: { completion in
  389. switch completion {
  390. case .finished:
  391. XCTFail("Unexpected success return from getData)")
  392. case let .failure(error):
  393. let message = String(describing: error)
  394. XCTAssertTrue(message.contains("downloadSizeExceeded"))
  395. XCTAssertTrue(message.contains("1048576"))
  396. XCTAssertTrue(message.contains("1024"))
  397. expectation.fulfill()
  398. }
  399. }, receiveValue: { value in
  400. print("Received value \(value)")
  401. })
  402. .store(in: &cancellables)
  403. waitForExpectations()
  404. }
  405. func testSimpleGetDownloadURL() {
  406. var cancellables = Set<AnyCancellable>()
  407. let expectation = self.expectation(description: #function)
  408. // Download URL format is
  409. // "https://firebasestorage.googleapis.com/v0/b/{bucket}/o/{path}?alt=media&token={token}"
  410. let downloadURLPattern =
  411. "^https:\\/\\/firebasestorage.googleapis.com:443\\/v0\\/b\\/[^\\/]*\\/o\\/" +
  412. "ios%2Fpublic%2F1mb\\?alt=media&token=[a-z0-9-]*$"
  413. storage
  414. .reference(withPath: "ios/public/1mb")
  415. .downloadURL()
  416. .assertNoFailure()
  417. .sink { downloadURL in
  418. do {
  419. let testRegex = try NSRegularExpression(pattern: downloadURLPattern)
  420. let downloadURL = try XCTUnwrap(downloadURL, "Failed to unwrap downloadURL")
  421. let urlString = downloadURL.absoluteString
  422. XCTAssertEqual(testRegex.numberOfMatches(in: urlString,
  423. range: NSRange(location: 0,
  424. length: urlString.count)), 1)
  425. } catch {
  426. XCTFail("Throw in downloadURL completion block")
  427. }
  428. expectation.fulfill()
  429. }
  430. .store(in: &cancellables)
  431. waitForExpectations()
  432. }
  433. private func assertMetadata(actualMetadata: StorageMetadata,
  434. expectedContentType: String,
  435. expectedCustomMetadata: [String: String]) {
  436. XCTAssertEqual(actualMetadata.cacheControl, "cache-control")
  437. XCTAssertEqual(actualMetadata.contentDisposition, "content-disposition")
  438. XCTAssertEqual(actualMetadata.contentEncoding, "gzip")
  439. XCTAssertEqual(actualMetadata.contentLanguage, "de")
  440. XCTAssertEqual(actualMetadata.contentType, expectedContentType)
  441. XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
  442. for (key, value) in expectedCustomMetadata {
  443. XCTAssertEqual(actualMetadata.customMetadata![key], value)
  444. }
  445. }
  446. private func assertMetadataNil(actualMetadata: StorageMetadata) {
  447. XCTAssertNil(actualMetadata.cacheControl)
  448. XCTAssertNil(actualMetadata.contentDisposition)
  449. XCTAssertEqual(actualMetadata.contentEncoding, "identity")
  450. XCTAssertNil(actualMetadata.contentLanguage)
  451. XCTAssertNil(actualMetadata.contentType)
  452. XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
  453. XCTAssertNil(actualMetadata.customMetadata)
  454. }
  455. func testUpdateMetadata2() {
  456. var cancellables = Set<AnyCancellable>()
  457. let expectation = self.expectation(description: #function)
  458. let metadata = StorageMetadata()
  459. metadata.cacheControl = "cache-control"
  460. metadata.contentDisposition = "content-disposition"
  461. metadata.contentEncoding = "gzip"
  462. metadata.contentLanguage = "de"
  463. metadata.contentType = "content-type-a"
  464. metadata.customMetadata = ["a": "b"]
  465. let ref = storage.reference(withPath: "ios/public/1mb")
  466. ref
  467. .updateMetadata(metadata)
  468. .assertNoFailure()
  469. .sink { updatedMetadata in
  470. self.assertMetadata(actualMetadata: updatedMetadata,
  471. expectedContentType: "content-type-a",
  472. expectedCustomMetadata: ["a": "b"])
  473. let metadata = updatedMetadata
  474. metadata.contentType = "content-type-b"
  475. metadata.customMetadata = ["a": "b", "c": "d"]
  476. ref
  477. .updateMetadata(metadata)
  478. .assertNoFailure()
  479. .sink { updatedMetadata in
  480. self.assertMetadata(actualMetadata: updatedMetadata,
  481. expectedContentType: "content-type-b",
  482. expectedCustomMetadata: ["a": "b", "c": "d"])
  483. metadata.cacheControl = nil
  484. metadata.contentDisposition = nil
  485. metadata.contentEncoding = nil
  486. metadata.contentLanguage = nil
  487. metadata.contentType = nil
  488. metadata.customMetadata = nil
  489. ref
  490. .updateMetadata(metadata)
  491. .assertNoFailure()
  492. .sink { _ in
  493. expectation.fulfill()
  494. }
  495. .store(in: &cancellables)
  496. }
  497. .store(in: &cancellables)
  498. }
  499. .store(in: &cancellables)
  500. waitForExpectations()
  501. }
  502. func testPagedListFiles() {
  503. var cancellables = Set<AnyCancellable>()
  504. let expectation = self.expectation(description: #function)
  505. let ref = storage.reference(withPath: "ios/public/list")
  506. ref
  507. .list(maxResults: 2)
  508. .assertNoFailure()
  509. .sink { listResult in
  510. XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
  511. XCTAssertEqual(listResult.prefixes, [])
  512. guard let pageToken = listResult.pageToken else {
  513. XCTFail("pageToken should not be nil")
  514. expectation.fulfill()
  515. return
  516. }
  517. ref
  518. .list(maxResults: 2, pageToken: pageToken)
  519. .assertNoFailure()
  520. .sink { listResult in
  521. XCTAssertEqual(listResult.items, [])
  522. XCTAssertEqual(listResult.prefixes, [ref.child("prefix")])
  523. XCTAssertNil(listResult.pageToken, "pageToken should be nil")
  524. expectation.fulfill()
  525. }
  526. .store(in: &cancellables)
  527. }
  528. .store(in: &cancellables)
  529. waitForExpectations()
  530. }
  531. func testListAllFiles() {
  532. var cancellables = Set<AnyCancellable>()
  533. let expectation = self.expectation(description: #function)
  534. let ref = storage.reference(withPath: "ios/public/list")
  535. ref
  536. .listAll()
  537. .assertNoFailure()
  538. .sink { listResult in
  539. XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
  540. XCTAssertEqual(listResult.prefixes, [ref.child("prefix")])
  541. XCTAssertNil(listResult.pageToken, "pageToken should be nil")
  542. expectation.fulfill()
  543. }
  544. .store(in: &cancellables)
  545. waitForExpectations()
  546. }
  547. private func signInAndWait() {
  548. var cancellables = Set<AnyCancellable>()
  549. let expectation = self.expectation(description: #function)
  550. auth
  551. .signIn(withEmail: Credentials.kUserName,
  552. password: Credentials.kPassword)
  553. .assertNoFailure()
  554. .sink { _ in
  555. StorageIntegration.signedIn = true
  556. print("Successfully signed in")
  557. expectation.fulfill()
  558. }
  559. .store(in: &cancellables)
  560. waitForExpectations()
  561. }
  562. private func waitForExpectations() {
  563. let kFIRStorageIntegrationTestTimeout = 30.0
  564. waitForExpectations(timeout: kFIRStorageIntegrationTestTimeout,
  565. handler: { error in
  566. if let error {
  567. print(error)
  568. }
  569. })
  570. }
  571. }