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