Przeglądaj źródła

[MLModelDownloader] Disable keychain-dependent tests on macCatalyst and macOS (#10148)

Nick Cooke 3 lat temu
rodzic
commit
f8ee68bfc7

+ 507 - 498
FirebaseMLModelDownloader/Tests/Integration/ModelDownloaderIntegrationTests.swift

@@ -12,568 +12,577 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import XCTest
-@testable import FirebaseCore
-@testable import FirebaseInstallations
-@testable import FirebaseMLModelDownloader
-
-extension UserDefaults {
-  /// Returns a new cleared instance of user defaults.
-  static func createTestInstance(testName: String) -> UserDefaults {
-    let suiteName = "com.google.firebase.ml.test.\(testName)"
-    let defaults = UserDefaults(suiteName: suiteName)!
-    defaults.removePersistentDomain(forName: suiteName)
-    return defaults
-  }
-
-  /// Returns the existing user defaults instance.
-  static func getTestInstance(testName: String) -> UserDefaults {
-    let suiteName = "com.google.firebase.ml.test.\(testName)"
-    return UserDefaults(suiteName: suiteName)!
-  }
-}
-
-final class ModelDownloaderIntegrationTests: XCTestCase {
-  override class func setUp() {
-    super.setUp()
-    // TODO: Use FirebaseApp internal for test app.
-    let bundle = Bundle(for: self)
-    if let plistPath = bundle.path(forResource: "GoogleService-Info", ofType: "plist"),
-       let options = FirebaseOptions(contentsOfFile: plistPath) {
-      FirebaseApp.configure(options: options)
-    } else {
-      XCTFail("Could not locate GoogleService-Info.plist.")
+// Skip keychain tests on Catalyst and macOS. Tests are skipped because they
+// involve interactions with the keychain that require a provisioning profile.
+// This is because MLModelDownloader depends on Installations.
+// See go/firebase-macos-keychain-popups for more details.
+#if !targetEnvironment(macCatalyst) && !os(macOS)
+
+  import XCTest
+  @testable import FirebaseCore
+  @testable import FirebaseInstallations
+  @testable import FirebaseMLModelDownloader
+
+  extension UserDefaults {
+    /// Returns a new cleared instance of user defaults.
+    static func createTestInstance(testName: String) -> UserDefaults {
+      let suiteName = "com.google.firebase.ml.test.\(testName)"
+      let defaults = UserDefaults(suiteName: suiteName)!
+      defaults.removePersistentDomain(forName: suiteName)
+      return defaults
     }
-    FirebaseConfiguration.shared.setLoggerLevel(.debug)
-  }
 
-  override func setUp() {
-    do {
-      try ModelFileManager.emptyModelsDirectory()
-    } catch {
-      XCTFail("Could not empty models directory.")
+    /// Returns the existing user defaults instance.
+    static func getTestInstance(testName: String) -> UserDefaults {
+      let suiteName = "com.google.firebase.ml.test.\(testName)"
+      return UserDefaults(suiteName: suiteName)!
     }
   }
 
-  /// Test to download model info - makes an actual network call.
-  func testDownloadModelInfo() {
-    guard let testApp = FirebaseApp.app() else {
-      XCTFail("Default app was not configured.")
-      return
-    }
-    testApp.isDataCollectionDefaultEnabled = false
-
-    let testName = String(#function.dropLast(2))
-    let testModelName = "pose-detection"
-
-    let modelInfoRetriever = ModelInfoRetriever(
-      modelName: testModelName,
-      projectID: testApp.options.projectID!,
-      apiKey: testApp.options.apiKey!,
-      appName: testApp.name, installations: Installations.installations(app: testApp)
-    )
-
-    let modelInfoDownloadExpectation = expectation(description: "Wait for model info to download.")
-    modelInfoRetriever.downloadModelInfo(completion: { result in
-      switch result {
-      case let .success(modelInfoResult):
-        switch modelInfoResult {
-        case let .modelInfo(modelInfo):
-          XCTAssertNotNil(modelInfo.urlExpiryTime)
-          XCTAssertGreaterThan(modelInfo.downloadURL.absoluteString.count, 0)
-          XCTAssertGreaterThan(modelInfo.modelHash.count, 0)
-          XCTAssertGreaterThan(modelInfo.size, 0)
-          let localModelInfo = LocalModelInfo(from: modelInfo)
-          localModelInfo.writeToDefaults(
-            .createTestInstance(testName: testName),
-            appName: testApp.name
-          )
-        case .notModified:
-          XCTFail("Failed to retrieve model info.")
-        }
-      case let .failure(error):
-        XCTAssertNotNil(error)
-        XCTFail("Failed to retrieve model info - \(error)")
+  final class ModelDownloaderIntegrationTests: XCTestCase {
+    override class func setUp() {
+      super.setUp()
+      // TODO: Use FirebaseApp internal for test app.
+      let bundle = Bundle(for: self)
+      if let plistPath = bundle.path(forResource: "GoogleService-Info", ofType: "plist"),
+         let options = FirebaseOptions(contentsOfFile: plistPath) {
+        FirebaseApp.configure(options: options)
+      } else {
+        XCTFail("Could not locate GoogleService-Info.plist.")
       }
-      modelInfoDownloadExpectation.fulfill()
-    })
-
-    wait(for: [modelInfoDownloadExpectation], timeout: 5)
-
-    if let localInfo = LocalModelInfo(
-      fromDefaults: .getTestInstance(testName: testName),
-      name: testModelName,
-      appName: testApp.name
-    ) {
-      XCTAssertNotNil(localInfo)
-      testRetrieveModelInfo(localInfo: localInfo)
-    } else {
-      XCTFail("Could not save model info locally.")
+      FirebaseConfiguration.shared.setLoggerLevel(.debug)
     }
-  }
 
-  func testRetrieveModelInfo(localInfo: LocalModelInfo) {
-    guard let testApp = FirebaseApp.app() else {
-      XCTFail("Default app was not configured.")
-      return
-    }
-    let testModelName = "pose-detection"
-
-    let modelInfoRetriever = ModelInfoRetriever(
-      modelName: testModelName,
-      projectID: testApp.options.projectID!,
-      apiKey: testApp.options.apiKey!,
-      appName: testApp.name, installations: Installations.installations(app: testApp),
-      localModelInfo: localInfo
-    )
-
-    let modelInfoRetrieveExpectation =
-      expectation(description: "Wait for model info to be retrieved.")
-    modelInfoRetriever.downloadModelInfo(completion: { result in
-      switch result {
-      case let .success(modelInfoResult):
-        switch modelInfoResult {
-        case .modelInfo:
-          XCTFail("Local model info is already the latest and should not be set again.")
-        case .notModified: break
-        }
-      case let .failure(error):
-        XCTAssertNotNil(error)
-        XCTFail("Failed to retrieve model info - \(error)")
+    override func setUp() {
+      do {
+        try ModelFileManager.emptyModelsDirectory()
+      } catch {
+        XCTFail("Could not empty models directory.")
       }
-      modelInfoRetrieveExpectation.fulfill()
-    })
-
-    wait(for: [modelInfoRetrieveExpectation], timeout: 5)
-  }
-
-  /// Test to download model file - makes an actual network call.
-  func testModelDownload() throws {
-    guard let testApp = FirebaseApp.app() else {
-      XCTFail("Default app was not configured.")
-      return
-    }
-    testApp.isDataCollectionDefaultEnabled = false
-
-    let testName = String(#function.dropLast(2))
-    let testModelName = "\(testName)-test-model"
-    let urlString =
-      "https://tfhub.dev/tensorflow/lite-model/ssd_mobilenet_v1/1/metadata/1?lite-format=tflite"
-    let url = URL(string: urlString)!
-
-    let remoteModelInfo = RemoteModelInfo(
-      name: testModelName,
-      downloadURL: url,
-      modelHash: "mock-valid-hash",
-      size: 10,
-      urlExpiryTime: Date()
-    )
-
-    let conditions = ModelDownloadConditions()
-    let downloadExpectation = expectation(description: "Wait for model to download.")
-    let downloader = ModelFileDownloader(conditions: conditions)
-    let taskProgressHandler: ModelDownloadTask.ProgressHandler = { progress in
-      XCTAssertLessThanOrEqual(progress, 1)
-      XCTAssertGreaterThanOrEqual(progress, 0)
     }
-    let taskCompletion: ModelDownloadTask.Completion = { result in
-      switch result {
-      case let .success(model):
-        let modelURL = URL(fileURLWithPath: model.path)
-        XCTAssertTrue(ModelFileManager.isFileReachable(at: modelURL))
-        // Remove downloaded model file.
-        do {
-          try ModelFileManager.removeFile(at: modelURL)
-        } catch {
-          XCTFail("Model removal failed - \(error)")
+
+    /// Test to download model info - makes an actual network call.
+    func testDownloadModelInfo() {
+      guard let testApp = FirebaseApp.app() else {
+        XCTFail("Default app was not configured.")
+        return
+      }
+      testApp.isDataCollectionDefaultEnabled = false
+
+      let testName = String(#function.dropLast(2))
+      let testModelName = "pose-detection"
+
+      let modelInfoRetriever = ModelInfoRetriever(
+        modelName: testModelName,
+        projectID: testApp.options.projectID!,
+        apiKey: testApp.options.apiKey!,
+        appName: testApp.name, installations: Installations.installations(app: testApp)
+      )
+
+      let modelInfoDownloadExpectation =
+        expectation(description: "Wait for model info to download.")
+      modelInfoRetriever.downloadModelInfo(completion: { result in
+        switch result {
+        case let .success(modelInfoResult):
+          switch modelInfoResult {
+          case let .modelInfo(modelInfo):
+            XCTAssertNotNil(modelInfo.urlExpiryTime)
+            XCTAssertGreaterThan(modelInfo.downloadURL.absoluteString.count, 0)
+            XCTAssertGreaterThan(modelInfo.modelHash.count, 0)
+            XCTAssertGreaterThan(modelInfo.size, 0)
+            let localModelInfo = LocalModelInfo(from: modelInfo)
+            localModelInfo.writeToDefaults(
+              .createTestInstance(testName: testName),
+              appName: testApp.name
+            )
+          case .notModified:
+            XCTFail("Failed to retrieve model info.")
+          }
+        case let .failure(error):
+          XCTAssertNotNil(error)
+          XCTFail("Failed to retrieve model info - \(error)")
         }
-      case let .failure(error):
-        XCTFail("Error: \(error)")
+        modelInfoDownloadExpectation.fulfill()
+      })
+
+      wait(for: [modelInfoDownloadExpectation], timeout: 5)
+
+      if let localInfo = LocalModelInfo(
+        fromDefaults: .getTestInstance(testName: testName),
+        name: testModelName,
+        appName: testApp.name
+      ) {
+        XCTAssertNotNil(localInfo)
+        testRetrieveModelInfo(localInfo: localInfo)
+      } else {
+        XCTFail("Could not save model info locally.")
       }
-      downloadExpectation.fulfill()
     }
-    let modelDownloadManager = ModelDownloadTask(
-      remoteModelInfo: remoteModelInfo,
-      appName: testApp.name,
-      defaults: .createTestInstance(testName: testName),
-      downloader: downloader,
-      progressHandler: taskProgressHandler,
-      completion: taskCompletion
-    )
-
-    modelDownloadManager.resume()
-    wait(for: [downloadExpectation], timeout: 5)
-    XCTAssertEqual(modelDownloadManager.downloadStatus, .complete)
-  }
 
-  func testGetModel() {
-    guard let testApp = FirebaseApp.app() else {
-      XCTFail("Default app was not configured.")
-      return
+    func testRetrieveModelInfo(localInfo: LocalModelInfo) {
+      guard let testApp = FirebaseApp.app() else {
+        XCTFail("Default app was not configured.")
+        return
+      }
+      let testModelName = "pose-detection"
+
+      let modelInfoRetriever = ModelInfoRetriever(
+        modelName: testModelName,
+        projectID: testApp.options.projectID!,
+        apiKey: testApp.options.apiKey!,
+        appName: testApp.name, installations: Installations.installations(app: testApp),
+        localModelInfo: localInfo
+      )
+
+      let modelInfoRetrieveExpectation =
+        expectation(description: "Wait for model info to be retrieved.")
+      modelInfoRetriever.downloadModelInfo(completion: { result in
+        switch result {
+        case let .success(modelInfoResult):
+          switch modelInfoResult {
+          case .modelInfo:
+            XCTFail("Local model info is already the latest and should not be set again.")
+          case .notModified: break
+          }
+        case let .failure(error):
+          XCTAssertNotNil(error)
+          XCTFail("Failed to retrieve model info - \(error)")
+        }
+        modelInfoRetrieveExpectation.fulfill()
+      })
+
+      wait(for: [modelInfoRetrieveExpectation], timeout: 5)
     }
-    testApp.isDataCollectionDefaultEnabled = false
-
-    let testName = String(#function.dropLast(2))
-    let testModelName = "image-classification"
-
-    let conditions = ModelDownloadConditions()
-    let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
-      .createTestInstance(testName: testName),
-      app: testApp
-    )
-
-    /// Test download type - latest model.
-    var downloadType: ModelDownloadType = .latestModel
-    let latestModelExpectation = expectation(description: "Get latest model.")
-
-    modelDownloader.getModel(
-      name: testModelName,
-      downloadType: downloadType,
-      conditions: conditions,
-      progressHandler: { progress in
-        XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
 
+    /// Test to download model file - makes an actual network call.
+    func testModelDownload() throws {
+      guard let testApp = FirebaseApp.app() else {
+        XCTFail("Default app was not configured.")
+        return
+      }
+      testApp.isDataCollectionDefaultEnabled = false
+
+      let testName = String(#function.dropLast(2))
+      let testModelName = "\(testName)-test-model"
+      let urlString =
+        "https://tfhub.dev/tensorflow/lite-model/ssd_mobilenet_v1/1/metadata/1?lite-format=tflite"
+      let url = URL(string: urlString)!
+
+      let remoteModelInfo = RemoteModelInfo(
+        name: testModelName,
+        downloadURL: url,
+        modelHash: "mock-valid-hash",
+        size: 10,
+        urlExpiryTime: Date()
+      )
+
+      let conditions = ModelDownloadConditions()
+      let downloadExpectation = expectation(description: "Wait for model to download.")
+      let downloader = ModelFileDownloader(conditions: conditions)
+      let taskProgressHandler: ModelDownloadTask.ProgressHandler = { progress in
         XCTAssertLessThanOrEqual(progress, 1)
         XCTAssertGreaterThanOrEqual(progress, 0)
       }
-    ) { result in
-      XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
-
-      switch result {
-      case let .success(model):
-        XCTAssertNotNil(model.path)
-        let modelURL = URL(fileURLWithPath: model.path)
-        XCTAssertTrue(ModelFileManager.isFileReachable(at: modelURL))
-      case let .failure(error):
-        XCTFail("Failed to download model - \(error)")
+      let taskCompletion: ModelDownloadTask.Completion = { result in
+        switch result {
+        case let .success(model):
+          let modelURL = URL(fileURLWithPath: model.path)
+          XCTAssertTrue(ModelFileManager.isFileReachable(at: modelURL))
+          // Remove downloaded model file.
+          do {
+            try ModelFileManager.removeFile(at: modelURL)
+          } catch {
+            XCTFail("Model removal failed - \(error)")
+          }
+        case let .failure(error):
+          XCTFail("Error: \(error)")
+        }
+        downloadExpectation.fulfill()
       }
-      latestModelExpectation.fulfill()
+      let modelDownloadManager = ModelDownloadTask(
+        remoteModelInfo: remoteModelInfo,
+        appName: testApp.name,
+        defaults: .createTestInstance(testName: testName),
+        downloader: downloader,
+        progressHandler: taskProgressHandler,
+        completion: taskCompletion
+      )
+
+      modelDownloadManager.resume()
+      wait(for: [downloadExpectation], timeout: 5)
+      XCTAssertEqual(modelDownloadManager.downloadStatus, .complete)
     }
 
-    wait(for: [latestModelExpectation], timeout: 5)
-
-    /// Test download type - local model update in background.
-    downloadType = .localModelUpdateInBackground
-    let backgroundModelExpectation =
-      expectation(description: "Get local model and update in background.")
-
-    modelDownloader.getModel(
-      name: testModelName,
-      downloadType: downloadType,
-      conditions: conditions,
-      progressHandler: { progress in
-        XCTFail("Model is already available on device.")
+    func testGetModel() {
+      guard let testApp = FirebaseApp.app() else {
+        XCTFail("Default app was not configured.")
+        return
       }
-    ) { result in
-      switch result {
-      case let .success(model):
-        XCTAssertNotNil(model.path)
-        let modelURL = URL(fileURLWithPath: model.path)
-        XCTAssertTrue(ModelFileManager.isFileReachable(at: modelURL))
-      case let .failure(error):
-        XCTFail("Failed to download model - \(error)")
+      testApp.isDataCollectionDefaultEnabled = false
+
+      let testName = String(#function.dropLast(2))
+      let testModelName = "image-classification"
+
+      let conditions = ModelDownloadConditions()
+      let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
+        .createTestInstance(testName: testName),
+        app: testApp
+      )
+
+      /// Test download type - latest model.
+      var downloadType: ModelDownloadType = .latestModel
+      let latestModelExpectation = expectation(description: "Get latest model.")
+
+      modelDownloader.getModel(
+        name: testModelName,
+        downloadType: downloadType,
+        conditions: conditions,
+        progressHandler: { progress in
+          XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
+
+          XCTAssertLessThanOrEqual(progress, 1)
+          XCTAssertGreaterThanOrEqual(progress, 0)
+        }
+      ) { result in
+        XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
+
+        switch result {
+        case let .success(model):
+          XCTAssertNotNil(model.path)
+          let modelURL = URL(fileURLWithPath: model.path)
+          XCTAssertTrue(ModelFileManager.isFileReachable(at: modelURL))
+        case let .failure(error):
+          XCTFail("Failed to download model - \(error)")
+        }
+        latestModelExpectation.fulfill()
       }
-      backgroundModelExpectation.fulfill()
-    }
-    wait(for: [backgroundModelExpectation], timeout: 5)
-
-    /// Test download type - local model.
-    downloadType = .localModel
-    let localModelExpectation = expectation(description: "Get local model.")
-
-    modelDownloader.getModel(
-      name: testModelName,
-      downloadType: downloadType,
-      conditions: conditions,
-      progressHandler: { progress in
-        XCTFail("Model is already available on device.")
+
+      wait(for: [latestModelExpectation], timeout: 5)
+
+      /// Test download type - local model update in background.
+      downloadType = .localModelUpdateInBackground
+      let backgroundModelExpectation =
+        expectation(description: "Get local model and update in background.")
+
+      modelDownloader.getModel(
+        name: testModelName,
+        downloadType: downloadType,
+        conditions: conditions,
+        progressHandler: { progress in
+          XCTFail("Model is already available on device.")
+        }
+      ) { result in
+        switch result {
+        case let .success(model):
+          XCTAssertNotNil(model.path)
+          let modelURL = URL(fileURLWithPath: model.path)
+          XCTAssertTrue(ModelFileManager.isFileReachable(at: modelURL))
+        case let .failure(error):
+          XCTFail("Failed to download model - \(error)")
+        }
+        backgroundModelExpectation.fulfill()
       }
-    ) { result in
-      switch result {
-      case let .success(model):
-        XCTAssertNotNil(model.path)
-        let modelURL = URL(fileURLWithPath: model.path)
-        XCTAssertTrue(ModelFileManager.isFileReachable(at: modelURL))
-        // Remove downloaded model file.
-        do {
-          try ModelFileManager.removeFile(at: modelURL)
-        } catch {
-          XCTFail("Model removal failed - \(error)")
+      wait(for: [backgroundModelExpectation], timeout: 5)
+
+      /// Test download type - local model.
+      downloadType = .localModel
+      let localModelExpectation = expectation(description: "Get local model.")
+
+      modelDownloader.getModel(
+        name: testModelName,
+        downloadType: downloadType,
+        conditions: conditions,
+        progressHandler: { progress in
+          XCTFail("Model is already available on device.")
+        }
+      ) { result in
+        switch result {
+        case let .success(model):
+          XCTAssertNotNil(model.path)
+          let modelURL = URL(fileURLWithPath: model.path)
+          XCTAssertTrue(ModelFileManager.isFileReachable(at: modelURL))
+          // Remove downloaded model file.
+          do {
+            try ModelFileManager.removeFile(at: modelURL)
+          } catch {
+            XCTFail("Model removal failed - \(error)")
+          }
+        case let .failure(error):
+          XCTFail("Failed to download model - \(error)")
         }
-      case let .failure(error):
-        XCTFail("Failed to download model - \(error)")
+        localModelExpectation.fulfill()
       }
-      localModelExpectation.fulfill()
+      wait(for: [localModelExpectation], timeout: 5)
     }
-    wait(for: [localModelExpectation], timeout: 5)
-  }
 
-  func testGetModelWhenNameIsEmpty() {
-    guard let testApp = FirebaseApp.app() else {
-      XCTFail("Default app was not configured.")
-      return
-    }
-    testApp.isDataCollectionDefaultEnabled = false
-
-    let testName = String(#function.dropLast(2))
-    let emptyModelName = ""
-
-    let conditions = ModelDownloadConditions()
-    let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
-      .createTestInstance(testName: testName),
-      app: testApp
-    )
-
-    let completionExpectation = expectation(description: "getModel")
-    let progressExpectation = expectation(description: "progressHandler")
-    progressExpectation.isInverted = true
-
-    modelDownloader.getModel(
-      name: emptyModelName,
-      downloadType: .latestModel,
-      conditions: conditions,
-      progressHandler: { progress in
-        progressExpectation.fulfill()
+    func testGetModelWhenNameIsEmpty() {
+      guard let testApp = FirebaseApp.app() else {
+        XCTFail("Default app was not configured.")
+        return
       }
-    ) { result in
-      XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
+      testApp.isDataCollectionDefaultEnabled = false
+
+      let testName = String(#function.dropLast(2))
+      let emptyModelName = ""
+
+      let conditions = ModelDownloadConditions()
+      let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
+        .createTestInstance(testName: testName),
+        app: testApp
+      )
+
+      let completionExpectation = expectation(description: "getModel")
+      let progressExpectation = expectation(description: "progressHandler")
+      progressExpectation.isInverted = true
+
+      modelDownloader.getModel(
+        name: emptyModelName,
+        downloadType: .latestModel,
+        conditions: conditions,
+        progressHandler: { progress in
+          progressExpectation.fulfill()
+        }
+      ) { result in
+        XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
 
-      switch result {
-      case .failure(.emptyModelName):
-        // The expected error.
-        break
+        switch result {
+        case .failure(.emptyModelName):
+          // The expected error.
+          break
 
-      default:
-        XCTFail("Unexpected result: \(result)")
+        default:
+          XCTFail("Unexpected result: \(result)")
+        }
+        completionExpectation.fulfill()
       }
-      completionExpectation.fulfill()
-    }
 
-    wait(for: [completionExpectation, progressExpectation], timeout: 5)
-  }
-
-  /// Delete previously downloaded model.
-  func testDeleteModel() {
-    guard let testApp = FirebaseApp.app() else {
-      XCTFail("Default app was not configured.")
-      return
+      wait(for: [completionExpectation, progressExpectation], timeout: 5)
     }
-    testApp.isDataCollectionDefaultEnabled = false
-
-    let testName = String(#function.dropLast(2))
-    let testModelName = "pose-detection"
 
-    let conditions = ModelDownloadConditions()
-    let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
-      .createTestInstance(testName: testName),
-      app: testApp
-    )
-
-    let downloadType: ModelDownloadType = .latestModel
-    let latestModelExpectation = expectation(description: "Get latest model for deletion.")
-
-    modelDownloader.getModel(
-      name: testModelName,
-      downloadType: downloadType,
-      conditions: conditions,
-      progressHandler: { progress in
-        XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
-        XCTAssertLessThanOrEqual(progress, 1)
-        XCTAssertGreaterThanOrEqual(progress, 0)
+    /// Delete previously downloaded model.
+    func testDeleteModel() {
+      guard let testApp = FirebaseApp.app() else {
+        XCTFail("Default app was not configured.")
+        return
       }
-    ) { result in
-      XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
-
-      switch result {
-      case let .success(model):
-        XCTAssertNotNil(model.path)
-        let filePath = URL(fileURLWithPath: model.path)
-        XCTAssertTrue(ModelFileManager.isFileReachable(at: filePath))
-      case let .failure(error):
-        XCTFail("Failed to download model - \(error)")
+      testApp.isDataCollectionDefaultEnabled = false
+
+      let testName = String(#function.dropLast(2))
+      let testModelName = "pose-detection"
+
+      let conditions = ModelDownloadConditions()
+      let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
+        .createTestInstance(testName: testName),
+        app: testApp
+      )
+
+      let downloadType: ModelDownloadType = .latestModel
+      let latestModelExpectation = expectation(description: "Get latest model for deletion.")
+
+      modelDownloader.getModel(
+        name: testModelName,
+        downloadType: downloadType,
+        conditions: conditions,
+        progressHandler: { progress in
+          XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
+          XCTAssertLessThanOrEqual(progress, 1)
+          XCTAssertGreaterThanOrEqual(progress, 0)
+        }
+      ) { result in
+        XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
+
+        switch result {
+        case let .success(model):
+          XCTAssertNotNil(model.path)
+          let filePath = URL(fileURLWithPath: model.path)
+          XCTAssertTrue(ModelFileManager.isFileReachable(at: filePath))
+        case let .failure(error):
+          XCTFail("Failed to download model - \(error)")
+        }
+        latestModelExpectation.fulfill()
       }
-      latestModelExpectation.fulfill()
-    }
 
-    wait(for: [latestModelExpectation], timeout: 5)
+      wait(for: [latestModelExpectation], timeout: 5)
 
-    let deleteExpectation = expectation(description: "Wait for model deletion.")
-    modelDownloader.deleteDownloadedModel(name: testModelName) { result in
-      deleteExpectation.fulfill()
-      XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
+      let deleteExpectation = expectation(description: "Wait for model deletion.")
+      modelDownloader.deleteDownloadedModel(name: testModelName) { result in
+        deleteExpectation.fulfill()
+        XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
 
-      switch result {
-      case .success: break
-      case let .failure(error):
-        XCTFail("Failed to delete model - \(error)")
+        switch result {
+        case .success: break
+        case let .failure(error):
+          XCTFail("Failed to delete model - \(error)")
+        }
       }
-    }
-
-    wait(for: [deleteExpectation], timeout: 5)
-  }
 
-  /// Test listing models in model directory.
-  func testListModels() {
-    guard let testApp = FirebaseApp.app() else {
-      XCTFail("Default app was not configured.")
-      return
+      wait(for: [deleteExpectation], timeout: 5)
     }
-    testApp.isDataCollectionDefaultEnabled = false
-
-    let testName = String(#function.dropLast(2))
-    let testModelName = "pose-detection"
-
-    let conditions = ModelDownloadConditions()
-    let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
-      .createTestInstance(testName: testName),
-      app: testApp
-    )
 
-    let downloadType: ModelDownloadType = .latestModel
-    let latestModelExpectation = expectation(description: "Get latest model.")
-
-    modelDownloader.getModel(
-      name: testModelName,
-      downloadType: downloadType,
-      conditions: conditions,
-      progressHandler: { progress in
-        XCTAssertLessThanOrEqual(progress, 1)
-        XCTAssertGreaterThanOrEqual(progress, 0)
+    /// Test listing models in model directory.
+    func testListModels() {
+      guard let testApp = FirebaseApp.app() else {
+        XCTFail("Default app was not configured.")
+        return
       }
-    ) { result in
-      switch result {
-      case let .success(model):
-        XCTAssertNotNil(model.path)
-        let filePath = URL(fileURLWithPath: model.path)
-        XCTAssertTrue(ModelFileManager.isFileReachable(at: filePath))
-      case let .failure(error):
-        XCTFail("Failed to download model - \(error)")
+      testApp.isDataCollectionDefaultEnabled = false
+
+      let testName = String(#function.dropLast(2))
+      let testModelName = "pose-detection"
+
+      let conditions = ModelDownloadConditions()
+      let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
+        .createTestInstance(testName: testName),
+        app: testApp
+      )
+
+      let downloadType: ModelDownloadType = .latestModel
+      let latestModelExpectation = expectation(description: "Get latest model.")
+
+      modelDownloader.getModel(
+        name: testModelName,
+        downloadType: downloadType,
+        conditions: conditions,
+        progressHandler: { progress in
+          XCTAssertLessThanOrEqual(progress, 1)
+          XCTAssertGreaterThanOrEqual(progress, 0)
+        }
+      ) { result in
+        switch result {
+        case let .success(model):
+          XCTAssertNotNil(model.path)
+          let filePath = URL(fileURLWithPath: model.path)
+          XCTAssertTrue(ModelFileManager.isFileReachable(at: filePath))
+        case let .failure(error):
+          XCTFail("Failed to download model - \(error)")
+        }
+        latestModelExpectation.fulfill()
       }
-      latestModelExpectation.fulfill()
-    }
 
-    wait(for: [latestModelExpectation], timeout: 5)
+      wait(for: [latestModelExpectation], timeout: 5)
 
-    let listExpectation = expectation(description: "Wait for list models.")
-    modelDownloader.listDownloadedModels { result in
-      listExpectation.fulfill()
-      switch result {
-      case let .success(models):
-        XCTAssertGreaterThan(models.count, 0)
-      case let .failure(error):
-        XCTFail("Failed to list models - \(error)")
+      let listExpectation = expectation(description: "Wait for list models.")
+      modelDownloader.listDownloadedModels { result in
+        listExpectation.fulfill()
+        switch result {
+        case let .success(models):
+          XCTAssertGreaterThan(models.count, 0)
+        case let .failure(error):
+          XCTFail("Failed to list models - \(error)")
+        }
       }
-    }
 
-    wait(for: [listExpectation], timeout: 5)
-  }
-
-  /// Test logging telemetry event.
-  func testLogTelemetryEvent() {
-    guard let testApp = FirebaseApp.app() else {
-      XCTFail("Default app was not configured.")
-      return
+      wait(for: [listExpectation], timeout: 5)
     }
-    // Flip this to `true` to test logging.
-    testApp.isDataCollectionDefaultEnabled = false
-
-    let testModelName = "digit-classification"
-    let testName = String(#function.dropLast(2))
-
-    let conditions = ModelDownloadConditions()
-    let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
-      .createTestInstance(testName: testName),
-      app: testApp
-    )
-
-    let latestModelExpectation = expectation(description: "Test get model telemetry.")
-
-    modelDownloader.getModel(
-      name: testModelName,
-      downloadType: .latestModel,
-      conditions: conditions
-    ) { result in
-      switch result {
-      case .success: break
-      case let .failure(error):
-        XCTFail("Failed to download model - \(error)")
+
+    /// Test logging telemetry event.
+    func testLogTelemetryEvent() {
+      guard let testApp = FirebaseApp.app() else {
+        XCTFail("Default app was not configured.")
+        return
       }
-      latestModelExpectation.fulfill()
-    }
-    wait(for: [latestModelExpectation], timeout: 5)
-
-    let deleteModelExpectation = expectation(description: "Test delete model telemetry.")
-    modelDownloader.deleteDownloadedModel(name: testModelName) { result in
-      switch result {
-      case .success(()): break
-      case let .failure(error):
-        XCTFail("Failed to delete model - \(error)")
+      // Flip this to `true` to test logging.
+      testApp.isDataCollectionDefaultEnabled = false
+
+      let testModelName = "digit-classification"
+      let testName = String(#function.dropLast(2))
+
+      let conditions = ModelDownloadConditions()
+      let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
+        .createTestInstance(testName: testName),
+        app: testApp
+      )
+
+      let latestModelExpectation = expectation(description: "Test get model telemetry.")
+
+      modelDownloader.getModel(
+        name: testModelName,
+        downloadType: .latestModel,
+        conditions: conditions
+      ) { result in
+        switch result {
+        case .success: break
+        case let .failure(error):
+          XCTFail("Failed to download model - \(error)")
+        }
+        latestModelExpectation.fulfill()
+      }
+      wait(for: [latestModelExpectation], timeout: 5)
+
+      let deleteModelExpectation = expectation(description: "Test delete model telemetry.")
+      modelDownloader.deleteDownloadedModel(name: testModelName) { result in
+        switch result {
+        case .success(()): break
+        case let .failure(error):
+          XCTFail("Failed to delete model - \(error)")
+        }
+        deleteModelExpectation.fulfill()
       }
-      deleteModelExpectation.fulfill()
-    }
 
-    wait(for: [deleteModelExpectation], timeout: 5)
-
-    let notFoundModelName = "fakeModelName"
-    let notFoundModelExpectation =
-      expectation(description: "Test get model telemetry with unknown model.")
-
-    modelDownloader.getModel(
-      name: notFoundModelName,
-      downloadType: .latestModel,
-      conditions: conditions
-    ) { result in
-      switch result {
-      case .success: XCTFail("Unexpected model success.")
-      case let .failure(error):
-        XCTAssertEqual(error, .notFound)
+      wait(for: [deleteModelExpectation], timeout: 5)
+
+      let notFoundModelName = "fakeModelName"
+      let notFoundModelExpectation =
+        expectation(description: "Test get model telemetry with unknown model.")
+
+      modelDownloader.getModel(
+        name: notFoundModelName,
+        downloadType: .latestModel,
+        conditions: conditions
+      ) { result in
+        switch result {
+        case .success: XCTFail("Unexpected model success.")
+        case let .failure(error):
+          XCTAssertEqual(error, .notFound)
+        }
+        notFoundModelExpectation.fulfill()
       }
-      notFoundModelExpectation.fulfill()
+      wait(for: [notFoundModelExpectation], timeout: 5)
     }
-    wait(for: [notFoundModelExpectation], timeout: 5)
-  }
 
-  func testGetModelWithConditions() {
-    guard let testApp = FirebaseApp.app() else {
-      XCTFail("Default app was not configured.")
-      return
-    }
-    testApp.isDataCollectionDefaultEnabled = false
+    func testGetModelWithConditions() {
+      guard let testApp = FirebaseApp.app() else {
+        XCTFail("Default app was not configured.")
+        return
+      }
+      testApp.isDataCollectionDefaultEnabled = false
 
-    let testModelName = "pose-detection"
-    let testName = String(#function.dropLast(2))
+      let testModelName = "pose-detection"
+      let testName = String(#function.dropLast(2))
 
-    let conditions = ModelDownloadConditions(allowsCellularAccess: false)
+      let conditions = ModelDownloadConditions(allowsCellularAccess: false)
 
-    let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
-      .createTestInstance(testName: testName),
-      app: testApp
-    )
+      let modelDownloader = ModelDownloader.modelDownloaderWithDefaults(
+        .createTestInstance(testName: testName),
+        app: testApp
+      )
 
-    let latestModelExpectation = expectation(description: "Get latest model with conditions.")
+      let latestModelExpectation = expectation(description: "Get latest model with conditions.")
 
-    modelDownloader.getModel(
-      name: testModelName,
-      downloadType: .latestModel,
-      conditions: conditions,
-      progressHandler: { progress in
+      modelDownloader.getModel(
+        name: testModelName,
+        downloadType: .latestModel,
+        conditions: conditions,
+        progressHandler: { progress in
+          XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
+          XCTAssertLessThanOrEqual(progress, 1)
+          XCTAssertGreaterThanOrEqual(progress, 0)
+        }
+      ) { result in
         XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
-        XCTAssertLessThanOrEqual(progress, 1)
-        XCTAssertGreaterThanOrEqual(progress, 0)
-      }
-    ) { result in
-      XCTAssertTrue(Thread.isMainThread, "Completion must be called on the main thread.")
-
-      switch result {
-      case let .success(model):
-        XCTAssertNotNil(model.path)
-        let filePath = URL(fileURLWithPath: model.path)
-        XCTAssertTrue(ModelFileManager.isFileReachable(at: filePath))
-      case let .failure(error):
-        XCTFail("Failed to download model - \(error)")
+
+        switch result {
+        case let .success(model):
+          XCTAssertNotNil(model.path)
+          let filePath = URL(fileURLWithPath: model.path)
+          XCTAssertTrue(ModelFileManager.isFileReachable(at: filePath))
+        case let .failure(error):
+          XCTFail("Failed to download model - \(error)")
+        }
+        latestModelExpectation.fulfill()
       }
-      latestModelExpectation.fulfill()
+      wait(for: [latestModelExpectation], timeout: 5)
     }
-    wait(for: [latestModelExpectation], timeout: 5)
   }
-}
+
+#endif // !targetEnvironment(macCatalyst) && !os(macOS)

Plik diff jest za duży
+ 768 - 621
FirebaseMLModelDownloader/Tests/Unit/ModelDownloaderUnitTests.swift


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików