From 136e8088be581db49a5773b9e7aaf604b31913e7 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Fri, 25 Aug 2023 03:16:57 +0800 Subject: [PATCH] MainAssembly // Implementing Unit Tests. * Further unit tests will be implemented later according to other necessities. --- .../KeyCodeMapForTests.swift | 17 ++ .../ComponentsForTests/LMAssemblyImpl.swift | 65 ++++++++ .../ComponentsForTests/LMDataForTests.swift | 112 +++++++++++++ .../ComponentsForTests/MockedClient.swift | 119 ++++++++++++++ .../NSEventImplForTests.swift | 64 ++++++++ .../MainAssemblyTests/MainAssemblyTests.swift | 15 -- .../MainAssemblyTests_Core.swift | 104 ++++++++++++ .../MainAssemblyTests_Test1.swift | 154 ++++++++++++++++++ 8 files changed, 635 insertions(+), 15 deletions(-) create mode 100644 Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/KeyCodeMapForTests.swift create mode 100644 Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/LMAssemblyImpl.swift create mode 100644 Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/LMDataForTests.swift create mode 100644 Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/MockedClient.swift create mode 100644 Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/NSEventImplForTests.swift delete mode 100644 Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests.swift create mode 100644 Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests_Core.swift create mode 100644 Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests_Test1.swift diff --git a/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/KeyCodeMapForTests.swift b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/KeyCodeMapForTests.swift new file mode 100644 index 00000000..433d03d0 --- /dev/null +++ b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/KeyCodeMapForTests.swift @@ -0,0 +1,17 @@ +// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// No trademark license is granted to use the trade names, trademarks, service +// marks, or product names of Contributor, except as required to fulfill notice +// requirements defined in MIT License. + +import Foundation + +let mapKeyCodesANSIForTests: [String: UInt16] = [ + "1": 18, "2": 19, "3": 20, "4": 21, "5": 23, "6": 22, "7": 26, "8": 28, "9": 25, "0": 29, "-": 27, + "=": 24, "q": 12, "w": 13, "e": 14, "r": 15, "t": 17, "y": 16, "u": 32, "i": 34, "o": 31, "p": 35, + "[": 33, "]": 30, "\\": 42, "a": 0, "s": 1, "d": 2, "f": 3, "g": 5, "h": 4, "j": 38, "k": 40, + "l": 37, ";": 41, "'": 39, "z": 6, "x": 7, "c": 8, "v": 9, "b": 11, "n": 45, "m": 46, ",": 43, + ".": 47, "/": 44, +] diff --git a/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/LMAssemblyImpl.swift b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/LMAssemblyImpl.swift new file mode 100644 index 00000000..48d4a7af --- /dev/null +++ b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/LMAssemblyImpl.swift @@ -0,0 +1,65 @@ +// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// No trademark license is granted to use the trade names, trademarks, service +// marks, or product names of Contributor, except as required to fulfill notice +// requirements defined in MIT License. + +import LangModelAssembly +import MainAssembly + +// MARK: - Converting Sample String Data to LMCoreJSON Instance. + +extension String { + func toDictMap(swapKeyValue: Bool = false, encrypt: Bool = false) -> [String: [String]] { + var theDict = [String: [String]]() + enumerateLines { currentLine, _ in + if currentLine.isEmpty || currentLine.hasPrefix("#") { + return + } + let linestream = currentLine.split(separator: " ") + let col0 = String(linestream[0]) + let col1 = String(linestream[1]) + let col2: Double? = Double(linestream[2]) + var key = swapKeyValue ? col1 : col0 + if encrypt { + key = vChewingLM.LMCoreJSON.cnvPhonabetToASCII(key) + } + var storedValue = swapKeyValue ? col0 : col1 + if let col2 = col2 { + storedValue.insert(contentsOf: "\(col2.description) ", at: storedValue.startIndex) + } + theDict[key, default: []].append(storedValue) + } + return theDict + } +} + +// MARK: - Allow LMInstantiator to Load Test Data. + +extension vChewingLM.LMInstantiator { + static func construct( + isCHS: Bool = false, completionHandler: @escaping (_ this: vChewingLM.LMInstantiator) -> Void + ) -> vChewingLM.LMInstantiator { + let this = vChewingLM.LMInstantiator(isCHS: isCHS) + completionHandler(this) + return this + } + + func loadTestData() { + resetFactoryJSONModels() + loadLanguageModel( + json: ( + dict: strSampleDataFactoryCore.toDictMap(swapKeyValue: false, encrypt: true), + path: "/dev/null" + ) + ) + loadSymbolData( + json: ( + dict: strSampleDataFactorySymbol.toDictMap(swapKeyValue: true, encrypt: true), + path: "/dev/null" + ) + ) + } +} diff --git a/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/LMDataForTests.swift b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/LMDataForTests.swift new file mode 100644 index 00000000..07a112f7 --- /dev/null +++ b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/LMDataForTests.swift @@ -0,0 +1,112 @@ +// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// No trademark license is granted to use the trade names, trademarks, service +// marks, or product names of Contributor, except as required to fulfill notice +// requirements defined in MIT License. + +import MainAssembly + +public let strSampleDataFactorySymbol = #""" +ㄈㄥ 🐝 -11 +ㄇㄧˋ-ㄈㄥ 🐝 -11 +"""# + +public let strSampleDataFactoryCore = #""" +# +# 下述詞頻資料取自 libTaBE 資料庫 (http://sourceforge.net/projects/libtabe/) +# (2002 最終版). 該專案於 1999 年由 Pai-Hsiang Hsiao 發起、以 BSD 授權發行。 +# +ㄇㄧˋ-ㄈㄥ 蜜蜂 -3.6231 +ㄇㄧˋ 蜜 -4.6231 +ㄈㄥ 蜂 -4.6231 +ㄋㄧˇ 你 -6.000000 // Non-LibTaBE +ㄓㄜˋ 這 -6.000000 // Non-LibTaBE +ㄧㄤˋ 樣 -6.000000 // Non-LibTaBE +ㄙ 絲 -9.495858 +ㄙ 思 -9.006414 +ㄙ 私 -99.000000 +ㄙ 斯 -8.091803 +ㄙ 司 -99.000000 +ㄙ 嘶 -13.513987 +ㄙ 撕 -12.259095 +ㄍㄠ 高 -7.171551 +ㄎㄜ 顆 -10.574273 +ㄎㄜ 棵 -11.504072 +ㄎㄜ 刻 -10.450457 +ㄎㄜ 科 -7.171052 +ㄎㄜ 柯 -99.000000 +ㄍㄠ 膏 -11.928720 +ㄍㄠ 篙 -13.624335 +ㄍㄠ 糕 -12.390804 +ㄉㄜ˙ 的 -3.516024 +ㄉㄧˊ 的 -3.516024 +ㄉㄧˋ 的 -3.516024 +ㄓㄨㄥ 中 -5.809297 +ㄉㄜ˙ 得 -7.427179 +ㄍㄨㄥ 共 -8.381971 +ㄍㄨㄥ 供 -8.501463 +ㄐㄧˋ 既 -99.000000 +ㄐㄧㄣ 今 -8.034095 +ㄍㄨㄥ 紅 -8.858181 +ㄐㄧˋ 際 -7.608341 +ㄐㄧˋ 季 -99.000000 +ㄐㄧㄣ 金 -7.290109 +ㄐㄧˋ 騎 -10.939895 +ㄓㄨㄥ 終 -99.000000 +ㄐㄧˋ 記 -99.000000 +ㄐㄧˋ 寄 -99.000000 +ㄐㄧㄣ 斤 -99.000000 +ㄐㄧˋ 繼 -9.715317 +ㄐㄧˋ 計 -7.926683 +ㄐㄧˋ 暨 -8.373022 +ㄓㄨㄥ 鐘 -9.877580 +ㄐㄧㄣ 禁 -10.711079 +ㄍㄨㄥ 公 -7.877973 +ㄍㄨㄥ 工 -7.822167 +ㄍㄨㄥ 攻 -99.000000 +ㄍㄨㄥ 功 -99.000000 +ㄍㄨㄥ 宮 -99.000000 +ㄓㄨㄥ 鍾 -9.685671 +ㄐㄧˋ 繫 -10.425662 +ㄍㄨㄥ 弓 -99.000000 +ㄍㄨㄥ 恭 -99.000000 +ㄐㄧˋ 劑 -8.888722 +ㄐㄧˋ 祭 -10.204425 +ㄐㄧㄣ 浸 -11.378321 +ㄓㄨㄥ 盅 -99.000000 +ㄐㄧˋ 忌 -99.000000 +ㄐㄧˋ 技 -8.450826 +ㄐㄧㄣ 筋 -11.074890 +ㄍㄨㄥ 躬 -99.000000 +ㄐㄧˋ 冀 -12.045357 +ㄓㄨㄥ 忠 -99.000000 +ㄐㄧˋ 妓 -99.000000 +ㄐㄧˋ 濟 -9.517568 +ㄐㄧˋ 薊 -12.021587 +ㄐㄧㄣ 巾 -99.000000 +ㄐㄧㄣ 襟 -12.784206 +ㄋㄧㄢˊ 年 -6.086515 +ㄐㄧㄤˇ 講 -9.164384 +ㄐㄧㄤˇ 獎 -8.690941 +ㄐㄧㄤˇ 蔣 -10.127828 +ㄋㄧㄢˊ 黏 -11.336864 +ㄋㄧㄢˊ 粘 -11.285740 +ㄐㄧㄤˇ 槳 -12.492933 +ㄍㄨㄥ-ㄙ 公司 -6.299461 +ㄎㄜ-ㄐㄧˋ 科技 -6.736613 +ㄐㄧˋ-ㄍㄨㄥ 濟公 -13.336653 +ㄐㄧㄤˇ-ㄐㄧㄣ 獎金 -10.344678 +ㄋㄧㄢˊ-ㄓㄨㄥ 年終 -11.668947 +ㄋㄧㄢˊ-ㄓㄨㄥ 年中 -11.373044 +ㄍㄠ-ㄎㄜ-ㄐㄧˋ 高科技 -9.842421 +ㄓㄜˋ-ㄧㄤˋ 這樣 -6.000000 // Non-LibTaBE +ㄋㄧˇ-ㄓㄜˋ 你這 -9.000000 // Non-LibTaBE +ㄎㄜ-ㄎㄜ 顆顆 -8.000000 // Non-LibTaBE +ㄐㄧㄠˋ 教 -3.676169 +ㄐㄧㄠˋ 較 -3.24869962 +ㄐㄧㄠˋ-ㄩˋ 教育 -3.32220565 +ㄩˋ 育 -3.30192952 + +"""# diff --git a/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/MockedClient.swift b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/MockedClient.swift new file mode 100644 index 00000000..b58dae8e --- /dev/null +++ b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/MockedClient.swift @@ -0,0 +1,119 @@ +// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// No trademark license is granted to use the trade names, trademarks, service +// marks, or product names of Contributor, except as required to fulfill notice +// requirements defined in MIT License. + +import InputMethodKit + +class FakeClient: NSObject, IMKTextInput { + var attributedString: NSMutableAttributedString = .init(string: "") + var cursor = 0 { + didSet { + cursor = max(0, min(cursor, attributedString.length)) + } + } + + var selectedRangeStored: NSRange = .notFound + var markedRangeStored: NSRange = .notFound + var markedText: NSAttributedString = .init(string: "") + + func toString() -> String { + attributedString.string + } + + func clear() { + cursor = 0 + attributedString = .init() + } + + func insertText(_ string: Any!, replacementRange: NSRange) { + guard let string = string as? String else { return } + var insertionPoint = replacementRange.location + if insertionPoint == NSNotFound { + insertionPoint = cursor + } + cursor = insertionPoint + attributedString.insert(.init(string: string), at: cursor) + cursor += string.utf16.count + } + + func setMarkedText(_ string: Any!, selectionRange _: NSRange, replacementRange: NSRange) { + markedText = string as? NSAttributedString ?? .init(string: string as? String ?? "") + var insertionPoint = replacementRange.location + if insertionPoint == NSNotFound { + insertionPoint = cursor + } + cursor = insertionPoint + } + + func selectedRange() -> NSRange { + NSIntersectionRange(selectedRangeStored, .init(location: 0, length: attributedString.length)) + } + + func markedRange() -> NSRange { + NSIntersectionRange(markedRangeStored, .init(location: 0, length: attributedString.length)) + } + + func attributedSubstring(from range: NSRange) -> NSAttributedString! { + let usableRange = NSIntersectionRange(range, .init(location: 0, length: attributedString.length)) + return attributedString.attributedSubstring(from: usableRange) + } + + func length() -> Int { + attributedString.length + } + + func characterIndex(for _: NSPoint, tracking _: IMKLocationToOffsetMappingMode, inMarkedRange _: UnsafeMutablePointer!) -> Int { + cursor + } + + func attributes(forCharacterIndex _: Int, lineHeightRectangle _: UnsafeMutablePointer!) -> [AnyHashable: Any]! { + [:] + } + + func validAttributesForMarkedText() -> [Any]! { + [] + } + + func overrideKeyboard(withKeyboardNamed keyboardUniqueName: String!) { + _ = keyboardUniqueName + } + + func selectMode(_ modeIdentifier: String!) { + _ = modeIdentifier + } + + func supportsUnicode() -> Bool { + true + } + + func bundleIdentifier() -> String { + "org.atelierInmu.vChewing.MainAssembly.UnitTests.MockedClient" + } + + func windowLevel() -> CGWindowLevel { + CGShieldingWindowLevel() + } + + func supportsProperty(_: TSMDocumentPropertyTag) -> Bool { + false + } + + func uniqueClientIdentifierString() -> String { + bundleIdentifier() + } + + func string(from range: NSRange, actualRange: NSRangePointer!) -> String! { + let actualNSRange = actualRange.move() + var usableRange = NSIntersectionRange(actualNSRange, range) + usableRange = NSIntersectionRange(usableRange, .init(location: 0, length: attributedString.length)) + return attributedString.attributedSubstring(from: usableRange).string + } + + func firstRect(forCharacterRange _: NSRange, actualRange _: NSRangePointer!) -> NSRect { + .zero + } +} diff --git a/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/NSEventImplForTests.swift b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/NSEventImplForTests.swift new file mode 100644 index 00000000..da4063b3 --- /dev/null +++ b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/ComponentsForTests/NSEventImplForTests.swift @@ -0,0 +1,64 @@ +// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// No trademark license is granted to use the trade names, trademarks, service +// marks, or product names of Contributor, except as required to fulfill notice +// requirements defined in MIT License. + +import AppKit + +public extension NSEvent { + struct KeyEventData { + public var type: EventType = .keyDown + public var flags: ModifierFlags + public var chars: String + public var charsSansModifiers: String + public var keyCode: UInt16 + + public init(type: EventType = .keyDown, flags: ModifierFlags = [], chars: String, charsSansModifiers: String? = nil, keyCode: UInt16? = nil) { + self.type = type + self.flags = flags + self.chars = chars + self.charsSansModifiers = charsSansModifiers ?? chars + self.keyCode = keyCode ?? mapKeyCodesANSIForTests[chars] ?? 65535 + } + + public func toEvents(paired: Bool = false) -> [NSEvent] { + NSEvent.keyEvents(data: self, paired: paired) + } + + public var asPairedEvents: [NSEvent] { + NSEvent.keyEvents(data: self, paired: true) + } + + public var asEvent: NSEvent? { + NSEvent.keyEvent(data: self) + } + } + + static func keyEvents(data: KeyEventData, paired: Bool = false) -> [NSEvent] { + var resultArray = [NSEvent]() + if let eventA: NSEvent = Self.keyEvent(data: data) { + resultArray.append(eventA) + if paired, eventA.type == .keyDown, + let eventB = eventA.reinitiate(with: .keyUp, characters: nil, charactersIgnoringModifiers: nil) + { + resultArray.append(eventB) + } + } + return resultArray + } + + static func keyEvent(data: KeyEventData) -> NSEvent? { + Self.keyEventSimple(type: data.type, flags: data.flags, chars: data.chars, charsSansModifiers: data.charsSansModifiers, keyCode: data.keyCode) + } + + static func keyEventSimple(type: EventType, flags: ModifierFlags, chars: String, charsSansModifiers: String? = nil, keyCode: UInt16) -> NSEvent? { + Self.keyEvent( + with: type, location: .zero, modifierFlags: flags, timestamp: .init(), + windowNumber: 0, context: nil, characters: chars, + charactersIgnoringModifiers: charsSansModifiers ?? chars, isARepeat: false, keyCode: keyCode + ) + } +} diff --git a/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests.swift b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests.swift deleted file mode 100644 index eea6aee0..00000000 --- a/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests.swift +++ /dev/null @@ -1,15 +0,0 @@ -@testable import MainAssembly -import XCTest - -final class MainAssemblyTests: XCTestCase { - override func setUpWithError() throws { - UserDefaults.unitTests = .init(suiteName: "org.atelierInmu.vChewing.MainAssembly.UnitTests") - UserDefaults.pendingUnitTests = true - } - - override func tearDownWithError() throws { - UserDefaults.unitTests?.removeSuite(named: "org.atelierInmu.vChewing.MainAssembly.UnitTests") - } - - func testExample() throws {} -} diff --git a/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests_Core.swift b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests_Core.swift new file mode 100644 index 00000000..42e94859 --- /dev/null +++ b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests_Core.swift @@ -0,0 +1,104 @@ +// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// No trademark license is granted to use the trade names, trademarks, service +// marks, or product names of Contributor, except as required to fulfill notice +// requirements defined in MIT License. + +import CocoaExtension +import InputMethodKit +import LangModelAssembly +@testable import MainAssembly +import Shared +import XCTest + +let testClient = FakeClient() + +public extension SessionCtl { + override func client() -> (IMKTextInput & NSObjectProtocol)! { testClient } +} + +func vCTestLog(_ str: String) { + print("[VCLOG] \(str)") +} + +/// 威注音輸入法的控制模組單元測試。 +/// - Remark: 歡迎來到威注音輸入法的控制模組單元測試。 +/// +/// 不似其他同類產品的單元測試,威注音輸入法的單元測試 +/// 會盡量模擬使用者的日常打字擊鍵行為與使用方法。 +/// 單元測試的內容可能算不上豐富,但會隨著今後來自各位 +/// 使用者所提報的故障、而繼續逐漸擴大測試範圍。 +/// +/// 該單元測試使用獨立的語彙資料,因此會在選字時的候選字 +/// 順序等方面與威注音輸入法實際使用時的體驗有差異。 +class MainAssemblyTests: XCTestCase { + let testUOM = vChewingLM.LMUserOverride(dataURL: .init(fileURLWithPath: "/dev/null")) + var testLM = vChewingLM.LMInstantiator.construct { $0.loadTestData() } + static let testServer = IMKServer(name: "org.atelierInmu.vChewing.MainAssembly.UnitTests_Connection", bundleIdentifier: "org.atelierInmu.vChewing.MainAssembly.UnitTests") + + static var _testHandler: InputHandler? + var testHandler: InputHandler { + let result = Self._testHandler ?? InputHandler(lm: testLM, uom: testUOM, pref: PrefMgr.shared) + if Self._testHandler == nil { Self._testHandler = result } + return result + } + + static var _testSession: SessionCtl? + var testSession: SessionCtl { + guard let session = Self._testSession ?? SessionCtl(server: Self.testServer, delegate: testHandler, client: testClient) else { + fatalError("Session failed from booting!") + } + if Self._testSession == nil { Self._testSession = session } + return session + } + + // MARK: - Utilities + + let dataArrowLeft = NSEvent.KeyEventData(chars: NSEvent.SpecialKey.leftArrow.unicodeScalar.description, keyCode: KeyCode.kLeftArrow.rawValue) + let dataArrowDown = NSEvent.KeyEventData(chars: NSEvent.SpecialKey.downArrow.unicodeScalar.description, keyCode: KeyCode.kDownArrow.rawValue) + let dataEnterReturn = NSEvent.KeyEventData(chars: NSEvent.SpecialKey.carriageReturn.unicodeScalar.description, keyCode: KeyCode.kLineFeed.rawValue) + let dataTab = NSEvent.KeyEventData(chars: NSEvent.SpecialKey.tab.unicodeScalar.description, keyCode: KeyCode.kTab.rawValue) + + func clearTestUOM() { + testUOM.clearData(withURL: URL(fileURLWithPath: "/dev/null")) + } + + func typeSentenceOrCandidates(_ sequence: String) { + if !([.ofEmpty, .ofInputting].contains(testSession.state.type) || testSession.state.isCandidateContainer) { return } + let typingSequence: [NSEvent] = sequence.compactMap { charRAW in + var finalArray = [NSEvent]() + let char = charRAW.description + let keyEventData = NSEvent.KeyEventData(chars: char) + finalArray.append(contentsOf: keyEventData.asPairedEvents) + return finalArray + }.flatMap { $0 } + typingSequence.forEach { theEvent in + let dismissed = !testSession.handle(theEvent, client: testClient) + if theEvent.type == .keyDown { XCTAssertFalse(dismissed) } + } + } + + // MARK: - Preparing Unit Tests. + + override func setUpWithError() throws { + UserDefaults.unitTests = .init(suiteName: "org.atelierInmu.vChewing.MainAssembly.UnitTests") + UserDef.resetAll() + UserDefaults.pendingUnitTests = true + testSession.activateServer(testClient) + testSession.isActivated = true + testSession.inputHandler = testHandler + testHandler.delegate = testSession + testSession.syncBaseLMPrefs() + testClient.clear() + } + + override func tearDownWithError() throws { + testSession.switchState(IMEState.ofAbortion()) + UserDefaults.unitTests?.removeSuite(named: "org.atelierInmu.vChewing.MainAssembly.UnitTests") + UserDef.resetAll() + testClient.clear() + testSession.deactivateServer(testClient) + } +} diff --git a/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests_Test1.swift b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests_Test1.swift new file mode 100644 index 00000000..afaebdf2 --- /dev/null +++ b/Packages/vChewing_MainAssembly/Tests/MainAssemblyTests/MainAssemblyTests_Test1.swift @@ -0,0 +1,154 @@ +// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// No trademark license is granted to use the trade names, trademarks, service +// marks, or product names of Contributor, except as required to fulfill notice +// requirements defined in MIT License. + +import CocoaExtension +import InputMethodKit +import LangModelAssembly +@testable import MainAssembly +import Shared +import XCTest + +extension MainAssemblyTests { + func test001_ClientTest_BundleIdentifier() throws { + guard let identifier = testSession.client()?.bundleIdentifier() else { + fatalError("致命錯誤:客體唯一標幟碼無效。") + } + vCTestLog("測試客體唯一標幟碼:\(identifier)") + } + + func test002_ClientTest_TextInsertion() throws { + testClient.clear() + let testString = UUID().uuidString + testSession.client().insertText(testString, replacementRange: .notFound) + XCTAssertEqual(testClient.attributedString.string, testString) + testClient.clear() + } + + // MARK: - Input Handler Tests. + + /// 測試基本的打字組句(不是ㄅ半注音)。 + func test101_InputHandler_BasicSentenceComposition() throws { + PrefMgr.shared.useSCPCTypingMode = false + clearTestUOM() + vCTestLog("測試組句:高科技公司的年中獎金") + testSession.resetInputHandler(forceComposerCleanup: true) + typeSentenceOrCandidates("el dk ru4ej/ n 2k7su065j/ ru;3rup ") + let resultText1 = testSession.state.displayedText + vCTestLog("- // 組字結果:\(resultText1)") + XCTAssertEqual(resultText1, "高科技公司的年中獎金") + } + + /// 測試基本的逐字選字(ㄅ半注音)。 + func test102_InputHandler_BasicSCPCTyping() throws { + PrefMgr.shared.useSCPCTypingMode = true + clearTestUOM() + vCTestLog("測試逐字選字:高科技公司的年中獎金") + testSession.resetInputHandler(forceComposerCleanup: true) + typeSentenceOrCandidates("el dk ru44ej/ 2n ") + dataArrowDown.asPairedEvents.forEach { theEvent in + let dismissed = !testSession.handle(theEvent, client: testClient) + if theEvent.type == .keyDown { XCTAssertFalse(dismissed) } + } + typeSentenceOrCandidates("12k7su065j/ ru;3rup ") + XCTAssert(testSession.candidateUI?.visible ?? false) + dataEnterReturn.asPairedEvents.forEach { theEvent in + let dismissed = !testSession.handle(theEvent, client: testClient) + if theEvent.type == .keyDown { XCTAssertFalse(dismissed) } + } + let resultText1 = testClient.toString() + vCTestLog("- // 組字結果:\(resultText1)") + XCTAssertEqual(resultText1, "高科技公司的年中獎金") + testClient.clear() + } + + /// 測試就地輪替候選字。 + func test103_InputHandler_RevolvingCandidates() throws { + PrefMgr.shared.useSCPCTypingMode = false + PrefMgr.shared.useRearCursorMode = false + clearTestUOM() + + testSession.resetInputHandler(forceComposerCleanup: true) + typeSentenceOrCandidates("el dk ru4ej/ n 2k7su065j/ ru;3rup ") + + // Testing Inline Candidate Revolver. + + vCTestLog("測試就地輪替候選字:高科技公司的年中獎金 -> 高科技公司的年終獎金") + + [dataArrowLeft, dataArrowLeft].map(\.asPairedEvents).flatMap { $0 }.forEach { theEvent in + let dismissed = !testSession.handle(theEvent, client: testClient) + if theEvent.type == .keyDown { XCTAssertFalse(dismissed) } + } + + dataTab.asPairedEvents.forEach { theEvent in + let dismissed = !testSession.handle(theEvent, client: testClient) + if theEvent.type == .keyDown { XCTAssertFalse(dismissed) } + } + let resultText2 = testSession.state.displayedText + vCTestLog("- // 組字結果:\(resultText2)") + XCTAssertEqual(resultText2, "高科技公司的年終獎金") + } + + /// 測試藉由選字窗選字、且同時測試半衰記憶模組在此情況下的記憶資料生成與適用情況。 + /// - Remark: 這裡順便測試一下「在選字窗選字後自動推進游標」這個有被預設啟用的功能。 + func test104_InputHandler_ManualCandidateSelectionAndUOM() throws { + PrefMgr.shared.useSCPCTypingMode = false + PrefMgr.shared.useRearCursorMode = false + PrefMgr.shared.moveCursorAfterSelectingCandidate = true + clearTestUOM() + + var sequenceChars = "el dk ru4ej/ n 2k7su065j/ ru;3rup " + + testSession.resetInputHandler(forceComposerCleanup: true) + typeSentenceOrCandidates(sequenceChars) + + // Testing Manual Candidate Selection, UOM Observation, and Post-Candidate-Selection Cursor Jumping. + + vCTestLog("測試選字窗選字:高科技公司的年終獎金 -> 高科技公司的年中獎金") + let keyOne = NSEvent.KeyEventData(chars: "1") + [dataArrowDown, keyOne].map(\.asPairedEvents).flatMap { $0 }.forEach { theEvent in + let dismissed = !testSession.handle(theEvent, client: testClient) + if theEvent.type == .keyDown { XCTAssertFalse(dismissed) } + } + let resultText3 = testSession.state.displayedText + vCTestLog("- // 組字結果:\(resultText3)") + XCTAssertEqual(resultText3, "高科技公司的年中獎金") + XCTAssertEqual(testHandler.compositor.cursor, 10) + + // Continuing UOM Tests (in the Current Context). + + vCTestLog("測試半衰記憶的適用範圍:「年終」的記憶應僅對下述給定上下文情形生效。") + vCTestLog("- 該給定上下文情形為「((ㄍㄨㄥ-ㄙ,公司),(ㄉㄜ˙,的),ㄋㄧㄢˊ-ㄓㄨㄥ)」。") + clearTestUOM() + let keyTwo = NSEvent.KeyEventData(chars: "2") + [dataArrowLeft, dataArrowLeft, dataArrowDown, keyTwo].map(\.asPairedEvents).flatMap { $0 }.forEach { theEvent in + let dismissed = !testSession.handle(theEvent, client: testClient) + if theEvent.type == .keyDown { XCTAssertFalse(dismissed) } + } + let resultText4 = testSession.state.displayedText + vCTestLog("- // 組字結果:\(resultText4)") + XCTAssertEqual(resultText4, "高科技公司的年終獎金") + + vCTestLog("- 清空組字區,重新打剛才那句話來測試。") + testSession.switchState(IMEState.ofAbortion()) + typeSentenceOrCandidates(sequenceChars) + let resultText5 = testSession.state.displayedText + vCTestLog("- // 組字結果:\(resultText5)") + XCTAssertEqual(resultText5, "高科技公司的年終獎金") + vCTestLog("- 已成功證實「年終」的記憶對該給定上下文情形生效。") + + vCTestLog("- 清空組字區,重新打另一句話來測試。") + testSession.switchState(IMEState.ofAbortion()) + + sequenceChars = "ru4ej/ 2k7su065j/ ru;3rup " + typeSentenceOrCandidates(sequenceChars) + let resultText6 = testSession.state.displayedText + vCTestLog("- // 組字結果:\(resultText6)") + XCTAssertEqual(resultText6, "濟公的年中獎金") + vCTestLog("- 已成功證實「年終」的記憶不會對除了給定上下文以外的情形生效。") + } +}