Terminal: pass vttest 1 (#408)

* fix(compatibility): pass vttest 1

* fix(tests): move unit tests to a separate file

* style(fmt): rustfmt

* fix(grid): correct comment

* style(fmt): rustfmt
This commit is contained in:
Aram Drevekenin 2021-04-29 15:25:08 +02:00 committed by GitHub
parent 03be8e309e
commit cedd830a2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 985 additions and 27 deletions

View File

@ -161,6 +161,7 @@ pub struct Grid {
pending_styles: CharacterStyles,
pub should_render: bool,
pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "")
pub erasure_mode: bool, // ERM
pub clear_viewport_before_rendering: bool,
pub width: usize,
pub height: usize,
@ -170,9 +171,9 @@ impl Debug for Grid {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for (i, row) in self.viewport.iter().enumerate() {
if row.is_canonical {
writeln!(f, "{:?} (C): {:?}", i, row)?;
writeln!(f, "{:02?} (C): {:?}", i, row)?;
} else {
writeln!(f, "{:?} (W): {:?}", i, row)?;
writeln!(f, "{:02?} (W): {:?}", i, row)?;
}
}
Ok(())
@ -192,20 +193,20 @@ impl Grid {
pending_styles: CharacterStyles::new(),
should_render: true,
cursor_key_mode: false,
erasure_mode: false,
alternative_lines_above_viewport_and_cursor: None,
clear_viewport_before_rendering: false,
}
}
pub fn advance_to_next_tabstop(&mut self, styles: CharacterStyles) {
let columns_until_next_tabstop = TABSTOP_WIDTH - (self.cursor.x % TABSTOP_WIDTH);
let columns_until_screen_end = self.width - self.cursor.x;
let columns_until_screen_end = (self.width - self.cursor.x).saturating_sub(1);
let columns_to_advance =
std::cmp::min(columns_until_next_tabstop, columns_until_screen_end);
let mut empty_character = EMPTY_TERMINAL_CHARACTER;
empty_character.styles = styles;
for _ in 0..columns_to_advance {
self.add_character(empty_character)
}
self.cursor.x += columns_to_advance;
self.pad_current_line_until(self.cursor.x);
}
fn cursor_canonical_line_index(&self) -> usize {
let mut cursor_canonical_line_index = 0;
@ -475,6 +476,13 @@ impl Grid {
}
}
}
pub fn fill_viewport(&mut self, character: TerminalCharacter) {
self.viewport.clear();
for _ in 0..self.height {
let columns = vec![character; self.width];
self.viewport.push(Row::from_columns(columns).canonical());
}
}
pub fn add_canonical_line(&mut self) {
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
if self.cursor.y == scroll_region_bottom {
@ -490,8 +498,12 @@ impl Grid {
}
self.viewport.remove(scroll_region_top);
let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
if self.viewport.len() >= scroll_region_bottom {
self.viewport
.insert(scroll_region_bottom, Row::from_columns(columns).canonical());
} else {
self.viewport.push(Row::from_columns(columns).canonical());
}
return;
}
}
@ -518,13 +530,6 @@ impl Grid {
pub fn move_cursor_to_beginning_of_line(&mut self) {
self.cursor.x = 0;
}
pub fn move_cursor_backwards(&mut self, count: usize) {
if self.cursor.x > count {
self.cursor.x -= count;
} else {
self.cursor.x = 0;
}
}
pub fn insert_character_at_cursor_position(&mut self, terminal_character: TerminalCharacter) {
match self.viewport.get_mut(self.cursor.y) {
Some(row) => {
@ -601,17 +606,13 @@ impl Grid {
}
}
pub fn replace_characters_in_line_before_cursor(&mut self, replace_with: TerminalCharacter) {
let line_part = vec![replace_with; self.cursor.x];
let line_part = vec![replace_with; self.cursor.x + 1];
let row = self.viewport.get_mut(self.cursor.y).unwrap();
row.replace_beginning_with(line_part);
}
pub fn clear_all_after_cursor(&mut self, replace_with: TerminalCharacter) {
if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y).as_mut() {
cursor_row.truncate(self.cursor.x);
let mut replace_with_columns_in_cursor_row =
vec![replace_with; self.width - self.cursor.x];
cursor_row.append(&mut replace_with_columns_in_cursor_row);
let replace_with_columns = vec![replace_with; self.width];
self.replace_characters_in_line_after_cursor(replace_with);
for row in self.viewport.iter_mut().skip(self.cursor.y + 1) {
@ -619,6 +620,15 @@ impl Grid {
}
}
}
pub fn clear_all_before_cursor(&mut self, replace_with: TerminalCharacter) {
if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y).as_mut() {
self.replace_characters_in_line_before_cursor(replace_with);
let replace_with_columns = vec![replace_with; self.width];
for row in self.viewport.iter_mut().take(self.cursor.y) {
row.replace_columns(replace_with_columns.clone());
}
}
}
pub fn clear_cursor_line(&mut self) {
self.viewport.get_mut(self.cursor.y).unwrap().truncate(0);
}
@ -642,11 +652,26 @@ impl Grid {
}
}
pub fn move_cursor_to(&mut self, x: usize, y: usize, pad_character: TerminalCharacter) {
match self.scroll_region {
Some((scroll_region_top, scroll_region_bottom)) => {
self.cursor.x = std::cmp::min(self.width - 1, x);
let y_offset = if self.erasure_mode {
scroll_region_top
} else {
0
};
self.cursor.y = std::cmp::min(scroll_region_bottom, y + y_offset);
self.pad_lines_until(self.cursor.y, pad_character);
self.pad_current_line_until(self.cursor.x);
}
None => {
self.cursor.x = std::cmp::min(self.width - 1, x);
self.cursor.y = std::cmp::min(self.height - 1, y);
self.pad_lines_until(self.cursor.y, pad_character);
self.pad_current_line_until(self.cursor.x);
}
}
}
pub fn move_cursor_up(&mut self, count: usize) {
self.cursor.y = if self.cursor.y < count {
0
@ -690,6 +715,10 @@ impl Grid {
self.pad_lines_until(self.cursor.y, pad_character);
}
pub fn move_cursor_back(&mut self, count: usize) {
if self.cursor.x == self.width {
// on the rightmost screen edge, backspace skips one character
self.cursor.x -= 1;
}
if self.cursor.x < count {
self.cursor.x = 0;
} else {
@ -827,14 +856,15 @@ impl vte::Perform for Grid {
match byte {
8 => {
// backspace
self.move_cursor_backwards(1);
self.move_cursor_back(1);
}
9 => {
// tab
self.advance_to_next_tabstop(self.pending_styles);
}
10 => {
10 | 11 => {
// 0a, newline
// 0b, form-feed
self.add_newline();
}
13 => {
@ -891,11 +921,13 @@ impl vte::Perform for Grid {
char_to_replace.styles = self.pending_styles;
if params[0] == 0 {
self.clear_all_after_cursor(char_to_replace);
} else if params[0] == 1 {
self.clear_all_before_cursor(char_to_replace);
} else if params[0] == 2 {
self.clear_all(char_to_replace);
}
// TODO: implement 1
} else if c == 'H' {
} else if c == 'H' || c == 'f' {
// goto row/col
// we subtract 1 from the row/column because these are 1 indexed
// (except when they are 0, in which case they should be 1
@ -963,6 +995,16 @@ impl vte::Perform for Grid {
Some(&1) => {
self.cursor_key_mode = false;
}
Some(&3) => {
// DECCOLM - only side effects
self.scroll_region = None;
self.clear_all(EMPTY_TERMINAL_CHARACTER);
self.cursor.x = 0;
self.cursor.y = 0;
}
Some(&6) => {
self.erasure_mode = false;
}
_ => {}
};
}
@ -993,6 +1035,16 @@ impl vte::Perform for Grid {
Some(&1) => {
self.cursor_key_mode = true;
}
Some(&3) => {
// DECCOLM - only side effects
self.scroll_region = None;
self.clear_all(EMPTY_TERMINAL_CHARACTER);
self.cursor.x = 0;
self.cursor.y = 0;
}
Some(&6) => {
self.erasure_mode = true;
}
_ => {}
};
}
@ -1098,12 +1150,27 @@ impl vte::Perform for Grid {
fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) {
match (byte, intermediates.get(0)) {
(b'D', None) => {
self.add_newline();
}
(b'E', None) => {
self.add_newline();
self.move_cursor_to_beginning_of_line();
}
(b'M', None) => {
self.move_cursor_up_with_scrolling(1);
}
(b'c', None) => {
self.reset_terminal_state();
}
(b'H', None) => {
self.advance_to_next_tabstop(self.pending_styles);
}
(b'8', Some(b'#')) => {
let mut fill_character = EMPTY_TERMINAL_CHARACTER;
fill_character.character = 'E';
self.fill_viewport(fill_character);
}
_ => {}
}
}
@ -1254,3 +1321,7 @@ impl Cursor {
}
}
}
#[cfg(test)]
#[path = "./unit/grid_tests.rs"]
mod grid_tests;

View File

@ -0,0 +1,85 @@
use super::super::Grid;
use ::insta::assert_snapshot;
fn read_fixture(fixture_name: &str) -> Vec<u8> {
let mut path_to_file = std::path::PathBuf::new();
path_to_file.push("src");
path_to_file.push("tests");
path_to_file.push("fixtures");
path_to_file.push(fixture_name);
let content = std::fs::read(path_to_file)
.unwrap_or_else(|_| panic!("could not read fixture {:?}", &fixture_name));
content
}
#[test]
fn vttest1_0() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let fixture_name = "vttest1-0";
let content = read_fixture(fixture_name);
for byte in content {
vte_parser.advance(&mut grid, byte);
}
assert_snapshot!(format!("{:?}", grid));
}
#[test]
fn vttest1_1() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let fixture_name = "vttest1-1";
let content = read_fixture(fixture_name);
for byte in content {
vte_parser.advance(&mut grid, byte);
}
assert_snapshot!(format!("{:?}", grid));
}
#[test]
fn vttest1_2() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let fixture_name = "vttest1-2";
let content = read_fixture(fixture_name);
for byte in content {
vte_parser.advance(&mut grid, byte);
}
assert_snapshot!(format!("{:?}", grid));
}
#[test]
fn vttest1_3() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let fixture_name = "vttest1-3";
let content = read_fixture(fixture_name);
for byte in content {
vte_parser.advance(&mut grid, byte);
}
assert_snapshot!(format!("{:?}", grid));
}
#[test]
fn vttest1_4() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let fixture_name = "vttest1-4";
let content = read_fixture(fixture_name);
for byte in content {
vte_parser.advance(&mut grid, byte);
}
assert_snapshot!(format!("{:?}", grid));
}
#[test]
fn vttest1_5() {
let mut vte_parser = vte::Parser::new();
let mut grid = Grid::new(41, 110);
let fixture_name = "vttest1-5";
let content = read_fixture(fixture_name);
for byte in content {
vte_parser.advance(&mut grid, byte);
}
assert_snapshot!(format!("{:?}", grid));
}

View File

@ -0,0 +1,47 @@
---
source: src/client/panes/grid.rs
expression: "format!(\"{:?}\", grid)"
---
00 (C): ********************************************************************************
01 (C): *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
02 (C): *+ +*
03 (C): *+ +*
04 (C): *+ +*
05 (C): *+ +*
06 (C): *+ +*
07 (C): *+ +*
08 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
09 (C): *+ E E +*
10 (C): *+ E The screen should be cleared, and have an unbroken bor- E +*
11 (C): *+ E der of *'s and +'s around the edge, and exactly in the E +*
12 (C): *+ E middle there should be a frame of E's around this text E +*
13 (C): *+ E with one (1) free position around it. Push <RETURN> E +*
14 (C): *+ E E +*
15 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
16 (C): *+ +*
17 (C): *+ +*
18 (C): *+ +*
19 (C): *+ +*
20 (C): *+ +*
21 (C): *+ +*
22 (C): *+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
23 (C): ********************************************************************************
24 (C):
25 (C):
26 (C):
27 (C):
28 (C):
29 (C):
30 (C):
31 (C):
32 (C):
33 (C):
34 (C):
35 (C):
36 (C):
37 (C):
38 (C):
39 (C):
40 (C):

View File

@ -0,0 +1,47 @@
---
source: src/client/panes/grid.rs
expression: "format!(\"{:?}\", grid)"
---
00 (C): **************************************************************************************************************
01 (C): *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
02 (C): *+ +*
03 (C): *+ +*
04 (C): *+ +*
05 (C): *+ +*
06 (C): *+ +*
07 (C): *+ +*
08 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
09 (C): *+ E E +*
10 (C): *+ E The screen should be cleared, and have an unbroken bor- E +*
11 (C): *+ E der of *'s and +'s around the edge, and exactly in the E +*
12 (C): *+ E middle there should be a frame of E's around this text E +*
13 (C): *+ E with one (1) free position around it. Push <RETURN> E +*
14 (C): *+ E E +*
15 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
16 (C): *+ +*
17 (C): *+ +*
18 (C): *+ +*
19 (C): *+ +*
20 (C): *+ +*
21 (C): *+ +*
22 (C): ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ *
23 (C): **************************************************************************************************************
24 (C):
25 (C):
26 (C):
27 (C):
28 (C):
29 (C):
30 (C):
31 (C):
32 (C):
33 (C):
34 (C):
35 (C):
36 (C):
37 (C):
38 (C):
39 (C):
40 (C):

View File

@ -0,0 +1,47 @@
---
source: src/client/panes/grid.rs
expression: "format!(\"{:?}\", grid)"
---
00 (C): Test of autowrap, mixing control and print characters.
01 (C): The left/right margins should have letters in order:
02 (C): I iJ
03 (C): j
04 (C): K K k
05 (C): L l
06 (C): M mN
07 (C): n
08 (C): O O o
09 (C): P p
10 (C): Q qR
11 (C): r
12 (C): S S s
13 (C): T t
14 (C): U uV
15 (C): v
16 (C): W W w
17 (C): X x
18 (C): Y yZ
19 (C): z
20 (C):
21 (C): Push <RETURN>
22 (C):
23 (C):
24 (C):
25 (C):
26 (C):
27 (C):
28 (C):
29 (C):
30 (C):
31 (C):
32 (C):
33 (C):
34 (C):
35 (C):
36 (C):
37 (C):
38 (C):
39 (C):
40 (C):

View File

@ -0,0 +1,47 @@
---
source: src/client/panes/grid.rs
expression: "format!(\"{:?}\", grid)"
---
00 (C): Test of autowrap, mixing control and print characters.
01 (C): The left/right margins should have letters in order:
02 (C): I i
03 (C): J j
04 (C): K k
05 (C): L l
06 (C): M m
07 (C): N n
08 (C): O o
09 (C): P p
10 (C): Q q
11 (C): R r
12 (C): S s
13 (C): T t
14 (C): U u
15 (C): V v
16 (C): W w
17 (C): X x
18 (C): Y y
19 (C): Z z
20 (C):
21 (C): Push <RETURN>
22 (C):
23 (C):
24 (C):
25 (C):
26 (C):
27 (C):
28 (C):
29 (C):
30 (C):
31 (C):
32 (C):
33 (C):
34 (C):
35 (C):
36 (C):
37 (C):
38 (C):
39 (C):
40 (C):

View File

@ -0,0 +1,47 @@
---
source: src/client/panes/grid.rs
expression: "format!(\"{:?}\", grid)"
---
00 (C): Test of cursor-control characters inside ESC sequences.
01 (C): Below should be four identical lines:
02 (C):
03 (C): A B C D E F G H I
04 (C): A B C D E F G H I
05 (C): A B C D E F G H I
06 (C): A B C D E F G H I
07 (C):
08 (C): Push <RETURN>
09 (C):
10 (C):
11 (C):
12 (C):
13 (C):
14 (C):
15 (C):
16 (C):
17 (C):
18 (C):
19 (C):
20 (C):
21 (C):
22 (C):
23 (C):
24 (C):
25 (C):
26 (C):
27 (C):
28 (C):
29 (C):
30 (C):
31 (C):
32 (C):
33 (C):
34 (C):
35 (C):
36 (C):
37 (C):
38 (C):
39 (C):
40 (C):

View File

@ -0,0 +1,47 @@
---
source: src/client/panes/grid.rs
expression: "format!(\"{:?}\", grid)"
---
00 (C): Test of leading zeros in ESC sequences.
01 (C): Two lines below you should see the sentence "This is a correct sentence".
02 (C):
03 (C): This is a correct sentence
04 (C):
05 (C):
06 (C):
07 (C):
08 (C):
09 (C):
10 (C):
11 (C):
12 (C):
13 (C):
14 (C):
15 (C):
16 (C):
17 (C):
18 (C):
19 (C): Push <RETURN>
20 (C):
21 (C):
22 (C):
23 (C):
24 (C):
25 (C):
26 (C):
27 (C):
28 (C):
29 (C):
30 (C):
31 (C):
32 (C):
33 (C):
34 (C):
35 (C):
36 (C):
37 (C):
38 (C):
39 (C):
40 (C):

34
src/tests/fixtures/vttest1-0 vendored Normal file
View File

@ -0,0 +1,34 @@
⏎(B ⏎ Welcome to fish, the friendly interactive shell
[?2004h]0;fish /home/aram/code/zellij(B⋊>(B ~/c/zellij(B on vttest-1(B 11:25:27(B   v11:25:27(B  v(B11:25:27(B  ttest(B11:25:27(B  ttest(B11:25:27(B  test(B11:25:27(B  est(B11:25:27(B  st(B11:25:27(B  t(B11:25:27(B  vttest(B11:25:27(B  11:25:27(B  
(B[?2004l]0;vttest /home/aram/code/zellij(B [?1l[?3l[?4l[?5l[?6l[?7h[?8l[?40h[?45lVT100 test program, version 2.7 (20210210)Line speed 38400bd Choose test type:

0. Exit
1. Test of cursor movements
2. Test of screen features
3. Test of character sets
4. Test of double-sized characters
5. Test of keyboard
6. Test of terminal reports
7. Test of VT52 mode
8. Test of VT102 features (Insert/Delete Char/Line)
9. Test of known bugs
10. Test of reset and self-test
11. Test non-VT100 (e.g., VT220, XTERM) terminals
12. Modify test-parameters
Enter choice number (0 - 12): 1
[?3l#8****************************************************************************************************************************************************************+D+D+D+D+D+D+D+D+D+D+D+D+D+D+D+D+D+D+D+D+D+D+M+M+M+M+M+M+M+M+M+M+M+M+M+M+M+M+M+M+M+M+M+M**E**E**E**E**E**E**E**E**
**
**
**
**
**
**
**
**
**
**
**
**
**
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++      The screen should be cleared, and have an unbroken bor-der of *'s and +'s around the edge, and exactly in themiddle there should be a frame of E's around this textwith one (1) free position around it. Push <RETURN>

48
src/tests/fixtures/vttest1-1 vendored Normal file

File diff suppressed because one or more lines are too long

76
src/tests/fixtures/vttest1-2 vendored Normal file

File diff suppressed because one or more lines are too long

137
src/tests/fixtures/vttest1-3 vendored Normal file

File diff suppressed because one or more lines are too long

112
src/tests/fixtures/vttest1-4 vendored Normal file

File diff suppressed because one or more lines are too long

113
src/tests/fixtures/vttest1-5 vendored Normal file

File diff suppressed because one or more lines are too long