#[cfg(test)]
mod tests {
use crate::event::{Rect as QRect, Viewport};
use crate::gtk::backend::GtkBackend;
use crate::primitives::terminal::{Terminal, TerminalCell, TerminalCursorShape};
use crate::theme::Theme;
use crate::types::{Color, WidgetId};
use crate::Backend;
use pangocairo::cairo::{Context, Format, ImageSurface};
const W: i32 = 200;
const H: i32 = 40;
const CHAR_W: f64 = 10.0;
const LINE_H: f64 = 20.0;
fn pixel(data: &[u8], stride: usize, x: i32, y: i32) -> (u8, u8, u8) {
let off = y as usize * stride + x as usize * 4;
(data[off + 2], data[off + 1], data[off])
}
fn cell(ch: char, fg: Color, bg: Color) -> TerminalCell {
TerminalCell {
text: ch.to_string(),
fg,
bg,
bold: false,
italic: false,
underline: false,
dim: false,
selected: false,
is_cursor: false,
is_find_match: false,
is_find_active: false,
cursor_shape: TerminalCursorShape::Block,
cursor_blinking: false,
}
}
fn paint(term: &Terminal) -> ImageSurface {
let mut backend = GtkBackend::new();
backend.set_current_theme(Theme::default());
backend.set_current_line_height(LINE_H);
backend.set_current_char_width(CHAR_W);
Backend::begin_frame(&mut backend, Viewport::new(W as f32, H as f32, 1.0));
let surface = ImageSurface::create(Format::ARgb32, W, H).expect("create ImageSurface");
{
let cr = Context::new(&surface).expect("Context::new");
let pango_layout = pangocairo::functions::create_layout(&cr);
backend.enter_frame_scope(&cr, &pango_layout, |b| {
b.draw_terminal(QRect::new(0.0, 0.0, W as f32, H as f32), term);
});
}
surface
}
#[test]
fn wide_cell_background_spans_two_columns() {
let magenta = Color::rgb(200, 30, 200);
let cyan = Color::rgb(30, 200, 200);
let row = vec![cell('日', magenta, magenta), cell(' ', magenta, cyan)];
let term = Terminal {
id: WidgetId::new("term"),
cells: vec![row],
scrollbar: None,
};
let mut s = paint(&term);
s.flush();
let stride = s.stride() as usize;
let data = s.data().expect("surface data");
let probe_x = (CHAR_W * 1.5) as i32;
let (r, g, b) = pixel(&data, stride, probe_x, 5);
assert_eq!(
(r, g, b),
(magenta.r, magenta.g, magenta.b),
"wide cell's background should span both columns, not be \
overpainted by the continuation cell's background"
);
}
#[test]
fn narrow_cells_advance_by_single_char_width() {
let magenta = Color::rgb(200, 30, 200);
let cyan = Color::rgb(30, 200, 200);
let white = Color::rgb(255, 255, 255);
let row = vec![cell('A', white, magenta), cell('B', cyan, cyan)];
let term = Terminal {
id: WidgetId::new("term"),
cells: vec![row],
scrollbar: None,
};
let mut s = paint(&term);
s.flush();
let stride = s.stride() as usize;
let data = s.data().expect("surface data");
let probe_x = (CHAR_W * 1.5) as i32;
let (r, g, b) = pixel(&data, stride, probe_x, 5);
assert_eq!(
(r, g, b),
(cyan.r, cyan.g, cyan.b),
"narrow cells must still advance by exactly char_width"
);
}
#[test]
fn bar_cursor_paints_a_left_edge_accent_stripe() {
let fg = Color::rgb(255, 0, 0);
let bg = Color::rgb(0, 0, 0);
let mut c = cell(' ', fg, bg);
c.is_cursor = true;
c.cursor_shape = TerminalCursorShape::Bar;
let term = Terminal {
id: WidgetId::new("term"),
cells: vec![vec![c]],
scrollbar: None,
};
let mut s = paint(&term);
s.flush();
let stride = s.stride() as usize;
let data = s.data().expect("surface data");
let (r, g, b) = pixel(&data, stride, 1, 5);
assert_eq!(
(r, g, b),
(fg.r, fg.g, fg.b),
"bar cursor accent should paint at the cell's left edge"
);
let (r2, g2, b2) = pixel(&data, stride, 8, 5);
assert_eq!(
(r2, g2, b2),
(bg.r, bg.g, bg.b),
"bar cursor must not invert the rest of the cell"
);
}
#[test]
fn underline_cursor_paints_a_bottom_edge_accent_stripe() {
let fg = Color::rgb(0, 255, 0);
let bg = Color::rgb(0, 0, 0);
let mut c = cell(' ', fg, bg);
c.is_cursor = true;
c.cursor_shape = TerminalCursorShape::Underline;
let term = Terminal {
id: WidgetId::new("term"),
cells: vec![vec![c]],
scrollbar: None,
};
let mut s = paint(&term);
s.flush();
let stride = s.stride() as usize;
let data = s.data().expect("surface data");
let (r, g, b) = pixel(&data, stride, 5, 19);
assert_eq!(
(r, g, b),
(fg.r, fg.g, fg.b),
"underline cursor accent should paint at the cell's bottom edge"
);
let (r2, g2, b2) = pixel(&data, stride, 5, 2);
assert_eq!(
(r2, g2, b2),
(bg.r, bg.g, bg.b),
"underline cursor must not invert the rest of the cell"
);
}
#[test]
fn block_cursor_still_inverts_whole_cell() {
let fg = Color::rgb(255, 0, 0);
let bg = Color::rgb(0, 0, 0);
let mut c = cell(' ', fg, bg);
c.is_cursor = true;
let term = Terminal {
id: WidgetId::new("term"),
cells: vec![vec![c]],
scrollbar: None,
};
let mut s = paint(&term);
s.flush();
let stride = s.stride() as usize;
let data = s.data().expect("surface data");
let (r, g, b) = pixel(&data, stride, 5, 10);
assert_eq!(
(r, g, b),
(fg.r, fg.g, fg.b),
"block cursor should invert: the whole cell shows the cell's own fg as its background"
);
}
}