2ed16e1fbf
Add gap-filling hit regions, release-to-commit with slide-to-reselect, touch intent offset, and full-pinyin next-key bias; cut release 1.6.1 (build 45).
182 lines
5.5 KiB
Swift
182 lines
5.5 KiB
Swift
// PinyinNextKeyResolverTests.swift
|
|
// OSGKeyboard · Ext unit tests
|
|
//
|
|
// Phase 4: legal next-key sets and weighted ambiguous hit resolution.
|
|
|
|
import XCTest
|
|
@testable import OSGKeyboardShared
|
|
|
|
final class PinyinNextKeyResolverTests: XCTestCase {
|
|
func testZhongPrefixAllowsG() {
|
|
let keys = PinyinNextKeyResolver.validNextKeys(
|
|
rawInput: "zhon",
|
|
schema: .fullPinyin,
|
|
language: .chinese,
|
|
page: .letters
|
|
)
|
|
XCTAssertEqual(keys, ["g"])
|
|
}
|
|
|
|
func testCompleteZhongAllowsNewSyllableInitials() {
|
|
let keys = PinyinNextKeyResolver.validNextKeys(
|
|
rawInput: "zhong",
|
|
schema: .fullPinyin,
|
|
language: .chinese,
|
|
page: .letters
|
|
)
|
|
XCTAssertNotNil(keys)
|
|
XCTAssertTrue(keys?.contains("g") == true) // zhongguo
|
|
XCTAssertTrue(keys?.contains("w") == true)
|
|
}
|
|
|
|
func testMultiSyllableTrailingGAllowsU() {
|
|
let keys = PinyinNextKeyResolver.validNextKeys(
|
|
rawInput: "zhongg",
|
|
schema: .fullPinyin,
|
|
language: .chinese,
|
|
page: .letters
|
|
)
|
|
XCTAssertTrue(keys?.contains("u") == true) // gu / guo
|
|
}
|
|
|
|
func testNiAllowsExtensionAndNewInitial() {
|
|
let keys = PinyinNextKeyResolver.validNextKeys(
|
|
rawInput: "ni",
|
|
schema: .fullPinyin,
|
|
language: .chinese,
|
|
page: .letters
|
|
)
|
|
XCTAssertTrue(keys?.contains("a") == true) // nia…
|
|
XCTAssertTrue(keys?.contains("h") == true) // nihao
|
|
}
|
|
|
|
func testEmptyRawDisablesBias() {
|
|
let keys = PinyinNextKeyResolver.validNextKeys(
|
|
rawInput: "",
|
|
schema: .fullPinyin,
|
|
language: .chinese,
|
|
page: .letters
|
|
)
|
|
XCTAssertNil(keys)
|
|
}
|
|
|
|
func testEnglishDisablesBias() {
|
|
let keys = PinyinNextKeyResolver.validNextKeys(
|
|
rawInput: "ni",
|
|
schema: .fullPinyin,
|
|
language: .english,
|
|
page: .letters
|
|
)
|
|
XCTAssertNil(keys)
|
|
}
|
|
|
|
func testDoublePinyinDisablesBias() {
|
|
let keys = PinyinNextKeyResolver.validNextKeys(
|
|
rawInput: "nihk",
|
|
schema: .microsoftDoublePinyin,
|
|
language: .chinese,
|
|
page: .letters
|
|
)
|
|
XCTAssertNil(keys)
|
|
}
|
|
|
|
func testNumbersPageDisablesBias() {
|
|
let keys = PinyinNextKeyResolver.validNextKeys(
|
|
rawInput: "ni",
|
|
schema: .fullPinyin,
|
|
language: .chinese,
|
|
page: .numbers
|
|
)
|
|
XCTAssertNil(keys)
|
|
}
|
|
|
|
func testHitWeightsBoostLegalLetters() {
|
|
let keys = [
|
|
TypingKeyHitTarget(
|
|
id: "grid.0.0",
|
|
label: "G",
|
|
visualFrame: .zero,
|
|
behavior: .commitOnRelease
|
|
),
|
|
TypingKeyHitTarget(
|
|
id: "grid.0.1",
|
|
label: "H",
|
|
visualFrame: .zero,
|
|
behavior: .commitOnRelease
|
|
),
|
|
TypingKeyHitTarget(
|
|
id: "grid.0.2",
|
|
label: "⌫",
|
|
visualFrame: .zero,
|
|
behavior: .deleteRepeat
|
|
)
|
|
]
|
|
let weights = PinyinNextKeyResolver.hitWeights(for: keys, validNext: ["g"])
|
|
XCTAssertEqual(weights["grid.0.0"], KeyHitBiasMetrics.legalBoost)
|
|
XCTAssertEqual(weights["grid.0.1"], KeyHitBiasMetrics.illegalShrink)
|
|
XCTAssertNil(weights["grid.0.2"])
|
|
}
|
|
|
|
func testWeightedNearestPrefersLegalKeyInGap() {
|
|
let left = TypingKeyHitTarget(
|
|
id: "L",
|
|
label: "F",
|
|
visualFrame: CGRect(x: 0, y: 0, width: 40, height: 50),
|
|
behavior: .commitOnRelease
|
|
)
|
|
let right = TypingKeyHitTarget(
|
|
id: "R",
|
|
label: "G",
|
|
visualFrame: CGRect(x: 46, y: 0, width: 40, height: 50),
|
|
behavior: .commitOnRelease
|
|
)
|
|
let plane = left.visualFrame.union(right.visualFrame)
|
|
// Shared expanded edge (half of 6pt gap) — both frames contain x=43.
|
|
let point = CGPoint(x: 43, y: 25)
|
|
let weighted = KeyHitTesting.hitTarget(
|
|
at: point,
|
|
targets: [left, right],
|
|
keyPlaneBounds: plane,
|
|
horizontalGap: 6,
|
|
verticalGap: 7,
|
|
edgeExpansion: 0,
|
|
hitWeights: [
|
|
"L": KeyHitBiasMetrics.illegalShrink,
|
|
"R": KeyHitBiasMetrics.legalBoost
|
|
]
|
|
)
|
|
XCTAssertEqual(weighted?.id, "R")
|
|
}
|
|
|
|
func testClearSingleHitIgnoresBias() {
|
|
// Point clearly inside F — must not jump to boosted G.
|
|
let left = TypingKeyHitTarget(
|
|
id: "L",
|
|
label: "F",
|
|
visualFrame: CGRect(x: 0, y: 0, width: 40, height: 50),
|
|
behavior: .commitOnRelease
|
|
)
|
|
let right = TypingKeyHitTarget(
|
|
id: "R",
|
|
label: "G",
|
|
visualFrame: CGRect(x: 46, y: 0, width: 40, height: 50),
|
|
behavior: .commitOnRelease
|
|
)
|
|
let plane = left.visualFrame.union(right.visualFrame)
|
|
let point = CGPoint(x: 20, y: 25)
|
|
let hit = KeyHitTesting.hitTarget(
|
|
at: point,
|
|
targets: [left, right],
|
|
keyPlaneBounds: plane,
|
|
horizontalGap: 6,
|
|
verticalGap: 7,
|
|
edgeExpansion: 0,
|
|
hitWeights: [
|
|
"L": KeyHitBiasMetrics.illegalShrink,
|
|
"R": KeyHitBiasMetrics.legalBoost
|
|
]
|
|
)
|
|
XCTAssertEqual(hit?.id, "L")
|
|
}
|
|
}
|