From 61e9a4631f8e15f233b8eec28a034921b4168cc4 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 6 Jan 2025 20:39:48 -0500 Subject: [PATCH] wip: test coverage for range select --- .gitignore | 1 + src/ui/mod.rs | 26 +++++-- src/ui/test.rs | 202 ++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 210 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 1d33feb..7137221 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ result/ *.json tarpaulin-report.* +*.profraw diff --git a/src/ui/mod.rs b/src/ui/mod.rs index d36d28c..ba05d9d 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -386,7 +386,7 @@ impl<'ws> Workspace<'ws> { } KeyCode::Char('p') if key.modifiers == KeyModifiers::CONTROL => { self.text_area - .set_yank_text(dbg!(self.selected_range_to_string())); + .set_yank_text(self.selected_range_to_string()); self.text_area.paste(); self.state.dirty = true; return Ok(None); @@ -549,7 +549,9 @@ impl<'ws> Workspace<'ws> { self.maybe_update_range_end(); } KeyCode::Char(' ') | KeyCode::Enter => { - self.update_range_selection()?; + if self.update_range_selection()? { + self.exit_range_select_mode()?; + } } KeyCode::Char('n') if key.modifiers == KeyModifiers::CONTROL => { self.state.range_select.reset_range_selection(); @@ -570,16 +572,24 @@ impl<'ws> Workspace<'ws> { KeyCode::Char('C') if key .modifiers - .contains(KeyModifiers::CONTROL | KeyModifiers::SHIFT) => + .contains(KeyModifiers::CONTROL) => { // TODO(zaphar): Share the algorithm below between both copies self.copy_range(true)?; + self.exit_range_select_mode()?; + } + KeyCode::Char('Y') => { + self.copy_range(true)?; + self.exit_range_select_mode()?; } - KeyCode::Char('Y') => self.copy_range(true)?, KeyCode::Char('c') if key.modifiers == KeyModifiers::CONTROL => { self.copy_range(false)?; + self.exit_range_select_mode()?; + } + KeyCode::Char('y') => { + self.copy_range(false)?; + self.exit_range_select_mode()?; } - KeyCode::Char('y') => self.copy_range(false)?, KeyCode::Char('x') => { if let (Some(from), Some(to)) = (self.state.range_select.start.as_ref(), self.state.range_select.end.as_ref()) { self.book.extend_to(from, to)?; @@ -627,17 +637,17 @@ impl<'ws> Workspace<'ws> { })); } } - self.exit_range_select_mode()?; Ok(()) } - fn update_range_selection(&mut self) -> Result<(), anyhow::Error> { + fn update_range_selection(&mut self) -> Result { Ok(if self.state.range_select.start.is_none() { self.state.range_select.start = Some(self.book.location.clone()); self.state.range_select.end = Some(self.book.location.clone()); + false } else { self.state.range_select.end = Some(self.book.location.clone()); - self.exit_range_select_mode()?; + true }) } diff --git a/src/ui/test.rs b/src/ui/test.rs index 9e52b74..1b1a618 100644 --- a/src/ui/test.rs +++ b/src/ui/test.rs @@ -17,6 +17,10 @@ impl InputScript { self.event(construct_key_event(KeyCode::Char(c))) } + pub fn chars(self, cs: &str) -> Self { + cs.chars().fold(self, |s, c| s.char(c)) + } + pub fn ctrl(self, c: char) -> Self { self.modified_char(c, KeyModifiers::CONTROL) } @@ -849,16 +853,14 @@ fn test_cell_replace() { macro_rules! assert_command_finish { ($script : expr) => { - let mut ws = new_workspace(); + let mut ws = new_workspace(); assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); InputScript::default() .char(':') .run(&mut ws) .expect("Failed to handle ':' key"); assert_eq!(Some(&Modality::Command), ws.state.modality_stack.last()); - $script - .run(&mut ws) - .expect("Failed to handle script"); + $script.run(&mut ws).expect("Failed to handle script"); assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); }; } @@ -877,8 +879,8 @@ fn test_command_mode_enter() { fn test_edit_mode_paste() { let mut ws = new_workspace(); assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); - ws.state.range_select.start = Some(Address { row: 1, col: 1, }); - ws.state.range_select.end = Some(Address { row: 2, col: 2, }); + ws.state.range_select.start = Some(Address { row: 1, col: 1 }); + ws.state.range_select.end = Some(Address { row: 2, col: 2 }); dbg!(ws.selected_range_to_string()); InputScript::default() .char('e') @@ -893,21 +895,199 @@ fn test_edit_mode_paste() { fn test_range_select_esc() { let mut ws = new_workspace(); assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); - InputScript::default().char('v').run(&mut ws).expect("Failed to handle script"); + InputScript::default() + .char('v') + .run(&mut ws) + .expect("Failed to handle script"); assert_eq!(Some(&Modality::RangeSelect), ws.state.modality_stack.last()); - InputScript::default().esc().run(&mut ws).expect("Failed to handle script"); + InputScript::default() + .esc() + .run(&mut ws) + .expect("Failed to handle script"); assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); - InputScript::default().char('v').chars("123").run(&mut ws) + InputScript::default() + .char('v') + .chars("123") + .run(&mut ws) .expect("Failed to handle script"); assert_eq!(Some(&Modality::RangeSelect), ws.state.modality_stack.last()); assert_eq!(3, ws.state.numeric_prefix.len()); - InputScript::default().esc().run(&mut ws).expect("Failed to handle script"); + InputScript::default() + .esc() + .run(&mut ws) + .expect("Failed to handle script"); assert_eq!(Some(&Modality::RangeSelect), ws.state.modality_stack.last()); assert_eq!(0, ws.state.numeric_prefix.len()); - InputScript::default().esc().run(&mut ws).expect("Failed to handle script"); + InputScript::default() + .esc() + .run(&mut ws) + .expect("Failed to handle script"); assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); } +macro_rules! assert_range_clear { + ($script : expr) => {{ + let mut ws = new_workspace(); + assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); + let first_corner = Address { row: 1, col: 1 }; + let second_corner = Address { row: 2, col: 2 }; + ws.book + .update_cell(&first_corner, "foo") + .expect("Failed to update cell"); + ws.book + .update_cell(&second_corner, "bar") + .expect("Failed to update cell"); + assert_eq!( + "foo".to_string(), + ws.book + .get_cell_addr_contents(&first_corner) + .expect("failed to get cell contents") + ); + assert_eq!( + "bar".to_string(), + ws.book + .get_cell_addr_contents(&second_corner) + .expect("failed to get cell contents") + ); + InputScript::default() + .char('v') + .run(&mut ws) + .expect("Failed to handle script"); + assert_eq!(Some(&Modality::RangeSelect), ws.state.modality_stack.last()); + $script.run(&mut ws) + .expect("Failed to handle script"); + assert_eq!( + "".to_string(), + ws.book + .get_cell_addr_contents(&first_corner) + .expect("failed to get cell contents") + ); + assert_eq!( + "".to_string(), + ws.book + .get_cell_addr_contents(&second_corner) + .expect("failed to get cell contents") + ); + }}; +} + +#[test] +fn test_range_select_clear_upper_d() { + assert_range_clear!(InputScript::default() + .char('j') + .char('l') + .char('D')); +} + +#[test] +fn test_range_select_movement() { + let mut ws = new_workspace(); + ws.book.new_sheet(Some("s2")).expect("Unable create s2 sheet"); + ws.book.new_sheet(Some("s3")).expect("Unable create s3 sheet"); + InputScript::default().ctrl('r').run(&mut ws) + .expect("failed to run script"); + assert_eq!(Some(&Modality::RangeSelect), ws.state.modality_stack.last()); + InputScript::default() + .char('3') + .char('j') + .char('3') + .char('l') + .char('1') + .char('h') + .char('1') + .char('k') + .run(&mut ws) + .expect("failed to run script"); + assert_eq!(&Address { row: 3, col: 3 }, &ws.book.location); + assert_eq!(0, ws.book.current_sheet); + InputScript::default() + .ctrl('n') + .run(&mut ws) + .expect("Unable to run script"); + assert_eq!(1, ws.book.current_sheet); + InputScript::default() + .ctrl('p') + .run(&mut ws) + .expect("Unable to run script"); + assert_eq!(0, ws.book.current_sheet); +} + +#[test] +fn test_range_select_clear_lower_d() { + assert_range_clear!(InputScript::default() + .char('j') + .char('l') + .char('d')); +} + +macro_rules! assert_range_copy { + ($script: expr) => {{ + let mut ws = new_workspace(); + let top_left_addr = Address { row: 2, col: 2 }; + let bot_right_addr = Address { row: 4, col: 4 }; + ws.book.update_cell(&top_left_addr, "top_left").expect("Failed to update top left"); + ws.book.update_cell(&bot_right_addr, "bot_right").expect("Failed to update top left"); + assert!(ws.state.clipboard.is_none()); + InputScript::default() + .ctrl('r') + .char('j') + .char('l') + .char(' ') + .run(&mut ws) + .expect("failed to run script"); + assert_eq!(&top_left_addr, ws.state.range_select.start.as_ref().expect("Didn't find a start of range")); + InputScript::default() + .char('2') + .char('j') + .char('2') + .char('l') + .run(&mut ws) + .expect("failed to run script"); + assert_eq!(&bot_right_addr, ws.state.range_select.end.as_ref().expect("Didn't find a start of range")); + assert_eq!(&Address { row: 1, col: 1}, ws.state.range_select.original_location + .as_ref().expect("Expected an original location")); + assert_eq!(0, ws.state.range_select.original_sheet. + expect("Expected an original sheet")); + assert_eq!(Some(&Modality::RangeSelect), ws.state.modality_stack.iter().last()); + dbg!(ws.state.range_select.get_range()); + $script.run(&mut ws) + .expect("failed to run script"); + assert!(ws.state.clipboard.is_some()); + match ws.state.clipboard.unwrap() { + crate::ui::ClipboardContents::Cell(_) => assert!(false, "Not rows in Clipboard"), + crate::ui::ClipboardContents::Range(rows) => { + assert_eq!(vec![ + vec!["top_left".to_string(), "".to_string(), "".to_string()], + vec!["".to_string(), "".to_string(), "".to_string()], + vec!["".to_string(), "".to_string(), "bot_right".to_string()], + ], rows); + }, + } + assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.iter().last()); + }}; +} + +#[test] +fn test_range_select_copy_c() { + assert_range_copy!(InputScript::default().ctrl('c')); +} + +#[test] +fn test_range_select_copy_y() { + assert_range_copy!(InputScript::default().char('y')); +} + +#[test] +fn test_range_select_copy_capital_y() { + assert_range_copy!(InputScript::default().char('Y')); +} + +#[test] +fn test_range_select_copy_capital_c() { + assert_range_copy!(InputScript::default().ctrl('C')); +} + + fn new_workspace<'a>() -> Workspace<'a> { Workspace::new_empty("en", "America/New_York").expect("Failed to get empty workbook") }