diff --git a/McBopomofo.xcodeproj/project.pbxproj b/McBopomofo.xcodeproj/project.pbxproj index 5f4ba050..5c06ae41 100644 --- a/McBopomofo.xcodeproj/project.pbxproj +++ b/McBopomofo.xcodeproj/project.pbxproj @@ -60,6 +60,7 @@ D485D3B92796A8A000657FF3 /* PreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D485D3B82796A8A000657FF3 /* PreferencesTests.swift */; }; D485D3C02796CE3200657FF3 /* VersionUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D485D3BF2796CE3200657FF3 /* VersionUpdateTests.swift */; }; D4A13D5A27A59F0B003BE359 /* InputMethodController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A13D5927A59D5C003BE359 /* InputMethodController.swift */; }; + D4A8E43627A9E982002F7A07 /* KeyHandlerPlainBopomofoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A8E43527A9E982002F7A07 /* KeyHandlerPlainBopomofoTests.swift */; }; D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8827A838CF006DB1CF /* Localizable.strings */; }; D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */; }; D4E569DC27A34D0E00AC2CEF /* KeyHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */; }; @@ -218,6 +219,7 @@ D485D3BF2796CE3200657FF3 /* VersionUpdateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionUpdateTests.swift; sourceTree = ""; }; D495583A27A5C6C4006ADE1C /* LanguageModelManager+Privates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LanguageModelManager+Privates.h"; sourceTree = ""; }; D4A13D5927A59D5C003BE359 /* InputMethodController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputMethodController.swift; sourceTree = ""; }; + D4A8E43527A9E982002F7A07 /* KeyHandlerPlainBopomofoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandlerPlainBopomofoTests.swift; sourceTree = ""; }; D4E33D8927A838CF006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; D4E33D8B27A838D5006DB1CF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; D4E33D8C27A838D8006DB1CF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; @@ -501,6 +503,7 @@ isa = PBXGroup; children = ( D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.swift */, + D4A8E43527A9E982002F7A07 /* KeyHandlerPlainBopomofoTests.swift */, D485D3B82796A8A000657FF3 /* PreferencesTests.swift */, D485D3BF2796CE3200657FF3 /* VersionUpdateTests.swift */, ); @@ -754,6 +757,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D4A8E43627A9E982002F7A07 /* KeyHandlerPlainBopomofoTests.swift in Sources */, D47D73A427A5D43900255A50 /* KeyHandlerBopomofoTests.swift in Sources */, D485D3B92796A8A000657FF3 /* PreferencesTests.swift in Sources */, D485D3C02796CE3200657FF3 /* VersionUpdateTests.swift in Sources */, diff --git a/McBopomofoTests/KeyHandlerPlainBopomofoTests.swift b/McBopomofoTests/KeyHandlerPlainBopomofoTests.swift new file mode 100644 index 00000000..7eaecfc2 --- /dev/null +++ b/McBopomofoTests/KeyHandlerPlainBopomofoTests.swift @@ -0,0 +1,163 @@ +import XCTest +@testable import McBopomofo + +class KeyHandlerPlainBopomofoTests: XCTestCase { + var handler = KeyHandler() + + override func setUpWithError() throws { + LanguageModelManager.loadDataModels() + handler = KeyHandler() + handler.inputMode = .plainBopomofo + } + + override func tearDownWithError() throws { + } + + func testPunctuationComma() { + let input = KeyHandlerInput(inputText: "<", keyCode: 0, charCode: charCode("<"), flags: .shift, isVerticalMode: false) + var state: InputState = InputState.Empty() + handler.handle(input, state: state) { newState in + state = newState + } candidateSelectionCallback: { + } errorCallback: { + } + + XCTAssertTrue(state is InputState.ChoosingCandidate, "\(state)") + if let state = state as? InputState.ChoosingCandidate { + XCTAssertEqual(state.composingBuffer, ",") + } + } + + func testPunctuationPeriod() { + let input = KeyHandlerInput(inputText: ">", keyCode: 0, charCode: charCode(">"), flags: .shift, isVerticalMode: false) + var state: InputState = InputState.Empty() + handler.handle(input, state: state) { newState in + state = newState + } candidateSelectionCallback: { + } errorCallback: { + } + + XCTAssertTrue(state is InputState.ChoosingCandidate, "\(state)") + if let state = state as? InputState.ChoosingCandidate { + XCTAssertEqual(state.composingBuffer, "。") + } + } + + func testInputNe() { + let input = KeyHandlerInput(inputText: "s", keyCode: 0, charCode: charCode("s"), flags: .shift, isVerticalMode: false) + var state: InputState = InputState.Empty() + handler.handle(input, state: state) { newState in + state = newState + } candidateSelectionCallback: { + } errorCallback: { + } + + XCTAssertTrue(state is InputState.Inputting, "\(state)") + if let state = state as? InputState.Inputting { + XCTAssertEqual(state.composingBuffer, "ㄋ") + } + } + + func testInputNi() { + var state: InputState = InputState.Empty() + let keys = Array("su").map { String($0) } + for key in keys { + let input = KeyHandlerInput(inputText: key, keyCode: 0, charCode: charCode(key), flags: [], isVerticalMode: false) + handler.handle(input, state: state) { newState in + state = newState + } candidateSelectionCallback: { + } errorCallback: { + } + } + + XCTAssertTrue(state is InputState.Inputting, "\(state)") + if let state = state as? InputState.Inputting { + XCTAssertEqual(state.composingBuffer, "ㄋㄧ") + } + } + + func testInputNi3() { + var state: InputState = InputState.Empty() + let keys = Array("su3").map { String($0) } + for key in keys { + let input = KeyHandlerInput(inputText: key, keyCode: 0, charCode: charCode(key), flags: [], isVerticalMode: false) + handler.handle(input, state: state) { newState in + state = newState + } candidateSelectionCallback: { + } errorCallback: { + } + } + + XCTAssertTrue(state is InputState.ChoosingCandidate, "\(state)") + if let state = state as? InputState.ChoosingCandidate { + XCTAssertTrue(state.candidates.contains("你")) + } + } + + func testCancelCandidateUsingDelete() { + var state: InputState = InputState.Empty() + let keys = Array("su3").map { String($0) } + for key in keys { + let input = KeyHandlerInput(inputText: key, keyCode: 0, charCode: charCode(key), flags: [], isVerticalMode: false) + handler.handle(input, state: state) { newState in + state = newState + } candidateSelectionCallback: { + } errorCallback: { + } + } + + let input = KeyHandlerInput(inputText: " ", keyCode: KeyCode.delete.rawValue, charCode: 0, flags: [], isVerticalMode: false) + handler.handle(input, state: state) { newState in + state = newState + } candidateSelectionCallback: { + } errorCallback: { + } + + XCTAssertTrue(state is InputState.EmptyIgnoringPreviousState, "\(state)") + } + + func testCancelCandidateUsingEsc() { + var state: InputState = InputState.Empty() + let keys = Array("su3").map { String($0) } + for key in keys { + let input = KeyHandlerInput(inputText: key, keyCode: 0, charCode: charCode(key), flags: [], isVerticalMode: false) + handler.handle(input, state: state) { newState in + state = newState + } candidateSelectionCallback: { + } errorCallback: { + } + } + + let input = KeyHandlerInput(inputText: " ", keyCode: 0, charCode: 27, flags: [], isVerticalMode: false) + handler.handle(input, state: state) { newState in + state = newState + } candidateSelectionCallback: { + } errorCallback: { + } + + XCTAssertTrue(state is InputState.EmptyIgnoringPreviousState, "\(state)") + } + + func testAssociatedPhrases() { + let enabled = Preferences.associatedPhrasesEnabled + Preferences.associatedPhrasesEnabled = true + var state: InputState = InputState.Empty() + let keys = Array("aul ").map { String($0) } + for key in keys { + let input = KeyHandlerInput(inputText: key, keyCode: 0, charCode: charCode(key), flags: [], isVerticalMode: false) + handler.handle(input, state: state) { newState in + state = newState + } candidateSelectionCallback: { + } errorCallback: { + } + } + + XCTAssertTrue(state is InputState.AssociatedPhrases, "\(state)") + if let state = state as? InputState.AssociatedPhrases { + XCTAssertTrue(state.candidates.contains("嗚")) + } + Preferences.associatedPhrasesEnabled = enabled + } + + +} diff --git a/McBopomofoTests/PreferencesTests.swift b/McBopomofoTests/PreferencesTests.swift index 03e71781..9977ed6d 100644 --- a/McBopomofoTests/PreferencesTests.swift +++ b/McBopomofoTests/PreferencesTests.swift @@ -3,12 +3,37 @@ import XCTest class PreferencesTests: XCTestCase { + func reset() { + Preferences.allKeys.forEach { + UserDefaults.standard.removeObject(forKey: $0) + } + } + + func makeSnapshot() -> [String: Any] { + var dict = [String: Any]() + Preferences.allKeys.forEach { + dict[$0] = UserDefaults.standard.object(forKey: $0) + } + return dict + } + + func restore(from snapshot:[String: Any]) { + Preferences.allKeys.forEach { + UserDefaults.standard.set(snapshot[$0], forKey: $0) + } + } + + var snapshot: [String: Any]? + override func setUpWithError() throws { - Preferences.reset() + snapshot = makeSnapshot() + reset() } override func tearDownWithError() throws { - Preferences.reset() + if let snapshot = snapshot { + restore(from: snapshot) + } } func testKeyboardLayout() { diff --git a/Source/Preferences.swift b/Source/Preferences.swift index dd332818..8d0b6f64 100644 --- a/Source/Preferences.swift +++ b/Source/Preferences.swift @@ -198,29 +198,29 @@ struct ComposingBufferSize { // MARK: - class Preferences: NSObject { - static func reset() { - let defaults = UserDefaults.standard - defaults.removeObject(forKey: kKeyboardLayoutPreferenceKey) - defaults.removeObject(forKey: kBasisKeyboardLayoutPreferenceKey) - defaults.removeObject(forKey: kFunctionKeyKeyboardLayoutPreferenceKey) - defaults.removeObject(forKey: kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey) - defaults.removeObject(forKey: kCandidateListTextSizeKey) - defaults.removeObject(forKey: kSelectPhraseAfterCursorAsCandidatePreferenceKey) - defaults.removeObject(forKey: kUseHorizontalCandidateListPreferenceKey) - defaults.removeObject(forKey: kComposingBufferSizePreferenceKey) - defaults.removeObject(forKey: kChooseCandidateUsingSpaceKey) - defaults.removeObject(forKey: kChineseConversionEnabledKey) - defaults.removeObject(forKey: kHalfWidthPunctuationEnabledKey) - defaults.removeObject(forKey: kEscToCleanInputBufferKey) - defaults.removeObject(forKey: kCandidateTextFontName) - defaults.removeObject(forKey: kCandidateKeyLabelFontName) - defaults.removeObject(forKey: kCandidateKeys) - defaults.removeObject(forKey: kPhraseReplacementEnabledKey) - defaults.removeObject(forKey: kChineseConversionEngineKey) - defaults.removeObject(forKey: kChineseConversionStyle) - defaults.removeObject(forKey: kAssociatedPhrasesEnabledKey) + static var allKeys:[String] { + [kKeyboardLayoutPreferenceKey, + kBasisKeyboardLayoutPreferenceKey, + kFunctionKeyKeyboardLayoutPreferenceKey, + kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey, + kCandidateListTextSizeKey, + kSelectPhraseAfterCursorAsCandidatePreferenceKey, + kUseHorizontalCandidateListPreferenceKey, + kComposingBufferSizePreferenceKey, + kChooseCandidateUsingSpaceKey, + kChineseConversionEnabledKey, + kHalfWidthPunctuationEnabledKey, + kEscToCleanInputBufferKey, + kCandidateTextFontName, + kCandidateKeyLabelFontName, + kCandidateKeys, + kPhraseReplacementEnabledKey, + kChineseConversionEngineKey, + kChineseConversionStyle, + kAssociatedPhrasesEnabledKey] } + @UserDefault(key: kKeyboardLayoutPreferenceKey, defaultValue: 0) @objc static var keyboardLayout: Int