aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-12-03 06:26:48 -0500
committerJesse Luehrs <doy@tozt.net>2019-12-06 21:26:07 -0500
commit9dffd607455fac176a2eece2571d3c49901175f4 (patch)
tree768af30b2787ac3e012ca6d6d5362f5efcc5a66e
parentb815e4dabbc3d8bb61c569126c54f18e20bce6ec (diff)
downloadvt100-rust-9dffd607455fac176a2eece2571d3c49901175f4.tar.gz
vt100-rust-9dffd607455fac176a2eece2571d3c49901175f4.zip
start restructuring the test suite
-rw-r--r--Cargo.toml4
-rw-r--r--tests/helpers/fixtures.rs320
-rw-r--r--tests/helpers/mod.rs216
-rw-r--r--tests/window_contents.rs186
4 files changed, 576 insertions, 150 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 0232570..9e60834 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,3 +22,7 @@ log = "0.4"
unicode-normalization = { version = "0.1", optional = true }
unicode-width = "0.1"
vte = "0.3"
+
+[dev-dependencies]
+serde = { version = "1", features = ["derive"] }
+serde_json = "1"
diff --git a/tests/helpers/fixtures.rs b/tests/helpers/fixtures.rs
new file mode 100644
index 0000000..8f9bfdb
--- /dev/null
+++ b/tests/helpers/fixtures.rs
@@ -0,0 +1,320 @@
+use serde::de::Deserialize as _;
+use std::io::Read as _;
+
+#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
+pub struct FixtureCell {
+ contents: String,
+ #[serde(default, skip_serializing_if = "is_default")]
+ is_wide: bool,
+ #[serde(default, skip_serializing_if = "is_default")]
+ is_wide_continuation: bool,
+ #[serde(
+ default,
+ deserialize_with = "deserialize_color",
+ serialize_with = "serialize_color",
+ skip_serializing_if = "is_default"
+ )]
+ fgcolor: vt100::Color,
+ #[serde(
+ default,
+ deserialize_with = "deserialize_color",
+ serialize_with = "serialize_color",
+ skip_serializing_if = "is_default"
+ )]
+ bgcolor: vt100::Color,
+ #[serde(default, skip_serializing_if = "is_default")]
+ bold: bool,
+ #[serde(default, skip_serializing_if = "is_default")]
+ italic: bool,
+ #[serde(default, skip_serializing_if = "is_default")]
+ underline: bool,
+ #[serde(default, skip_serializing_if = "is_default")]
+ inverse: bool,
+}
+
+impl FixtureCell {
+ pub fn from_cell(cell: &vt100::Cell) -> Self {
+ Self {
+ contents: cell.contents(),
+ is_wide: cell.is_wide(),
+ is_wide_continuation: cell.is_wide_continuation(),
+ fgcolor: cell.fgcolor(),
+ bgcolor: cell.bgcolor(),
+ bold: cell.bold(),
+ italic: cell.italic(),
+ underline: cell.underline(),
+ inverse: cell.inverse(),
+ }
+ }
+}
+
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct FixtureScreen {
+ contents: String,
+ cells: std::collections::BTreeMap<String, FixtureCell>,
+ cursor_position: (u16, u16),
+ #[serde(default, skip_serializing_if = "is_default")]
+ title: String,
+ #[serde(default, skip_serializing_if = "is_default")]
+ icon_name: String,
+ #[serde(default, skip_serializing_if = "is_default")]
+ application_keypad: bool,
+ #[serde(default, skip_serializing_if = "is_default")]
+ application_cursor: bool,
+ #[serde(default, skip_serializing_if = "is_default")]
+ hide_cursor: bool,
+ #[serde(default, skip_serializing_if = "is_default")]
+ bracketed_paste: bool,
+ #[serde(
+ default,
+ deserialize_with = "deserialize_mouse_protocol_mode",
+ serialize_with = "serialize_mouse_protocol_mode",
+ skip_serializing_if = "is_default"
+ )]
+ mouse_protocol_mode: vt100::MouseProtocolMode,
+ #[serde(
+ default,
+ deserialize_with = "deserialize_mouse_protocol_encoding",
+ serialize_with = "serialize_mouse_protocol_encoding",
+ skip_serializing_if = "is_default"
+ )]
+ mouse_protocol_encoding: vt100::MouseProtocolEncoding,
+}
+
+impl FixtureScreen {
+ fn load<R: std::io::Read>(r: R) -> Self {
+ serde_json::from_reader(r).unwrap()
+ }
+
+ #[allow(dead_code)]
+ pub fn from_screen(screen: &vt100::Screen) -> Self {
+ let mut cells = std::collections::BTreeMap::new();
+ let (rows, cols) = screen.size();
+ for row in 0..rows {
+ for col in 0..cols {
+ let cell = screen.cell(row, col).unwrap();
+ if cell != &vt100::Cell::default() {
+ cells.insert(
+ format!("{},{}", row, col),
+ FixtureCell::from_cell(cell),
+ );
+ }
+ }
+ }
+ Self {
+ contents: screen.contents(),
+ cells,
+ cursor_position: screen.cursor_position(),
+ title: screen.title().to_string(),
+ icon_name: screen.icon_name().to_string(),
+ application_keypad: screen.application_keypad(),
+ application_cursor: screen.application_cursor(),
+ hide_cursor: screen.hide_cursor(),
+ bracketed_paste: screen.bracketed_paste(),
+ mouse_protocol_mode: screen.mouse_protocol_mode(),
+ mouse_protocol_encoding: screen.mouse_protocol_encoding(),
+ }
+ }
+}
+
+fn is_default<T: Default + PartialEq>(t: &T) -> bool {
+ t == &T::default()
+}
+
+fn deserialize_color<'a, D>(
+ deserializer: D,
+) -> std::result::Result<vt100::Color, D::Error>
+where
+ D: serde::de::Deserializer<'a>,
+{
+ let val = <Option<String>>::deserialize(deserializer)?;
+ match val {
+ None => Ok(vt100::Color::Default),
+ Some(x) if x.starts_with('#') => {
+ let x = x.as_bytes();
+ if x.len() != 7 {
+ return Err(serde::de::Error::custom("invalid rgb color"));
+ }
+ let r =
+ super::hex(x[1], x[2]).map_err(serde::de::Error::custom)?;
+ let g =
+ super::hex(x[3], x[4]).map_err(serde::de::Error::custom)?;
+ let b =
+ super::hex(x[5], x[6]).map_err(serde::de::Error::custom)?;
+ Ok(vt100::Color::Rgb(r, g, b))
+ }
+ Some(x) => Ok(vt100::Color::Idx(
+ x.parse().map_err(serde::de::Error::custom)?,
+ )),
+ }
+}
+
+#[allow(clippy::trivially_copy_pass_by_ref)]
+fn serialize_color<S>(
+ color: &vt100::Color,
+ serializer: S,
+) -> Result<S::Ok, S::Error>
+where
+ S: serde::Serializer,
+{
+ let s = match color {
+ vt100::Color::Default => unreachable!(),
+ vt100::Color::Idx(n) => format!("{}", n),
+ vt100::Color::Rgb(r, g, b) => format!("#{:02x}{:02x}{:02x}", r, g, b),
+ };
+ serializer.serialize_str(&s)
+}
+
+fn deserialize_mouse_protocol_mode<'a, D>(
+ deserializer: D,
+) -> std::result::Result<vt100::MouseProtocolMode, D::Error>
+where
+ D: serde::de::Deserializer<'a>,
+{
+ let name = <String>::deserialize(deserializer)?;
+ match name.as_ref() {
+ "none" => Ok(vt100::MouseProtocolMode::None),
+ "press" => Ok(vt100::MouseProtocolMode::Press),
+ "press_release" => Ok(vt100::MouseProtocolMode::PressRelease),
+ "button_motion" => Ok(vt100::MouseProtocolMode::ButtonMotion),
+ "any_motion" => Ok(vt100::MouseProtocolMode::AnyMotion),
+ _ => unimplemented!(),
+ }
+}
+
+#[allow(clippy::trivially_copy_pass_by_ref)]
+fn serialize_mouse_protocol_mode<S>(
+ mode: &vt100::MouseProtocolMode,
+ serializer: S,
+) -> Result<S::Ok, S::Error>
+where
+ S: serde::Serializer,
+{
+ let s = match mode {
+ vt100::MouseProtocolMode::None => "none",
+ vt100::MouseProtocolMode::Press => "press",
+ vt100::MouseProtocolMode::PressRelease => "press_release",
+ vt100::MouseProtocolMode::ButtonMotion => "button_motion",
+ vt100::MouseProtocolMode::AnyMotion => "any_motion",
+ };
+ serializer.serialize_str(s)
+}
+
+fn deserialize_mouse_protocol_encoding<'a, D>(
+ deserializer: D,
+) -> std::result::Result<vt100::MouseProtocolEncoding, D::Error>
+where
+ D: serde::de::Deserializer<'a>,
+{
+ let name = <String>::deserialize(deserializer)?;
+ match name.as_ref() {
+ "default" => Ok(vt100::MouseProtocolEncoding::Default),
+ "utf8" => Ok(vt100::MouseProtocolEncoding::Utf8),
+ "sgr" => Ok(vt100::MouseProtocolEncoding::Sgr),
+ _ => unimplemented!(),
+ }
+}
+
+#[allow(clippy::trivially_copy_pass_by_ref)]
+fn serialize_mouse_protocol_encoding<S>(
+ encoding: &vt100::MouseProtocolEncoding,
+ serializer: S,
+) -> Result<S::Ok, S::Error>
+where
+ S: serde::Serializer,
+{
+ let s = match encoding {
+ vt100::MouseProtocolEncoding::Default => "default",
+ vt100::MouseProtocolEncoding::Utf8 => "utf8",
+ vt100::MouseProtocolEncoding::Sgr => "sgr",
+ };
+ serializer.serialize_str(s)
+}
+
+fn load_input(name: &str, i: usize) -> Option<Vec<u8>> {
+ let mut file = std::fs::File::open(format!(
+ "tests/data/fixtures/{}/{}.typescript",
+ name, i
+ ))
+ .ok()?;
+ let mut input = vec![];
+ file.read_to_end(&mut input).unwrap();
+ Some(input)
+}
+
+fn load_screen(name: &str, i: usize) -> Option<FixtureScreen> {
+ let mut file = std::fs::File::open(format!(
+ "tests/data/fixtures/{}/{}.json",
+ name, i
+ ))
+ .ok()?;
+ Some(FixtureScreen::load(&mut file))
+}
+
+fn assert_produces(input: &[u8], expected: &FixtureScreen) {
+ let mut parser = vt100::Parser::default();
+ parser.process(input);
+
+ assert_eq!(parser.screen().contents(), expected.contents);
+ assert_eq!(parser.screen().cursor_position(), expected.cursor_position);
+ assert_eq!(parser.screen().title(), expected.title);
+ assert_eq!(parser.screen().icon_name(), expected.icon_name);
+ assert_eq!(
+ parser.screen().application_keypad(),
+ expected.application_keypad
+ );
+ assert_eq!(
+ parser.screen().application_cursor(),
+ expected.application_cursor
+ );
+ assert_eq!(parser.screen().hide_cursor(), expected.hide_cursor);
+ assert_eq!(parser.screen().bracketed_paste(), expected.bracketed_paste);
+ assert_eq!(
+ parser.screen().mouse_protocol_mode(),
+ expected.mouse_protocol_mode
+ );
+ assert_eq!(
+ parser.screen().mouse_protocol_encoding(),
+ expected.mouse_protocol_encoding
+ );
+
+ let (rows, cols) = parser.screen().size();
+ for row in 0..rows {
+ for col in 0..cols {
+ let expected_cell = expected
+ .cells
+ .get(&format!("{},{}", row, col))
+ .cloned()
+ .unwrap_or_else(FixtureCell::default);
+ let got_cell = parser.screen().cell(row, col).unwrap();
+ assert_eq!(got_cell.contents(), expected_cell.contents);
+ assert_eq!(got_cell.is_wide(), expected_cell.is_wide);
+ assert_eq!(
+ got_cell.is_wide_continuation(),
+ expected_cell.is_wide_continuation
+ );
+ assert_eq!(got_cell.fgcolor(), expected_cell.fgcolor);
+ assert_eq!(got_cell.bgcolor(), expected_cell.bgcolor);
+ assert_eq!(got_cell.bold(), expected_cell.bold);
+ assert_eq!(got_cell.italic(), expected_cell.italic);
+ assert_eq!(got_cell.underline(), expected_cell.underline);
+ assert_eq!(got_cell.inverse(), expected_cell.inverse);
+ }
+ }
+}
+
+#[allow(dead_code)]
+pub fn fixture(name: &str) {
+ let mut i = 1;
+ let mut prev_input = vec![];
+ while let Some(input) = load_input(name, i) {
+ super::assert_reproduces_state_from(&input, &prev_input);
+ prev_input.extend(input);
+
+ let expected = load_screen(name, i).unwrap();
+ assert_produces(&prev_input, &expected);
+
+ i += 1;
+ }
+ assert!(i > 1, "couldn't find fixtures to test");
+}
diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs
new file mode 100644
index 0000000..8ec0ac3
--- /dev/null
+++ b/tests/helpers/mod.rs
@@ -0,0 +1,216 @@
+mod fixtures;
+pub use fixtures::fixture;
+pub use fixtures::FixtureScreen;
+
+macro_rules! is {
+ ($got:expr, $expected:expr) => {
+ if ($got) != ($expected) {
+ eprintln!("{} != {}:", stringify!($got), stringify!($expected));
+ eprintln!(" got: {:?}", $got);
+ eprintln!("expected: {:?}", $expected);
+ return false;
+ }
+ };
+}
+macro_rules! ok {
+ ($e:expr) => {
+ if !($e) {
+ eprintln!("!{}", stringify!($e));
+ return false;
+ }
+ };
+}
+
+pub fn compare_screens(
+ got: &vt100::Screen,
+ expected: &vt100::Screen,
+) -> bool {
+ is!(got.contents(), expected.contents());
+ is!(got.contents_formatted(), expected.contents_formatted());
+ is!(
+ got.contents_diff(vt100::Parser::default().screen()),
+ expected.contents_diff(vt100::Parser::default().screen())
+ );
+
+ let (rows, cols) = got.size();
+
+ for row in 0..rows {
+ for col in 0..cols {
+ let expected_cell = expected.cell(row, col);
+ let got_cell = got.cell(row, col);
+ is!(got_cell, expected_cell);
+ }
+ }
+
+ is!(got.cursor_position(), expected.cursor_position());
+ ok!(got.cursor_position().0 <= rows);
+ ok!(expected.cursor_position().0 <= rows);
+ ok!(got.cursor_position().1 <= cols);
+ ok!(expected.cursor_position().1 <= cols);
+
+ is!(got.title(), expected.title());
+ is!(got.icon_name(), expected.icon_name());
+
+ is!(
+ got.audible_bell_count() > 0,
+ expected.audible_bell_count() > 0
+ );
+ is!(
+ got.visual_bell_count() > 0,
+ expected.visual_bell_count() > 0
+ );
+
+ is!(got.application_keypad(), expected.application_keypad());
+ is!(got.application_cursor(), expected.application_cursor());
+ is!(got.hide_cursor(), expected.hide_cursor());
+ is!(got.bracketed_paste(), expected.bracketed_paste());
+ is!(got.mouse_protocol_mode(), expected.mouse_protocol_mode());
+ is!(
+ got.mouse_protocol_encoding(),
+ expected.mouse_protocol_encoding()
+ );
+
+ true
+}
+
+#[allow(dead_code)]
+pub fn contents_formatted_reproduces_state(input: &[u8]) -> bool {
+ let mut parser = vt100::Parser::default();
+ parser.process(input);
+ contents_formatted_reproduces_screen(parser.screen())
+}
+
+pub fn contents_formatted_reproduces_screen(screen: &vt100::Screen) -> bool {
+ let empty_screen = vt100::Parser::default().screen().clone();
+
+ let mut new_input = screen.contents_formatted();
+ new_input.extend(screen.input_mode_formatted());
+ new_input.extend(screen.title_formatted());
+ new_input.extend(screen.bells_diff(&empty_screen));
+ let mut new_parser = vt100::Parser::default();
+ new_parser.process(&new_input);
+ let got_screen = new_parser.screen().clone();
+
+ compare_screens(&got_screen, &screen)
+}
+
+fn assert_contents_formatted_reproduces_state(input: &[u8]) {
+ assert!(contents_formatted_reproduces_state(input));
+}
+
+#[allow(dead_code)]
+pub fn contents_diff_reproduces_state(input: &[u8]) -> bool {
+ contents_diff_reproduces_state_from(input, &[])
+}
+
+pub fn contents_diff_reproduces_state_from(
+ input: &[u8],
+ prev_input: &[u8],
+) -> bool {
+ let mut parser = vt100::Parser::default();
+ parser.process(prev_input);
+ let prev_screen = parser.screen().clone();
+ parser.process(input);
+
+ contents_diff_reproduces_state_from_screens(&prev_screen, parser.screen())
+}
+
+pub fn contents_diff_reproduces_state_from_screens(
+ prev_screen: &vt100::Screen,
+ screen: &vt100::Screen,
+) -> bool {
+ let mut diff_input = screen.contents_diff(&prev_screen);
+ diff_input.extend(screen.input_mode_diff(&prev_screen));
+ diff_input.extend(screen.title_diff(&prev_screen));
+ diff_input.extend(screen.bells_diff(&prev_screen));
+
+ let mut diff_prev_input = prev_screen.contents_formatted();
+ diff_prev_input.extend(screen.input_mode_formatted());
+ diff_prev_input.extend(screen.title_formatted());
+ diff_prev_input
+ .extend(screen.bells_diff(vt100::Parser::default().screen()));
+
+ let mut new_parser = vt100::Parser::default();
+ new_parser.process(&diff_prev_input);
+ new_parser.process(&diff_input);
+ let got_screen = new_parser.screen().clone();
+
+ compare_screens(&got_screen, &screen)
+}
+
+#[allow(dead_code)]
+pub fn assert_contents_diff_reproduces_state_from_screens(
+ prev_screen: &vt100::Screen,
+ screen: &vt100::Screen,
+) {
+ assert!(contents_diff_reproduces_state_from_screens(
+ prev_screen,
+ screen,
+ ));
+}
+
+fn assert_contents_diff_reproduces_state_from(
+ input: &[u8],
+ prev_input: &[u8],
+) {
+ assert!(contents_diff_reproduces_state_from(input, prev_input));
+}
+
+#[allow(dead_code)]
+pub fn assert_reproduces_state(input: &[u8]) {
+ assert_reproduces_state_from(input, &[]);
+}
+
+pub fn assert_reproduces_state_from(input: &[u8], prev_input: &[u8]) {
+ let full_input: Vec<_> =
+ prev_input.iter().chain(input.iter()).copied().collect();
+ assert_contents_formatted_reproduces_state(&full_input);
+ assert_contents_diff_reproduces_state_from(input, prev_input);
+}
+
+#[allow(dead_code)]
+pub fn format_bytes(bytes: &[u8]) -> String {
+ let mut v = vec![];
+ for b in bytes {
+ match *b {
+ 10 => v.extend(b"\\n"),
+ 13 => v.extend(b"\\r"),
+ 27 => v.extend(b"\\e"),
+ c if c < 32 => v.extend(format!("\\x{:02x}", c).as_bytes()),
+ b => v.push(b),
+ }
+ }
+ String::from_utf8_lossy(&v).to_string()
+}
+
+fn hex_char(c: u8) -> Result<u8, String> {
+ match c {
+ b'0' => Ok(0),
+ b'1' => Ok(1),
+ b'2' => Ok(2),
+ b'3' => Ok(3),
+ b'4' => Ok(4),
+ b'5' => Ok(5),
+ b'6' => Ok(6),
+ b'7' => Ok(7),
+ b'8' => Ok(8),
+ b'9' => Ok(9),
+ b'a' => Ok(10),
+ b'b' => Ok(11),
+ b'c' => Ok(12),
+ b'd' => Ok(13),
+ b'e' => Ok(14),
+ b'f' => Ok(15),
+ b'A' => Ok(10),
+ b'B' => Ok(11),
+ b'C' => Ok(12),
+ b'D' => Ok(13),
+ b'E' => Ok(14),
+ b'F' => Ok(15),
+ _ => Err("invalid hex char".to_string()),
+ }
+}
+
+pub fn hex(upper: u8, lower: u8) -> Result<u8, String> {
+ Ok(hex_char(upper)? * 16 + hex_char(lower)?)
+}
diff --git a/tests/window_contents.rs b/tests/window_contents.rs
index 352ffd5..39c8ad7 100644
--- a/tests/window_contents.rs
+++ b/tests/window_contents.rs
@@ -1,18 +1,18 @@
-#![allow(clippy::cognitive_complexity)]
+mod helpers;
use std::io::Read as _;
#[test]
fn formatted() {
let mut parser = vt100::Parser::default();
- compare_formatted(parser.screen());
+ helpers::contents_formatted_reproduces_screen(parser.screen());
assert_eq!(
parser.screen().contents_formatted(),
b"\x1b[?25h\x1b[m\x1b[H\x1b[J"
);
parser.process(b"foobar");
- compare_formatted(parser.screen());
+ helpers::contents_formatted_reproduces_screen(parser.screen());
assert!(!parser.screen().cell(0, 2).unwrap().bold());
assert!(!parser.screen().cell(0, 3).unwrap().bold());
assert!(!parser.screen().cell(0, 4).unwrap().bold());
@@ -23,7 +23,7 @@ fn formatted() {
);
parser.process(b"\x1b[1;4H\x1b[1;7m\x1b[33mb");
- compare_formatted(parser.screen());
+ helpers::contents_formatted_reproduces_screen(parser.screen());
assert!(!parser.screen().cell(0, 2).unwrap().bold());
assert!(parser.screen().cell(0, 3).unwrap().bold());
assert!(!parser.screen().cell(0, 4).unwrap().bold());
@@ -34,7 +34,7 @@ fn formatted() {
);
parser.process(b"\x1b[1;5H\x1b[22;42ma");
- compare_formatted(parser.screen());
+ helpers::contents_formatted_reproduces_screen(parser.screen());
assert!(!parser.screen().cell(0, 2).unwrap().bold());
assert!(parser.screen().cell(0, 3).unwrap().bold());
assert!(!parser.screen().cell(0, 4).unwrap().bold());
@@ -46,14 +46,14 @@ fn formatted() {
);
parser.process(b"\x1b[1;6H\x1b[35mr\r\nquux");
- compare_formatted(parser.screen());
+ helpers::contents_formatted_reproduces_screen(parser.screen());
assert_eq!(
parser.screen().contents_formatted(),
&b"\x1b[?25h\x1b[m\x1b[H\x1b[Jfoo\x1b[33;1;7mb\x1b[42;22ma\x1b[35mr\r\nquux"[..]
);
parser.process(b"\x1b[2;1H\x1b[45mquux");
- compare_formatted(parser.screen());
+ helpers::contents_formatted_reproduces_screen(parser.screen());
assert_eq!(
parser.screen().contents_formatted(),
&b"\x1b[?25h\x1b[m\x1b[H\x1b[Jfoo\x1b[33;1;7mb\x1b[42;22ma\x1b[35mr\r\n\x1b[45mquux"[..]
@@ -61,7 +61,7 @@ fn formatted() {
parser
.process(b"\x1b[2;2H\x1b[38;2;123;213;231mu\x1b[38;5;254mu\x1b[39mx");
- compare_formatted(parser.screen());
+ helpers::contents_formatted_reproduces_screen(parser.screen());
assert_eq!(parser.screen().contents_formatted(), &b"\x1b[?25h\x1b[m\x1b[H\x1b[Jfoo\x1b[33;1;7mb\x1b[42;22ma\x1b[35mr\r\n\x1b[45mq\x1b[38;2;123;213;231mu\x1b[38;5;254mu\x1b[39mx"[..]);
}
@@ -69,7 +69,7 @@ fn formatted() {
fn empty_cells() {
let mut parser = vt100::Parser::default();
parser.process(b"\x1b[5C\x1b[32m bar\x1b[H\x1b[31mfoo");
- compare_formatted(parser.screen());
+ helpers::contents_formatted_reproduces_screen(parser.screen());
assert_eq!(parser.screen().contents(), "foo bar");
assert_eq!(
parser.screen().contents_formatted(),
@@ -181,9 +181,8 @@ fn rows() {
String::new(),
]
);
- assert_eq!(
- screen1.rows_formatted(0, 80).collect::<Vec<Vec<u8>>>(),
- vec![
+ assert_eq!(screen1.rows_formatted(0, 80).collect::<Vec<Vec<u8>>>(), {
+ let x: Vec<Vec<u8>> = vec![
vec![],
vec![],
vec![],
@@ -208,8 +207,9 @@ fn rows() {
vec![],
vec![],
vec![],
- ]
- );
+ ];
+ x
+ });
assert_eq!(
screen1.rows(5, 15).collect::<Vec<String>>(),
vec![
@@ -239,9 +239,8 @@ fn rows() {
String::new(),
]
);
- assert_eq!(
- screen1.rows_formatted(5, 15).collect::<Vec<Vec<u8>>>(),
- vec![
+ assert_eq!(screen1.rows_formatted(5, 15).collect::<Vec<Vec<u8>>>(), {
+ let x: Vec<Vec<u8>> = vec![
vec![],
vec![],
vec![],
@@ -266,8 +265,9 @@ fn rows() {
vec![],
vec![],
vec![],
- ]
- );
+ ];
+ x
+ });
parser
.process(b"\x1b[31mfoo\x1b[10;10H\x1b[32mbar\x1b[20;20H\x1b[33mbaz");
@@ -459,25 +459,29 @@ fn diff_basic() {
parser.process(b"\x1b[5C\x1b[32m bar");
let screen2 = parser.screen().clone();
assert_eq!(screen2.contents_diff(&screen1), b"\x1b[5C\x1b[32m bar");
- compare_diff(&screen1, &screen2, b"");
+ helpers::assert_contents_diff_reproduces_state_from_screens(
+ &screen1, &screen2,
+ );
parser.process(b"\x1b[H\x1b[31mfoo");
let screen3 = parser.screen().clone();
assert_eq!(screen3.contents_diff(&screen2), b"\x1b[H\x1b[31mfoo");
- compare_diff(&screen2, &screen3, b"\x1b[5C\x1b[32m bar");
+ helpers::assert_contents_diff_reproduces_state_from_screens(
+ &screen2, &screen3,
+ );
parser.process(b"\x1b[1;7H\x1b[32mbaz");
let screen4 = parser.screen().clone();
assert_eq!(screen4.contents_diff(&screen3), b"\x1b[5C\x1b[32mz");
- compare_diff(&screen3, &screen4, b"\x1b[5C\x1b[32m bar\x1b[H\x1b[31mfoo");
+ helpers::assert_contents_diff_reproduces_state_from_screens(
+ &screen3, &screen4,
+ );
parser.process(b"\x1b[1;8H\x1b[X");
let screen5 = parser.screen().clone();
assert_eq!(screen5.contents_diff(&screen4), b"\x1b[1;8H\x1b[X");
- compare_diff(
- &screen4,
- &screen5,
- b"\x1b[5C\x1b[32m bar\x1b[H\x1b[31mfoo\x1b[1;7H\x1b[32mbaz",
+ helpers::assert_contents_diff_reproduces_state_from_screens(
+ &screen4, &screen5,
);
}
@@ -523,137 +527,19 @@ fn diff_crawl(i: usize) {
let mut frame = vec![];
file.read_to_end(&mut frame).unwrap();
parser.process(&frame);
- (frame.clone(), parser.screen().clone())
+ parser.screen().clone()
})
.collect();
- let mut all_frames: Vec<u8> = vec![];
for two_screens in screens.windows(2) {
match two_screens {
- [(prev_frame, prev_screen), (_, screen)] => {
- all_frames.extend(prev_frame);
- compare_diff(prev_screen, screen, &all_frames);
+ [prev_screen, screen] => {
+ helpers::assert_contents_diff_reproduces_state_from_screens(
+ prev_screen,
+ screen,
+ );
}
_ => unreachable!(),
}
}
}
-
-fn compare_formatted(screen: &vt100::Screen) {
- let (rows, cols) = screen.size();
- let mut parser = vt100::Parser::new(rows, cols, 0);
- let contents = screen.contents_formatted();
- parser.process(&contents);
- compare_cells(screen, parser.screen());
-}
-
-fn compare_diff(
- prev_screen: &vt100::Screen,
- screen: &vt100::Screen,
- prev_parsed: &[u8],
-) {
- let (rows, cols) = screen.size();
- let mut parser = vt100::Parser::new(rows, cols, 0);
- parser.process(prev_parsed);
- // need to reparse the formatted contents here in case we're in the middle
- // of parsing an escape sequence, since applying the diff at that location
- // directly won't work in that case
- let contents = parser.screen().contents_formatted();
- let mut parser = vt100::Parser::new(rows, cols, 0);
- parser.process(&contents);
- compare_cells(parser.screen(), &prev_screen);
- assert_eq!(parser.screen().hide_cursor(), prev_screen.hide_cursor());
- assert_eq!(
- parser.screen().cursor_position(),
- prev_screen.cursor_position()
- );
- assert_eq!(
- parser.screen().contents_formatted(),
- prev_screen.contents_formatted()
- );
-
- parser.process(&screen.contents_diff(prev_screen));
- if parser.screen().contents_formatted() != screen.contents_formatted() {
- use std::io::Write as _;
- let mut prev_screen_file =
- std::fs::File::create("prev_screen").unwrap();
- prev_screen_file
- .write_all(&prev_screen.contents_formatted())
- .unwrap();
- let mut screen_file = std::fs::File::create("screen").unwrap();
- screen_file.write_all(&screen.contents_formatted()).unwrap();
- let mut diff_file = std::fs::File::create("diff").unwrap();
- diff_file
- .write_all(&screen.contents_diff(prev_screen))
- .unwrap();
- }
- compare_cells(parser.screen(), &screen);
- assert_eq!(parser.screen().hide_cursor(), screen.hide_cursor());
- assert_eq!(parser.screen().cursor_position(), screen.cursor_position());
- assert_eq!(
- parser.screen().contents_formatted(),
- screen.contents_formatted()
- );
-}
-
-fn compare_cells(screen1: &vt100::Screen, screen2: &vt100::Screen) {
- assert_eq!(screen1.size(), screen2.size());
- let (rows, cols) = screen1.size();
-
- for row in 0..rows {
- for col in 0..cols {
- let cell1 = screen1.cell(row, col).unwrap();
- let cell2 = screen2.cell(row, col).unwrap();
-
- assert_eq!(
- cell1.contents(),
- cell2.contents(),
- "cell at position ({},{}) had different contents",
- row + 1,
- col + 1
- );
- assert_eq!(
- cell1.fgcolor(),
- cell2.fgcolor(),
- "cell at position ({},{}) had different fgcolor",
- row + 1,
- col + 1
- );
- assert_eq!(
- cell1.bgcolor(),
- cell2.bgcolor(),
- "cell at position ({},{}) had different bgcolor",
- row + 1,
- col + 1
- );
- assert_eq!(
- cell1.bold(),
- cell2.bold(),
- "cell at position ({},{}) had different bold",
- row + 1,
- col + 1
- );
- assert_eq!(
- cell1.italic(),
- cell2.italic(),
- "cell at position ({},{}) had different italic",
- row + 1,
- col + 1
- );
- assert_eq!(
- cell1.underline(),
- cell2.underline(),
- "cell at position ({},{}) had different underline",
- row + 1,
- col + 1
- );
- assert_eq!(
- cell1.inverse(),
- cell2.inverse(),
- "cell at position ({},{}) had different inverse",
- row + 1,
- col + 1
- );
- }
- }
-}