refactor(ios/engine): target abstraction - ResourceFileManager.install

This commit is contained in:
jahorton 2020-06-03 16:28:09 +07:00
parent df2a31f075
commit 6535fac385
3 changed files with 113 additions and 96 deletions

View file

@ -617,60 +617,11 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
throw KMPError.wrongPackageType
}
for r in kmp.resources {
let installableFiles: [(LanguageResource, [(String, URL)])] = r.installableResources.map { resource in
// (source file, destination file)
var set: [(String, URL)] = [(resource.sourceFilename, Storage.active.resourceURL(for: resource)!)]
let installableFonts: [(String, URL)] = resource.fonts.map { font in
// KMPs only list a single font file for each entry whenever one is included.
// A pre-existing assumption.
let fontFile = font.source[0]
return (fontFile, Storage.active.fontURL(forResource: resource, filename: fontFile)!)
}
set.append(contentsOf: installableFonts)
// (resource, source-destination file mapping)
return (resource, set)
}
var haveInstalledOne = false
for set in installableFiles {
let resource = set.0
let files = set.1
do {
try FileManager.default.createDirectory(at: Storage.active.resourceDir(for: resource)!,
withIntermediateDirectories: true)
} catch {
log.error("Could not create dir for download: \(error)")
throw KMPError.fileSystem
}
do {
for item in files {
var filePath = folder
filePath.appendPathComponent(item.0)
// TODO: The rest of this block may be replaced with ResourceFileManager.copyWithOverwrite
// once this method is fully abstracted and its core is placed within said class.
if(FileManager.default.fileExists(atPath: (item.1).path)) {
try FileManager.default.removeItem(at: item.1)
}
try FileManager.default.copyItem(at: filePath, to: item.1)
}
} catch {
log.error("Error saving the download: \(error)")
throw KMPError.copyFiles
}
// Only one keyboard is installed by default from a KMP.
// Also, only the first language-pairing it contains
if !haveInstalledOne {
Manager.shared.addKeyboard(resource as! InstallableKeyboard)
haveInstalledOne = true
}
for resourceSet in kmp.installableResourceSets {
for resource in resourceSet {
try ResourceFileManager.shared.install(resource, from: kmp)
// Install the keyboard for only the first language pairing defined in the package.
break
}
}
}
@ -681,48 +632,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
throw KMPError.wrongPackageType
}
for r in kmp.resources {
let installableFiles: [(LanguageResource, [(String, URL)])] = r.installableResources.map { resource in
// (source file, destination file)
let set: [(String, URL)] = [(resource.sourceFilename, Storage.active.resourceURL(for: resource)!)]
// no fonts for lexical models
// (resource, source-destination file mapping)
return (resource, set)
}
for set in installableFiles {
let resource = set.0
let files = set.1
do {
try FileManager.default.createDirectory(at: Storage.active.resourceDir(for: resource)!,
withIntermediateDirectories: true)
} catch {
log.error("Could not create dir for download: \(error)")
throw KMPError.fileSystem
}
do {
for item in files {
var filePath = folder
filePath.appendPathComponent(item.0)
// TODO: The rest of this block may be replaced with ResourceFileManager.copyWithOverwrite
// once this method is fully abstracted and its core is placed within said class.
if(FileManager.default.fileExists(atPath: (item.1).path)) {
try FileManager.default.removeItem(at: item.1)
}
try FileManager.default.copyItem(at: filePath, to: item.1)
}
} catch {
log.error("Error saving the lexical model download: \(error)")
throw KMPError.copyFiles
}
// All pairings are installed for lexical models.
// Also, all models within the KMP
Manager.addLexicalModel(resource as! InstallableLexicalModel)
try kmp.installableResourceSets.forEach { resourceSet in
try resourceSet.forEach { resource in
try ResourceFileManager.shared.install(resource, from: kmp)
}
}
}

View file

@ -178,4 +178,51 @@ public class ResourceFileManager {
completionHandler(error)
}
}
public func install(_ resource: LanguageResource, from package: KeymanPackage) throws {
// Ideally, we wouldn't need the guard... but it's best to check just in case.
guard package.contains(resource) else {
throw KMPError.resourceNotInPackage
}
// (source, destination)
var installableFiles: [(String, URL)] = [(resource.sourceFilename, Storage.active.resourceURL(for: resource)!)]
let installableFonts: [(String, URL)] = resource.fonts.map { font in
// KMPs only list a single font file for each entry whenever one is included.
// A pre-existing assumption.
let fontFile = font.source[0]
return (fontFile, Storage.active.fontURL(forResource: resource, filename: fontFile)!)
}
installableFiles.append(contentsOf: installableFonts)
do {
try FileManager.default.createDirectory(at: Storage.active.resourceDir(for: resource)!,
withIntermediateDirectories: true)
} catch {
log.error("Could not create installation directory: \(error)")
throw KMPError.fileSystem
}
do {
for item in installableFiles {
var filePath = package.sourceFolder
filePath.appendPathComponent(item.0)
try copyWithOverwrite(from: filePath, to: item.1)
}
} catch {
log.error("Error installing the resource: \(error)")
throw KMPError.copyFiles
}
// There's no generalized method for this quite yet. Manager doesn't need even
// more of these.
if let keyboard = resource as? InstallableKeyboard {
Manager.shared.addKeyboard(keyboard)
} else if let lexicalModel = resource as? InstallableLexicalModel {
Manager.addLexicalModel(lexicalModel)
} else {
fatalError("Cannot install instance of unexpected LanguageResource subclass")
}
}
}

View file

@ -69,4 +69,62 @@ class FileManagementTests: XCTestCase {
}
}
}
func testInstallKeyboardFromPackage() throws {
ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.khmerAngkorKMP) { kmp, error in
XCTAssertNotNil(kmp, "Failed to prepare KMP for installation")
XCTAssertNil(error, "Error occurred while preparing KMP for installation")
XCTAssertNotNil(kmp as? KeyboardKeymanPackage, "KMP resource type improperly recognized - expected a keyboard package!")
do {
try ResourceFileManager.shared.install(TestUtils.Keyboards.khmer_angkor, from: kmp!)
} catch {
XCTFail("Unexpected error during KeyboardPackage install")
}
let installURL = Storage.active.keyboardURL(forID: "khmer_angkor", version: "1.0.6")
XCTAssertTrue(FileManager.default.fileExists(atPath: installURL.path),
"Could not find installed keyboard file")
let keyboards = Storage.active.userDefaults.userKeyboards!
XCTAssertEqual(keyboards.count, 1, "Unexpected number of keyboards were installed")
XCTAssertEqual(keyboards[0].id, "khmer_angkor", "Installed keyboard ID mismatch")
// While the KMP's version of the specified InstallableKeyboard does specify Fonts,
// the literal-based testing version does NOT. Since we're installing from the predefined,
// test-copy instance, we expect NOT to see the font from this test's install!
//
// Yes, the KeymanPackage.contains check isn't exhaustive - it just does a pair of
// id checks. This version of events couldn't happen if we thorough enough there.
let fontURL = Storage.active.fontURL(forKeyboardID: "khmer_angkor", filename: "Mondulkiri-R.ttf")
XCTAssertFalse(FileManager.default.fileExists(atPath: fontURL.path))
}
}
func testInstallLexicalModelFromPackage() {
ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.LexicalModels.mtntKMP) { kmp, error in
XCTAssertNotNil(kmp, "Failed to prepare KMP for installation")
XCTAssertNil(error, "Error occurred while preparing KMP for installation")
XCTAssertNotNil(kmp as? LexicalModelKeymanPackage, "KMP resource type improperly recognized - expected a lexical model package!")
do {
try ResourceFileManager.shared.install(TestUtils.LexicalModels.mtnt, from: kmp!)
} catch {
XCTFail("Unexpected error during LexicalModelPackage install")
}
let installURL = Storage.active.lexicalModelURL(forID: "nrc.en.mtnt", version: "0.1.4")
XCTAssertTrue(FileManager.default.fileExists(atPath: installURL.path),
"Could not find installed lexical model file")
let models = Storage.active.userDefaults.userLexicalModels!
// This variant is selective - only a single pairing should be installed for the model.
XCTAssertEqual(models.count, 1, "Unexpected number of models were installed")
XCTAssertEqual(models[0].id, "nrc.en.mtnt", "Installed lexical model ID mismatch")
}
}
}