#[cfg(test)]
mod parity {
use skey_engine::spelling;
use skey_engine::engine;
type VSeq = (&'static str, &'static str, bool, bool, Option<usize>, Option<usize>);
fn all_vseqs() -> Vec<VSeq> {
vec![
("a", "a", true, true, None, None),
("â", "â", true, true, Some(0), None),
("ă", "ă", true, true, None, Some(0)),
("e", "e", true, true, None, None),
("ê", "ê", true, true, Some(0), None),
("i", "i", true, true, None, None),
("o", "o", true, true, None, None),
("ô", "ô", true, true, Some(0), None),
("ơ", "ơ", true, true, None, Some(0)),
("u", "u", true, true, None, None),
("ư", "ư", true, true, None, Some(0)),
("y", "y", true, true, None, None),
("ai", "ai", true, false, None, None),
("ao", "ao", true, false, None, None),
("au", "au", true, false, None, None),
("ay", "ay", true, false, None, None),
("âu", "âu", true, false, Some(0), None),
("ây", "ây", true, false, Some(0), None),
("eo", "eo", true, false, None, None),
("êu", "êu", true, false, Some(0), None),
("ia", "ia", true, false, None, None),
("iu", "iu", true, false, None, None),
("oi", "oi", true, false, None, None),
("ôi", "ôi", true, false, Some(0), None),
("ơi", "ơi", true, false, None, Some(0)),
("ua", "ua", true, false, None, None),
("ui", "ui", true, false, None, None),
("ưa", "ưa", true, false, None, Some(0)),
("ưi", "ưi", true, false, None, Some(0)),
("ưu", "ưu", true, false, None, Some(0)),
("ie", "ie", false, true, None, None),
("iê", "iê", true, true, Some(1), None),
("oa", "oa", true, true, None, None),
("oă", "oă", true, true, None, Some(1)),
("oe", "oe", true, true, None, None),
("uâ", "uâ", true, true, Some(1), None),
("ue", "ue", false, true, None, None),
("uê", "uê", true, true, Some(1), None),
("uo", "uo", false, true, None, None),
("uô", "uô", true, true, Some(1), None),
("ươ", "ươ", true, true, None, Some(1)),
("uy", "uy", true, true, None, None),
("ye", "ye", false, true, None, None),
("yê", "yê", true, true, Some(1), None),
("ưo", "ưo", false, true, None, Some(0)),
("iêu", "iêu", true, false, Some(1), None),
("oai", "oai", true, false, None, None),
("oay", "oay", true, false, None, None),
("oeo", "oeo", true, false, None, None),
("uây", "uây", true, false, Some(1), None),
("uôi", "uôi", true, false, Some(1), None),
("uya", "uya", true, false, None, None),
("uye", "uye", false, true, None, None),
("uyê", "uyê", true, true, Some(2), None),
("uyu", "uyu", true, false, None, None),
("ươi", "ươi", true, false, None, Some(0)),
("ươu", "ươu", true, false, None, Some(0)),
("yêu", "yêu", true, false, Some(1), None),
]
}
#[test]
#[ignore]
fn vowel_sequence_validation_coverage() {
println!("\n=== Vowel sequence validation coverage ===\n");
let mut passing = 0;
let mut failing = Vec::new();
for (name, chars, complete, con_suffix, _roof, _hook) in &all_vseqs() {
let valid_standalone = spelling::is_valid_cvc(chars);
let with_c = format!("b{}", chars);
let valid_with_c = spelling::is_valid_cvc(&with_c);
let valid_with_suffix = if *con_suffix {
let with_s = format!("b{}n", chars);
spelling::is_valid_cvc(&with_s)
} else {
true };
let mut issues = Vec::new();
if !complete && valid_standalone {
issues.push(format!("standalone: should be INVALID (incomplete seq)"));
}
if *con_suffix {
let with_s = format!("b{}n", chars);
if !spelling::is_valid_cvc(&with_s) {
issues.push(format!("with suffix 'n': should be VALID but rejected"));
}
}
if issues.is_empty() {
passing += 1;
} else {
failing.push(format!(" {} ({}) — standalone={} with_c={} with_suffix={}",
chars, name, valid_standalone, valid_with_c, valid_with_suffix));
for issue in &issues {
failing.push(format!(" ✗ {}", issue));
}
}
}
for f in &failing {
println!("{}", f);
}
println!("\n Validated: {}/{} vowel sequences match x-unikey expectations",
passing, passing + failing.len());
if !failing.is_empty() {
println!(" ⚠ {} discrepancies remain\n", failing.len());
} else {
println!(" ✅ All vowel sequences validated correctly\n");
}
}
#[test]
#[ignore]
fn tone_placement_coverage() {
println!("\n=== Tone placement coverage (telex, traditional) ===\n");
let cases: &[(&str, &str, &str)] = &[
("as", "á", "a + acute"),
("af", "à", "a + grave"),
("aws", "ắ", "ă + acute"),
("aas", "ấ", "â + acute"),
("ees", "ế", "ê + acute"),
("oos", "ố", "ô + acute"),
("ows", "ớ", "ơ + acute"),
("uws", "ứ", "ư + acute"),
("ais", "ái", "ai + acute"),
("aos", "áo", "ao + acute"),
("aus", "áu", "au + acute"),
("ays", "áy", "ay + acute"),
("aaus", "ấu", "âu + acute → tone on â"),
("aays", "ấy", "ây + acute → tone on â"),
("eesf", "ếu", "êu + acute → tone on ê"),
("toans", "toán", "toa + acute → oá → toá"),
("hoaf", "hòa", "hoa + grave → hòa"),
("thowif", "thời", "thơi + acute → thời"),
("cuar", "của", "cua + hook → của"),
("ngoais", "ngoái", "ngoai + acute → tone on a"),
("ngoayr", "ngoảy", "ngoay + hook → tone on a"),
("uyeef", "uyề", "uyê + grave"),
("uyees", "uyế", "uyê + acute"),
("tieengs", "tiếng", "iê + ng + acute → tone on ê"),
("uoois", "uối", "uôi + acute → tone on ô"),
("uoiws", "ưới", "ươi + acute → tone on ơ"),
("gias", "giá", "gi + a + acute"),
("quas", "quá", "qu + a + acute"),
("gif", "gì", "gi + grave"),
("cuowcs", "cước", "cươc + acute"),
("dduwowcj", "được", "đươc + dot"),
("nguwowif", "người", "ngươi + grave"),
("thuowngf", "thường", "thương + grave"),
("hoanf", "hoàn", "hoan + grave → hoàn"),
("huyeenf", "huyền", "huyê + n + grave"),
("nguyeexn", "nguyễn", "nguyê + n + acute → nguyễn (tilde)"),
];
let mut ok = 0;
let mut bad = Vec::new();
for (input, expected, desc) in cases {
let result = engine::convert_telex(input, false, true);
if result == *expected {
ok += 1;
} else {
bad.push(format!(" '{}' → '{}' (expected '{}') — {}", input, result, expected, desc));
}
}
for b in &bad {
println!("{}", b);
}
println!("\n Tone placement: {}/{} correct", ok, ok + bad.len());
if !bad.is_empty() {
println!(" ⚠ {} tone placement errors\n", bad.len());
} else {
println!(" ✅ All tone placements correct\n");
}
}
#[test]
#[ignore]
fn rare_vowel_sequences_engine() {
println!("\n=== Rare vowel sequence engine output ===\n");
let rare_cases: &[(&str, &str)] = &[
("thuees", "thuế"), ("huef", "huề"), ("hueen", "huên"), ("hueenh", "huênh"), ("hoawcj", "hoặc"), ("thoaws", "thoắc"), ("thoawts", "thoắt"), ("thuyt", "thuýt"), ("huyp", "huýp"), ("nguyr", "nguỷ"), ("khuya", "khuya"), ("khuyar", "khuỷa"), ("khuyus", "khuyú"), ("ruowus", "rướu"), ("huowuf", "hườu"), ];
let mut ok = 0;
let mut bad = Vec::new();
for (input, expected) in rare_cases {
let result = engine::convert_telex(input, false, true);
if result == *expected {
ok += 1;
} else {
bad.push(format!(" '{}' → '{}' (expected '{}')", input, result, expected));
}
}
for b in &bad {
println!("{}", b);
}
println!("\n Rare sequences: {}/{} correct", ok, ok + bad.len());
if !bad.is_empty() {
println!(" ⚠ {} engine gaps for rare sequences\n", bad.len());
} else {
println!(" ✅ All rare sequences handled correctly\n");
}
}
}