diff --git a/.github/workflows/mlmodeldownloader.yml b/.github/workflows/mlmodeldownloader.yml index b111631f03..2ecfeeb4f3 100644 --- a/.github/workflows/mlmodeldownloader.yml +++ b/.github/workflows/mlmodeldownloader.yml @@ -16,7 +16,8 @@ jobs: if: github.repository != 'FirebasePrivate/firebase-ios-sdk' || github.event_name == 'pull_request' runs-on: macOS-latest - + env: + plist_secret: ${{ secrets.GHASecretsGPGPassphrase1 }} strategy: matrix: target: [ios, tvos, macos] @@ -24,6 +25,11 @@ jobs: - uses: actions/checkout@v2 - name: Setup Bundler run: scripts/setup_bundler.sh + - name: Install GoogleService-Info.plist + run: | + mkdir FirebaseMLModelDownloader/Tests/Integration/Resources + scripts/decrypt_gha_secret.sh scripts/gha-encrypted/MLModelDownloader/GoogleService-Info.plist.gpg \ + FirebaseMLModelDownloader/Tests/Integration/Resources/GoogleService-Info.plist "$plist_secret" - name: Build and test run: scripts/third_party/travis/retry.sh scripts/pod_lib_lint.rb FirebaseMLModelDownloader.podspec --platforms=${{ matrix.target }} diff --git a/FirebaseMLModelDownloader.podspec b/FirebaseMLModelDownloader.podspec index d95487ba00..f87d9c8e58 100644 --- a/FirebaseMLModelDownloader.podspec +++ b/FirebaseMLModelDownloader.podspec @@ -45,4 +45,11 @@ Pod::Spec.new do |s| unit_tests.source_files = 'FirebaseMLModelDownloader/Tests/Unit/**/*.swift' unit_tests.requires_app_host = true end + + s.test_spec 'integration' do |int_tests| + int_tests.platforms = {:ios => '10.0', :osx => '10.12', :tvos => '10.0'} + int_tests.source_files = 'FirebaseMLModelDownloader/Tests/Integration/**/*.swift' + int_tests.resources = 'FirebaseMLModelDownloader/Tests/Integration/Resources/GoogleService-Info.plist' + int_tests.requires_app_host = true + end end diff --git a/FirebaseMLModelDownloader/Tests/Integration/ModelDownloaderIntegrationTests.swift b/FirebaseMLModelDownloader/Tests/Integration/ModelDownloaderIntegrationTests.swift new file mode 100644 index 0000000000..0069d92959 --- /dev/null +++ b/FirebaseMLModelDownloader/Tests/Integration/ModelDownloaderIntegrationTests.swift @@ -0,0 +1,110 @@ +// Copyright 2020 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 XCTest +@testable import FirebaseCore +@testable import FirebaseMLModelDownloader + +extension UserDefaults { + /// For testing: returns a new cleared instance of user defaults. + static func getTestInstance() -> UserDefaults { + let suiteName = "com.google.firebase.ml.test" + // TODO: reconsider force unwrapping + let defaults = UserDefaults(suiteName: suiteName)! + defaults.removePersistentDomain(forName: suiteName) + return defaults + } +} + +final class ModelDownloaderIntegrationTests: XCTestCase { + override class func setUp() { + super.setUp() + 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.") + } + } + + /// Test to retrieve FIS token - makes an actual network call. + func testGetAuthToken() { + guard let testApp = FirebaseApp.app() else { + XCTFail("Default app was not configured.") + return + } + let testModelName = "image-classification" + let modelInfoRetriever = ModelInfoRetriever( + app: testApp, + modelName: testModelName, + defaults: .getTestInstance() + ) + let expectation = self.expectation(description: "Wait for FIS auth token.") + modelInfoRetriever.getAuthToken(completion: { result in + switch result { + case let .success(token): + XCTAssertNotNil(token) + case let .failure(error): + XCTFail(error.localizedDescription) + } + expectation.fulfill() + + }) + waitForExpectations(timeout: 5, handler: nil) + } + + /// 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 + } + let testModelName = "pose-detection" + let modelInfoRetriever = ModelInfoRetriever( + app: testApp, + modelName: testModelName, + defaults: .getTestInstance() + ) + let downloadExpectation = expectation(description: "Wait for model info to download.") + modelInfoRetriever.downloadModelInfo(completion: { error in + XCTAssertNil(error) + guard let modelInfo = modelInfoRetriever.modelInfo else { + XCTFail("Empty model info.") + return + } + XCTAssertNotEqual(modelInfo.downloadURL, "") + XCTAssertNotEqual(modelInfo.modelHash, "") + XCTAssertGreaterThan(modelInfo.size, 0) + downloadExpectation.fulfill() + }) + + waitForExpectations(timeout: 5, handler: nil) + + let retrieveExpectation = expectation(description: "Wait for model info to be retrieved.") + modelInfoRetriever.downloadModelInfo(completion: { error in + XCTAssertNil(error) + guard let modelInfo = modelInfoRetriever.modelInfo else { + XCTFail("Empty model info.") + return + } + XCTAssertNotEqual(modelInfo.downloadURL, "") + XCTAssertNotEqual(modelInfo.modelHash, "") + XCTAssertGreaterThan(modelInfo.size, 0) + retrieveExpectation.fulfill() + }) + + waitForExpectations(timeout: 500, handler: nil) + } +} diff --git a/FirebaseMLModelDownloader/Tests/Unit/ModelDownloaderTests.swift b/FirebaseMLModelDownloader/Tests/Unit/ModelDownloaderUnitTests.swift similarity index 59% rename from FirebaseMLModelDownloader/Tests/Unit/ModelDownloaderTests.swift rename to FirebaseMLModelDownloader/Tests/Unit/ModelDownloaderUnitTests.swift index d3f36d0e1d..a49017ac32 100644 --- a/FirebaseMLModelDownloader/Tests/Unit/ModelDownloaderTests.swift +++ b/FirebaseMLModelDownloader/Tests/Unit/ModelDownloaderUnitTests.swift @@ -16,6 +16,14 @@ import XCTest @testable import FirebaseCore @testable import FirebaseMLModelDownloader +/// Mock options to configure default Firebase app. +private enum MockOptions { + static let appID = "1:123:ios:123abc" + static let gcmSenderID = "mock-sender-id" + static let projectID = "mock-project-id" + static let apiKey = "ABcdEf-APIKeyWithValidFormat_0123456789" +} + extension UserDefaults { /// For testing: returns a new cleared instance of user defaults. static func getTestInstance() -> UserDefaults { @@ -27,16 +35,23 @@ extension UserDefaults { } } -// TODO: break this up into separate unit and integration tests -final class ModelDownloaderTests: XCTestCase { +final class ModelDownloaderUnitTests: XCTestCase { override class func setUp() { - super.setUp() - FirebaseApp.configure() + let options = FirebaseOptions( + googleAppID: MockOptions.appID, + gcmSenderID: MockOptions.gcmSenderID + ) + options.apiKey = MockOptions.apiKey + options.projectID = MockOptions.projectID + FirebaseApp.configure(options: options) } /// Unit test for reading and writing to user defaults. func testUserDefaults() { - let testApp = FirebaseApp.app()! + guard let testApp = FirebaseApp.app() else { + XCTFail("Default app was not configured.") + return + } let functionName = #function let testModelName = "\(functionName)-test-model" let modelInfo = ModelInfo( @@ -52,74 +67,16 @@ final class ModelDownloaderTests: XCTestCase { XCTAssertEqual(modelInfo.path, nil) } - /// Test to retrieve FIS token - makes an actual network call. - // TODO: Move this into a separate integration test and add unit test with mocks. - func testGetAuthToken() { - let testApp = FirebaseApp.app()! - let testModelName = "image-classification" - let modelInfoRetriever = ModelInfoRetriever( - app: testApp, - modelName: testModelName, - defaults: .getTestInstance() - ) - let expectation = self.expectation(description: "Wait for FIS auth token.") - modelInfoRetriever.getAuthToken(completion: { result in - switch result { - case let .success(token): - XCTAssertNotNil(token) - case let .failure(error): - XCTFail(error.localizedDescription) - } - expectation.fulfill() - - }) - waitForExpectations(timeout: 5, handler: nil) - } - - /// Test to download model info - makes an actual network call. - // TODO: Move this into a separate integration test and add unit test with mocks. - func testDownloadModelInfo() { - let testApp = FirebaseApp.app()! - let testModelName = "pose-detection" - let modelInfoRetriever = ModelInfoRetriever( - app: testApp, - modelName: testModelName, - defaults: .getTestInstance() - ) - let downloadExpectation = expectation(description: "Wait for model info to download.") - modelInfoRetriever.downloadModelInfo(completion: { error in - XCTAssertNil(error) - guard let modelInfo = modelInfoRetriever.modelInfo else { - XCTFail("Empty model info.") - return - } - XCTAssertNotEqual(modelInfo.downloadURL, "") - XCTAssertNotEqual(modelInfo.modelHash, "") - XCTAssertGreaterThan(modelInfo.size, 0) - downloadExpectation.fulfill() - }) - - waitForExpectations(timeout: 5, handler: nil) - - let retrieveExpectation = expectation(description: "Wait for model info to be retrieved.") - modelInfoRetriever.downloadModelInfo(completion: { error in - XCTAssertNil(error) - guard let modelInfo = modelInfoRetriever.modelInfo else { - XCTFail("Empty model info.") - return - } - XCTAssertNotEqual(modelInfo.downloadURL, "") - XCTAssertNotEqual(modelInfo.modelHash, "") - XCTAssertGreaterThan(modelInfo.size, 0) - retrieveExpectation.fulfill() - }) - - waitForExpectations(timeout: 500, handler: nil) - } + /// Test to download model info. + // TODO: Add unit test with mocks. + func testDownloadModelInfo() {} /// Unit test to save model info to user defaults. func testSaveModelInfo() { - let testApp = FirebaseApp.app()! + guard let testApp = FirebaseApp.app() else { + XCTFail("Default app was not configured.") + return + } let functionName = #function let testModelName = "\(functionName)-test-model" let modelInfoRetriever = ModelInfoRetriever( diff --git a/scripts/gha-encrypted/MLModelDownloader/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/MLModelDownloader/GoogleService-Info.plist.gpg new file mode 100644 index 0000000000..420034eaac Binary files /dev/null and b/scripts/gha-encrypted/MLModelDownloader/GoogleService-Info.plist.gpg differ