summaryrefslogtreecommitdiff
path: root/helix-tui/src/backend/test.rs
diff options
context:
space:
mode:
Diffstat (limited to 'helix-tui/src/backend/test.rs')
-rw-r--r--helix-tui/src/backend/test.rs151
1 files changed, 151 insertions, 0 deletions
diff --git a/helix-tui/src/backend/test.rs b/helix-tui/src/backend/test.rs
new file mode 100644
index 00000000..46b37261
--- /dev/null
+++ b/helix-tui/src/backend/test.rs
@@ -0,0 +1,151 @@
+use crate::{
+ backend::Backend,
+ buffer::{Buffer, Cell},
+ layout::Rect,
+};
+use std::{fmt::Write, io};
+use unicode_width::UnicodeWidthStr;
+
+/// A backend used for the integration tests.
+#[derive(Debug)]
+pub struct TestBackend {
+ width: u16,
+ buffer: Buffer,
+ height: u16,
+ cursor: bool,
+ pos: (u16, u16),
+}
+
+/// Returns a string representation of the given buffer for debugging purpose.
+fn buffer_view(buffer: &Buffer) -> String {
+ let mut view = String::with_capacity(buffer.content.len() + buffer.area.height as usize * 3);
+ for cells in buffer.content.chunks(buffer.area.width as usize) {
+ let mut overwritten = vec![];
+ let mut skip: usize = 0;
+ view.push('"');
+ for (x, c) in cells.iter().enumerate() {
+ if skip == 0 {
+ view.push_str(&c.symbol);
+ } else {
+ overwritten.push((x, &c.symbol))
+ }
+ skip = std::cmp::max(skip, c.symbol.width()).saturating_sub(1);
+ }
+ view.push('"');
+ if !overwritten.is_empty() {
+ write!(
+ &mut view,
+ " Hidden by multi-width symbols: {:?}",
+ overwritten
+ )
+ .unwrap();
+ }
+ view.push('\n');
+ }
+ view
+}
+
+impl TestBackend {
+ pub fn new(width: u16, height: u16) -> TestBackend {
+ TestBackend {
+ width,
+ height,
+ buffer: Buffer::empty(Rect::new(0, 0, width, height)),
+ cursor: false,
+ pos: (0, 0),
+ }
+ }
+
+ pub fn buffer(&self) -> &Buffer {
+ &self.buffer
+ }
+
+ pub fn resize(&mut self, width: u16, height: u16) {
+ self.buffer.resize(Rect::new(0, 0, width, height));
+ self.width = width;
+ self.height = height;
+ }
+
+ pub fn assert_buffer(&self, expected: &Buffer) {
+ assert_eq!(expected.area, self.buffer.area);
+ let diff = expected.diff(&self.buffer);
+ if diff.is_empty() {
+ return;
+ }
+
+ let mut debug_info = String::from("Buffers are not equal");
+ debug_info.push('\n');
+ debug_info.push_str("Expected:");
+ debug_info.push('\n');
+ let expected_view = buffer_view(expected);
+ debug_info.push_str(&expected_view);
+ debug_info.push('\n');
+ debug_info.push_str("Got:");
+ debug_info.push('\n');
+ let view = buffer_view(&self.buffer);
+ debug_info.push_str(&view);
+ debug_info.push('\n');
+
+ debug_info.push_str("Diff:");
+ debug_info.push('\n');
+ let nice_diff = diff
+ .iter()
+ .enumerate()
+ .map(|(i, (x, y, cell))| {
+ let expected_cell = expected.get(*x, *y);
+ format!(
+ "{}: at ({}, {}) expected {:?} got {:?}",
+ i, x, y, expected_cell, cell
+ )
+ })
+ .collect::<Vec<String>>()
+ .join("\n");
+ debug_info.push_str(&nice_diff);
+ panic!("{}", debug_info);
+ }
+}
+
+impl Backend for TestBackend {
+ fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
+ where
+ I: Iterator<Item = (u16, u16, &'a Cell)>,
+ {
+ for (x, y, c) in content {
+ let cell = self.buffer.get_mut(x, y);
+ *cell = c.clone();
+ }
+ Ok(())
+ }
+
+ fn hide_cursor(&mut self) -> Result<(), io::Error> {
+ self.cursor = false;
+ Ok(())
+ }
+
+ fn show_cursor(&mut self) -> Result<(), io::Error> {
+ self.cursor = true;
+ Ok(())
+ }
+
+ fn get_cursor(&mut self) -> Result<(u16, u16), io::Error> {
+ Ok(self.pos)
+ }
+
+ fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error> {
+ self.pos = (x, y);
+ Ok(())
+ }
+
+ fn clear(&mut self) -> Result<(), io::Error> {
+ self.buffer.reset();
+ Ok(())
+ }
+
+ fn size(&self) -> Result<Rect, io::Error> {
+ Ok(Rect::new(0, 0, self.width, self.height))
+ }
+
+ fn flush(&mut self) -> Result<(), io::Error> {
+ Ok(())
+ }
+}