Skip to main content

altui_core/backend/
test.rs

1use crate::{
2    backend::Backend,
3    buffer::{Buffer, Cell},
4    layout::Rect,
5};
6use std::{fmt::Write, io};
7use unicode_width::UnicodeWidthStr;
8
9/// A backend used for the integration tests.
10#[derive(Debug)]
11pub struct TestBackend {
12    width: u16,
13    buffer: Buffer,
14    height: u16,
15    cursor: bool,
16    pos: (u16, u16),
17}
18
19/// Returns a string representation of the given buffer for debugging purpose.
20fn buffer_view(buffer: &Buffer) -> String {
21    let mut view = String::with_capacity(buffer.content.len() + buffer.area.height as usize * 3);
22    for cells in buffer.content.chunks(buffer.area.width as usize) {
23        let mut overwritten = vec![];
24        let mut skip: usize = 0;
25        view.push('"');
26        for (x, c) in cells.iter().enumerate() {
27            if skip == 0 {
28                view.push_str(&c.symbol);
29            } else {
30                overwritten.push((x, &c.symbol))
31            }
32            skip = std::cmp::max(skip, c.symbol.width()).saturating_sub(1);
33        }
34        view.push('"');
35        if !overwritten.is_empty() {
36            write!(
37                &mut view,
38                " Hidden by multi-width symbols: {:?}",
39                overwritten
40            )
41            .unwrap();
42        }
43        view.push('\n');
44    }
45    view
46}
47
48impl TestBackend {
49    pub fn new(width: u16, height: u16) -> TestBackend {
50        TestBackend {
51            width,
52            height,
53            buffer: Buffer::empty(Rect::new(0, 0, width, height)),
54            cursor: false,
55            pos: (0, 0),
56        }
57    }
58
59    pub fn buffer(&self) -> &Buffer {
60        &self.buffer
61    }
62
63    pub fn resize(&mut self, width: u16, height: u16) {
64        self.buffer.resize(Rect::new(0, 0, width, height));
65        self.width = width;
66        self.height = height;
67    }
68
69    pub fn assert_buffer(&self, expected: &Buffer) {
70        assert_eq!(expected.area, self.buffer.area);
71        let diff = expected.diff(&self.buffer);
72        if diff.is_empty() {
73            return;
74        }
75
76        let mut debug_info = String::from("Buffers are not equal");
77        debug_info.push('\n');
78        debug_info.push_str("Expected:");
79        debug_info.push('\n');
80        let expected_view = buffer_view(expected);
81        debug_info.push_str(&expected_view);
82        debug_info.push('\n');
83        debug_info.push_str("Got:");
84        debug_info.push('\n');
85        let view = buffer_view(&self.buffer);
86        debug_info.push_str(&view);
87        debug_info.push('\n');
88
89        debug_info.push_str("Diff:");
90        debug_info.push('\n');
91        let nice_diff = diff
92            .iter()
93            .enumerate()
94            .map(|(i, (x, y, cell))| {
95                let expected_cell = expected.get(*x, *y);
96                format!(
97                    "{}: at ({}, {}) expected {:?} got {:?}",
98                    i, x, y, expected_cell, cell
99                )
100            })
101            .collect::<Vec<String>>()
102            .join("\n");
103        debug_info.push_str(&nice_diff);
104        panic!("{}", debug_info);
105    }
106}
107
108impl Backend for TestBackend {
109    fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
110    where
111        I: Iterator<Item = (u16, u16, &'a Cell)>,
112    {
113        for (x, y, c) in content {
114            let cell = self.buffer.get_mut(x, y);
115            *cell = c.clone();
116        }
117        Ok(())
118    }
119
120    fn hide_cursor(&mut self) -> Result<(), io::Error> {
121        self.cursor = false;
122        Ok(())
123    }
124
125    fn show_cursor(&mut self) -> Result<(), io::Error> {
126        self.cursor = true;
127        Ok(())
128    }
129
130    fn get_cursor(&mut self) -> Result<(u16, u16), io::Error> {
131        Ok(self.pos)
132    }
133
134    fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error> {
135        self.pos = (x, y);
136        Ok(())
137    }
138
139    fn clear(&mut self) -> Result<(), io::Error> {
140        self.buffer.reset();
141        Ok(())
142    }
143
144    fn size(&self) -> Result<Rect, io::Error> {
145        Ok(Rect::new(0, 0, self.width, self.height))
146    }
147
148    fn flush(&mut self) -> Result<(), io::Error> {
149        Ok(())
150    }
151}