Skip to content

Commit b03baa6

Browse files
author
devfive
committed
Cover the branches the accuracy work added
CI enforces 100% line coverage on Linux and the branch had dropped to 99.63%. Add unit tests for the paths that had none: the print-variant folding and the slash-joined compatibility unit, the closed-enclosure scan, the dictionaryless `ever` shape, the spaced-colon lookahead, the Korean name before a parenthetical, the word-shortcut no-op, and the Greek, quoted-number, emphasis, bullet, parenthesis, signed-number and label-colon routes.
1 parent d166106 commit b03baa6

15 files changed

Lines changed: 319 additions & 2 deletions

File tree

libs/braillify/src/english_logic.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,3 +961,35 @@ mod tests {
961961
let _ = super::should_keep_english_mode_for_symbol('@', &chars, 4, &[]);
962962
}
963963
}
964+
965+
#[cfg(test)]
966+
mod enclosure_route_coverage {
967+
use super::*;
968+
969+
/// 제46항 `BMI(체질량 지수)`: a closed enclosure is Korean punctuation
970+
/// unless its body is Roman letters (제32항 `ABC(def)`).
971+
#[rstest::rstest]
972+
#[case::korean_body(&['(', '체', '질', '량', ')'], true)]
973+
#[case::digits_only(&['(', '7', '3', ')'], true)]
974+
#[case::roman_body(&['(', 'd', 'e', 'f', ')'], false)]
975+
#[case::nested_roman(&['(', '(', 'd', ')', 'e', ')'], false)]
976+
#[case::never_closes(&['(', 'd', 'e', 'f'], false)]
977+
fn closed_enclosure_is_korean_unless_its_body_is_roman(
978+
#[case] word: &[char],
979+
#[case] expected: bool,
980+
) {
981+
assert_eq!(
982+
closed_parenthesis_is_korean_punctuation(word, 0, &[]),
983+
expected
984+
);
985+
}
986+
987+
#[test]
988+
fn an_enclosure_closing_in_a_later_word_is_still_scanned() {
989+
assert!(closed_parenthesis_is_korean_punctuation(
990+
&['(', 'A'],
991+
0,
992+
&["체질량)"]
993+
));
994+
}
995+
}

libs/braillify/src/lib.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3075,3 +3075,51 @@ mod debug_reader {
30753075
}
30763076
}
30773077
}
3078+
3079+
#[cfg(test)]
3080+
mod print_variant_coverage {
3081+
use super::*;
3082+
3083+
#[rstest::rstest]
3084+
#[case::celsius("25\u{00B0}C", "25\u{2103}")]
3085+
#[case::fahrenheit("77\u{00B0}F", "77\u{2109}")]
3086+
#[case::ring_celsius("25\u{02DA}C", "25\u{2103}")]
3087+
#[case::ring_fahrenheit("77\u{02DA}F", "77\u{2109}")]
3088+
fn degree_letter_pair_folds_to_the_unit_glyph(#[case] input: &str, #[case] expected: &str) {
3089+
assert_eq!(
3090+
normalize_print_variants(std::borrow::Cow::Borrowed(input)).as_ref(),
3091+
expected
3092+
);
3093+
}
3094+
3095+
#[rstest::rstest]
3096+
#[case::fullwidth_percent('\u{FF05}', true)]
3097+
#[case::fullwidth_letter('\u{FF4D}', true)]
3098+
#[case::fullwidth_hash_is_the_math_cardinal('\u{FF03}', false)]
3099+
#[case::fullwidth_colon_is_the_old_hangul_mark('\u{FF1A}', false)]
3100+
#[case::ascii_is_not_a_variant('m', false)]
3101+
fn fullwidth_folding_excludes_the_two_reserved_glyphs(
3102+
#[case] input: char,
3103+
#[case] expected: bool,
3104+
) {
3105+
assert_eq!(is_foldable_fullwidth(input), expected);
3106+
}
3107+
3108+
#[rstest::rstest]
3109+
#[case::before_slash("\u{338F}/h")]
3110+
#[case::after_slash("h/\u{338F}")]
3111+
fn a_square_unit_joined_through_a_slash_decomposes(#[case] input: &str) {
3112+
let folded = normalize_pure_roman_compatibility_units(std::borrow::Cow::Borrowed(input));
3113+
assert!(
3114+
folded.contains("kg"),
3115+
"expected the unit to spell out, got {folded:?}"
3116+
);
3117+
}
3118+
3119+
#[test]
3120+
fn a_detached_square_unit_keeps_its_glyph() {
3121+
let folded =
3122+
normalize_pure_roman_compatibility_units(std::borrow::Cow::Borrowed("\u{338F}"));
3123+
assert_eq!(folded.as_ref(), "\u{338F}");
3124+
}
3125+
}

libs/braillify/src/rules/emit.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::char_struct::{CharType, KoreanChar};
1+
use crate::char_struct::{CharType, KoreanChar};
22
use crate::english_logic;
33
use crate::fraction;
44
use crate::rules::context::{EncoderState, RuleContext};
@@ -2128,3 +2128,47 @@ mod tests {
21282128
);
21292129
}
21302130
}
2131+
2132+
#[cfg(test)]
2133+
mod spaced_colon_coverage {
2134+
use super::*;
2135+
use crate::rules::token::{SpaceKind, WordMeta};
2136+
2137+
fn word(text: &str) -> Token<'static> {
2138+
let chars: Vec<char> = text.chars().collect();
2139+
Token::Word(WordToken {
2140+
text: std::borrow::Cow::Owned(text.to_string()),
2141+
meta: WordMeta::from_chars(&chars),
2142+
chars,
2143+
})
2144+
}
2145+
2146+
/// 제29항·제32항·제35항: a standalone colon joins two Roman items only when
2147+
/// the item after it is proved Roman.
2148+
#[test]
2149+
fn a_roman_item_after_the_colon_connects_the_section() {
2150+
let tokens = [word(":"), Token::Space(SpaceKind::Regular), word("Beta")];
2151+
assert!(spaced_colon_connects_roman_items(&tokens, 0));
2152+
}
2153+
2154+
#[test]
2155+
fn a_korean_item_after_the_colon_breaks_the_section() {
2156+
let tokens = [word(":"), Token::Space(SpaceKind::Regular), word("베타")];
2157+
assert!(!spaced_colon_connects_roman_items(&tokens, 0));
2158+
}
2159+
2160+
#[test]
2161+
fn a_pre_encoded_item_after_the_colon_breaks_the_section() {
2162+
let tokens = [word(":"), Token::PreEncoded(vec![1])];
2163+
assert!(!spaced_colon_connects_roman_items(&tokens, 0));
2164+
}
2165+
2166+
#[test]
2167+
fn a_token_that_is_not_a_lone_colon_never_connects() {
2168+
assert!(!spaced_colon_connects_roman_items(&[word("::")], 0));
2169+
assert!(!spaced_colon_connects_roman_items(
2170+
&[Token::PreEncoded(vec![1])],
2171+
0
2172+
));
2173+
}
2174+
}

libs/braillify/src/rules/english_ueb/engine/word_methods.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,3 +1033,15 @@ mod tests {
10331033
assert!(out.starts_with(&[GRADE1, CAPITAL, CAPITAL]));
10341034
}
10351035
}
1036+
1037+
#[cfg(test)]
1038+
mod word_body_coverage {
1039+
/// §10.9: a whole-word shortform is written by its own cells; any other
1040+
/// word falls through to the ordinary contraction encoder.
1041+
#[rstest::rstest]
1042+
#[case::whole_word_shortform("그는 about 를")]
1043+
#[case::ordinary_word("그는 tomato 를")]
1044+
fn a_roman_word_in_korean_text_encodes(#[case] input: &str) {
1045+
assert!(crate::encode_to_unicode(input).is_ok());
1046+
}
1047+
}

libs/braillify/src/rules/english_ueb/rule_10_7_pron.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,3 +649,20 @@ mod tests {
649649
assert!(!contains_contiguous(&[], &[]));
650650
}
651651
}
652+
653+
#[cfg(test)]
654+
mod ever_shape_coverage {
655+
/// §10.7: with no dictionary entry the `ever` sign is decided by shape —
656+
/// word-final, or with a single inflectional `s` left.
657+
#[rstest::rstest]
658+
#[case::word_final("그는 cantilever 를", "⠐⠑")]
659+
#[case::inflected_s("그는 Clevers 를", "⠐⠑")]
660+
#[case::longer_tail("그는 Cleverse 를", "⠑⠧⠻")]
661+
fn dictionaryless_ever_follows_the_word_edge(#[case] input: &str, #[case] expected: &str) {
662+
let encoded = crate::encode_to_unicode(input).unwrap();
663+
assert!(
664+
encoded.contains(expected),
665+
"expected {expected} in {encoded}"
666+
);
667+
}
668+
}

libs/braillify/src/rules/korean/rule_28.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,3 +794,16 @@ mod tests {
794794
assert!(matches!(outcome, RuleResult::Skip));
795795
}
796796
}
797+
798+
#[cfg(test)]
799+
mod uppercase_run_coverage {
800+
/// UEB 10.12.1: an all-capitals initialism in Korean text is spelled with
801+
/// alphabet signs; a mixed-case Roman run keeps its ordinary route.
802+
#[rstest::rstest]
803+
#[case::initialism("그는 WHO 를")]
804+
#[case::hyphenated_run("그는 CV3-AD685 를")]
805+
#[case::mixed_case("그는 Lincoln 을")]
806+
fn a_roman_run_in_korean_text_encodes(#[case] input: &str) {
807+
assert!(crate::encode_to_unicode(input).is_ok());
808+
}
809+
}

libs/braillify/src/rules/korean/rule_31.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,14 @@ mod tests {
196196
assert!(!result.is_empty());
197197
}
198198
}
199+
200+
#[cfg(test)]
201+
mod greek_run_coverage {
202+
/// 제31항: Greek letters take their own cells, alone or in a run.
203+
#[rstest::rstest]
204+
#[case::single("알파 \u{03B1} 값")]
205+
#[case::run("\u{03B1}\u{03B2}\u{03B3}")]
206+
fn greek_letters_encode(#[case] input: &str) {
207+
assert!(crate::encode_to_unicode(input).is_ok());
208+
}
209+
}

libs/braillify/src/rules/korean/rule_40.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,16 @@ mod tests {
223223
assert!(matches!(outcome, RuleResult::Skip));
224224
}
225225
}
226+
227+
#[cfg(test)]
228+
mod number_prefix_coverage {
229+
/// 제40항 + 제61항: a number opened after an apostrophe writes the 수표 and
230+
/// then the apostrophe cell.
231+
#[rstest::rstest]
232+
#[case::straight_quote("그는 '2026 년")]
233+
#[case::typographic_quote("그는 \u{2019}2026 년")]
234+
#[case::plain_number("그는 2026 년")]
235+
fn a_number_after_a_quote_encodes(#[case] input: &str) {
236+
assert!(crate::encode_to_unicode(input).is_ok());
237+
}
238+
}

libs/braillify/src/rules/korean/rule_49.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,16 @@ mod tests {
346346
);
347347
}
348348
}
349+
350+
#[cfg(test)]
351+
mod emphasis_mark_coverage {
352+
/// 제56항 입력 표기: a print source may still carry the `"˙` opening and the
353+
/// `__` closing of an emphasis span.
354+
#[rstest::rstest]
355+
#[case::opening("\"\u{02D9}강조")]
356+
#[case::closing("강조__")]
357+
#[case::plain_quote("\"강조\"")]
358+
fn emphasis_input_notation_encodes(#[case] input: &str) {
359+
assert!(crate::encode_to_unicode(input).is_ok());
360+
}
361+
}

libs/braillify/src/rules/korean/rule_72.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,17 @@ mod tests {
370370
assert!(matches!(outcome, RuleResult::Skip));
371371
}
372372
}
373+
374+
#[cfg(test)]
375+
mod bullet_tail_coverage {
376+
/// 제72항: a bullet owns the item that follows it, whether that item runs
377+
/// to the end of the line or stops at punctuation.
378+
#[rstest::rstest]
379+
#[case::bullet_then_item("\u{25CB} 정원은 넓다")]
380+
#[case::bullet_then_punctuation("\u{25CB} 정원.")]
381+
#[case::bullet_alone("\u{25CB}")]
382+
#[case::bullet_then_bullet("\u{25CB} \u{25A1} 정원")]
383+
fn a_bullet_item_encodes(#[case] input: &str) {
384+
assert!(crate::encode_to_unicode(input).is_ok());
385+
}
386+
}

0 commit comments

Comments
 (0)