Files
firebase-ios-sdk/FirebaseStorage/Tests/Unit/StorageTests.swift

322 lines
13 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
import FirebaseCore
@testable import FirebaseStorage
import FirebaseAppCheckInterop
import FirebaseAuthInterop
import SharedTestUtilities
import XCTest
class StorageTests: XCTestCase {
override class func setUp() {
let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
gcmSenderID: "00000000000000000-00000000000-000000000")
options.projectID = "myProjectID"
FirebaseApp.configure(name: "test-StorageTests", options: options)
}
func testBucketNotEnforced() throws {
let app = try getApp(bucket: "")
let storage = Storage.storage(app: app)
_ = storage.reference(forURL: "gs://benwu-test1.storage.firebase.com/child")
_ = storage.reference(forURL: "gs://benwu-test2.storage.firebase.com/child")
}
func testBucketEnforced() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app, url: "gs://benwu-test1.storage.firebase.com")
let url = try XCTUnwrap(URL(string: "gs://benwu-test2.storage.firebase.com/child"))
XCTAssertThrowsError(try storage.reference(for: url), "This was supposed to fail.") { error in
XCTAssertEqual(
"\(error)",
"bucketMismatch(message: \"Provided bucket: `benwu-test2.storage.firebase.com` does not match the " +
"Storage bucket of the current instance: `benwu-test1.storage.firebase.com`\")"
)
}
}
func testInitWithCustomURL() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app, url: "gs://foo-bar.appspot.com")
XCTAssertEqual("gs://foo-bar.appspot.com/", storage.reference().description)
let storage2 = Storage.storage(app: app, url: "gs://foo-bar.appspot.com/")
XCTAssertEqual("gs://foo-bar.appspot.com/", storage2.reference().description)
}
func SKIPtestInitWithWrongScheme() throws {
let app = try getApp(bucket: "bucket")
XCTAssertThrowsObjCException {
_ = Storage.storage(app: app, url: "http://foo-bar.appspot.com")
}
}
func SKIPtestInitWithNoScheme() throws {
let app = try getApp(bucket: "bucket")
XCTAssertThrowsObjCException {
_ = Storage.storage(app: app, url: "foo-bar.appspot.com")
}
}
func testInitWithoutURL() throws {
let app = try getApp(bucket: "bucket")
XCTAssertNoThrowObjCException {
_ = Storage.storage(app: app)
}
}
func SKIPtestInitWithPath() throws {
let app = try getApp(bucket: "bucket")
XCTAssertThrowsObjCException {
_ = Storage.storage(app: app, url: "gs://foo-bar.appspot.com/child")
}
}
func testInitWithDefaultAndCustomURL() throws {
let app = try getApp(bucket: "bucket")
let defaultInstance = Storage.storage(app: app)
let customInstance = Storage.storage(app: app, url: "gs://foo-bar.appspot.com")
XCTAssertEqual("gs://foo-bar.appspot.com/", customInstance.reference().description)
XCTAssertEqual("gs://bucket/", defaultInstance.reference().description)
}
func testStorageDefaultApp() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app)
XCTAssertEqual(storage.app.name, app.name)
}
func SKIPtestStorageNoBucketInConfig() throws {
let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
gcmSenderID: "00000000000000000-00000000000-000000000")
options.projectID = "myProjectID"
let name = "StorageTestsNil"
let app = FirebaseApp(instanceWithName: name, options: options)
XCTAssertThrowsObjCException {
_ = Storage.storage(app: app)
}
}
func testStorageEmptyBucketInConfig() throws {
let app = try getApp(bucket: "")
let storage = Storage.storage(app: app)
let ref = storage.reference(forURL: "gs://bucket/path/to/object")
XCTAssertEqual(ref.bucket, "bucket")
}
func testStorageWrongBucketInConfig() throws {
let app = try getApp(bucket: "notMyBucket")
let storage = Storage.storage(app: app)
let url = try XCTUnwrap(URL(string: "gs://bucket/path/to/object"))
XCTAssertThrowsError(try storage.reference(for: url), "This was supposed to fail.") { error in
XCTAssertEqual(
"\(error)",
"bucketMismatch(message: \"Provided bucket: `bucket` does not match the " +
"Storage bucket of the current instance: `notMyBucket`\")"
)
}
}
func testUseEmulator() throws {
let app = try getApp(bucket: "bucket-for-testUseEmulator")
let storage = Storage.storage(app: app)
storage.useEmulator(withHost: "localhost", port: 8080)
XCTAssertNoThrow(storage.reference())
}
func SKIPtestUseEmulatorValidatesHost() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app, url: "gs://foo-bar.appspot.com")
XCTAssertThrowsObjCException {
storage.useEmulator(withHost: "", port: 8080)
}
}
func SKIPtestUseEmulatorValidatesPort() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app, url: "gs://foo-bar.appspot.com")
XCTAssertThrowsObjCException {
storage.useEmulator(withHost: "localhost", port: -1)
}
}
func SKIPtestUseEmulatorCannotBeCalledAfterObtainingReference() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app, url: "gs://benwu-test1.storage.firebase.com")
_ = storage.reference()
XCTAssertThrowsObjCException {
storage.useEmulator(withHost: "localhost", port: 8080)
}
}
func testRefDefaultApp() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage(app: app, bucket: "bucket")
let convenienceRef = storage.reference(forURL: "gs://bucket/path/to/object")
let path = StoragePath(with: "bucket", object: "path/to/object")
let builtRef = StorageReference(storage: storage, path: path)
XCTAssertEqual(convenienceRef.description, builtRef.description)
XCTAssertEqual(convenienceRef.storage.app, builtRef.storage.app)
}
func testRefCustomApp() throws {
let secondApp = try getApp(bucket: "bucket2")
let storage2 = Storage.storage(app: secondApp)
let convenienceRef = storage2.reference(forURL: "gs://bucket2/path/to/object")
let path = StoragePath(with: "bucket2", object: "path/to/object")
let builtRef = StorageReference(storage: storage2, path: path)
XCTAssertEqual(convenienceRef.description, builtRef.description)
XCTAssertEqual(convenienceRef.storage.app, builtRef.storage.app)
}
func testRootRefDefaultApp() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app)
let convenienceRef = storage.reference()
let path = StoragePath(with: "bucket")
let builtRef = StorageReference(storage: storage, path: path)
XCTAssertEqual(convenienceRef.description, builtRef.description)
XCTAssertEqual(convenienceRef.storage.app, builtRef.storage.app)
}
func testRefWithPathDefaultApp() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app)
let convenienceRef = storage.reference(forURL: "gs://bucket/path/to/object")
let path = StoragePath(with: "bucket", object: "path/to/object")
let builtRef = StorageReference(storage: storage, path: path)
XCTAssertEqual(convenienceRef.description, builtRef.description)
XCTAssertEqual(convenienceRef.storage.app, builtRef.storage.app)
}
func testEqual() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app)
let copy = try XCTUnwrap(storage.copy() as? Storage)
XCTAssertEqual(storage.app.name, copy.app.name)
XCTAssertEqual(storage.hash, copy.hash)
}
func testNotEqual() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app)
let secondApp = try getApp(bucket: "bucket2")
let storage2 = Storage.storage(app: secondApp)
XCTAssertNotEqual(storage, storage2)
XCTAssertNotEqual(storage.hash, storage2.hash)
}
func testHash() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app)
let copy = try XCTUnwrap(storage.copy() as? Storage)
XCTAssertEqual(storage.app.name, copy.app.name)
}
func testHashIsIndependentOfCallbackQueue() throws {
let app = try getApp(bucket: "bucket")
// Use an uncached instance so that changing its callback queue doesn't affect other tests.
let storage = Storage(app: app, bucket: "bucket")
let ref = storage.reference(withPath: "path/to/object")
let storageHash = storage.hash
let refHash = ref.hash
let copy = try XCTUnwrap(storage.copy() as? Storage)
copy.callbackQueue = DispatchQueue(label: "com.google.firebase.storage.tests.callbackQueue1")
let copyRef = copy.reference(withPath: "path/to/object")
XCTAssertEqual(storage, copy)
XCTAssertEqual(storage.hash, copy.hash)
XCTAssertEqual(ref, copyRef)
XCTAssertEqual(ref.hash, copyRef.hash)
storage.callbackQueue = DispatchQueue(label: "com.google.firebase.storage.tests.callbackQueue2")
XCTAssertEqual(storage.hash, storageHash)
XCTAssertEqual(ref.hash, refHash)
}
func testTranslateRetryTime() {
// The 1st retry attempt runs after 1 second.
// The 2nd retry attempt is delayed by 2 seconds (3s total)
// The 3rd retry attempt is delayed by 4 seconds (7s total)
// The 4th retry attempt is delayed by 8 seconds (15s total)
// The 5th retry attempt is delayed by 16 seconds (31s total)
// The 6th retry attempt is delayed by 32 seconds (63s total)
// Thus, we should exit just between the 5th and 6th retry attempt and cut off before 32s.
XCTAssertEqual(1.0, Storage.computeRetryInterval(fromRetryTime: 1.0))
XCTAssertEqual(2.0, Storage.computeRetryInterval(fromRetryTime: 2.0))
XCTAssertEqual(4.0, Storage.computeRetryInterval(fromRetryTime: 4.0))
XCTAssertEqual(8.0, Storage.computeRetryInterval(fromRetryTime: 10.0))
XCTAssertEqual(16.0, Storage.computeRetryInterval(fromRetryTime: 20.0))
XCTAssertEqual(16.0, Storage.computeRetryInterval(fromRetryTime: 30.0))
XCTAssertEqual(32.0, Storage.computeRetryInterval(fromRetryTime: 40.0))
XCTAssertEqual(32.0, Storage.computeRetryInterval(fromRetryTime: 50.0))
XCTAssertEqual(32.0, Storage.computeRetryInterval(fromRetryTime: 60.0))
}
func testRetryTimeChange() throws {
let app = try getApp(bucket: "")
let storage = Storage.storage(app: app)
XCTAssertEqual(storage.maxOperationRetryInterval, 64)
storage.maxOperationRetryTime = 11
XCTAssertEqual(storage.maxOperationRetryTime, 11)
XCTAssertEqual(storage.maxOperationRetryInterval, 8)
}
func testPOSIXErrorFormatting() throws {
let app = try getApp(bucket: "bucket")
let storage = Storage.storage(app: app)
let ref = storage.reference(withPath: "ios/public/testPOSIX40")
let posixError = NSError(domain: NSPOSIXErrorDomain, code: 40, userInfo: nil)
let storageError = StorageErrorCode.error(withServerError: posixError, ref: ref)
XCTAssertEqual(storageError.domain, StorageErrorDomain)
XCTAssertEqual(storageError.code, StorageErrorCode.unknown.rawValue)
XCTAssertEqual(
storageError.userInfo[NSLocalizedDescriptionKey] as? String,
"POSIX errno 40 (The operation couldn’t be completed. Message too long)"
)
}
// MARK: Private Helpers
// Cache the app associated with each Storage bucket
private static var appDictionary: [String: FirebaseApp] = [:]
private func getApp(bucket: String) throws -> FirebaseApp {
let savedApp = StorageTests.appDictionary[bucket]
guard savedApp == nil else {
return try XCTUnwrap(savedApp)
}
let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
gcmSenderID: "00000000000000000-00000000000-000000000")
options.projectID = "myProjectID"
options.storageBucket = bucket
let name = "StorageTests\(bucket)"
let app = FirebaseApp(instanceWithName: name, options: options)
StorageTests.appDictionary[bucket] = app
return app
}
private func XCTAssertThrowsObjCException(_ closure: @escaping () -> Void) {
XCTAssertThrowsError(try ExceptionCatcher.catchException(closure))
}
private func XCTAssertNoThrowObjCException(_ closure: @escaping () -> Void) {
XCTAssertNoThrow(try ExceptionCatcher.catchException(closure))
}
}