From d663a8b707868c43be74020d766177b36d9e45f2 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 7 Dec 2019 06:48:44 -0500 Subject: add a basic quickcheck test --- Cargo.toml | 2 + tests/quickcheck.rs | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 tests/quickcheck.rs diff --git a/Cargo.toml b/Cargo.toml index 14d7774..e8dd588 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,5 +20,7 @@ unicode-width = "0.1" vte = "0.3" [dev-dependencies] +quickcheck = "0.9" +rand = "0.7" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/tests/quickcheck.rs b/tests/quickcheck.rs new file mode 100644 index 0000000..1ab5219 --- /dev/null +++ b/tests/quickcheck.rs @@ -0,0 +1,145 @@ +use rand::Rng as _; + +mod helpers; + +#[derive(Clone, Debug)] +struct TerminalInput(Vec); + +impl quickcheck::Arbitrary for TerminalInput { + fn arbitrary(g: &mut G) -> Self { + let size = { + let s = g.size(); + g.gen_range(0, s) + }; + TerminalInput( + (0..size) + .map(|_| choose_terminal_input_fragment(g)) + .flatten() + .collect(), + ) + } + + fn shrink(&self) -> Box> { + Box::new(self.0.shrink().map(TerminalInput)) + } +} + +#[allow(clippy::many_single_char_names)] +fn choose_terminal_input_fragment(g: &mut G) -> Vec { + enum Fragment { + Text, + Control, + Escape, + CSI, + #[allow(dead_code)] + OSC, + #[allow(dead_code)] + DCS, + } + + impl rand::distributions::Distribution + for rand::distributions::Standard + { + fn sample(&self, rng: &mut R) -> Fragment { + match rng.gen() { + 0u8..=231 => Fragment::Text, + 232..=239 => Fragment::Control, + 240..=247 => Fragment::Escape, + 248..=255 => Fragment::CSI, + } + } + } + + match g.gen() { + Fragment::Text => { + let mut u: u32 = g.gen_range(32, 2u32.pow(20) - 2048); + // surrogates aren't valid codepoints on their own + if u >= 0xD800 { + u += 2048; + } + let c: Result = std::convert::TryFrom::try_from(u); + let c = match c { + Ok(c) => c, + Err(e) => panic!("failed to create char from {}: {}", u, e), + }; + let mut b = [0; 4]; + let s = c.encode_utf8(&mut b); + s.to_string().into_bytes() + } + Fragment::Control => vec![g.gen_range(7, 14)], + Fragment::Escape => { + let mut v = vec![0x1b]; + let c = g.gen_range(b'0', b'~'); + v.push(c); + v + } + Fragment::CSI => { + let mut v = vec![0x1b, b'[']; + // TODO: params + let c = g.gen_range(b'@', b'~'); + v.push(c); + v + } + Fragment::OSC => { + // TODO + unimplemented!() + } + Fragment::DCS => { + // TODO + unimplemented!() + } + } + // TODO: sometimes add garbage in random places +} + +fn contents_formatted_reproduces_state_random(input: Vec) -> bool { + helpers::contents_formatted_reproduces_state(&input) +} + +fn contents_formatted_reproduces_state_structured( + input: TerminalInput, +) -> bool { + helpers::contents_formatted_reproduces_state(&input.0) +} + +#[test] +#[ignore] +fn qc_structured_long() { + let mut qc = quickcheck::QuickCheck::new() + .tests(1_000_000) + .max_tests(1_000_000); + qc.quickcheck( + contents_formatted_reproduces_state_structured + as fn(TerminalInput) -> bool, + ); +} + +#[test] +fn qc_structured_short() { + let mut qc = quickcheck::QuickCheck::new().tests(1_000).max_tests(1_000); + qc.quickcheck( + contents_formatted_reproduces_state_structured + as fn(TerminalInput) -> bool, + ); +} + +#[test] +#[ignore] +fn qc_random_long() { + let mut qc = quickcheck::QuickCheck::new() + .tests(10_000_000) + .max_tests(10_000_000); + qc.quickcheck( + contents_formatted_reproduces_state_random as fn(Vec) -> bool, + ); +} + +#[test] +fn qc_random_short() { + let mut qc = quickcheck::QuickCheck::new() + .tests(10_000) + .max_tests(10_000); + qc.quickcheck( + contents_formatted_reproduces_state_random as fn(Vec) -> bool, + ); +} -- cgit v1.2.3-54-g00ecf