mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-12 20:05:34 +00:00
refactor(ios/engine): rips out search lex-model querying
This commit is contained in:
parent
576baf3e00
commit
bc061ebda6
4 changed files with 120 additions and 514 deletions
|
|
@ -48,7 +48,7 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
|
|||
* for associated lexical models (or to indicate that none should occur). Otherwise, the lexical model
|
||||
* search closure provided to the keyboard search will never evaluate.
|
||||
*/
|
||||
case untagged(KeymanPackage.Key, URL, (DelayedLanguageSelection) -> Void)
|
||||
case untagged(KeymanPackage.Key, URL)
|
||||
|
||||
/**
|
||||
* Indicates that a package has been selected for download and is already associated with a
|
||||
|
|
@ -71,7 +71,6 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
|
|||
|
||||
private var hasFinalized = false
|
||||
private let keyboardSelectionClosure: SelectionCompletedHandler<FullKeyboardID>!
|
||||
private let lexicalModelSelectionClosure: SelectionCompletedHandler<FullLexicalModelID>!
|
||||
private let languageCode: String?
|
||||
private let session: URLSession
|
||||
|
||||
|
|
@ -90,12 +89,10 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
|
|||
|
||||
public init(languageCode: String? = nil,
|
||||
withSession session: URLSession = URLSession.shared,
|
||||
keyboardSelectionBlock: @escaping SelectionCompletedHandler<FullKeyboardID>,
|
||||
lexicalModelSelectionBlock: @escaping SelectionCompletedHandler<FullLexicalModelID>) {
|
||||
keyboardSelectionBlock: @escaping SelectionCompletedHandler<FullKeyboardID>) {
|
||||
self.languageCode = languageCode
|
||||
self.session = session
|
||||
self.keyboardSelectionClosure = keyboardSelectionBlock
|
||||
self.lexicalModelSelectionClosure = lexicalModelSelectionBlock
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +138,6 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
|
|||
// If the view is being dismissed but no matching link has been intercepted...
|
||||
if self.isMovingFromParent, !hasFinalized {
|
||||
self.keyboardSelectionClosure(.cancelled)
|
||||
self.lexicalModelSelectionClosure(.cancelled)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -175,50 +171,13 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
|
|||
if let lang_id = lang_id {
|
||||
let resourceKey = FullKeyboardID(keyboardID: keyboard_id, languageID: lang_id)
|
||||
let kbdURL = ResourceDownloadManager.shared.defaultDownloadURL(forPackage: packageKey, andResource: resourceKey, asUpdate: false)
|
||||
performLanguageSearch(languageID: lang_id)
|
||||
self.keyboardSelectionClosure(.tagged(packageKey, kbdURL, resourceKey))
|
||||
} else {
|
||||
// No language ID? Defer the language search!
|
||||
let deferredModelClosure: DeferredLexicalModelSearch = { arg in
|
||||
if case let .tag(lgCode) = arg {
|
||||
self.performLanguageSearch(languageID: lgCode)
|
||||
} else {
|
||||
self.lexicalModelSelectionClosure(.cancelled)
|
||||
}
|
||||
}
|
||||
let kbdURL = ResourceDownloadManager.shared.defaultDownloadURL(forPackage: packageKey, asUpdate: false)
|
||||
self.keyboardSelectionClosure(.untagged(packageKey, kbdURL, deferredModelClosure))
|
||||
self.keyboardSelectionClosure(.untagged(packageKey, kbdURL))
|
||||
}
|
||||
}
|
||||
|
||||
internal func performLanguageSearch(languageID: String) {
|
||||
if Storage.active.userDefaults.userLexicalModels?.contains(where: { $0.languageID == languageID }) == nil {
|
||||
Queries.LexicalModel.fetchModels(forLanguageCode: languageID,
|
||||
withSession: session) { results, error in
|
||||
if let results = results {
|
||||
if results.count == 0 {
|
||||
self.lexicalModelSelectionClosure(.cancelled)
|
||||
} else {
|
||||
let lexicalModel = results[0].0
|
||||
self.lexicalModelSelectionClosure(.tagged(lexicalModel.packageKey, results[0].1, lexicalModel.fullID))
|
||||
}
|
||||
} else {
|
||||
// .cancelled because if we already errored once, a redo of the query is likely
|
||||
// to raise an error again. We -could- defer as if there were no language tag, though.
|
||||
self.lexicalModelSelectionClosure(.cancelled)
|
||||
|
||||
if let error = error {
|
||||
log.error("Could not find a lexical model for language id \"\(languageID)\" due to error: \(String(describing: error))")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We already have a lexical model for the language. No need to download a new one.
|
||||
self.lexicalModelSelectionClosure(.cancelled)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static func defaultKeyboardInstallationClosure(installCompletionBlock: ((DefaultInstallationResult) -> Void)? = nil) -> SelectionCompletedHandler<FullKeyboardID> {
|
||||
return defaultKeyboardInstallationClosure(withDownloadManager: ResourceDownloadManager.shared,
|
||||
installCompletionBlock: installCompletionBlock)
|
||||
|
|
@ -226,9 +185,7 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
|
|||
|
||||
// For unit testing.
|
||||
internal static func defaultKeyboardInstallationClosure(withDownloadManager downloadManager: ResourceDownloadManager,
|
||||
dispatchGroup: DispatchGroup? = nil,
|
||||
installCompletionBlock: ((DefaultInstallationResult) -> Void)? = nil) -> SelectionCompletedHandler<FullKeyboardID> {
|
||||
dispatchGroup?.enter() // register the closure for group synchronization.
|
||||
|
||||
// Used to finalize the results of the closure, allowing the callback to complete
|
||||
// before signaling 'group completion' to the DispatchGroup synchronization object.
|
||||
|
|
@ -238,8 +195,6 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
|
|||
} else if let message = message {
|
||||
log.error(message)
|
||||
}
|
||||
|
||||
dispatchGroup?.leave() // "fulfill" this closure's aspect of the group's synchronization scheme.
|
||||
}
|
||||
|
||||
return { searchResult in
|
||||
|
|
@ -250,7 +205,7 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
|
|||
case .cancelled:
|
||||
finalize(as: .cancelled)
|
||||
return
|
||||
case .untagged(let key, let url, _):
|
||||
case .untagged(let key, let url):
|
||||
packageKey = key
|
||||
packageURL = url
|
||||
case .tagged(let key, let url, _):
|
||||
|
|
@ -264,14 +219,12 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
|
|||
let message = "Could not download package \(packageKey): \(errString)"
|
||||
|
||||
finalize(as: .error(error), noCallbackLog: message)
|
||||
if case let .untagged(_, _, deferredLexicalModelSearch) = searchResult {
|
||||
deferredLexicalModelSearch(.none)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: needs changes to integrate with AssociatingPackageInstaller
|
||||
switch searchResult {
|
||||
case .untagged(_, _, let deferredLexicalModelSearch):
|
||||
case .untagged(_, _):
|
||||
do {
|
||||
// TODO: We don't know which resource the user actually wants. Prompt them.
|
||||
// But for now, the old 'default' installation.
|
||||
|
|
@ -282,10 +235,8 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
|
|||
// Yeah, it's ugly... but it's best to fix as part of resolution of the TODO above.
|
||||
// That's "the first language pairing of the first keyboard in the package."
|
||||
finalize(as: .success(resourceKey))
|
||||
deferredLexicalModelSearch(.tag(resourceKey.languageID))
|
||||
} catch {
|
||||
finalize(as: .error(error), noCallbackLog: "Could not install package \(packageKey): \(error)")
|
||||
deferredLexicalModelSearch(.none)
|
||||
throw error // For more accurate notifications
|
||||
}
|
||||
case .tagged(_, _, let resourceKey):
|
||||
|
|
@ -305,59 +256,4 @@ public class KeyboardSearchViewController: UIViewController, WKNavigationDelegat
|
|||
downloadManager.downloadPackage(withKey: packageKey, from: packageURL, withNotifications: true, completionBlock: downloadClosure)
|
||||
}
|
||||
}
|
||||
|
||||
public static func defaultLexicalModelInstallationClosure(installCompletionBlock: ((DefaultInstallationResult) -> Void)? = nil) -> SelectionCompletedHandler<FullLexicalModelID> {
|
||||
return defaultLexicalModelInstallationClosure(withDownloadManager: ResourceDownloadManager.shared)
|
||||
}
|
||||
|
||||
// For unit testing.
|
||||
internal static func defaultLexicalModelInstallationClosure(withDownloadManager downloadManager: ResourceDownloadManager,
|
||||
dispatchGroup: DispatchGroup? = nil,
|
||||
installCompletionBlock: ((DefaultInstallationResult) -> Void)? = nil) -> SelectionCompletedHandler<FullLexicalModelID> {
|
||||
dispatchGroup?.enter() // register the closure for group synchronization.
|
||||
|
||||
// Used to finalize the results of the closure, allowing the callback to complete
|
||||
// before signaling 'group completion' to the DispatchGroup synchronization object.
|
||||
func finalize(as result: DefaultInstallationResult, noCallbackLog message: String? = nil) {
|
||||
if let installCompletionBlock = installCompletionBlock {
|
||||
installCompletionBlock(result)
|
||||
} else if let message = message {
|
||||
log.error(message)
|
||||
}
|
||||
|
||||
dispatchGroup?.leave() // "fulfill" this closure's aspect of the group's synchronization scheme.
|
||||
}
|
||||
|
||||
return { searchResult in
|
||||
switch searchResult {
|
||||
case .cancelled:
|
||||
finalize(as: .cancelled)
|
||||
return
|
||||
case .tagged(let packageKey, let packageURL, let resourceKey):
|
||||
var lmInstallClosure: ResourceDownloadManager.CompletionHandler<LexicalModelKeymanPackage>
|
||||
lmInstallClosure = { package, error in
|
||||
if let package = package, error == nil {
|
||||
// perform the actual installation
|
||||
do {
|
||||
try ResourceFileManager.shared.install(resourceWithID: resourceKey, from: package)
|
||||
finalize(as: .success(resourceKey))
|
||||
} catch {
|
||||
finalize(as: .error(error), noCallbackLog: "Could not install \(resourceKey) from package \(packageKey): \(error)")
|
||||
throw error
|
||||
}
|
||||
} else {
|
||||
let errString = error != nil ? String(describing: error) : "<unknown error>"
|
||||
let message = "Could not download package \(packageKey): \(errString)"
|
||||
|
||||
finalize(as: .error(error), noCallbackLog: message)
|
||||
throw error ?? NSError()
|
||||
}
|
||||
}
|
||||
|
||||
downloadManager.downloadPackage(withKey: packageKey, from: packageURL, withNotifications: true, completionBlock: lmInstallClosure)
|
||||
case .untagged(_, _, _):
|
||||
fatalError() // Explicitly illegal state, as documented in the enum's definition.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -431,8 +431,7 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie
|
|||
extension InstalledLanguagesViewController {
|
||||
|
||||
@objc func addClicked(_ sender: Any) {
|
||||
let keyboardSearchVC = KeyboardSearchViewController(keyboardSelectionBlock: KeyboardSearchViewController.defaultKeyboardInstallationClosure(),
|
||||
lexicalModelSelectionBlock: KeyboardSearchViewController.defaultLexicalModelInstallationClosure())
|
||||
let keyboardSearchVC = KeyboardSearchViewController(keyboardSelectionBlock: KeyboardSearchViewController.defaultKeyboardInstallationClosure())
|
||||
navigationController!.pushViewController(keyboardSearchVC, animated: true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,8 +301,7 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
|
||||
@objc func addClicked(_ sender: Any) {
|
||||
let keyboardSearchVC = KeyboardSearchViewController(languageCode: self.language.id,
|
||||
keyboardSelectionBlock: KeyboardSearchViewController.defaultKeyboardInstallationClosure(),
|
||||
lexicalModelSelectionBlock: KeyboardSearchViewController.defaultLexicalModelInstallationClosure())
|
||||
keyboardSelectionBlock: KeyboardSearchViewController.defaultKeyboardInstallationClosure())
|
||||
navigationController!.pushViewController(keyboardSearchVC, animated: true)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,64 +68,29 @@ class KeyboardSearchTests: XCTestCase {
|
|||
|
||||
func testFinalizeNoLanguage() {
|
||||
let kbdExpectation = XCTestExpectation()
|
||||
let lmExpectation = XCTestExpectation()
|
||||
let groupExpectation = XCTestExpectation()
|
||||
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
dispatchGroup.enter()
|
||||
let noLangKbdBlock: KeyboardSearchViewController.SelectionCompletedHandler<FullKeyboardID> = { searchResult in
|
||||
let kbdBlock: KeyboardSearchViewController.SelectionCompletedHandler<FullKeyboardID> = { searchResult in
|
||||
switch searchResult {
|
||||
case .untagged(let packageKey, let url, let langTaggingClosure):
|
||||
case .untagged(let packageKey, let url):
|
||||
XCTAssertEqual(packageKey, TestUtils.Packages.Keys.khmer_angkor)
|
||||
XCTAssertNotNil(url)
|
||||
|
||||
langTaggingClosure(.none)
|
||||
default:
|
||||
XCTFail()
|
||||
}
|
||||
|
||||
kbdExpectation.fulfill()
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
|
||||
dispatchGroup.enter()
|
||||
let noLangLMBlock: KeyboardSearchViewController.SelectionCompletedHandler<FullLexicalModelID> = { searchResult in
|
||||
switch searchResult {
|
||||
case .cancelled:
|
||||
break
|
||||
default:
|
||||
XCTFail()
|
||||
}
|
||||
|
||||
lmExpectation.fulfill()
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
groupExpectation.fulfill()
|
||||
}
|
||||
|
||||
let searchNoLang = KeyboardSearchViewController(languageCode: nil,
|
||||
withSession: mockedURLSession!,
|
||||
keyboardSelectionBlock: noLangKbdBlock,
|
||||
lexicalModelSelectionBlock: noLangLMBlock)
|
||||
keyboardSelectionBlock: kbdBlock)
|
||||
searchNoLang.finalize(with: "khmer_angkor", for: nil)
|
||||
|
||||
wait(for: [kbdExpectation, lmExpectation, groupExpectation], timeout: 5)
|
||||
wait(for: [kbdExpectation], timeout: 5)
|
||||
}
|
||||
|
||||
func testFinalizeWithLanguage() {
|
||||
let mockedModelQuery = TestUtils.Downloading.MockResult(location: TestUtils.Queries.model_case_en, error: nil)
|
||||
mockedURLSession?.queueMockResult(.data(mockedModelQuery))
|
||||
|
||||
let kbdExpectation = XCTestExpectation()
|
||||
let lmExpectation = XCTestExpectation()
|
||||
let groupExpectation = XCTestExpectation()
|
||||
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
dispatchGroup.enter()
|
||||
let kbdBlock: KeyboardSearchViewController.SelectionCompletedHandler<FullKeyboardID> = { searchResult in
|
||||
switch searchResult {
|
||||
case .tagged(let packageKey, let url, let resourceKey):
|
||||
|
|
@ -137,371 +102,118 @@ class KeyboardSearchTests: XCTestCase {
|
|||
}
|
||||
|
||||
kbdExpectation.fulfill()
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
|
||||
dispatchGroup.enter()
|
||||
let lmBlock: KeyboardSearchViewController.SelectionCompletedHandler<FullLexicalModelID> = { searchResult in
|
||||
switch searchResult {
|
||||
case .tagged(let packageKey, let url, let resourceKey):
|
||||
XCTAssertEqual(packageKey, TestUtils.Packages.Keys.nrc_en_mtnt)
|
||||
XCTAssertNotNil(url)
|
||||
XCTAssertEqual(resourceKey, TestUtils.LexicalModels.mtnt.fullID)
|
||||
default:
|
||||
XCTFail()
|
||||
}
|
||||
|
||||
lmExpectation.fulfill()
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
groupExpectation.fulfill()
|
||||
}
|
||||
|
||||
let searchNoLang = KeyboardSearchViewController(languageCode: nil,
|
||||
let search = KeyboardSearchViewController(languageCode: nil,
|
||||
withSession: mockedURLSession!,
|
||||
keyboardSelectionBlock: kbdBlock,
|
||||
lexicalModelSelectionBlock: lmBlock)
|
||||
searchNoLang.finalize(with: "sil_euro_latin", for: "en")
|
||||
keyboardSelectionBlock: kbdBlock)
|
||||
search.finalize(with: "sil_euro_latin", for: "en")
|
||||
|
||||
wait(for: [kbdExpectation, lmExpectation, groupExpectation], timeout: 5)
|
||||
wait(for: [kbdExpectation], timeout: 5)
|
||||
}
|
||||
|
||||
func testFinalizeWithLanguageModelPreinstalled() throws {
|
||||
guard let mtntPackage = try ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.LexicalModels.mtntKMP) as? LexicalModelKeymanPackage else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
try ResourceFileManager.shared.install(resourceWithID: TestUtils.LexicalModels.mtnt.fullID, from: mtntPackage)
|
||||
|
||||
let kbdExpectation = XCTestExpectation()
|
||||
let lmExpectation = XCTestExpectation()
|
||||
let groupExpectation = XCTestExpectation()
|
||||
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
dispatchGroup.enter()
|
||||
let kbdBlock: KeyboardSearchViewController.SelectionCompletedHandler<FullKeyboardID> = { searchResult in
|
||||
switch searchResult {
|
||||
case .tagged(let packageKey, let url, let resourceKey):
|
||||
XCTAssertEqual(packageKey, TestUtils.Packages.Keys.sil_euro_latin)
|
||||
XCTAssertNotNil(url)
|
||||
XCTAssertEqual(resourceKey, TestUtils.Keyboards.sil_euro_latin.fullID)
|
||||
default:
|
||||
XCTFail()
|
||||
}
|
||||
|
||||
kbdExpectation.fulfill()
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
|
||||
dispatchGroup.enter()
|
||||
let lmBlock: KeyboardSearchViewController.SelectionCompletedHandler<FullLexicalModelID> = { searchResult in
|
||||
switch searchResult {
|
||||
case .cancelled:
|
||||
break
|
||||
default:
|
||||
XCTFail()
|
||||
}
|
||||
|
||||
lmExpectation.fulfill()
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
groupExpectation.fulfill()
|
||||
}
|
||||
|
||||
let searchNoLang = KeyboardSearchViewController(languageCode: nil,
|
||||
withSession: mockedURLSession!,
|
||||
keyboardSelectionBlock: kbdBlock,
|
||||
lexicalModelSelectionBlock: lmBlock)
|
||||
searchNoLang.finalize(with: "sil_euro_latin", for: "en")
|
||||
|
||||
wait(for: [kbdExpectation, lmExpectation, groupExpectation], timeout: 5)
|
||||
}
|
||||
|
||||
func testDefaultKeyboardInstallationClosureTaggedSuccess() throws {
|
||||
// Step 1: mocking.
|
||||
let packageDownloadTask = TestUtils.Downloading.MockResult(location: TestUtils.Keyboards.khmerAngkorKMP, error: nil)
|
||||
mockedURLSession!.queueMockResult(.download(packageDownloadTask))
|
||||
|
||||
let installExpectation = XCTestExpectation(description: "Keyboard package download & installation should complete")
|
||||
let groupExpectation = XCTestExpectation(description: "DispatchGroup should notify")
|
||||
|
||||
// Step 2 - build closure & synchronization check
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
let closure = KeyboardSearchViewController.defaultKeyboardInstallationClosure(withDownloadManager: downloadManager, dispatchGroup: dispatchGroup) { result in
|
||||
if case let .success(fullID) = result {
|
||||
XCTAssertEqual(fullID as? FullKeyboardID, TestUtils.Keyboards.khmer_angkor.fullID)
|
||||
} else {
|
||||
XCTFail("keyboard installation did not succeed.")
|
||||
}
|
||||
|
||||
installExpectation.fulfill()
|
||||
}
|
||||
|
||||
// As the closure has now been built (and thus, DispatchGroup.enter() called),
|
||||
// notification only occurs the callback completes.
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
groupExpectation.fulfill()
|
||||
}
|
||||
|
||||
// Step 3 - run closure
|
||||
closure(.tagged(TestUtils.Packages.Keys.khmer_angkor, TestUtils.Keyboards.khmerAngkorKMP, TestUtils.Keyboards.khmer_angkor.fullID))
|
||||
|
||||
wait(for: [installExpectation, groupExpectation], timeout: 5, enforceOrder: true)
|
||||
|
||||
// Step 4 - verify installation
|
||||
XCTAssertTrue(Storage.active.userDefaults.userKeyboards!.contains { $0.fullID == TestUtils.Keyboards.khmer_angkor.fullID })
|
||||
XCTAssertNotNil(ResourceFileManager.shared.getInstalledPackage(withKey: TestUtils.Packages.Keys.khmer_angkor))
|
||||
}
|
||||
|
||||
func testDefaultKeyboardInstallationClosureTaggedError() throws {
|
||||
// Step 1: mocking.
|
||||
let packageDownloadTask = TestUtils.Downloading.MockResult(location: TestUtils.Keyboards.khmerAngkorKMP, error: TestUtils.mockedError)
|
||||
mockedURLSession!.queueMockResult(.download(packageDownloadTask))
|
||||
|
||||
let installExpectation = XCTestExpectation(description: "Keyboard package download & installation should complete")
|
||||
let groupExpectation = XCTestExpectation(description: "DispatchGroup should notify")
|
||||
|
||||
// Step 2 - build closure & synchronization check
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
let closure = KeyboardSearchViewController.defaultKeyboardInstallationClosure(withDownloadManager: downloadManager, dispatchGroup: dispatchGroup) { result in
|
||||
if case .error(_) = result {
|
||||
// Success!
|
||||
} else {
|
||||
XCTFail("keyboard installation did not result in mocked error.")
|
||||
}
|
||||
|
||||
installExpectation.fulfill()
|
||||
}
|
||||
|
||||
// As the closure has now been built (and thus, DispatchGroup.enter() called),
|
||||
// notification only occurs the callback completes.
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
groupExpectation.fulfill()
|
||||
}
|
||||
|
||||
// Step 3 - run closure
|
||||
closure(.tagged(TestUtils.Packages.Keys.khmer_angkor, TestUtils.Keyboards.khmerAngkorKMP, TestUtils.Keyboards.khmer_angkor.fullID))
|
||||
|
||||
wait(for: [installExpectation, groupExpectation], timeout: 5, enforceOrder: true)
|
||||
|
||||
// Step 4 - verify lack of installation
|
||||
XCTAssertFalse(Storage.active.userDefaults.userKeyboards?.contains { $0.fullID == TestUtils.Keyboards.khmer_angkor.fullID } ?? false)
|
||||
XCTAssertNil(ResourceFileManager.shared.getInstalledPackage(withKey: TestUtils.Packages.Keys.khmer_angkor))
|
||||
}
|
||||
|
||||
func testDefaultKeyboardInstallationClosureTaggedCancel() throws {
|
||||
// Step 1: mocking.
|
||||
let installExpectation = XCTestExpectation(description: "Keyboard package download & installation should complete")
|
||||
let groupExpectation = XCTestExpectation(description: "DispatchGroup should notify")
|
||||
|
||||
// Step 2 - build closure & synchronization check
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
let closure = KeyboardSearchViewController.defaultKeyboardInstallationClosure(withDownloadManager: downloadManager, dispatchGroup: dispatchGroup) { result in
|
||||
if case .cancelled = result {
|
||||
// Success!
|
||||
} else {
|
||||
XCTFail("keyboard-search cancellation handled improperly.")
|
||||
}
|
||||
|
||||
installExpectation.fulfill()
|
||||
}
|
||||
|
||||
// As the closure has now been built (and thus, DispatchGroup.enter() called),
|
||||
// notification only occurs the callback completes.
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
groupExpectation.fulfill()
|
||||
}
|
||||
|
||||
// Step 3 - run closure
|
||||
closure(.cancelled)
|
||||
|
||||
wait(for: [installExpectation, groupExpectation], timeout: 5, enforceOrder: true)
|
||||
}
|
||||
|
||||
func testDefaultLexicalModelInstallationClosureTaggedSuccess() throws {
|
||||
// Step 1 - mocking
|
||||
let packageDownloadTask = TestUtils.Downloading.MockResult(location: TestUtils.LexicalModels.mtntKMP, error: nil)
|
||||
mockedURLSession!.queueMockResult(.download(packageDownloadTask))
|
||||
|
||||
let installExpectation = XCTestExpectation(description: "Lexical model package download & installation should complete")
|
||||
let groupExpectation = XCTestExpectation(description: "DispatchGroup should notify")
|
||||
|
||||
// Step 2 - build closure
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
let closure = KeyboardSearchViewController.defaultLexicalModelInstallationClosure(withDownloadManager: downloadManager, dispatchGroup: dispatchGroup) { result in
|
||||
if case let .success(fullID) = result {
|
||||
XCTAssertEqual(fullID as? FullLexicalModelID, TestUtils.LexicalModels.mtnt.fullID)
|
||||
} else {
|
||||
XCTFail("Lexical model installation did not succeed.")
|
||||
}
|
||||
|
||||
installExpectation.fulfill()
|
||||
}
|
||||
|
||||
// As the closure has now been built (and thus, DispatchGroup.enter() called),
|
||||
// notification only occurs the callback completes.
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
groupExpectation.fulfill()
|
||||
}
|
||||
|
||||
// Step 3 - run closure
|
||||
closure(.tagged(TestUtils.Packages.Keys.nrc_en_mtnt, TestUtils.LexicalModels.mtntKMP, TestUtils.LexicalModels.mtnt.fullID))
|
||||
|
||||
wait(for: [installExpectation, groupExpectation], timeout: 5, enforceOrder: true)
|
||||
|
||||
// Step 4: Verify installation
|
||||
XCTAssertTrue(Storage.active.userDefaults.userLexicalModels!.contains { $0.fullID == TestUtils.LexicalModels.mtnt.fullID })
|
||||
XCTAssertNotNil(ResourceFileManager.shared.getInstalledPackage(withKey: TestUtils.Packages.Keys.nrc_en_mtnt))
|
||||
}
|
||||
|
||||
func testDefaultLexicalModelInstallationClosureTaggedError() throws {
|
||||
// Step 1 - mocking
|
||||
let packageDownloadTask = TestUtils.Downloading.MockResult(location: TestUtils.LexicalModels.mtntKMP, error: TestUtils.mockedError)
|
||||
mockedURLSession!.queueMockResult(.download(packageDownloadTask))
|
||||
|
||||
let installExpectation = XCTestExpectation(description: "Lexical model package download & installation should complete")
|
||||
let groupExpectation = XCTestExpectation(description: "DispatchGroup should notify")
|
||||
|
||||
// Step 2 - build closure
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
let closure = KeyboardSearchViewController.defaultLexicalModelInstallationClosure(withDownloadManager: downloadManager, dispatchGroup: dispatchGroup) { result in
|
||||
if case .error(_) = result {
|
||||
// Mocked success!
|
||||
} else {
|
||||
XCTFail("Lexical model installation did not generated mocked error.")
|
||||
}
|
||||
|
||||
installExpectation.fulfill()
|
||||
}
|
||||
|
||||
// As the closure has now been built (and thus, DispatchGroup.enter() called),
|
||||
// notification only occurs the callback completes.
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
groupExpectation.fulfill()
|
||||
}
|
||||
|
||||
// Step 3 - run closure
|
||||
closure(.tagged(TestUtils.Packages.Keys.nrc_en_mtnt, TestUtils.LexicalModels.mtntKMP, TestUtils.LexicalModels.mtnt.fullID))
|
||||
|
||||
wait(for: [installExpectation, groupExpectation], timeout: 5, enforceOrder: true)
|
||||
|
||||
// Step 4: Verify lack of installation
|
||||
XCTAssertFalse(Storage.active.userDefaults.userLexicalModels?.contains { $0.fullID == TestUtils.LexicalModels.mtnt.fullID } ?? false)
|
||||
XCTAssertNil(ResourceFileManager.shared.getInstalledPackage(withKey: TestUtils.Packages.Keys.nrc_en_mtnt))
|
||||
}
|
||||
|
||||
func testDefaultLexicalModelInstallationClosureTaggedCancel() throws {
|
||||
let installExpectation = XCTestExpectation(description: "Lexical model package download & installation should complete")
|
||||
let groupExpectation = XCTestExpectation(description: "DispatchGroup should notify")
|
||||
|
||||
// Step 2 - build closure
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
let closure = KeyboardSearchViewController.defaultLexicalModelInstallationClosure(withDownloadManager: downloadManager, dispatchGroup: dispatchGroup) { result in
|
||||
if case .cancelled = result {
|
||||
// Mocked success!
|
||||
} else {
|
||||
XCTFail("Keyboard-search model cancellation handled improperly.")
|
||||
}
|
||||
|
||||
installExpectation.fulfill()
|
||||
}
|
||||
|
||||
// As the closure has now been built (and thus, DispatchGroup.enter() called),
|
||||
// notification only occurs the callback completes.
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
groupExpectation.fulfill()
|
||||
}
|
||||
|
||||
// Step 3 - run closure
|
||||
closure(.cancelled)
|
||||
|
||||
wait(for: [installExpectation, groupExpectation], timeout: 5, enforceOrder: true)
|
||||
}
|
||||
|
||||
func testDeferredLexicalModelSearch() {
|
||||
// Step 1: mocking.
|
||||
let packageDownloadTask = TestUtils.Downloading.MockResult(location: TestUtils.Keyboards.khmerAngkorKMP, error: nil)
|
||||
mockedURLSession!.queueMockResult(.download(packageDownloadTask))
|
||||
|
||||
let kbdExpectation = XCTestExpectation(description: "Keyboard package download & installation should complete")
|
||||
let lmExpectation = XCTestExpectation(description: "Deferred lexical model search should be triggered")
|
||||
let groupExpectation = XCTestExpectation(description: "DispatchGroup should notify")
|
||||
|
||||
// Step 2 - build closures & synchronization check
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
let kbdClosure = KeyboardSearchViewController.defaultKeyboardInstallationClosure(withDownloadManager: downloadManager, dispatchGroup: dispatchGroup) { result in
|
||||
if case let .success(fullID) = result {
|
||||
XCTAssertEqual(fullID as? FullKeyboardID, TestUtils.Keyboards.khmer_angkor.fullID)
|
||||
} else {
|
||||
XCTFail("keyboard installation did not succeed.")
|
||||
}
|
||||
|
||||
kbdExpectation.fulfill()
|
||||
}
|
||||
|
||||
// This whole shebang is to mock the "untagged" closure and ensure it is properly called
|
||||
// when a language tag is discovered at/after package install, rather than at download time.
|
||||
dispatchGroup.enter()
|
||||
let lmCallClosure: KeyboardSearchViewController.DeferredLexicalModelSearch = { param in
|
||||
switch param {
|
||||
case .tag(let tag):
|
||||
XCTAssertEqual(tag, "km")
|
||||
default:
|
||||
XCTFail()
|
||||
}
|
||||
|
||||
lmExpectation.fulfill()
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
|
||||
// As the closure has now been built (and thus, DispatchGroup.enter() called),
|
||||
// notification only occurs the callback completes.
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
groupExpectation.fulfill()
|
||||
}
|
||||
|
||||
// Step 3 - run closure
|
||||
kbdClosure(.untagged(TestUtils.Packages.Keys.khmer_angkor, TestUtils.Keyboards.khmerAngkorKMP, lmCallClosure))
|
||||
|
||||
wait(for: [kbdExpectation, lmExpectation, groupExpectation], timeout: 5, enforceOrder: true)
|
||||
}
|
||||
|
||||
// Synchronization test: using both closures TOGETHER.
|
||||
func testDefaultClosureSynchronization() {
|
||||
let kbdExpectation = XCTestExpectation()
|
||||
let lmExpectation = XCTestExpectation()
|
||||
let groupExpectation = XCTestExpectation(description: "DispatchGroup should notify")
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
let kbdClosure = KeyboardSearchViewController.defaultKeyboardInstallationClosure(withDownloadManager: downloadManager) { _ in kbdExpectation.fulfill() }
|
||||
let lmClosure = KeyboardSearchViewController.defaultLexicalModelInstallationClosure(withDownloadManager: downloadManager) { _ in lmExpectation.fulfill() }
|
||||
|
||||
// As the closures have now been built (and thus, DispatchGroup.enter() called),
|
||||
// notification only occurs the callback completes.
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
// Signals that both have completed.
|
||||
groupExpectation.fulfill()
|
||||
}
|
||||
|
||||
// Step 3 - run closure
|
||||
kbdClosure(.cancelled)
|
||||
lmClosure(.cancelled)
|
||||
|
||||
// These calls will technically be synchronous, so we can rely on kbd and lm to be
|
||||
// in the correct order. The key is that the groupExpectation is only fulfilled after
|
||||
// the other two.
|
||||
wait(for: [kbdExpectation, lmExpectation, groupExpectation], timeout: 5, enforceOrder: true)
|
||||
}
|
||||
//
|
||||
// func testDefaultKeyboardInstallationClosureTaggedSuccess() throws {
|
||||
// // Step 1: mocking.
|
||||
// let packageDownloadTask = TestUtils.Downloading.MockResult(location: TestUtils.Keyboards.khmerAngkorKMP, error: nil)
|
||||
// mockedURLSession!.queueMockResult(.download(packageDownloadTask))
|
||||
//
|
||||
// let installExpectation = XCTestExpectation(description: "Keyboard package download & installation should complete")
|
||||
// let groupExpectation = XCTestExpectation(description: "DispatchGroup should notify")
|
||||
//
|
||||
// // Step 2 - build closure & synchronization check
|
||||
// let dispatchGroup = DispatchGroup()
|
||||
//
|
||||
// let closure = KeyboardSearchViewController.defaultKeyboardInstallationClosure(withDownloadManager: downloadManager, dispatchGroup: dispatchGroup) { result in
|
||||
// if case let .success(fullID) = result {
|
||||
// XCTAssertEqual(fullID as? FullKeyboardID, TestUtils.Keyboards.khmer_angkor.fullID)
|
||||
// } else {
|
||||
// XCTFail("keyboard installation did not succeed.")
|
||||
// }
|
||||
//
|
||||
// installExpectation.fulfill()
|
||||
// }
|
||||
//
|
||||
// // As the closure has now been built (and thus, DispatchGroup.enter() called),
|
||||
// // notification only occurs the callback completes.
|
||||
// dispatchGroup.notify(queue: .main) {
|
||||
// groupExpectation.fulfill()
|
||||
// }
|
||||
//
|
||||
// // Step 3 - run closure
|
||||
// closure(.tagged(TestUtils.Packages.Keys.khmer_angkor, TestUtils.Keyboards.khmerAngkorKMP, TestUtils.Keyboards.khmer_angkor.fullID))
|
||||
//
|
||||
// wait(for: [installExpectation, groupExpectation], timeout: 5, enforceOrder: true)
|
||||
//
|
||||
// // Step 4 - verify installation
|
||||
// XCTAssertTrue(Storage.active.userDefaults.userKeyboards!.contains { $0.fullID == TestUtils.Keyboards.khmer_angkor.fullID })
|
||||
// XCTAssertNotNil(ResourceFileManager.shared.getInstalledPackage(withKey: TestUtils.Packages.Keys.khmer_angkor))
|
||||
// }
|
||||
//
|
||||
// func testDefaultKeyboardInstallationClosureTaggedError() throws {
|
||||
// // Step 1: mocking.
|
||||
// let packageDownloadTask = TestUtils.Downloading.MockResult(location: TestUtils.Keyboards.khmerAngkorKMP, error: TestUtils.mockedError)
|
||||
// mockedURLSession!.queueMockResult(.download(packageDownloadTask))
|
||||
//
|
||||
// let installExpectation = XCTestExpectation(description: "Keyboard package download & installation should complete")
|
||||
// let groupExpectation = XCTestExpectation(description: "DispatchGroup should notify")
|
||||
//
|
||||
// // Step 2 - build closure & synchronization check
|
||||
// let dispatchGroup = DispatchGroup()
|
||||
//
|
||||
// let closure = KeyboardSearchViewController.defaultKeyboardInstallationClosure(withDownloadManager: downloadManager, dispatchGroup: dispatchGroup) { result in
|
||||
// if case .error(_) = result {
|
||||
// // Success!
|
||||
// } else {
|
||||
// XCTFail("keyboard installation did not result in mocked error.")
|
||||
// }
|
||||
//
|
||||
// installExpectation.fulfill()
|
||||
// }
|
||||
//
|
||||
// // As the closure has now been built (and thus, DispatchGroup.enter() called),
|
||||
// // notification only occurs the callback completes.
|
||||
// dispatchGroup.notify(queue: .main) {
|
||||
// groupExpectation.fulfill()
|
||||
// }
|
||||
//
|
||||
// // Step 3 - run closure
|
||||
// closure(.tagged(TestUtils.Packages.Keys.khmer_angkor, TestUtils.Keyboards.khmerAngkorKMP, TestUtils.Keyboards.khmer_angkor.fullID))
|
||||
//
|
||||
// wait(for: [installExpectation, groupExpectation], timeout: 5, enforceOrder: true)
|
||||
//
|
||||
// // Step 4 - verify lack of installation
|
||||
// XCTAssertFalse(Storage.active.userDefaults.userKeyboards?.contains { $0.fullID == TestUtils.Keyboards.khmer_angkor.fullID } ?? false)
|
||||
// XCTAssertNil(ResourceFileManager.shared.getInstalledPackage(withKey: TestUtils.Packages.Keys.khmer_angkor))
|
||||
// }
|
||||
//
|
||||
// func testDefaultKeyboardInstallationClosureTaggedCancel() throws {
|
||||
// // Step 1: mocking.
|
||||
// let installExpectation = XCTestExpectation(description: "Keyboard package download & installation should complete")
|
||||
// let groupExpectation = XCTestExpectation(description: "DispatchGroup should notify")
|
||||
//
|
||||
// // Step 2 - build closure & synchronization check
|
||||
// let dispatchGroup = DispatchGroup()
|
||||
//
|
||||
// let closure = KeyboardSearchViewController.defaultKeyboardInstallationClosure(withDownloadManager: downloadManager, dispatchGroup: dispatchGroup) { result in
|
||||
// if case .cancelled = result {
|
||||
// // Success!
|
||||
// } else {
|
||||
// XCTFail("keyboard-search cancellation handled improperly.")
|
||||
// }
|
||||
//
|
||||
// installExpectation.fulfill()
|
||||
// }
|
||||
//
|
||||
// // As the closure has now been built (and thus, DispatchGroup.enter() called),
|
||||
// // notification only occurs the callback completes.
|
||||
// dispatchGroup.notify(queue: .main) {
|
||||
// groupExpectation.fulfill()
|
||||
// }
|
||||
//
|
||||
// // Step 3 - run closure
|
||||
// closure(.cancelled)
|
||||
//
|
||||
// wait(for: [installExpectation, groupExpectation], timeout: 5, enforceOrder: true)
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue