StorageAsyncAwait.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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. import FirebaseAuth
  15. import FirebaseCore
  16. import FirebaseStorage
  17. import FirebaseStorageSwift
  18. import XCTest
  19. #if swift(>=5.5)
  20. @available(iOS 15, *)
  21. class StorageAsyncAwait: StorageIntegrationCommon {
  22. func testGetMetadata() async throws {
  23. let ref = storage.reference().child("ios/public/1mb2")
  24. let result = try await ref.getMetadata()
  25. XCTAssertNotNil(result)
  26. }
  27. func testUpdateMetadata() async throws {
  28. let meta = StorageMetadata()
  29. meta.contentType = "lol/custom"
  30. meta.customMetadata = ["lol": "custom metadata is neat",
  31. "ちかてつ": "🚇",
  32. "shinkansen": "新幹線"]
  33. let ref = storage.reference(withPath: "ios/public/1mb2")
  34. let metadata = try await ref.updateMetadata(meta)
  35. XCTAssertEqual(meta.contentType, metadata.contentType)
  36. XCTAssertEqual(meta.customMetadata!["lol"], metadata.customMetadata!["lol"])
  37. XCTAssertEqual(meta.customMetadata!["ちかてつ"], metadata.customMetadata!["ちかてつ"])
  38. XCTAssertEqual(meta.customMetadata!["shinkansen"],
  39. metadata.customMetadata!["shinkansen"])
  40. }
  41. func testDelete() async throws {
  42. let ref = storage.reference(withPath: "ios/public/fileToDelete")
  43. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  44. let result = try await ref.putDataAsync(data)
  45. XCTAssertNotNil(result)
  46. _ = try await ref.delete()
  47. // Next delete should fail and verify the first delete succeeded.
  48. do {
  49. _ = try await ref.delete()
  50. } catch {
  51. XCTAssertEqual((error as NSError).code, StorageErrorCode.objectNotFound.rawValue)
  52. }
  53. }
  54. func testDeleteAfterPut() async throws {
  55. let ref = storage.reference(withPath: "ios/public/fileToDelete")
  56. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  57. let result = try await ref.putDataAsync(data)
  58. XCTAssertNotNil(result)
  59. let result2: Void = try await ref.delete()
  60. XCTAssertNotNil(result2)
  61. }
  62. func testSimplePutData() async throws {
  63. let ref = storage.reference(withPath: "ios/public/testBytesUpload")
  64. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  65. let result = try await ref.putDataAsync(data)
  66. XCTAssertNotNil(result)
  67. }
  68. func testSimplePutSpecialCharacter() async throws {
  69. let ref = storage.reference(withPath: "ios/public/-._~!$'()*,=:@&+;")
  70. let data = try XCTUnwrap("Hello Swift World-._~!$'()*,=:@&+;".data(using: .utf8),
  71. "Data construction failed")
  72. let result = try await ref.putDataAsync(data)
  73. XCTAssertNotNil(result)
  74. }
  75. func testSimplePutDataInBackgroundQueue() async throws {
  76. actor Background {
  77. func uploadData(_ ref: StorageReference) async throws -> StorageMetadata {
  78. let data = try XCTUnwrap(
  79. "Hello Swift World".data(using: .utf8),
  80. "Data construction failed"
  81. )
  82. XCTAssertFalse(Thread.isMainThread)
  83. return try await ref.putDataAsync(data)
  84. }
  85. }
  86. let ref = storage.reference(withPath: "ios/public/testBytesUpload")
  87. let result = try await Background().uploadData(ref)
  88. XCTAssertNotNil(result)
  89. }
  90. func testSimplePutEmptyData() async throws {
  91. let ref = storage.reference(withPath: "ios/public/testSimplePutEmptyData")
  92. let data = Data()
  93. let result = try await ref.putDataAsync(data)
  94. XCTAssertNotNil(result)
  95. }
  96. func testSimplePutDataUnauthorized() async throws {
  97. let ref = storage.reference(withPath: "ios/private/secretfile.txt")
  98. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  99. do {
  100. _ = try await ref.putDataAsync(data)
  101. XCTFail("Unexpected success from unauthorized putData")
  102. } catch {
  103. XCTAssertEqual((error as NSError).code, StorageErrorCode.unauthorized.rawValue)
  104. }
  105. }
  106. // TODO: Update this function when the task handle APIs are updated for the new Swift Concurrency.
  107. func testSimplePutFile() throws {}
  108. func testAttemptToUploadDirectoryShouldFail() async throws {
  109. // This `.numbers` file is actually a directory.
  110. let fileName = "HomeImprovement.numbers"
  111. let bundle = Bundle(for: StorageIntegrationCommon.self)
  112. let fileURL = try XCTUnwrap(bundle.url(forResource: fileName, withExtension: ""),
  113. "Failed to get filePath")
  114. let ref = storage.reference(withPath: "ios/public/" + fileName)
  115. do {
  116. _ = try await ref.putFileAsync(from: fileURL)
  117. XCTFail("Unexpected success from putFile of a directory")
  118. } catch {
  119. // TODO: Investigate generating a more descriptive error code than unknown.
  120. let e = error as NSError
  121. XCTAssertEqual(e.code, StorageErrorCode.unknown.rawValue)
  122. }
  123. }
  124. func testPutFileWithSpecialCharacters() async throws {
  125. let fileName = "hello&+@_ .txt"
  126. let ref = storage.reference(withPath: "ios/public/" + fileName)
  127. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  128. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  129. let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
  130. try data.write(to: fileURL, options: .atomicWrite)
  131. let metadata = try await ref.putFileAsync(from: fileURL)
  132. XCTAssertEqual(fileName, metadata.name)
  133. let result = try await ref.getMetadata()
  134. XCTAssertNotNil(result)
  135. }
  136. func testSimplePutDataNoMetadata() async throws {
  137. let ref = storage.reference(withPath: "ios/public/testSimplePutDataNoMetadata")
  138. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  139. let result = try await ref.putDataAsync(data)
  140. XCTAssertNotNil(result)
  141. }
  142. func testSimplePutFileNoMetadata() async throws {
  143. let fileName = "hello&+@_ .txt"
  144. let ref = storage.reference(withPath: "ios/public/" + fileName)
  145. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  146. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  147. let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
  148. try data.write(to: fileURL, options: .atomicWrite)
  149. let result = try await ref.putFileAsync(from: fileURL)
  150. XCTAssertNotNil(result)
  151. }
  152. func testSimpleGetData() async throws {
  153. let ref = storage.reference(withPath: "ios/public/1mb2")
  154. let result = try await ref.data(maxSize: 1024 * 1024)
  155. XCTAssertNotNil(result)
  156. }
  157. func testSimpleGetDataWithTask() async throws {
  158. let ref = storage.reference(withPath: "ios/public/1mb2")
  159. let result = try await ref.data(maxSize: 1024 * 1024)
  160. XCTAssertNotNil(result)
  161. }
  162. func testSimpleGetDataInBackgroundQueue() async throws {
  163. actor Background {
  164. func data(from ref: StorageReference) async throws -> Data {
  165. XCTAssertFalse(Thread.isMainThread)
  166. return try await ref.data(maxSize: 1024 * 1024)
  167. }
  168. }
  169. let ref = storage.reference(withPath: "ios/public/1mb2")
  170. let result = try await Background().data(from: ref)
  171. XCTAssertNotNil(result)
  172. }
  173. func testSimpleGetDataTooSmall() async {
  174. let ref = storage.reference(withPath: "ios/public/1mb2")
  175. do {
  176. _ = try await ref.data(maxSize: 1024)
  177. XCTFail("Unexpected success from getData too small")
  178. } catch {
  179. XCTAssertEqual((error as NSError).code, StorageErrorCode.downloadSizeExceeded.rawValue)
  180. }
  181. }
  182. func testSimpleGetDownloadURL() async throws {
  183. let ref = storage.reference(withPath: "ios/public/1mb2")
  184. // Download URL format is
  185. // "https://firebasestorage.googleapis.com:443/v0/b/{bucket}/o/{path}?alt=media&token={token}"
  186. let downloadURLPattern =
  187. "^https:\\/\\/firebasestorage.googleapis.com:443\\/v0\\/b\\/[^\\/]*\\/o\\/" +
  188. "ios%2Fpublic%2F1mb2\\?alt=media&token=[a-z0-9-]*$"
  189. let downloadURL = try await ref.downloadURL()
  190. let testRegex = try NSRegularExpression(pattern: downloadURLPattern)
  191. let urlString = downloadURL.absoluteString
  192. let range = NSRange(location: 0, length: urlString.count)
  193. XCTAssertNotNil(testRegex.firstMatch(in: urlString, options: [], range: range))
  194. }
  195. func testAsyncWrite() async throws {
  196. let ref = storage.reference(withPath: "ios/public/helloworld")
  197. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  198. let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
  199. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  200. _ = try await ref.putDataAsync(data)
  201. let url = try await ref.writeAsync(toFile: fileURL)
  202. XCTAssertEqual(url.lastPathComponent, "hello.txt")
  203. }
  204. func testSimpleGetFile() throws {
  205. let expectation = self.expectation(description: #function)
  206. let ref = storage.reference(withPath: "ios/public/helloworld")
  207. let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
  208. let fileURL = tmpDirURL.appendingPathComponent("hello.txt")
  209. let data = try XCTUnwrap("Hello Swift World".data(using: .utf8), "Data construction failed")
  210. Task {
  211. _ = try await ref.putDataAsync(data)
  212. let task = ref.write(toFile: fileURL)
  213. // TODO: Update to use Swift Tasks
  214. task.observe(StorageTaskStatus.success) { snapshot in
  215. do {
  216. let stringData = try String(contentsOf: fileURL, encoding: .utf8)
  217. XCTAssertEqual(stringData, "Hello Swift World")
  218. XCTAssertEqual(snapshot.description, "<State: Success>")
  219. } catch {
  220. XCTFail("Error processing success snapshot")
  221. }
  222. expectation.fulfill()
  223. }
  224. task.observe(StorageTaskStatus.progress) { snapshot in
  225. XCTAssertNil(snapshot.error, "Error should be nil")
  226. guard let progress = snapshot.progress else {
  227. XCTFail("Missing progress")
  228. return
  229. }
  230. print("\(progress.completedUnitCount) of \(progress.totalUnitCount)")
  231. }
  232. task.observe(StorageTaskStatus.failure) { snapshot in
  233. XCTAssertNil(snapshot.error, "Error should be nil")
  234. }
  235. }
  236. waitForExpectations()
  237. }
  238. private func assertMetadata(actualMetadata: StorageMetadata,
  239. expectedContentType: String,
  240. expectedCustomMetadata: [String: String]) {
  241. XCTAssertEqual(actualMetadata.cacheControl, "cache-control")
  242. XCTAssertEqual(actualMetadata.contentDisposition, "content-disposition")
  243. XCTAssertEqual(actualMetadata.contentEncoding, "gzip")
  244. XCTAssertEqual(actualMetadata.contentLanguage, "de")
  245. XCTAssertEqual(actualMetadata.contentType, expectedContentType)
  246. XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
  247. for (key, value) in expectedCustomMetadata {
  248. XCTAssertEqual(actualMetadata.customMetadata![key], value)
  249. }
  250. }
  251. private func assertMetadataNil(actualMetadata: StorageMetadata) {
  252. XCTAssertNil(actualMetadata.cacheControl)
  253. XCTAssertNil(actualMetadata.contentDisposition)
  254. XCTAssertEqual(actualMetadata.contentEncoding, "identity")
  255. XCTAssertNil(actualMetadata.contentLanguage)
  256. XCTAssertNil(actualMetadata.contentType)
  257. XCTAssertEqual(actualMetadata.md5Hash?.count, 24)
  258. XCTAssertNil(actualMetadata.customMetadata)
  259. }
  260. func testUpdateMetadata2() async throws {
  261. let ref = storage.reference(withPath: "ios/public/1mb2")
  262. let metadata = StorageMetadata()
  263. metadata.cacheControl = "cache-control"
  264. metadata.contentDisposition = "content-disposition"
  265. metadata.contentEncoding = "gzip"
  266. metadata.contentLanguage = "de"
  267. metadata.contentType = "content-type-a"
  268. metadata.customMetadata = ["a": "b"]
  269. let updatedMetadata = try await ref.updateMetadata(metadata)
  270. assertMetadata(actualMetadata: updatedMetadata,
  271. expectedContentType: "content-type-a",
  272. expectedCustomMetadata: ["a": "b"])
  273. let metadata2 = updatedMetadata
  274. metadata2.contentType = "content-type-b"
  275. metadata2.customMetadata = ["a": "b", "c": "d"]
  276. let metadata3 = try await ref.updateMetadata(metadata2)
  277. assertMetadata(actualMetadata: metadata3,
  278. expectedContentType: "content-type-b",
  279. expectedCustomMetadata: ["a": "b", "c": "d"])
  280. metadata.cacheControl = nil
  281. metadata.contentDisposition = nil
  282. metadata.contentEncoding = nil
  283. metadata.contentLanguage = nil
  284. metadata.contentType = nil
  285. metadata.customMetadata = nil
  286. let metadata4 = try await ref.updateMetadata(metadata)
  287. XCTAssertNotNil(metadata4)
  288. }
  289. func testPagedListFiles() async throws {
  290. let ref = storage.reference(withPath: "ios/public/list")
  291. let listResult = try await ref.list(maxResults: 2)
  292. XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
  293. XCTAssertEqual(listResult.prefixes, [])
  294. let pageToken = try XCTUnwrap(listResult.pageToken)
  295. let listResult2 = try await ref.list(maxResults: 2, pageToken: pageToken)
  296. XCTAssertEqual(listResult2.items, [])
  297. XCTAssertEqual(listResult2.prefixes, [ref.child("prefix")])
  298. XCTAssertNil(listResult2.pageToken, "pageToken should be nil")
  299. }
  300. func testListAllFiles() async throws {
  301. let ref = storage.reference(withPath: "ios/public/list")
  302. let listResult = try await ref.listAll()
  303. XCTAssertEqual(listResult.items, [ref.child("a"), ref.child("b")])
  304. XCTAssertEqual(listResult.prefixes, [ref.child("prefix")])
  305. XCTAssertNil(listResult.pageToken, "pageToken should be nil")
  306. }
  307. private func waitForExpectations() {
  308. let kTestTimeout = 60.0
  309. waitForExpectations(timeout: kTestTimeout,
  310. handler: { (error) -> Void in
  311. if let error = error {
  312. print(error)
  313. }
  314. })
  315. }
  316. }
  317. #endif