#![no_main] use arbitrary::{Arbitrary, Result, Unstructured}; use libfuzzer_sys::fuzz_target; use tui_textarea::{CursorMove, TextArea}; use tui_textarea_bench::{dummy_terminal, TerminalExt}; #[derive(Arbitrary)] enum Op { Move(CursorMove), InsertStr(String), InsertChar(char), InsertTab, InsertNewline, DeleteStr(usize), DeleteChar, DeleteNextChar, DeleteWord, DeleteNextWord, DeleteLineByEnd, DeleteLineByHead, DeleteNewline, Clear, } fn assert_invariants(textarea: &TextArea<'_>) { let lines = textarea.lines(); assert!(!lines.is_empty(), "lines must never be empty"); let (row, col) = textarea.cursor(); assert!( row < lines.len(), "cursor row {row} out of bounds (lines: {})", lines.len() ); assert!( col <= lines[row].chars().count(), "cursor col {col} out of bounds (line {row} chars: {})", lines[row].chars().count(), ); } fn fuzz(data: &[u8]) -> Result<()> { let mut term = dummy_terminal(); let mut textarea = TextArea::default(); let mut data = Unstructured::new(data); for _ in 0..100 { match Op::arbitrary(&mut data)? { Op::Move(m) => textarea.move_cursor(m), Op::InsertStr(s) => { textarea.insert_str(s); } Op::InsertChar(c) => textarea.insert_char(c), Op::InsertTab => { textarea.insert_tab(); } Op::InsertNewline => textarea.insert_newline(), Op::DeleteStr(n) => { textarea.delete_str(n); } Op::DeleteChar => { textarea.delete_char(); } Op::DeleteNextChar => { textarea.delete_next_char(); } Op::DeleteWord => { textarea.delete_word(); } Op::DeleteNextWord => { textarea.delete_next_word(); } Op::DeleteLineByEnd => { textarea.delete_line_by_end(); } Op::DeleteLineByHead => { textarea.delete_line_by_head(); } Op::DeleteNewline => { textarea.delete_newline(); } Op::Clear => { textarea.clear(); } } term.draw_textarea(&textarea); assert_invariants(&textarea); } Ok(()) } fuzz_target!(|data: &[u8]| { let _ = fuzz(data); });