1 mod fixtures;
2 pub use fixtures::fixture;
3 pub use fixtures::FixtureScreen;
4 
5 macro_rules! is {
6     ($got:expr, $expected:expr) => {
7         if ($got) != ($expected) {
8             eprintln!("{} != {}:", stringify!($got), stringify!($expected));
9             eprintln!("     got: {:?}", $got);
10             eprintln!("expected: {:?}", $expected);
11             return false;
12         }
13     };
14 }
15 macro_rules! ok {
16     ($e:expr) => {
17         if !($e) {
18             eprintln!("!{}", stringify!($e));
19             return false;
20         }
21     };
22 }
23 
compare_screens( got: &vt100::Screen, expected: &vt100::Screen, ) -> bool24 pub fn compare_screens(
25     got: &vt100::Screen,
26     expected: &vt100::Screen,
27 ) -> bool {
28     is!(got.contents(), expected.contents());
29     is!(got.contents_formatted(), expected.contents_formatted());
30     is!(
31         got.contents_diff(vt100::Parser::default().screen()),
32         expected.contents_diff(vt100::Parser::default().screen())
33     );
34 
35     let (rows, cols) = got.size();
36 
37     for row in 0..rows {
38         for col in 0..cols {
39             let expected_cell = expected.cell(row, col);
40             let got_cell = got.cell(row, col);
41             is!(got_cell, expected_cell);
42         }
43     }
44 
45     is!(got.cursor_position(), expected.cursor_position());
46     ok!(got.cursor_position().0 <= rows);
47     ok!(expected.cursor_position().0 <= rows);
48     ok!(got.cursor_position().1 <= cols);
49     ok!(expected.cursor_position().1 <= cols);
50 
51     is!(got.title(), expected.title());
52     is!(got.icon_name(), expected.icon_name());
53 
54     is!(
55         got.audible_bell_count() > 0,
56         expected.audible_bell_count() > 0
57     );
58     is!(
59         got.visual_bell_count() > 0,
60         expected.visual_bell_count() > 0
61     );
62 
63     is!(got.application_keypad(), expected.application_keypad());
64     is!(got.application_cursor(), expected.application_cursor());
65     is!(got.hide_cursor(), expected.hide_cursor());
66     is!(got.bracketed_paste(), expected.bracketed_paste());
67     is!(got.mouse_protocol_mode(), expected.mouse_protocol_mode());
68     is!(
69         got.mouse_protocol_encoding(),
70         expected.mouse_protocol_encoding()
71     );
72 
73     true
74 }
75 
76 #[allow(dead_code)]
contents_formatted_reproduces_state(input: &[u8]) -> bool77 pub fn contents_formatted_reproduces_state(input: &[u8]) -> bool {
78     let mut parser = vt100::Parser::default();
79     parser.process(input);
80     contents_formatted_reproduces_screen(parser.screen())
81 }
82 
contents_formatted_reproduces_screen(screen: &vt100::Screen) -> bool83 pub fn contents_formatted_reproduces_screen(screen: &vt100::Screen) -> bool {
84     let empty_screen = vt100::Parser::default().screen().clone();
85 
86     let mut new_input = screen.contents_formatted();
87     new_input.extend(screen.input_mode_formatted());
88     new_input.extend(screen.title_formatted());
89     new_input.extend(screen.bells_diff(&empty_screen));
90     let mut new_parser = vt100::Parser::default();
91     new_parser.process(&new_input);
92     let got_screen = new_parser.screen().clone();
93 
94     compare_screens(&got_screen, &screen)
95 }
96 
assert_contents_formatted_reproduces_state(input: &[u8])97 fn assert_contents_formatted_reproduces_state(input: &[u8]) {
98     assert!(contents_formatted_reproduces_state(input));
99 }
100 
101 #[allow(dead_code)]
contents_diff_reproduces_state(input: &[u8]) -> bool102 pub fn contents_diff_reproduces_state(input: &[u8]) -> bool {
103     contents_diff_reproduces_state_from(input, &[])
104 }
105 
contents_diff_reproduces_state_from( input: &[u8], prev_input: &[u8], ) -> bool106 pub fn contents_diff_reproduces_state_from(
107     input: &[u8],
108     prev_input: &[u8],
109 ) -> bool {
110     let mut parser = vt100::Parser::default();
111     parser.process(prev_input);
112     let prev_screen = parser.screen().clone();
113     parser.process(input);
114 
115     contents_diff_reproduces_state_from_screens(&prev_screen, parser.screen())
116 }
117 
contents_diff_reproduces_state_from_screens( prev_screen: &vt100::Screen, screen: &vt100::Screen, ) -> bool118 pub fn contents_diff_reproduces_state_from_screens(
119     prev_screen: &vt100::Screen,
120     screen: &vt100::Screen,
121 ) -> bool {
122     let mut diff_input = screen.contents_diff(&prev_screen);
123     diff_input.extend(screen.input_mode_diff(&prev_screen));
124     diff_input.extend(screen.title_diff(&prev_screen));
125     diff_input.extend(screen.bells_diff(&prev_screen));
126 
127     let mut diff_prev_input = prev_screen.contents_formatted();
128     diff_prev_input.extend(screen.input_mode_formatted());
129     diff_prev_input.extend(screen.title_formatted());
130     diff_prev_input
131         .extend(screen.bells_diff(vt100::Parser::default().screen()));
132 
133     let mut new_parser = vt100::Parser::default();
134     new_parser.process(&diff_prev_input);
135     new_parser.process(&diff_input);
136     let got_screen = new_parser.screen().clone();
137 
138     compare_screens(&got_screen, &screen)
139 }
140 
141 #[allow(dead_code)]
assert_contents_diff_reproduces_state_from_screens( prev_screen: &vt100::Screen, screen: &vt100::Screen, )142 pub fn assert_contents_diff_reproduces_state_from_screens(
143     prev_screen: &vt100::Screen,
144     screen: &vt100::Screen,
145 ) {
146     assert!(contents_diff_reproduces_state_from_screens(
147         prev_screen,
148         screen,
149     ));
150 }
151 
assert_contents_diff_reproduces_state_from( input: &[u8], prev_input: &[u8], )152 fn assert_contents_diff_reproduces_state_from(
153     input: &[u8],
154     prev_input: &[u8],
155 ) {
156     assert!(contents_diff_reproduces_state_from(input, prev_input));
157 }
158 
159 #[allow(dead_code)]
assert_reproduces_state(input: &[u8])160 pub fn assert_reproduces_state(input: &[u8]) {
161     assert_reproduces_state_from(input, &[]);
162 }
163 
assert_reproduces_state_from(input: &[u8], prev_input: &[u8])164 pub fn assert_reproduces_state_from(input: &[u8], prev_input: &[u8]) {
165     let full_input: Vec<_> =
166         prev_input.iter().chain(input.iter()).copied().collect();
167     assert_contents_formatted_reproduces_state(&full_input);
168     assert_contents_diff_reproduces_state_from(input, prev_input);
169 }
170 
171 #[allow(dead_code)]
format_bytes(bytes: &[u8]) -> String172 pub fn format_bytes(bytes: &[u8]) -> String {
173     let mut v = vec![];
174     for b in bytes {
175         match *b {
176             10 => v.extend(b"\\n"),
177             13 => v.extend(b"\\r"),
178             27 => v.extend(b"\\e"),
179             c if c < 32 => v.extend(format!("\\x{:02x}", c).as_bytes()),
180             b => v.push(b),
181         }
182     }
183     String::from_utf8_lossy(&v).to_string()
184 }
185 
hex_char(c: u8) -> Result<u8, String>186 fn hex_char(c: u8) -> Result<u8, String> {
187     match c {
188         b'0' => Ok(0),
189         b'1' => Ok(1),
190         b'2' => Ok(2),
191         b'3' => Ok(3),
192         b'4' => Ok(4),
193         b'5' => Ok(5),
194         b'6' => Ok(6),
195         b'7' => Ok(7),
196         b'8' => Ok(8),
197         b'9' => Ok(9),
198         b'a' => Ok(10),
199         b'b' => Ok(11),
200         b'c' => Ok(12),
201         b'd' => Ok(13),
202         b'e' => Ok(14),
203         b'f' => Ok(15),
204         b'A' => Ok(10),
205         b'B' => Ok(11),
206         b'C' => Ok(12),
207         b'D' => Ok(13),
208         b'E' => Ok(14),
209         b'F' => Ok(15),
210         _ => Err("invalid hex char".to_string()),
211     }
212 }
213 
hex(upper: u8, lower: u8) -> Result<u8, String>214 pub fn hex(upper: u8, lower: u8) -> Result<u8, String> {
215     Ok(hex_char(upper)? * 16 + hex_char(lower)?)
216 }
217