use std::error::Error;
use super::*;
#[test]
fn probe_result_success() {
let result = ProbeResult::Success;
assert!(matches!(result, ProbeResult::Success));
}
#[test]
fn probe_result_defer() {
let result = ProbeResult::Defer("not ready".to_string());
assert!(matches!(result, ProbeResult::Defer(ref s) if s == "not ready"));
}
#[test]
fn probe_result_failed() {
let err = ClientModuleError::other("boom");
let result = ProbeResult::Failed(err);
assert!(matches!(result, ProbeResult::Failed(ref e) if e.message() == "boom"));
}
#[test]
fn client_module_error_other() {
let err = ClientModuleError::other("test error");
assert_eq!(err.message(), "test error");
assert_eq!(err.to_string(), "test error");
}
#[test]
fn client_module_error_init_failed() {
let err = ClientModuleError::init_failed("no server", None);
assert_eq!(err.message(), "no server");
assert_eq!(err.to_string(), "init failed: no server");
assert!(err.source().is_none());
}
#[test]
fn client_module_error_init_failed_with_source() {
let source = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
let err = ClientModuleError::init_failed("config load failed", Some(Box::new(source)));
assert_eq!(err.message(), "config load failed");
assert!(err.source().is_some());
}
#[test]
fn client_module_error_exit_failed() {
let err = ClientModuleError::exit_failed("cleanup error");
assert_eq!(err.message(), "cleanup error");
assert_eq!(err.to_string(), "exit failed: cleanup error");
assert!(err.source().is_none());
}
#[test]
fn client_module_error_notification_parse() {
let err = ClientModuleError::notification_parse("whichkey", "invalid JSON");
assert_eq!(err.message(), "invalid JSON");
assert_eq!(err.to_string(), "notification parse error (whichkey): invalid JSON");
assert!(err.source().is_none());
}
#[test]
fn client_module_error_empty_message() {
let err = ClientModuleError::other("");
assert!(err.message().is_empty());
}
#[test]
fn client_module_error_debug() {
let err = ClientModuleError::other("debug test");
let debug = format!("{err:?}");
assert!(debug.contains("Other"));
assert!(debug.contains("debug test"));
}
#[test]
fn version_construction() {
let v = Version::new(1, 2, 3);
assert_eq!(v.major, 1);
assert_eq!(v.minor, 2);
assert_eq!(v.patch, 3);
}
#[test]
fn version_equality() {
let a = Version::new(1, 0, 0);
let b = Version::new(1, 0, 0);
assert_eq!(a, b);
}
#[test]
fn version_inequality() {
let a = Version::new(1, 0, 0);
let b = Version::new(2, 0, 0);
assert_ne!(a, b);
}
#[test]
fn version_display() {
let v = Version::new(0, 12, 3);
assert_eq!(v.to_string(), "0.12.3");
}
#[test]
fn version_copy_semantics() {
let a = Version::new(1, 0, 0);
let b = a;
assert_eq!(a, b);
}
#[test]
fn buffer_id_construction() {
let id = BufferId(42);
assert_eq!(id.0, 42);
}
#[test]
fn buffer_id_equality() {
assert_eq!(BufferId(1), BufferId(1));
assert_ne!(BufferId(1), BufferId(2));
}
#[test]
fn buffer_id_copy() {
let a = BufferId(5);
let b = a;
assert_eq!(a, b);
}
#[test]
fn buffer_id_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(BufferId(1));
set.insert(BufferId(2));
set.insert(BufferId(1));
assert_eq!(set.len(), 2);
}
#[test]
fn color_depth_variants() {
let depths = [
ColorDepth::Monochrome,
ColorDepth::Ansi16,
ColorDepth::Ansi256,
ColorDepth::TrueColor,
];
for (i, d) in depths.iter().enumerate() {
for (j, other) in depths.iter().enumerate() {
if i == j {
assert_eq!(d, other);
} else {
assert_ne!(d, other);
}
}
}
}
#[test]
fn rendering_model_variants() {
let models = [
RenderingModel::CellGrid,
RenderingModel::Canvas,
RenderingModel::NativeLayout,
];
for (i, m) in models.iter().enumerate() {
for (j, other) in models.iter().enumerate() {
if i == j {
assert_eq!(m, other);
} else {
assert_ne!(m, other);
}
}
}
}
#[test]
fn rect_construction() {
let r = Rect::new(1, 2, 80, 24);
assert_eq!(r.x, 1);
assert_eq!(r.y, 2);
assert_eq!(r.width, 80);
assert_eq!(r.height, 24);
}
#[test]
fn rect_default() {
let r = Rect::default();
assert_eq!(r, Rect::new(0, 0, 0, 0));
}
#[test]
fn rect_equality() {
assert_eq!(Rect::new(0, 0, 10, 20), Rect::new(0, 0, 10, 20));
assert_ne!(Rect::new(0, 0, 10, 20), Rect::new(0, 0, 10, 21));
}
#[test]
fn rect_copy() {
let a = Rect::new(1, 2, 3, 4);
let b = a;
assert_eq!(a, b);
}
#[test]
fn insets_construction() {
let i = Insets::new(1, 2, 3, 4);
assert_eq!(i.top, 1);
assert_eq!(i.bottom, 2);
assert_eq!(i.left, 3);
assert_eq!(i.right, 4);
}
#[test]
fn insets_default() {
let i = Insets::default();
assert_eq!(i, Insets::new(0, 0, 0, 0));
}
#[test]
fn insets_copy() {
let a = Insets::new(1, 2, 3, 4);
let b = a;
assert_eq!(a, b);
}
#[test]
fn option_value_bool() {
let v = OptionValue::Bool(true);
assert_eq!(v, OptionValue::Bool(true));
assert_ne!(v, OptionValue::Bool(false));
}
#[test]
fn option_value_integer() {
let v = OptionValue::Integer(42);
assert_eq!(v, OptionValue::Integer(42));
}
#[test]
fn option_value_string() {
let v = OptionValue::String("hello".to_string());
assert_eq!(v, OptionValue::String("hello".to_string()));
}
#[test]
fn option_value_cross_variant_inequality() {
assert_ne!(OptionValue::Bool(true), OptionValue::Integer(1));
}
#[test]
fn option_kind_variants() {
let kinds = [OptionKind::Bool, OptionKind::Integer, OptionKind::String];
for (i, k) in kinds.iter().enumerate() {
for (j, other) in kinds.iter().enumerate() {
if i == j {
assert_eq!(k, other);
} else {
assert_ne!(k, other);
}
}
}
}
#[test]
fn option_kind_copy() {
let a = OptionKind::Bool;
let b = a;
assert_eq!(a, b);
}
#[test]
fn option_metadata_construction() {
let meta = OptionMetadata {
name: "number".to_string(),
description: "Show line numbers".to_string(),
default_value: Some(OptionValue::Bool(false)),
kind: OptionKind::Bool,
};
assert_eq!(meta.name, "number");
assert_eq!(meta.kind, OptionKind::Bool);
assert_eq!(meta.default_value, Some(OptionValue::Bool(false)));
}
#[test]
fn option_metadata_no_default() {
let meta = OptionMetadata {
name: "custom".to_string(),
description: "A custom option".to_string(),
default_value: None,
kind: OptionKind::String,
};
assert!(meta.default_value.is_none());
}
#[test]
fn option_metadata_clone_eq() {
let a = OptionMetadata {
name: "tabstop".to_string(),
description: "Tab width".to_string(),
default_value: Some(OptionValue::Integer(8)),
kind: OptionKind::Integer,
};
let b = a.clone();
assert_eq!(a, b);
}
#[test]
fn render_behavior_highlight() {
assert_eq!(RenderBehavior::Highlight, RenderBehavior::Highlight);
}
#[test]
fn render_behavior_conceal() {
let rb = RenderBehavior::Conceal {
replacement: Cow::Borrowed("..."),
};
assert!(matches!(rb, RenderBehavior::Conceal { replacement } if replacement == "..."));
}
#[test]
fn render_behavior_background() {
let rb = RenderBehavior::Background(Color::Red);
assert!(matches!(rb, RenderBehavior::Background(Color::Red)));
}
#[test]
fn render_behavior_hide() {
assert_eq!(RenderBehavior::Hide, RenderBehavior::Hide);
}
#[test]
fn render_behavior_full_width_line() {
let rb = RenderBehavior::FullWidthLine {
ch: '-',
style: Style::default(),
};
assert!(matches!(rb, RenderBehavior::FullWidthLine { ch: '-', .. }));
}
#[test]
fn transformed_line_construction() {
let tl = TransformedLine {
segments: vec![("hello".to_string(), None)],
};
assert_eq!(tl.segments.len(), 1);
assert_eq!(tl.segments[0].0, "hello");
assert!(tl.segments[0].1.is_none());
}
#[test]
fn transformed_line_empty_segments() {
let tl = TransformedLine {
segments: Vec::new(),
};
assert!(tl.segments.is_empty());
}
#[test]
fn transformed_line_multi_segment() {
let style = Style {
fg: Some(Color::Red),
bg: None,
attributes: Attributes::BOLD,
};
let tl = TransformedLine {
segments: vec![
("hello ".to_string(), Some(style.clone())),
("world".to_string(), None),
],
};
assert_eq!(tl.segments.len(), 2);
assert_eq!(tl.segments[0].1, Some(style));
}
#[test]
fn virtual_line_construction() {
let vl = VirtualLine {
buffer_line: 10,
position: VirtualLinePosition::Before,
content: "diagnostic".to_string(),
style: Style::default(),
};
assert_eq!(vl.buffer_line, 10);
assert_eq!(vl.position, VirtualLinePosition::Before);
assert_eq!(vl.content, "diagnostic");
}
#[test]
fn virtual_line_position_variants() {
assert_ne!(VirtualLinePosition::Before, VirtualLinePosition::After);
}
#[test]
fn inline_decoration_construction() {
let dec = InlineDecoration {
col_start: 5,
col_end: 10,
style: Style::default(),
};
assert_eq!(dec.col_start, 5);
assert_eq!(dec.col_end, 10);
}
#[test]
fn chrome_position_variants() {
let positions = [
ChromePosition::Top,
ChromePosition::Bottom,
ChromePosition::Left,
ChromePosition::Right,
ChromePosition::Overlay,
];
for (i, p) in positions.iter().enumerate() {
for (j, other) in positions.iter().enumerate() {
if i == j {
assert_eq!(p, other);
} else {
assert_ne!(p, other);
}
}
}
}
#[test]
fn chrome_position_copy() {
let a = ChromePosition::Left;
let b = a;
assert_eq!(a, b);
}
#[test]
fn annotation_context_construction() {
let ctx = AnnotationContext {
buffer_id: BufferId(1),
total_lines: 100,
visible_range: (0, 40),
cursor_line: 15,
gutter_style: Style::default(),
};
assert_eq!(ctx.buffer_id, BufferId(1));
assert_eq!(ctx.total_lines, 100);
assert_eq!(ctx.visible_range, (0, 40));
assert_eq!(ctx.cursor_line, 15);
}
#[test]
fn column_width_fixed() {
let w = ColumnWidth::Fixed(4);
assert_eq!(w, ColumnWidth::Fixed(4));
assert_ne!(w, ColumnWidth::Fixed(5));
}
#[test]
fn column_width_dynamic() {
let w = ColumnWidth::Dynamic(2);
assert_eq!(w, ColumnWidth::Dynamic(2));
}
#[test]
fn column_width_cross_variant() {
assert_ne!(ColumnWidth::Fixed(4), ColumnWidth::Dynamic(4));
}
#[test]
fn gutter_cell_construction() {
let cell = GutterCell {
text: "42".to_string(),
style: Style::default(),
};
assert_eq!(cell.text, "42");
}
#[test]
fn buffer_update_event_construction() {
let event = BufferUpdateEvent {
buffer_id: BufferId(1),
revision: 5,
changed_range: 0..10,
new_lines: vec!["line1".to_string()],
total_lines: 100,
};
assert_eq!(event.buffer_id, BufferId(1));
assert_eq!(event.revision, 5);
assert_eq!(event.changed_range, 0..10);
assert_eq!(event.new_lines.len(), 1);
assert_eq!(event.total_lines, 100);
}
#[test]
fn window_id_construction() {
let id = WindowId(3);
assert_eq!(id.0, 3);
}
#[test]
fn window_id_equality() {
assert_eq!(WindowId(1), WindowId(1));
assert_ne!(WindowId(1), WindowId(2));
}
#[test]
fn window_id_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(WindowId(1));
set.insert(WindowId(2));
set.insert(WindowId(1));
assert_eq!(set.len(), 2);
}
#[test]
fn window_layout_construction() {
let layout = WindowLayout {
window_id: WindowId(1),
bounds: Rect::new(0, 0, 80, 24),
};
assert_eq!(layout.window_id, WindowId(1));
assert_eq!(layout.bounds, Rect::new(0, 0, 80, 24));
}
#[test]
fn attributes_new_is_empty() {
let a = Attributes::new();
assert!(a.is_empty());
assert_eq!(a.bits(), 0);
}
#[test]
fn attributes_default_is_empty() {
assert_eq!(Attributes::default(), Attributes::new());
}
#[test]
fn attributes_set_and_contains() {
let mut a = Attributes::new();
assert!(!a.contains(Attributes::BOLD));
a.set(Attributes::BOLD);
assert!(a.contains(Attributes::BOLD));
assert!(!a.contains(Attributes::ITALIC));
}
#[test]
fn attributes_unset() {
let mut a = Attributes::new();
a.set(Attributes::BOLD);
a.set(Attributes::ITALIC);
assert!(a.contains(Attributes::BOLD));
a.unset(Attributes::BOLD);
assert!(!a.contains(Attributes::BOLD));
assert!(a.contains(Attributes::ITALIC));
}
#[test]
fn attributes_all_flags() {
let flags = [
Attributes::BOLD,
Attributes::ITALIC,
Attributes::UNDERLINE,
Attributes::STRIKETHROUGH,
Attributes::REVERSE,
Attributes::DIM,
];
for flag in &flags {
let mut a = Attributes::new();
a.set(*flag);
assert!(a.contains(*flag));
assert!(!a.is_empty());
}
}
#[test]
fn attributes_bitor() {
let combined = Attributes::BOLD | Attributes::ITALIC;
assert!(combined.contains(Attributes::BOLD));
assert!(combined.contains(Attributes::ITALIC));
assert!(!combined.contains(Attributes::UNDERLINE));
}
#[test]
fn attributes_bitand() {
let a = Attributes::BOLD | Attributes::ITALIC;
let b = Attributes::BOLD | Attributes::UNDERLINE;
let intersection = a & b;
assert!(intersection.contains(Attributes::BOLD));
assert!(!intersection.contains(Attributes::ITALIC));
assert!(!intersection.contains(Attributes::UNDERLINE));
}
#[test]
fn attributes_contains_empty_in_nonempty() {
let a = Attributes::BOLD;
assert!(a.contains(Attributes::new()));
}
#[test]
fn attributes_multiple_flags_combined() {
let mut a = Attributes::new();
a.set(Attributes::BOLD);
a.set(Attributes::ITALIC);
a.set(Attributes::UNDERLINE);
assert!(a.contains(Attributes::BOLD | Attributes::ITALIC));
}
#[test]
fn style_new() {
let s = Style::new();
assert!(s.fg.is_none());
assert!(s.bg.is_none());
assert!(s.attributes.is_empty());
}
#[test]
fn style_default() {
assert_eq!(Style::default(), Style::new());
}
#[test]
fn style_with_colors() {
let s = Style {
fg: Some(Color::Red),
bg: Some(Color::Blue),
attributes: Attributes::BOLD,
};
assert_eq!(s.fg, Some(Color::Red));
assert_eq!(s.bg, Some(Color::Blue));
assert!(s.attributes.contains(Attributes::BOLD));
}
#[test]
fn style_equality() {
let a = Style {
fg: Some(Color::Red),
bg: None,
attributes: Attributes::BOLD,
};
let b = Style {
fg: Some(Color::Red),
bg: None,
attributes: Attributes::BOLD,
};
assert_eq!(a, b);
}
#[test]
fn style_inequality_fg() {
let a = Style {
fg: Some(Color::Red),
..Style::default()
};
let b = Style {
fg: Some(Color::Blue),
..Style::default()
};
assert_ne!(a, b);
}
#[test]
fn style_inequality_attributes() {
let a = Style {
attributes: Attributes::BOLD,
..Style::default()
};
let b = Style {
attributes: Attributes::ITALIC,
..Style::default()
};
assert_ne!(a, b);
}
#[test]
fn input_event_key_variant() {
let event = InputEvent::Key(KeyEvent::new(KeyCode::Char('a'), Modifiers::NONE));
assert!(matches!(event, InputEvent::Key(_)));
assert!(!format!("{event:?}").is_empty());
}
#[test]
fn input_event_pointer_variant() {
let event = InputEvent::Pointer(PointerEvent {
kind: PointerKind::Down(PointerButton::Left),
x: 10,
y: 20,
modifiers: Modifiers::NONE,
});
assert!(matches!(event, InputEvent::Pointer(_)));
}
#[test]
fn input_event_touch_variant() {
let event = InputEvent::Touch(TouchEvent {
kind: TouchKind::Start,
id: 1,
x: 100.0,
y: 200.0,
});
assert!(matches!(event, InputEvent::Touch(_)));
}
#[test]
fn input_event_focus_variant() {
let gained = InputEvent::Focus(FocusEvent::Gained);
let lost = InputEvent::Focus(FocusEvent::Lost);
assert!(matches!(gained, InputEvent::Focus(FocusEvent::Gained)));
assert!(matches!(lost, InputEvent::Focus(FocusEvent::Lost)));
}
#[test]
fn input_event_paste_variant() {
let event = InputEvent::Paste("hello".to_string());
assert!(matches!(event, InputEvent::Paste(_)));
let cloned = event.clone();
assert_eq!(event, cloned);
}
#[test]
fn key_event_construction() {
let event = KeyEvent::new(KeyCode::Enter, Modifiers::CTRL);
assert_eq!(event.code, KeyCode::Enter);
assert!(event.modifiers.contains(Modifiers::CTRL));
}
#[test]
fn key_event_clone_eq() {
let a = KeyEvent::new(KeyCode::Esc, Modifiers::ALT);
let b = a.clone();
assert_eq!(a, b);
}
#[test]
fn key_code_char_variants() {
assert_eq!(KeyCode::Char('a'), KeyCode::Char('a'));
assert_ne!(KeyCode::Char('a'), KeyCode::Char('b'));
}
#[test]
fn key_code_special_keys() {
let keys = [
KeyCode::Enter,
KeyCode::Esc,
KeyCode::Tab,
KeyCode::Backspace,
KeyCode::Left,
KeyCode::Right,
KeyCode::Up,
KeyCode::Down,
KeyCode::Home,
KeyCode::End,
KeyCode::PageUp,
KeyCode::PageDown,
KeyCode::Insert,
KeyCode::Delete,
KeyCode::Null,
];
for key in keys {
assert!(!format!("{key:?}").is_empty());
}
}
#[test]
fn key_code_function_keys() {
assert_eq!(KeyCode::F(1), KeyCode::F(1));
assert_ne!(KeyCode::F(1), KeyCode::F(12));
let f0 = KeyCode::F(0);
assert!(!format!("{f0:?}").is_empty());
}
#[test]
fn modifiers_empty() {
let m = Modifiers::new();
assert!(m.is_empty());
assert_eq!(m.bits(), 0);
assert_eq!(m, Modifiers::NONE);
}
#[test]
fn modifiers_set_unset() {
let mut m = Modifiers::new();
m.set(Modifiers::SHIFT);
assert!(m.contains(Modifiers::SHIFT));
assert!(!m.contains(Modifiers::CTRL));
m.set(Modifiers::CTRL);
assert!(m.contains(Modifiers::SHIFT));
assert!(m.contains(Modifiers::CTRL));
m.unset(Modifiers::SHIFT);
assert!(!m.contains(Modifiers::SHIFT));
assert!(m.contains(Modifiers::CTRL));
}
#[test]
fn modifiers_bitor() {
let m = Modifiers::SHIFT | Modifiers::CTRL;
assert!(m.contains(Modifiers::SHIFT));
assert!(m.contains(Modifiers::CTRL));
assert!(!m.contains(Modifiers::ALT));
}
#[test]
fn modifiers_bitand() {
let m = Modifiers::SHIFT | Modifiers::CTRL | Modifiers::ALT;
let masked = m & Modifiers::CTRL;
assert!(masked.contains(Modifiers::CTRL));
assert!(!masked.contains(Modifiers::SHIFT));
}
#[test]
fn modifiers_all_flags() {
let all = Modifiers::SHIFT | Modifiers::CTRL | Modifiers::ALT | Modifiers::SUPER;
assert!(all.contains(Modifiers::SHIFT));
assert!(all.contains(Modifiers::CTRL));
assert!(all.contains(Modifiers::ALT));
assert!(all.contains(Modifiers::SUPER));
assert!(!all.is_empty());
}
#[test]
fn modifiers_default() {
let m = Modifiers::default();
assert!(m.is_empty());
assert_eq!(m, Modifiers::NONE);
}
#[test]
fn pointer_event_construction() {
let event = PointerEvent {
kind: PointerKind::Down(PointerButton::Left),
x: 5,
y: 10,
modifiers: Modifiers::SHIFT,
};
assert_eq!(event.x, 5);
assert_eq!(event.y, 10);
assert!(event.modifiers.contains(Modifiers::SHIFT));
}
#[test]
fn pointer_kind_all_variants() {
let variants = [
PointerKind::Down(PointerButton::Left),
PointerKind::Down(PointerButton::Right),
PointerKind::Down(PointerButton::Middle),
PointerKind::Up(PointerButton::Left),
PointerKind::Drag(PointerButton::Left),
PointerKind::Move,
PointerKind::ScrollUp,
PointerKind::ScrollDown,
];
for v in variants {
assert!(!format!("{v:?}").is_empty());
}
}
#[test]
fn pointer_event_at_origin() {
let event = PointerEvent {
kind: PointerKind::Move,
x: 0,
y: 0,
modifiers: Modifiers::NONE,
};
assert_eq!(event.x, 0);
assert_eq!(event.y, 0);
}
#[test]
fn pointer_event_at_max() {
let event = PointerEvent {
kind: PointerKind::ScrollUp,
x: u16::MAX,
y: u16::MAX,
modifiers: Modifiers::NONE,
};
assert_eq!(event.x, u16::MAX);
assert_eq!(event.y, u16::MAX);
}
#[test]
fn touch_event_construction() {
let event = TouchEvent {
kind: TouchKind::Start,
id: 42,
x: 1.5,
y: 2.5,
};
assert_eq!(event.id, 42);
assert!((event.x - 1.5).abs() < f32::EPSILON);
assert!((event.y - 2.5).abs() < f32::EPSILON);
}
#[test]
fn touch_kind_all_variants() {
let variants = [
TouchKind::Start,
TouchKind::Move,
TouchKind::End,
TouchKind::Cancel,
];
for v in variants {
assert!(!format!("{v:?}").is_empty());
}
}
#[test]
fn touch_event_clone() {
let a = TouchEvent {
kind: TouchKind::End,
id: 1,
x: 0.0,
y: 0.0,
};
let b = a;
assert_eq!(a, b);
}
#[test]
fn focus_event_variants() {
assert_ne!(FocusEvent::Gained, FocusEvent::Lost);
let gained = FocusEvent::Gained;
let cloned = gained;
assert_eq!(gained, cloned);
}
#[test]
fn focus_event_debug() {
assert!(format!("{:?}", FocusEvent::Gained).contains("Gained"));
assert!(format!("{:?}", FocusEvent::Lost).contains("Lost"));
}
#[test]
fn line_number_mode_default() {
let mode = LineNumberMode::default();
assert_eq!(mode, LineNumberMode::None);
}
#[test]
fn line_number_mode_variants() {
let modes = [
LineNumberMode::None,
LineNumberMode::Absolute,
LineNumberMode::Relative,
LineNumberMode::Hybrid,
];
for (i, m) in modes.iter().enumerate() {
for (j, other) in modes.iter().enumerate() {
if i == j {
assert_eq!(m, other);
} else {
assert_ne!(m, other);
}
}
}
}
#[test]
fn line_number_mode_copy() {
let a = LineNumberMode::Relative;
let b = a;
assert_eq!(a, b);
}
#[test]
fn cursor_info_default() {
let c = CursorInfo::default();
assert_eq!(c.line, 0);
assert_eq!(c.column, 0);
}
#[test]
fn cursor_info_construction() {
let c = CursorInfo {
line: 42,
column: 10,
};
assert_eq!(c.line, 42);
assert_eq!(c.column, 10);
}
#[test]
fn cursor_info_equality() {
let a = CursorInfo { line: 1, column: 2 };
let b = CursorInfo { line: 1, column: 2 };
assert_eq!(a, b);
let c = CursorInfo { line: 1, column: 3 };
assert_ne!(a, c);
}
#[test]
fn cursor_info_copy() {
let a = CursorInfo {
line: 5,
column: 10,
};
let b = a;
assert_eq!(a, b);
}
#[test]
fn selection_mode_variants() {
let modes = [
SelectionMode::Char,
SelectionMode::Line,
SelectionMode::Block,
];
for (i, m) in modes.iter().enumerate() {
for (j, other) in modes.iter().enumerate() {
if i == j {
assert_eq!(m, other);
} else {
assert_ne!(m, other);
}
}
}
}
#[test]
fn selection_mode_copy() {
let a = SelectionMode::Block;
let b = a;
assert_eq!(a, b);
}
#[test]
fn selection_info_construction() {
let sel = SelectionInfo {
start_line: 1,
start_col: 0,
end_line: 3,
end_col: 5,
mode: SelectionMode::Char,
color: Color::Blue,
};
assert_eq!(sel.start_line, 1);
assert_eq!(sel.start_col, 0);
assert_eq!(sel.end_line, 3);
assert_eq!(sel.end_col, 5);
assert_eq!(sel.mode, SelectionMode::Char);
}
#[test]
fn selection_info_clone() {
let sel = SelectionInfo {
start_line: 0,
start_col: 0,
end_line: 0,
end_col: 10,
mode: SelectionMode::Line,
color: Color::Red,
};
assert_eq!(Clone::clone(&sel).start_line, 0);
assert_eq!(Clone::clone(&sel).end_col, 10);
assert_eq!(Clone::clone(&sel).mode, SelectionMode::Line);
}
#[test]
fn selection_info_block_mode() {
let sel = SelectionInfo {
start_line: 5,
start_col: 3,
end_line: 10,
end_col: 8,
mode: SelectionMode::Block,
color: Color::Rgb {
r: 50,
g: 50,
b: 100,
},
};
assert_eq!(sel.mode, SelectionMode::Block);
}
#[test]
fn remote_client_info_construction() {
let remote = RemoteClientInfo {
client_id: 42,
display_name: "Alice".to_string(),
cursor_line: 10,
cursor_col: 5,
mode: "normal".to_string(),
cursor_color: Color::Blue,
selection: None,
};
assert_eq!(remote.client_id, 42);
assert_eq!(remote.display_name, "Alice");
assert_eq!(remote.cursor_line, 10);
assert_eq!(remote.cursor_col, 5);
assert_eq!(remote.mode, "normal");
assert!(remote.selection.is_none());
}
#[test]
fn remote_client_info_with_selection() {
let remote = RemoteClientInfo {
client_id: 1,
display_name: "Bob".to_string(),
cursor_line: 5,
cursor_col: 0,
mode: "visual".to_string(),
cursor_color: Color::Green,
selection: Some(SelectionInfo {
start_line: 5,
start_col: 0,
end_line: 8,
end_col: 10,
mode: SelectionMode::Char,
color: Color::DarkBlue,
}),
};
assert!(remote.selection.is_some());
let sel = remote.selection.as_ref().unwrap();
assert_eq!(sel.end_line, 8);
}
#[test]
fn remote_client_info_clone() {
let remote = RemoteClientInfo {
client_id: 99,
display_name: "Clone".to_string(),
cursor_line: 0,
cursor_col: 0,
mode: "insert".to_string(),
cursor_color: Color::Red,
selection: None,
};
assert_eq!(Clone::clone(&remote).client_id, 99);
assert_eq!(Clone::clone(&remote).display_name, "Clone");
}
#[test]
fn viewport_context_minimal() {
let ctx = ViewportContext {
buffer_id: None,
buffer_lines: None,
cursor: None,
scroll_top: 0,
local_selection: None,
remote_clients: &[],
fold_ranges: &[],
virtual_lines: &[],
opacity: 1.0,
line_number_mode: LineNumberMode::None,
gutter_width: 0,
sidebar_width: 0,
is_insert_mode: false,
render_self_cursor: false,
my_client_id: 0,
};
assert!(ctx.buffer_id.is_none());
assert!(ctx.buffer_lines.is_none());
assert!(ctx.cursor.is_none());
assert!(ctx.local_selection.is_none());
assert!(ctx.remote_clients.is_empty());
assert!(ctx.fold_ranges.is_empty());
assert!(ctx.virtual_lines.is_empty());
assert!((ctx.opacity - 1.0).abs() < f32::EPSILON);
}
#[test]
fn viewport_context_with_content() {
let lines = vec!["hello".to_string(), "world".to_string()];
let remote = RemoteClientInfo {
client_id: 1,
display_name: "Peer".to_string(),
cursor_line: 0,
cursor_col: 3,
mode: "normal".to_string(),
cursor_color: Color::Blue,
selection: None,
};
let remotes = vec![remote];
let folds = vec![(5, 3)];
let ctx = ViewportContext {
buffer_id: Some(BufferId(1)),
buffer_lines: Some(&lines),
cursor: Some(CursorInfo { line: 0, column: 2 }),
scroll_top: 0,
local_selection: None,
remote_clients: &remotes,
fold_ranges: &folds,
virtual_lines: &[],
opacity: 0.8,
line_number_mode: LineNumberMode::Absolute,
gutter_width: 4,
sidebar_width: 0,
is_insert_mode: false,
render_self_cursor: true,
my_client_id: 42,
};
assert_eq!(ctx.buffer_id, Some(BufferId(1)));
assert_eq!(ctx.buffer_lines.unwrap().len(), 2);
assert_eq!(ctx.cursor.unwrap().column, 2);
assert_eq!(ctx.remote_clients.len(), 1);
assert_eq!(ctx.fold_ranges.len(), 1);
assert_eq!(ctx.gutter_width, 4);
assert!(ctx.render_self_cursor);
assert_eq!(ctx.my_client_id, 42);
}
#[test]
fn viewport_context_debug() {
let ctx = ViewportContext {
buffer_id: None,
buffer_lines: None,
cursor: None,
scroll_top: 0,
local_selection: None,
remote_clients: &[],
fold_ranges: &[],
virtual_lines: &[],
opacity: 1.0,
line_number_mode: LineNumberMode::None,
gutter_width: 0,
sidebar_width: 0,
is_insert_mode: false,
render_self_cursor: false,
my_client_id: 0,
};
let debug = format!("{ctx:?}");
assert!(debug.contains("ViewportContext"));
}
#[test]
fn pointer_button_all_variants() {
let buttons = [
PointerButton::Left,
PointerButton::Right,
PointerButton::Middle,
];
for b in buttons {
assert!(!format!("{b:?}").is_empty());
}
assert_ne!(PointerButton::Left, PointerButton::Right);
}
#[test]
fn client_module_probe_roundtrip() {
let probe = ClientModuleProbe::new(
"my-module",
"My Module",
Version::new(1, 2, 3),
CLIENT_MODULE_API_VERSION,
);
assert_eq!(probe.id_str(), "my-module");
assert_eq!(probe.name_str(), "My Module");
assert_eq!(probe.version, Version::new(1, 2, 3));
assert_eq!(probe.api_version, CLIENT_MODULE_API_VERSION);
}
#[test]
fn client_module_probe_deps() {
let probe = ClientModuleProbe::new(
"child",
"Child Module",
Version::new(1, 0, 0),
CLIENT_MODULE_API_VERSION,
)
.with_required_dep(0, "parent-a")
.with_required_dep(1, "parent-b")
.with_optional_dep(0, "optional-x");
let required = probe.required_deps();
assert_eq!(required.len(), 2);
assert_eq!(required[0], "parent-a");
assert_eq!(required[1], "parent-b");
let optional = probe.optional_deps();
assert_eq!(optional.len(), 1);
assert_eq!(optional[0], "optional-x");
}
#[test]
fn client_module_probe_empty_deps() {
let probe = ClientModuleProbe::new(
"standalone",
"Standalone",
Version::new(0, 1, 0),
CLIENT_MODULE_API_VERSION,
);
assert!(probe.required_deps().is_empty());
assert!(probe.optional_deps().is_empty());
}
#[test]
fn client_module_probe_copy_semantics() {
let a = ClientModuleProbe::new(
"copy-test",
"Copy Test",
Version::new(1, 0, 0),
CLIENT_MODULE_API_VERSION,
);
let b = a;
assert_eq!(a.id_str(), b.id_str());
assert_eq!(a.name_str(), b.name_str());
}
#[test]
fn client_module_probe_default_capabilities_all_false() {
let probe = ClientModuleProbe::new(
"cap-test",
"Cap Test",
Version::new(1, 0, 0),
CLIENT_MODULE_API_VERSION,
);
assert!(!probe.has_chrome());
assert!(!probe.has_buffer_contrib());
assert!(!probe.has_annotations());
assert_eq!(probe.capabilities, 0);
}
#[test]
fn client_module_probe_capabilities_all_set() {
let probe = ClientModuleProbe::new(
"cap-test",
"Cap Test",
Version::new(1, 0, 0),
CLIENT_MODULE_API_VERSION,
)
.with_capabilities(true, true, true);
assert!(probe.has_chrome());
assert!(probe.has_buffer_contrib());
assert!(probe.has_annotations());
assert_eq!(probe.capabilities, 0b111);
}
#[test]
fn client_module_probe_capabilities_individual() {
let chrome_only =
ClientModuleProbe::new("c", "C", Version::new(1, 0, 0), CLIENT_MODULE_API_VERSION)
.with_capabilities(true, false, false);
assert!(chrome_only.has_chrome());
assert!(!chrome_only.has_buffer_contrib());
assert!(!chrome_only.has_annotations());
let buffer_only =
ClientModuleProbe::new("b", "B", Version::new(1, 0, 0), CLIENT_MODULE_API_VERSION)
.with_capabilities(false, true, false);
assert!(!buffer_only.has_chrome());
assert!(buffer_only.has_buffer_contrib());
assert!(!buffer_only.has_annotations());
let annotation_only =
ClientModuleProbe::new("a", "A", Version::new(1, 0, 0), CLIENT_MODULE_API_VERSION)
.with_capabilities(false, false, true);
assert!(!annotation_only.has_chrome());
assert!(!annotation_only.has_buffer_contrib());
assert!(annotation_only.has_annotations());
}
#[test]
fn client_module_probe_capabilities_roundtrip_via_copy() {
let a =
ClientModuleProbe::new("rt", "Roundtrip", Version::new(1, 0, 0), CLIENT_MODULE_API_VERSION)
.with_capabilities(true, false, true);
let b = a;
assert!(b.has_chrome());
assert!(!b.has_buffer_contrib());
assert!(b.has_annotations());
}
#[test]
fn client_api_version_exists() {
let v = CLIENT_MODULE_API_VERSION;
assert_eq!(v.major, 0);
assert_eq!(v.minor, 3);
assert_eq!(v.patch, 0);
}
#[test]
fn is_client_compatible_same_version() {
assert!(is_client_compatible(Version::new(1, 0, 0), Version::new(1, 0, 0)));
}
#[test]
fn is_client_compatible_same_major_higher_minor() {
assert!(is_client_compatible(Version::new(1, 0, 0), Version::new(1, 2, 0)));
}
#[test]
fn is_client_compatible_same_major_lower_minor() {
assert!(!is_client_compatible(Version::new(1, 3, 0), Version::new(1, 2, 0)));
}
#[test]
fn is_client_compatible_different_major() {
assert!(!is_client_compatible(Version::new(2, 0, 0), Version::new(1, 0, 0)));
}
#[test]
fn is_client_compatible_zero_major() {
assert!(is_client_compatible(Version::new(0, 1, 0), Version::new(0, 1, 0)));
assert!(!is_client_compatible(Version::new(0, 1, 0), Version::new(0, 0, 0)));
}
#[test]
fn is_client_compatible_v01_module_against_v02_loader() {
assert!(is_client_compatible(Version::new(0, 1, 0), Version::new(0, 2, 0)));
}
#[test]
fn is_client_compatible_v02_module_against_v01_loader() {
assert!(!is_client_compatible(Version::new(0, 2, 0), Version::new(0, 1, 0)));
}
#[test]
fn rect_intersect_overlapping() {
let a = Rect::new(0, 0, 10, 10);
let b = Rect::new(5, 5, 10, 10);
let result = a.intersect(&b).unwrap();
assert_eq!(result, Rect::new(5, 5, 5, 5));
}
#[test]
fn rect_intersect_no_overlap_x() {
let a = Rect::new(0, 0, 5, 10);
let b = Rect::new(5, 0, 5, 10);
assert!(a.intersect(&b).is_none());
}
#[test]
fn rect_intersect_no_overlap_y() {
let a = Rect::new(0, 0, 10, 5);
let b = Rect::new(0, 5, 10, 5);
assert!(a.intersect(&b).is_none());
}
#[test]
fn rect_intersect_contained() {
let outer = Rect::new(0, 0, 20, 20);
let inner = Rect::new(5, 5, 5, 5);
let result = outer.intersect(&inner).unwrap();
assert_eq!(result, inner);
}
#[test]
fn rect_intersect_same() {
let r = Rect::new(3, 3, 10, 10);
let result = r.intersect(&r).unwrap();
assert_eq!(result, r);
}
#[test]
fn rect_intersect_no_overlap_both() {
let a = Rect::new(0, 0, 5, 5);
let b = Rect::new(10, 10, 5, 5);
assert!(a.intersect(&b).is_none());
}
#[test]
fn rect_contains_point_inside() {
let r = Rect::new(5, 5, 10, 10);
assert!(r.contains_point(5, 5)); assert!(r.contains_point(10, 10)); assert!(r.contains_point(14, 14)); }
#[test]
fn rect_contains_point_outside_left() {
let r = Rect::new(5, 5, 10, 10);
assert!(!r.contains_point(4, 10)); }
#[test]
fn rect_contains_point_outside_above() {
let r = Rect::new(5, 5, 10, 10);
assert!(!r.contains_point(10, 4)); }
#[test]
fn rect_contains_point_outside_right() {
let r = Rect::new(5, 5, 10, 10);
assert!(!r.contains_point(15, 10)); }
#[test]
fn rect_contains_point_outside_below() {
let r = Rect::new(5, 5, 10, 10);
assert!(!r.contains_point(10, 15)); }
#[test]
fn rect_contains_point_at_origin() {
let r = Rect::new(0, 0, 10, 10);
assert!(r.contains_point(0, 0));
assert!(!r.contains_point(10, 0));
assert!(!r.contains_point(0, 10));
}
#[test]
fn style_builder_fg() {
let s = Style::new().fg(Color::Red);
assert_eq!(s.fg, Some(Color::Red));
assert!(s.bg.is_none());
}
#[test]
fn style_builder_bg() {
let s = Style::new().bg(Color::Blue);
assert!(s.fg.is_none());
assert_eq!(s.bg, Some(Color::Blue));
}
#[test]
fn style_builder_bold() {
let s = Style::new().bold();
assert!(s.attributes.contains(Attributes::BOLD));
}
#[test]
fn style_builder_italic() {
let s = Style::new().italic();
assert!(s.attributes.contains(Attributes::ITALIC));
}
#[test]
fn style_builder_underline() {
let s = Style::new().underline();
assert!(s.attributes.contains(Attributes::UNDERLINE));
}
#[test]
fn style_builder_dim() {
let s = Style::new().dim();
assert!(s.attributes.contains(Attributes::DIM));
}
#[test]
fn style_builder_reverse() {
let s = Style::new().reverse();
assert!(s.attributes.contains(Attributes::REVERSE));
}
#[test]
fn style_builder_chained() {
let s = Style::new()
.fg(Color::Red)
.bg(Color::Blue)
.bold()
.italic()
.underline()
.dim()
.reverse();
assert_eq!(s.fg, Some(Color::Red));
assert_eq!(s.bg, Some(Color::Blue));
assert!(s.attributes.contains(Attributes::BOLD));
assert!(s.attributes.contains(Attributes::ITALIC));
assert!(s.attributes.contains(Attributes::UNDERLINE));
assert!(s.attributes.contains(Attributes::DIM));
assert!(s.attributes.contains(Attributes::REVERSE));
}
#[test]
fn client_module_probe_id_truncated_at_64() {
let long_id = "a".repeat(100);
let probe =
ClientModuleProbe::new(&long_id, "name", Version::new(1, 0, 0), CLIENT_MODULE_API_VERSION);
let id = probe.id_str();
assert_eq!(id.len(), 64);
}
#[test]
fn client_module_probe_name_truncated_at_128() {
let long_name = "b".repeat(200);
let probe =
ClientModuleProbe::new("id", &long_name, Version::new(1, 0, 0), CLIENT_MODULE_API_VERSION);
let name = probe.name_str();
assert_eq!(name.len(), 128);
}
#[test]
fn client_module_probe_with_required_dep_index_out_of_range() {
let probe =
ClientModuleProbe::new("id", "name", Version::new(1, 0, 0), CLIENT_MODULE_API_VERSION)
.with_required_dep(8, "dep");
assert!(probe.required_deps().is_empty());
}
#[test]
fn client_module_probe_with_optional_dep_index_out_of_range() {
let probe =
ClientModuleProbe::new("id", "name", Version::new(1, 0, 0), CLIENT_MODULE_API_VERSION)
.with_optional_dep(8, "dep");
assert!(probe.optional_deps().is_empty());
}
#[test]
fn client_module_probe_with_required_dep_long_name() {
let long_dep = "x".repeat(100);
let probe =
ClientModuleProbe::new("id", "name", Version::new(1, 0, 0), CLIENT_MODULE_API_VERSION)
.with_required_dep(0, &long_dep);
let deps = probe.required_deps();
assert_eq!(deps.len(), 1);
assert_eq!(deps[0].len(), 64);
}
#[test]
fn client_module_probe_with_optional_dep_long_name() {
let long_dep = "y".repeat(100);
let probe =
ClientModuleProbe::new("id", "name", Version::new(1, 0, 0), CLIENT_MODULE_API_VERSION)
.with_optional_dep(0, &long_dep);
let deps = probe.optional_deps();
assert_eq!(deps.len(), 1);
assert_eq!(deps[0].len(), 64);
}