73 lines
3.3 KiB
Rust
73 lines
3.3 KiB
Rust
//! 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<char> = CHARACTERS.chars().collect();
|
|
let boundaries: Vec<usize> = 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);
|
|
}
|
|
}
|
|
}
|