mod cursor_tests {
use uniworld::cursor::{
delete_backward, delete_forward, move_left, move_left_visual, move_right,
move_right_visual, select_word,
};
#[test]
fn cursor_devanagari_cluster() {
let s = "\u{0915}\u{093F}"; assert_eq!(move_right(s, 0), s.len());
assert_eq!(move_left(s, s.len()), 0);
}
#[test]
fn cursor_thai_text() {
let s = "\u{0E2A}\u{0E27}\u{0E31}\u{0E2A}\u{0E14}\u{0E35}";
let pos = move_right(s, 0);
assert!(pos > 0);
assert!(s.is_char_boundary(pos));
}
#[test]
fn cursor_emoji_flag_sequence() {
let s = "\u{1F1FA}\u{1F1F8}"; assert_eq!(move_right(s, 0), s.len());
assert_eq!(move_left(s, s.len()), 0);
}
#[test]
fn cursor_emoji_skin_tone() {
let wave = "\u{1F44B}\u{1F3FD}"; assert_eq!(move_right(wave, 0), wave.len());
assert_eq!(move_left(wave, wave.len()), 0);
}
#[test]
fn cursor_mixed_script_word_select() {
let s = "hello \u{4F60}\u{597D}";
let (start, end) = select_word(s, 2); assert_eq!(&s[start..end], "hello");
}
#[test]
fn cursor_delete_backward_multibyte() {
let s = "a\u{4E16}\u{754C}b"; let cjk_end = "a\u{4E16}\u{754C}".len();
let (result, pos) = delete_backward(s, cjk_end);
assert_eq!(result, "a\u{4E16}b");
assert_eq!(pos, "a\u{4E16}".len());
}
#[test]
fn cursor_delete_forward_emoji() {
let s = "x\u{1F600}y"; let (result, pos) = delete_forward(s, 1); assert_eq!(result, "xy");
assert_eq!(pos, 1);
}
#[test]
fn visual_cursor_ltr_paragraph() {
let s = "abc";
assert_eq!(move_right_visual(s, 0), move_right(s, 0));
assert_eq!(move_left_visual(s, 3), move_left(s, 3));
}
#[test]
fn visual_cursor_single_rtl_char() {
let s = "\u{05D0}"; assert_eq!(move_right_visual(s, 0), 0); assert_eq!(move_left_visual(s, 0), s.len()); assert_eq!(move_right_visual(s, s.len()), 0); assert_eq!(move_left_visual(s, s.len()), s.len()); }
}
mod width_tests {
use uniworld::width::{char_width, display_width};
#[test]
fn width_cjk_ideographs() {
assert_eq!(char_width('\u{4E00}'), 2); assert_eq!(char_width('\u{9FFF}'), 2); assert_eq!(display_width("\u{4F60}\u{597D}"), 4); }
#[test]
fn width_hangul() {
assert_eq!(char_width('\u{AC00}'), 2); assert_eq!(display_width("\u{D55C}\u{AE00}"), 4); }
#[test]
fn width_katakana_fullwidth() {
assert_eq!(char_width('\u{30A2}'), 2); }
#[test]
fn width_combining_sequence() {
let s = "e\u{0301}\u{0303}";
assert_eq!(display_width(s), 1);
}
#[test]
fn width_zero_width_joiner() {
assert_eq!(char_width('\u{200D}'), 0);
}
#[test]
fn width_mixed_script_string() {
let s = "Hello\u{4E16}";
assert_eq!(display_width(s), 7);
}
#[test]
fn width_empty_and_controls() {
assert_eq!(display_width(""), 0);
assert_eq!(display_width("\n"), 0);
assert_eq!(display_width("\r\n"), 0);
assert_eq!(display_width("\t"), 0); }
#[test]
fn width_latin_with_diacritics() {
assert_eq!(char_width('\u{00E9}'), 1);
assert_eq!(display_width("e\u{0301}"), 1);
}
}
mod truncate_tests {
use uniworld::truncate::{truncate_display_width, truncate_graphemes};
#[test]
fn truncate_graphemes_emoji_sequence() {
let family = "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}";
let t = truncate_graphemes(family, 1);
assert_eq!(t, family);
assert_eq!(truncate_graphemes(family, 0), "");
}
#[test]
fn truncate_graphemes_devanagari() {
let cluster = "\u{0915}\u{093F}";
let s = format!("{}x", cluster);
let t = truncate_graphemes(&s, 1);
assert_eq!(t, cluster);
}
#[test]
fn truncate_width_cjk_boundary() {
let s = "\u{4E00}\u{4E8C}\u{4E09}"; let t = truncate_display_width(s, 4);
assert_eq!(t, "\u{4E00}\u{4E8C}");
let t2 = truncate_display_width(s, 3);
assert_eq!(t2, "\u{4E00}");
}
#[test]
fn truncate_width_mixed_ascii_cjk() {
let s = "ab\u{4E00}c"; let t = truncate_display_width(s, 4);
assert_eq!(t, "ab\u{4E00}"); let t2 = truncate_display_width(s, 3);
assert_eq!(t2, "ab"); }
#[test]
fn truncate_empty() {
assert_eq!(truncate_graphemes("", 5), "");
assert_eq!(truncate_display_width("", 5), "");
}
#[test]
fn truncate_no_truncation_needed() {
let s = "hello";
assert_eq!(truncate_graphemes(s, 100), s);
assert_eq!(truncate_display_width(s, 100), s);
}
#[test]
fn truncate_regional_indicator_pair() {
let flag = "\u{1F1FA}\u{1F1F8}";
let s = format!("{}abc", flag);
let t = truncate_graphemes(&s, 1);
assert_eq!(t, flag);
}
}
mod casemap_tests {
use uniworld::casemap::{
case_fold, case_fold_locale, case_fold_simple, is_lowercase, is_uppercase, to_lowercase,
to_lowercase_locale, to_titlecase, to_uppercase, to_uppercase_locale,
};
#[test]
fn casemap_latin_extended() {
assert_eq!(to_uppercase("\u{00DF}"), "SS");
assert_eq!(to_lowercase("\u{0130}"), "i\u{0307}");
}
#[test]
fn casemap_greek_uppercase() {
let lower = "\u{03B1}\u{03B2}\u{03B3}"; let upper = to_uppercase(lower);
assert_eq!(upper, "\u{0391}\u{0392}\u{0393}");
}
#[test]
fn casemap_cyrillic() {
let lower = "\u{0430}\u{0431}\u{0432}"; let upper = to_uppercase(lower);
assert_eq!(upper, "\u{0410}\u{0411}\u{0412}"); assert_eq!(to_lowercase(&upper), lower);
}
#[test]
fn casemap_turkish_roundtrip() {
assert_eq!(to_uppercase_locale("i", "tr"), "\u{0130}"); assert_eq!(to_lowercase_locale("I", "tr"), "\u{0131}"); assert_eq!(to_uppercase_locale("i", "az"), "\u{0130}"); }
#[test]
fn casemap_titlecase_sentence() {
assert_eq!(to_titlecase("the quick brown fox"), "The Quick Brown Fox");
}
#[test]
fn casemap_case_fold_comparison() {
assert_eq!(case_fold("HELLO"), case_fold("hello"));
assert_eq!(case_fold("Stra\u{00DF}e"), case_fold("STRASSE"));
}
#[test]
fn casemap_fold_locale_turkic() {
let folded = case_fold_locale("I", "tr");
assert_eq!(folded, "\u{0131}");
}
#[test]
fn casemap_fold_simple_no_expansion() {
let s = "Stra\u{00DF}e";
let f = case_fold_simple(s);
assert_eq!(f.chars().count(), s.chars().count());
}
#[test]
fn casemap_classification() {
assert!(is_uppercase('A'));
assert!(is_uppercase('\u{0391}')); assert!(is_lowercase('a'));
assert!(is_lowercase('\u{03B1}')); assert!(!is_uppercase('1'));
assert!(!is_lowercase('!'));
}
#[test]
fn casemap_no_case_scripts() {
let cjk = "\u{4E16}\u{754C}";
assert_eq!(to_uppercase(cjk), cjk);
assert_eq!(to_lowercase(cjk), cjk);
}
}