mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-07 01:15:33 +00:00
363 lines
14 KiB
Swift
363 lines
14 KiB
Swift
//
|
|
// AssociatingPackageInstallerTests.swift
|
|
// KeymanEngineTests
|
|
//
|
|
// Created by Joshua Horton on 8/6/20.
|
|
// Copyright © 2020 SIL International. All rights reserved.
|
|
//
|
|
|
|
import XCTest
|
|
@testable import KeymanEngine
|
|
|
|
class AssociatingPackageInstallerTests: XCTestCase {
|
|
var mockedURLSession: TestUtils.Downloading.URLSessionMock!
|
|
var downloadManager: ResourceDownloadManager!
|
|
|
|
let mockedSearchCallback: LanguagePickAssociator.AssociationSearcher = { lgCodes, callback in
|
|
var searchResult: [String: (KeymanPackage.Key, URL)?] = [:]
|
|
|
|
// We aren't downloading these models, so a placeholder URL is fine.
|
|
let placeholderURL = URL.init(string: "http://place.holder.com")!
|
|
|
|
lgCodes.forEach { lgCode in
|
|
switch lgCode {
|
|
case "en":
|
|
searchResult["en"] = (TestUtils.Packages.Keys.nrc_en_mtnt, placeholderURL)
|
|
case "str":
|
|
searchResult["str"] = (TestUtils.Packages.Keys.nrc_str_sencoten, placeholderURL)
|
|
default:
|
|
searchResult[lgCode] = nil
|
|
}
|
|
}
|
|
|
|
callback(searchResult)
|
|
}
|
|
|
|
override func setUp() {
|
|
// Ensure Manager's standard init() occurs before our tests.
|
|
// Otherwise, if our first Manager reference is DURING a test, we'll see unwanted behavior from it.
|
|
_ = Manager.shared
|
|
|
|
// Resets resource directories for a clean slate.
|
|
// We'll be testing against actually-installed packages.
|
|
TestUtils.standardTearDown()
|
|
|
|
mockedURLSession = TestUtils.Downloading.URLSessionMock()
|
|
downloadManager = ResourceDownloadManager(session: mockedURLSession!, autoExecute: true)
|
|
}
|
|
|
|
override func tearDownWithError() throws {
|
|
TestUtils.standardTearDown()
|
|
|
|
let queueWasCleared = mockedURLSession!.queueIsEmpty
|
|
mockedURLSession = nil
|
|
|
|
if !queueWasCleared {
|
|
throw NSError(domain: "Keyman",
|
|
code: 4,
|
|
userInfo: [NSLocalizedDescriptionKey: "A test did not fully utilize its queued mock results!"])
|
|
}
|
|
}
|
|
|
|
func testKeyboardNoAssociations() throws {
|
|
guard let package = try ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.silEuroLatinKMP) as? KeyboardKeymanPackage else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
let startExpectation = XCTestExpectation()
|
|
let completeExpectation = XCTestExpectation()
|
|
|
|
let installer = AssociatingPackageInstaller(for: package,
|
|
defaultLanguageCode: "en",
|
|
downloadManager: downloadManager) { status in
|
|
if status == .starting {
|
|
startExpectation.fulfill()
|
|
} else if status == .complete {
|
|
completeExpectation.fulfill()
|
|
} else if status == .cancelled {
|
|
XCTFail()
|
|
}
|
|
}
|
|
|
|
installer.pickLanguages(withCodes: Set(["en"]))
|
|
|
|
wait(for: [startExpectation, completeExpectation], timeout: 5)
|
|
|
|
guard let userKeyboards = Storage.active.userDefaults.userKeyboards else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
XCTAssertTrue(userKeyboards.contains { $0.fullID == TestUtils.Keyboards.sil_euro_latin.fullID })
|
|
XCTAssertNil(Storage.active.userDefaults.userLexicalModels)
|
|
XCTAssertEqual(ResourceFileManager.shared.installState(forPackage: TestUtils.Packages.Keys.sil_euro_latin), .installed)
|
|
XCTAssertEqual(ResourceFileManager.shared.installState(forPackage: TestUtils.Packages.Keys.nrc_en_mtnt), .none)
|
|
}
|
|
|
|
func testLexicalModelNoAssociations() throws {
|
|
guard let package = try ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.LexicalModels.mtntKMP) as? LexicalModelKeymanPackage else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
let startExpectation = XCTestExpectation()
|
|
let completeExpectation = XCTestExpectation()
|
|
|
|
let installer = AssociatingPackageInstaller(for: package,
|
|
defaultLanguageCode: "en",
|
|
downloadManager: downloadManager) { status in
|
|
if status == .starting {
|
|
startExpectation.fulfill()
|
|
} else if status == .complete {
|
|
completeExpectation.fulfill()
|
|
} else if status == .cancelled {
|
|
XCTFail()
|
|
}
|
|
}
|
|
|
|
installer.pickLanguages(withCodes: Set(["en"]))
|
|
|
|
wait(for: [startExpectation, completeExpectation], timeout: 5)
|
|
|
|
guard let userLexicalModels = Storage.active.userDefaults.userLexicalModels else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
XCTAssertTrue(userLexicalModels.contains { $0.fullID == TestUtils.LexicalModels.mtnt.fullID })
|
|
XCTAssertNil(Storage.active.userDefaults.userKeyboards)
|
|
XCTAssertEqual(ResourceFileManager.shared.installState(forPackage: TestUtils.Packages.Keys.nrc_en_mtnt), .installed)
|
|
XCTAssertEqual(ResourceFileManager.shared.installState(forPackage: TestUtils.Packages.Keys.sil_euro_latin), .none)
|
|
}
|
|
|
|
func testKeyboardWithAssociatedLexicalModels() throws {
|
|
let mockedQuery = TestUtils.Downloading.MockResult(location: TestUtils.Queries.model_case_en, error: nil)
|
|
let mockedDownload = TestUtils.Downloading.MockResult(location: TestUtils.LexicalModels.mtntKMP, error: nil)
|
|
|
|
mockedURLSession.queueMockResult(.data(mockedQuery))
|
|
mockedURLSession.queueMockResult(.download(mockedDownload))
|
|
|
|
guard let package = try ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.silEuroLatinKMP) as? KeyboardKeymanPackage else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
let startExpectation = XCTestExpectation()
|
|
let completeExpectation = XCTestExpectation()
|
|
|
|
let installer = AssociatingPackageInstaller(for: package,
|
|
defaultLanguageCode: "en",
|
|
downloadManager: downloadManager,
|
|
withAssociators: [ .lexicalModels ]) { status in
|
|
if status == .starting {
|
|
startExpectation.fulfill()
|
|
} else if status == .complete {
|
|
completeExpectation.fulfill()
|
|
} else if status == .cancelled {
|
|
XCTFail()
|
|
}
|
|
}
|
|
|
|
installer.pickLanguages(withCodes: Set(["en"]))
|
|
|
|
wait(for: [startExpectation, completeExpectation], timeout: 5)
|
|
|
|
guard let userKeyboards = Storage.active.userDefaults.userKeyboards,
|
|
let userLexicalModels = Storage.active.userDefaults.userLexicalModels else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
XCTAssertTrue(userKeyboards.contains { $0.fullID == TestUtils.Keyboards.sil_euro_latin.fullID })
|
|
XCTAssertTrue(userLexicalModels.contains { $0.fullID == TestUtils.LexicalModels.mtnt.fullID} )
|
|
|
|
XCTAssertEqual(ResourceFileManager.shared.installState(forPackage: TestUtils.Packages.Keys.sil_euro_latin), .installed)
|
|
XCTAssertEqual(ResourceFileManager.shared.installState(forPackage: TestUtils.Packages.Keys.nrc_en_mtnt), .installed)
|
|
}
|
|
|
|
|
|
func testCancellationNoAssociations() throws {
|
|
guard let package = try ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.silEuroLatinKMP) as? KeyboardKeymanPackage else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
let cancelExpectation = XCTestExpectation()
|
|
|
|
let installer = AssociatingPackageInstaller(for: package,
|
|
defaultLanguageCode: "en",
|
|
downloadManager: downloadManager) { status in
|
|
if status == .cancelled {
|
|
// When the user 'cancels' language selection, this installer should also
|
|
// signal cancellation.
|
|
cancelExpectation.fulfill()
|
|
} else {
|
|
XCTFail()
|
|
}
|
|
}
|
|
|
|
// A bit of white-box testing - sets up the closure framework
|
|
installer.initializeSynchronizationGroups()
|
|
// And the install closure, which we'll send a 'cancel' signal to.
|
|
let installClosure = installer.coreInstallationClosure()
|
|
installClosure(nil)
|
|
|
|
wait(for: [cancelExpectation], timeout: 5)
|
|
|
|
XCTAssertNil(Storage.active.userDefaults.userKeyboards)
|
|
XCTAssertNil(Storage.active.userDefaults.userLexicalModels)
|
|
}
|
|
|
|
func testCancellationWithAssociations() throws {
|
|
guard let package = try ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.silEuroLatinKMP) as? KeyboardKeymanPackage else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
// While the query should occur, the installation phase should never be reached.
|
|
// So, we don't mock the download.
|
|
let mockedQuery = TestUtils.Downloading.MockResult(location: TestUtils.Queries.model_case_en, error: nil)
|
|
mockedURLSession.queueMockResult(.data(mockedQuery))
|
|
|
|
let cancelExpectation = XCTestExpectation()
|
|
|
|
let installer = AssociatingPackageInstaller(for: package,
|
|
defaultLanguageCode: "en",
|
|
downloadManager: downloadManager,
|
|
withAssociators: [.lexicalModels]) { status in
|
|
if status == .cancelled {
|
|
// When the user 'cancels' language selection, this installer should also
|
|
// signal cancellation.
|
|
cancelExpectation.fulfill()
|
|
} else {
|
|
XCTFail()
|
|
}
|
|
}
|
|
|
|
// Heavier white-box testing this time.
|
|
installer.initializeSynchronizationGroups()
|
|
installer.constructAssociationPickers()
|
|
installer.associationQueriers?.forEach {
|
|
$0.pickerInitialized()
|
|
$0.selectLanguages(Set(["en"])) // calls the expected query
|
|
// And, since we're modeling cancellation...
|
|
$0.pickerDismissed()
|
|
}
|
|
|
|
// Finally, the install closure, which we'll send a 'cancel' signal to.
|
|
let installClosure = installer.coreInstallationClosure()
|
|
installClosure(nil)
|
|
|
|
wait(for: [cancelExpectation], timeout: 5)
|
|
|
|
XCTAssertNil(Storage.active.userDefaults.userKeyboards)
|
|
XCTAssertNil(Storage.active.userDefaults.userLexicalModels)
|
|
}
|
|
|
|
func testDeinitConstructiveCancellation() throws {
|
|
guard let package = try ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.silEuroLatinKMP) as? KeyboardKeymanPackage else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
// While the query should occur, the installation phase should never be reached.
|
|
// So, we don't mock the download.
|
|
let mockedQuery = TestUtils.Downloading.MockResult(location: TestUtils.Queries.model_case_en, error: nil)
|
|
let queryExpectation = XCTestExpectation()
|
|
mockedURLSession.queueMockResult(.data(mockedQuery), expectation: queryExpectation)
|
|
|
|
let cancelExpectation = XCTestExpectation()
|
|
|
|
do {
|
|
let installer = AssociatingPackageInstaller(for: package,
|
|
defaultLanguageCode: "en",
|
|
downloadManager: downloadManager,
|
|
withAssociators: [.lexicalModels]) { status in
|
|
// When the user 'cancels' language selection, this installer should also
|
|
// signal cancellation.
|
|
if status == .cancelled {
|
|
// First, wait to ensure that the standard query did launch.
|
|
// Otherwise, we might accidentally skip the mocked query!
|
|
self.wait(for: [queryExpectation], timeout: 4)
|
|
cancelExpectation.fulfill()
|
|
} else {
|
|
XCTFail()
|
|
}
|
|
}
|
|
|
|
// Heavier white-box testing this time.
|
|
installer.initializeSynchronizationGroups()
|
|
installer.constructAssociationPickers()
|
|
installer.associationQueriers?.forEach {
|
|
$0.pickerInitialized()
|
|
$0.selectLanguages(Set(["en"])) // calls the expected query
|
|
// De-init will trigger the 'dismiss' signal.
|
|
}
|
|
}
|
|
|
|
wait(for: [cancelExpectation], timeout: 5)
|
|
|
|
XCTAssertNil(Storage.active.userDefaults.userKeyboards)
|
|
XCTAssertNil(Storage.active.userDefaults.userLexicalModels)
|
|
}
|
|
|
|
func testDeinitUnstarted() throws {
|
|
guard let package = try ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.silEuroLatinKMP) as? KeyboardKeymanPackage else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
let cancelExpectation = XCTestExpectation()
|
|
|
|
do {
|
|
let _ = AssociatingPackageInstaller(for: package,
|
|
defaultLanguageCode: "en",
|
|
downloadManager: downloadManager,
|
|
withAssociators: [.lexicalModels]) { status in
|
|
// Since language-picking never began, no "cancelled" signal should occur.
|
|
if status == .cancelled {
|
|
cancelExpectation.fulfill()
|
|
} else {
|
|
XCTFail()
|
|
}
|
|
}
|
|
}
|
|
|
|
wait(for: [cancelExpectation], timeout: 1)
|
|
|
|
XCTAssertNil(Storage.active.userDefaults.userKeyboards)
|
|
XCTAssertNil(Storage.active.userDefaults.userLexicalModels)
|
|
}
|
|
|
|
func testPackageLangCodePartialMatch() throws {
|
|
guard let strPackage = try ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.fvSencotenKMP) as? KeyboardKeymanPackage else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
guard let eurolatinPackage = try ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.silEuroLatinKMP) as? KeyboardKeymanPackage else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
|
|
let strInstaller = AssociatingPackageInstaller(for: strPackage,
|
|
defaultLanguageCode: "str", // correct code: str-latn
|
|
downloadManager: downloadManager) { _ in
|
|
}
|
|
|
|
// Package does not contain "str", but does contain "str-latn"
|
|
XCTAssertEqual(strInstaller.defaultLgCode, "str-latn")
|
|
|
|
let eurolatinInstaller = AssociatingPackageInstaller(for: eurolatinPackage,
|
|
defaultLanguageCode: "en-fake-bcp", // correct code: "en"
|
|
downloadManager: downloadManager) { _ in
|
|
}
|
|
|
|
// Package does not contain "en-fake-bcp", but does contain "en".
|
|
XCTAssertEqual(eurolatinInstaller.defaultLgCode, "en")
|
|
|
|
// Note: the current naive approach isn't exactly BCP-47 subtag aware - it relies on one tag
|
|
// containing the entire other tag as a substring.
|
|
}
|
|
}
|