//! Reproducible random UTF-8 input safety and edit consistency checks. use notescraft_vim::{Doc, Key, KeyCode, Outcome, Vim}; const SEQUENCES: usize = 20_000; const KEYS_PER_SEQUENCE: usize = 32; const INITIAL: &str = "é猫🙂 one\n\n[αβ](note.md) [[猫]]\n'quoted' {text}\n"; const CHARACTERS: &str = "hjklwbeWBE0^$gG{}%fFtT;,HMLdcy><~uUxXsSrRJpPoOAIaiCDYvV\"az_+*-/:?nN123456789é猫🙂 ()[]`'."; fn random(seed: &mut u64) -> u64 { *seed ^= *seed << 13; *seed ^= *seed >> 7; *seed ^= *seed << 17; *seed } fn check_edits(before: &str, document: &Doc, outcome: &Outcome) { assert_eq!(outcome.text_changed, before != document.text); let mut reconstructed = before.to_owned(); for edit in &outcome.edits { assert_eq!(reconstructed.get(edit.range.clone()), Some(edit.deleted.as_str())); reconstructed.replace_range(edit.range.clone(), &edit.inserted); } assert_eq!(reconstructed, document.text, "host edits must reproduce document"); } #[test] fn twenty_thousand_random_unicode_sequences_never_panic() { let mut seed = 0x1728_394a_5b6c_7d8e; let characters: Vec = CHARACTERS.chars().collect(); let boundaries: Vec = INITIAL.char_indices().map(|(offset, _)| offset).collect(); for sequence in 0..SEQUENCES { let mut document = Doc::new(INITIAL.into()).expect("valid UTF-8"); let cursor_index = usize::try_from(random(&mut seed) % u64::try_from(boundaries.len()).expect("boundary count")).expect("bounded cursor index"); document.cursor = *boundaries.get(cursor_index).expect("bounded cursor"); let mut vim = Vim::new(); for step in 0..KEYS_PER_SEQUENCE { let character_index = usize::try_from(random(&mut seed) % u64::try_from(characters.len()).expect("character count")).expect("bounded key index"); let character = *characters.get(character_index).expect("bounded key"); let code = match random(&mut seed) % 20 { 0 => KeyCode::Esc, 1 => KeyCode::Enter, 2 => KeyCode::Backspace, 3 => KeyCode::Tab, 4 => KeyCode::Left, 5 => KeyCode::Right, 6 => KeyCode::Up, 7 => KeyCode::Down, 8 => KeyCode::Home, 9 => KeyCode::End, 10 => KeyCode::PageUp, 11 => KeyCode::PageDown, 12 => KeyCode::F(1), _ => KeyCode::Char(character), }; let modifiers = random(&mut seed); let key = Key { code, ctrl: modifiers.is_multiple_of(19), shift: modifiers.is_multiple_of(13), alt: modifiers.is_multiple_of(29) }; let before = document.text.clone(); let outcome = vim.handle(key, &mut document); assert!(document.cursor <= document.text.len(), "sequence={sequence} step={step} key={key:?}"); assert!(document.text.is_char_boundary(document.cursor), "sequence={sequence} step={step} key={key:?}"); if let Some(anchor) = document.anchor { assert!(anchor <= document.text.len()); assert!(document.text.is_char_boundary(anchor)); } for range in &outcome.search { assert!(document.text.get(range.clone()).is_some()); } check_edits(&before, &document, &outcome); } } }