1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 13:21:38 +03:00

start building out unit tests

This commit is contained in:
Wez Furlong 2018-01-30 08:54:18 -08:00
parent 0eb4dd44b3
commit a55395d7c9
3 changed files with 174 additions and 11 deletions

View File

@ -24,7 +24,7 @@ pub enum AnsiColor {
White, White,
} }
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
pub struct RgbColor { pub struct RgbColor {
pub red: u8, pub red: u8,
pub green: u8, pub green: u8,
@ -33,7 +33,7 @@ pub struct RgbColor {
impl RgbColor {} impl RgbColor {}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum ColorAttribute { pub enum ColorAttribute {
Foreground, Foreground,
Background, Background,

View File

@ -10,11 +10,19 @@ pub mod color;
mod csi; mod csi;
use self::csi::*; use self::csi::*;
#[cfg(test)]
mod test;
/// The response we given when queries for device attributes. /// The response we given when queries for device attributes.
/// This particular string says "we are a VT102". /// This particular string says "we are a VT102".
/// TODO: Consider VT220 extended response which can advertise /// TODO: Consider VT220 extended response which can advertise
/// certain feature sets. /// certain feature sets.
const DEVICE_IDENT: &[u8] = b"\x1b[?6c"; pub const DEVICE_IDENT: &[u8] = b"\x1b[?6c";
pub const CSI: &[u8] = b"\x1b[";
pub const OSC: &[u8] = b"\x1b]";
pub const ST: &[u8] = b"\x1b\\";
pub const DCS: &[u8] = b"\x1bP";
bitflags! { bitflags! {
#[derive(Default)] #[derive(Default)]
@ -39,7 +47,7 @@ pub enum KeyCode {
Shift, Shift,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct CellAttributes { pub struct CellAttributes {
attributes: u16, attributes: u16,
pub foreground: color::ColorAttribute, pub foreground: color::ColorAttribute,
@ -142,12 +150,18 @@ impl Default for CellAttributes {
} }
} }
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Cell { pub struct Cell {
chars: [u8; 8], chars: [u8; 8],
pub attrs: CellAttributes, pub attrs: CellAttributes,
} }
impl Default for Cell {
fn default() -> Cell {
Cell::from_char(' ', &CellAttributes::default())
}
}
impl Cell { impl Cell {
#[inline] #[inline]
pub fn chars(&self) -> &[u8] { pub fn chars(&self) -> &[u8] {
@ -168,6 +182,12 @@ impl Cell {
} }
} }
impl From<char> for Cell {
fn from(c: char) -> Cell {
Cell::from_char(c, &CellAttributes::default())
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Line { pub struct Line {
pub cells: Vec<Cell>, pub cells: Vec<Cell>,
@ -223,6 +243,12 @@ impl Line {
} }
} }
impl<'a> From<&'a str> for Line {
fn from(s: &str) -> Line {
Line::from_text(s, &CellAttributes::default())
}
}
/// Holds the model of a screen. This can either be the primary screen /// Holds the model of a screen. This can either be the primary screen
/// which includes lines of scrollback text, or the alternate screen /// which includes lines of scrollback text, or the alternate screen
/// which holds no scrollback. The intent is to have one instance of /// which holds no scrollback. The intent is to have one instance of
@ -307,6 +333,12 @@ impl Screen {
self.lines[line_idx].dirty = false; self.lines[line_idx].dirty = false;
} }
/// Returns a slice over the visible lines in the screen (no scrollback)
fn visible_lines(&self) -> &[Line] {
let line_idx = self.lines.len() - self.physical_rows;
&self.lines[line_idx..line_idx + self.physical_rows]
}
/// Set a cell. the x and y coordinates are relative to the visible screeen /// Set a cell. the x and y coordinates are relative to the visible screeen
/// origin. 0,0 is the top left. /// origin. 0,0 is the top left.
pub fn set_cell(&mut self, x: usize, y: usize, c: char, attr: &CellAttributes) { pub fn set_cell(&mut self, x: usize, y: usize, c: char, attr: &CellAttributes) {
@ -324,13 +356,13 @@ impl Screen {
let width = cells.len(); let width = cells.len();
// if the line isn't wide enough, pad it out with the default attributes // if the line isn't wide enough, pad it out with the default attributes
if x >= width { if x >= width {
cells.resize(x + 1, Cell::from_char(' ', &CellAttributes::default())); cells.resize(x + 1, Cell::default());
} }
cells[x] = Cell::from_char(c, attr); cells[x] = Cell::from_char(c, attr);
} }
pub fn clear_line(&mut self, y: usize, cols: std::ops::Range<usize>) { pub fn clear_line(&mut self, y: usize, cols: std::ops::Range<usize>) {
let blank = Cell::from_char(' ', &CellAttributes::default()); let blank = Cell::default();
let line_idx = (self.lines.len() - self.physical_rows) + y; let line_idx = (self.lines.len() - self.physical_rows) + y;
let line = self.line_mut(line_idx); let line = self.line_mut(line_idx);
let max_col = line.cells.len(); let max_col = line.cells.len();
@ -881,13 +913,23 @@ impl vte::Perform for TerminalState {
} }
CSIAction::DeleteLines(n) => { CSIAction::DeleteLines(n) => {
let top = self.cursor_y; let top = self.cursor_y;
println!("execute delete {} lines with scroll up {} {}", n, top, top+n); println!(
self.screen_mut().scroll_up(top, top+n, n); "execute delete {} lines with scroll up {} {}",
n,
top,
top + n
);
self.screen_mut().scroll_up(top, top + n, n);
} }
CSIAction::InsertLines(n) => { CSIAction::InsertLines(n) => {
let top = self.cursor_y; let top = self.cursor_y;
println!("execute insert {} lines with scroll down {} {}", n, top, top+n); println!(
self.screen_mut().scroll_down(top, top+n, n); "execute insert {} lines with scroll down {} {}",
n,
top,
top + n
);
self.screen_mut().scroll_down(top, top + n, n);
} }
} }
} }

121
src/term/test/mod.rs Normal file
View File

@ -0,0 +1,121 @@
//! Various tests of the terminal model and escape sequence
//! processing routines.
use super::*;
fn set_mode(term: &mut Terminal, mode: &str, enable: bool) {
term.advance_bytes(CSI);
term.advance_bytes(mode);
term.advance_bytes(if enable { b"h" } else { b"l" });
}
fn cup(term: &mut Terminal, row: isize, col: isize) {
term.advance_bytes(CSI);
term.advance_bytes(format!("{};{}H", row, col));
}
fn erase_in_display(term: &mut Terminal, erase: DisplayErase) {
term.advance_bytes(CSI);
let num = match erase {
DisplayErase::Below => 0,
DisplayErase::Above => 1,
DisplayErase::All => 2,
DisplayErase::SavedLines => 3,
};
term.advance_bytes(format!("{}J", num));
}
fn erase_in_line(term: &mut Terminal, erase: LineErase) {
term.advance_bytes(CSI);
let num = match erase {
LineErase::ToRight => 0,
LineErase::ToLeft => 1,
LineErase::All => 2,
};
term.advance_bytes(format!("{}K", num));
}
/// Asserts that the visible lines of the terminal have the
/// same cell contents. The cells must exactly match.
fn assert_visible_lines(term: &Terminal, expect_lines: &[Line]) {
let screen = term.screen();
let lines = screen.visible_lines();
assert!(
lines.len() == expect_lines.len(),
"expectation has wrong number of lines"
);
let mut expect_iter = expect_lines.iter();
for (idx, line) in lines.iter().enumerate() {
let expect = expect_iter.next().unwrap();
assert!(
expect.cells == line.cells,
"line {} was {:?} but expected {:?}",
idx,
line.cells,
expect.cells
);
}
}
/// Asserts that the visible lines of the terminal have the
/// same character contents as the expected lines.
/// The other cell attributes are not compared; this is
/// a convenience for writing visually understandable tests.
fn assert_visible_contents(term: &Terminal, expect_lines: &[&str]) {
let screen = term.screen();
let lines = screen.visible_lines();
assert!(
lines.len() == expect_lines.len(),
"expectation has wrong number of lines"
);
let mut expect_iter = expect_lines.iter();
for (idx, line) in lines.iter().enumerate() {
let expect = expect_iter.next().unwrap();
let line_str = line.as_str();
assert!(
&line_str == expect,
"line {} was {:?} but expected {:?}",
idx,
line_str,
expect
);
}
}
#[test]
fn basic_output() {
let mut term = Terminal::new(5, 10, 0);
cup(&mut term, 2, 2);
term.advance_bytes("hello, world!");
assert_visible_contents(
&term,
&[
" ",
" hello, wo",
"rld! ",
" ",
" ",
],
);
erase_in_display(&mut term, DisplayErase::Above);
assert_visible_contents(
&term,
&[
" ",
" ",
"rld! ",
" ",
" ",
],
);
}