498f407585
Introduce optional Apple account-backed credits with scoped gateway access while preserving local and BYOK paths. Refresh assistant behavior, tests, privacy disclosures, docs, and the website for the 2.0 experience.
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.
|
|
|
|
@testable import OSGKeyboardShared
|
|
import XCTest
|
|
|
|
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")
|
|
}
|
|
}
|