From d903ea67a4f77c9483aed7bda1ef6694ee4465da Mon Sep 17 00:00:00 2001 From: Piotr Wach Date: Mon, 8 Jan 2024 23:17:53 +0000 Subject: Fixed tests --- src/interactive/app/eventloop.rs | 103 +++++++++++++--------- src/interactive/app/terminal_app.rs | 25 ++++++ src/interactive/app/tests/journeys_readonly.rs | 48 +++++----- src/interactive/app/tests/journeys_with_writes.rs | 6 +- src/interactive/app/tests/utils.rs | 34 ++++--- src/traverse.rs | 14 +-- 6 files changed, 144 insertions(+), 86 deletions(-) (limited to 'src') diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs index 1a8539d..2de51b6 100644 --- a/src/interactive/app/eventloop.rs +++ b/src/interactive/app/eventloop.rs @@ -10,7 +10,7 @@ use crossbeam::channel::Receiver; use crosstermion::crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; use crosstermion::input::Event; use dua::{ - traverse::{EntryData, ProcessEventResult, RunningTraversal, Traversal}, + traverse::{EntryData, TraversalProcessingEvent, RunningTraversal, Traversal}, WalkOptions, WalkResult, }; use std::path::PathBuf; @@ -70,7 +70,7 @@ impl AppState { walk_options: &WalkOptions, input: Vec, ) -> Result<()> { - let running_traversal = RunningTraversal::new(traversal.root_index, walk_options, input)?; + let running_traversal = RunningTraversal::start(traversal.root_index, walk_options, input)?; self.running_traversal = Some(running_traversal); Ok(()) } @@ -104,48 +104,71 @@ impl AppState { self.refresh_screen(window, traversal, display, terminal)?; loop { - if let Some(running_traversal) = &mut self.running_traversal { - crossbeam::select! { - recv(events) -> event => { - let Ok(event) = event else { - continue; - }; - let result = self.process_event( - window, - traversal, - display, - terminal, - event)?; - if let Some(processing_result) = result { - return Ok(processing_result); - } - }, - recv(&running_traversal.event_rx) -> event => { - let Ok(event) = event else { - continue; - }; - - let result = running_traversal.process_event(traversal, event); - if result != ProcessEventResult::NoOp { - if result == ProcessEventResult::Finished { - self.is_scanning = false; - self.running_traversal = None; - } - self.update_state(traversal); - self.refresh_screen(window, traversal, display, terminal)?; + if let Some(result) = self.process_event( + window, + traversal, + display, + terminal, + &events, + )? { + return Ok(result); + } + } + } + + pub fn process_event( + &mut self, + window: &mut MainWindow, + traversal: &mut Traversal, + display: &mut DisplayOptions, + terminal: &mut Terminal, + events: &Receiver, + ) -> Result> + where + B: Backend, + { + if let Some(running_traversal) = &mut self.running_traversal { + crossbeam::select! { + recv(events) -> event => { + let Ok(event) = event else { + return Ok(Some(ProcessingResult::ExitRequested(WalkResult { num_errors: 0 }))); + }; + let result = self.process_terminal_event( + window, + traversal, + display, + terminal, + event)?; + if let Some(processing_result) = result { + return Ok(Some(processing_result)); + } + }, + recv(&running_traversal.event_rx) -> event => { + let Ok(event) = event else { + return Ok(None); + }; + + let result = running_traversal.process_event(traversal, event); + if result != TraversalProcessingEvent::None { + if result == TraversalProcessingEvent::Finished { + self.is_scanning = false; + self.running_traversal = None; } + self.update_state(traversal); + self.refresh_screen(window, traversal, display, terminal)?; } } - } else { - let Ok(event) = events.recv() else { - continue; - }; - let result = self.process_event(window, traversal, display, terminal, event)?; - if let Some(processing_result) = result { - return Ok(processing_result); - } + } + } else { + let Ok(event) = events.recv() else { + return Ok(Some(ProcessingResult::ExitRequested(WalkResult { num_errors: 0 }))); + }; + let result = self.process_terminal_event(window, traversal, display, terminal, event)?; + if let Some(processing_result) = result { + return Ok(Some(processing_result)); } } + Ok(None) } fn update_state(&mut self, traversal: &Traversal) { @@ -165,7 +188,7 @@ impl AppState { self.reset_message(); // force "scanning" to appear } - fn process_event( + fn process_terminal_event( &mut self, window: &mut MainWindow, traversal: &mut Traversal, diff --git a/src/interactive/app/terminal_app.rs b/src/interactive/app/terminal_app.rs index 757ac76..82d7199 100644 --- a/src/interactive/app/terminal_app.rs +++ b/src/interactive/app/terminal_app.rs @@ -103,4 +103,29 @@ impl TerminalApp { ProcessingResult::ExitRequested(res) => Ok(res), } } + + pub fn run_until_traversed( + &mut self, + terminal: &mut Terminal, + events: Receiver, + ) -> Result + where + B: Backend + { + while self.state.running_traversal.is_some() { + match self.state.process_event( + &mut self.window, + &mut self.traversal, + &mut self.display, + terminal, + &events, + )? { + Some(ProcessingResult::ExitRequested(res)) => { + return Ok(res); + } + _ => {} + } + } + Ok(WalkResult { num_errors: 0 }) + } } diff --git a/src/interactive/app/tests/journeys_readonly.rs b/src/interactive/app/tests/journeys_readonly.rs index 72daf03..4821145 100644 --- a/src/interactive/app/tests/journeys_readonly.rs +++ b/src/interactive/app/tests/journeys_readonly.rs @@ -27,12 +27,9 @@ fn init_from_pdu_results() -> Result<()> { fn simple_user_journey_read_only() -> Result<()> { let long_root = "sample-02/dir"; let short_root = "sample-01"; - let (mut terminal, mut app, traversal_events) = + let (mut terminal, mut app) = initialized_app_and_terminal_from_fixture(&[short_root, long_root])?; - - // TODO: process traversal events before continuing? - // let (key_send, key_receive) = crossbeam::channel::bounded(0); - + // POST-INIT // after initialization, we expect that... { @@ -70,28 +67,28 @@ fn simple_user_journey_read_only() -> Result<()> { // SORTING { // when hitting the M key - app.process_events(&mut terminal, into_codes("m"), traversal_events)?; + app.process_events(&mut terminal, into_codes("m"))?; assert_eq!( app.state.sorting, SortMode::MTimeDescending, "it sets the sort mode to descending by mtime" ); // when hitting the M key again - app.process_events(&mut terminal, into_codes("m"), traversal_events)?; + app.process_events(&mut terminal, into_codes("m"))?; assert_eq!( app.state.sorting, SortMode::MTimeAscending, "it sets the sort mode to ascending by mtime" ); // when hitting the C key - app.process_events(&mut terminal, into_codes("c"), traversal_events)?; + app.process_events(&mut terminal, into_codes("c"))?; assert_eq!( app.state.sorting, SortMode::CountDescending, "it sets the sort mode to descending by count" ); // when hitting the C key again - app.process_events(&mut terminal, into_codes("c"), traversal_events)?; + app.process_events(&mut terminal, into_codes("c"))?; assert_eq!( app.state.sorting, SortMode::CountAscending, @@ -103,7 +100,7 @@ fn simple_user_journey_read_only() -> Result<()> { "it recomputes the cached items" ); // when hitting the S key - app.process_events(&mut terminal, into_codes("s"), traversal_events)?; + app.process_events(&mut terminal, into_codes("s"))?; assert_eq!( app.state.sorting, SortMode::SizeDescending, @@ -115,14 +112,14 @@ fn simple_user_journey_read_only() -> Result<()> { "it recomputes the cached items" ); // when hitting the S key again - app.process_events(&mut terminal, into_codes("s"), traversal_events)?; + app.process_events(&mut terminal, into_codes("s"))?; assert_eq!( app.state.sorting, SortMode::SizeAscending, "it sets the sort mode to ascending by size" ); // hit the S key again to get Descending - the rest depends on it - app.process_events(&mut terminal, into_codes("s"), traversal_events)?; + app.process_events(&mut terminal, into_codes("s"))?; assert_eq!(app.state.sorting, SortMode::SizeDescending,); assert_eq!( @@ -135,35 +132,35 @@ fn simple_user_journey_read_only() -> Result<()> { // Entry-Navigation { // when hitting the j key - app.process_events(&mut terminal, into_codes("j"), traversal_events)?; + app.process_events(&mut terminal, into_codes("j"))?; assert_eq!( node_by_name(&app, fixture_str(long_root)), node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()), "it moves the cursor down and selects the next item based on the current sort mode" ); // when hitting it while there is nowhere to go - app.process_events(&mut terminal, into_codes("j"), traversal_events)?; + app.process_events(&mut terminal, into_codes("j"))?; assert_eq!( node_by_name(&app, fixture_str(long_root)), node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()), "it stays at the previous position" ); // when hitting the k key - app.process_events(&mut terminal, into_codes("k"), traversal_events)?; + app.process_events(&mut terminal, into_codes("k"))?; assert_eq!( node_by_name(&app, fixture_str(short_root)), node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()), "it moves the cursor up and selects the next item based on the current sort mode" ); // when hitting the k key again - app.process_events(&mut terminal, into_codes("k"), traversal_events)?; + app.process_events(&mut terminal, into_codes("k"))?; assert_eq!( node_by_name(&app, fixture_str(short_root)), node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()), "it stays at the current cursor position as there is nowhere to go" ); // when hitting the o key with a directory selected - app.process_events(&mut terminal, into_codes("o"), traversal_events)?; + app.process_events(&mut terminal, into_codes("o"))?; { let new_root_idx = index_by_name(&app, fixture_str(short_root)); assert_eq!( @@ -178,7 +175,7 @@ fn simple_user_journey_read_only() -> Result<()> { ); // when hitting the u key while inside a sub-directory - app.process_events(&mut terminal, into_codes("u"), traversal_events)?; + app.process_events(&mut terminal, into_codes("u"))?; { assert_eq!( app.traversal.root_index, @@ -194,7 +191,7 @@ fn simple_user_journey_read_only() -> Result<()> { } // when hitting the u key while inside of the root directory // We are moving the cursor down just to have a non-default selection - app.process_events(&mut terminal, into_codes("ju"), traversal_events)?; + app.process_events(&mut terminal, into_codes("ju"))?; { assert_eq!( app.traversal.root_index, @@ -212,9 +209,9 @@ fn simple_user_journey_read_only() -> Result<()> { // Deletion { // when hitting the 'd' key (also move cursor back to start) - app.process_events(&mut terminal, into_codes("k"), traversal_events)?; + app.process_events(&mut terminal, into_codes("k"))?; let previously_selected_index = *app.state.navigation().selected.as_ref().unwrap(); - app.process_events(&mut terminal, into_codes("d"), traversal_events)?; + app.process_events(&mut terminal, into_codes("d"))?; { assert_eq!( Some(1), @@ -236,7 +233,7 @@ fn simple_user_journey_read_only() -> Result<()> { // when hitting the 'd' key again { - app.process_events(&mut terminal, into_codes("d"), traversal_events)?; + app.process_events(&mut terminal, into_codes("d"))?; assert_eq!( Some(2), @@ -253,7 +250,7 @@ fn simple_user_journey_read_only() -> Result<()> { // when hitting the 'd' key once again { - app.process_events(&mut terminal, into_codes("d"), traversal_events)?; + app.process_events(&mut terminal, into_codes("d"))?; assert_eq!( Some(1), @@ -270,7 +267,7 @@ fn simple_user_journey_read_only() -> Result<()> { } // when hitting the spacebar (after moving up to the first entry) { - app.process_events(&mut terminal, into_codes("k "), traversal_events)?; + app.process_events(&mut terminal, into_codes("k "))?; assert_eq!( None, @@ -289,7 +286,7 @@ fn simple_user_journey_read_only() -> Result<()> { // Marking { // select something - app.process_events(&mut terminal, into_codes(" j "), traversal_events)?; + app.process_events(&mut terminal, into_codes(" j "))?; assert_eq!( Some(false), app.window.mark_pane.as_ref().map(|p| p.has_focus()), @@ -306,7 +303,6 @@ fn simple_user_journey_read_only() -> Result<()> { app.process_events( &mut terminal, into_keys(Some(KeyCode::Tab)), - traversal_events, )?; { assert_eq!( diff --git a/src/interactive/app/tests/journeys_with_writes.rs b/src/interactive/app/tests/journeys_with_writes.rs index 80e65d7..09ca1be 100644 --- a/src/interactive/app/tests/journeys_with_writes.rs +++ b/src/interactive/app/tests/journeys_with_writes.rs @@ -9,6 +9,8 @@ use pretty_assertions::assert_eq; #[test] #[cfg(not(target_os = "windows"))] // it stopped working here, don't know if it's truly broken or if it's the test. Let's wait for windows users to report. fn basic_user_journey_with_deletion() -> Result<()> { + use crate::interactive::app::tests::utils::{into_keys, into_events}; + let fixture = WritableFixture::from("sample-02"); let (mut terminal, mut app) = initialized_app_and_terminal_from_paths(&[fixture.root.clone()])?; @@ -26,11 +28,11 @@ fn basic_user_journey_with_deletion() -> Result<()> { // When selecting the marker window and pressing the combination to delete entries app.process_events( &mut terminal, - vec![ + into_events(vec![ Event::Key(KeyCode::Tab.into()), Event::Key(KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL)), ] - .into_iter(), + .into_iter()), )?; assert!( app.window.mark_pane.is_none(), diff --git a/src/interactive/app/tests/utils.rs b/src/interactive/app/tests/utils.rs index 133dd9a..4f8c6fd 100644 --- a/src/interactive/app/tests/utils.rs +++ b/src/interactive/app/tests/utils.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Error, Result}; -use crossbeam::channel::Receiver; -use crosstermion::crossterm::event::KeyCode; +use crossbeam::channel::{Receiver, Sender}; +use crosstermion::{crossterm::{event::{KeyCode, KeyModifiers, KeyEvent}}, input::Event}; use dua::{ traverse::{EntryData, Tree, TreeIndex}, ByteFormat, TraversalSorting, WalkOptions, @@ -21,15 +21,25 @@ use tui_react::Terminal; use crate::interactive::{app::tests::FIXTURE_PATH, terminal_app::TerminalApp}; +pub fn into_events<'a>( + events: impl IntoIterator + 'a, +) -> Receiver { + let (key_send, key_receive) = crossbeam::channel::unbounded(); + for event in events { + _ = key_send.send(event); + } + key_receive +} + pub fn into_keys<'a>( codes: impl IntoIterator + 'a, -) -> impl Iterator + 'a { - codes +) -> Receiver { + into_events(codes .into_iter() - .map(|code| crosstermion::input::Event::Key(code.into())) + .map(|code| crosstermion::input::Event::Key(code.into()))) } -pub fn into_codes(input: &str) -> impl Iterator + '_ { +pub fn into_codes<'a>(input: &'a str) -> Receiver { into_keys(input.chars().map(KeyCode::Char)) } @@ -172,7 +182,7 @@ pub fn fixture_str(p: impl AsRef) -> String { pub fn initialized_app_and_terminal_with_closure( fixture_paths: &[impl AsRef], mut convert: impl FnMut(&Path) -> PathBuf, -) -> Result<(Terminal, TerminalApp, Receiver), Error> { +) -> Result<(Terminal, TerminalApp), Error> { let mut terminal = new_test_terminal()?; std::env::set_current_dir(Path::new(env!("CARGO_MANIFEST_DIR")))?; @@ -185,12 +195,14 @@ pub fn initialized_app_and_terminal_with_closure( ignore_dirs: Default::default(), }; + let (key_send, key_receive) = crossbeam::channel::bounded(0); let mut app = TerminalApp::initialize(&mut terminal, walk_options, ByteFormat::Metric)?; let input_paths = fixture_paths.iter().map(|c| convert(c.as_ref())).collect(); - let traversal_rx = app.traverse(input_paths)?; + app.traverse(input_paths)?; + app.run_until_traversed(&mut terminal, key_receive); - Ok((terminal, app, traversal_rx)) + Ok((terminal, app)) } pub fn new_test_terminal() -> std::io::Result> { @@ -199,7 +211,7 @@ pub fn new_test_terminal() -> std::io::Result> { pub fn initialized_app_and_terminal_from_paths( fixture_paths: &[PathBuf], -) -> Result<(Terminal, TerminalApp, Receiver), Error> { +) -> Result<(Terminal, TerminalApp), Error> { fn to_path_buf(p: &Path) -> PathBuf { p.to_path_buf() } @@ -208,7 +220,7 @@ pub fn initialized_app_and_terminal_from_paths( pub fn initialized_app_and_terminal_from_fixture( fixture_paths: &[&str], -) -> Result<(Terminal, TerminalApp, Receiver), Error> { +) -> Result<(Terminal, TerminalApp), Error> { #[allow(clippy::redundant_closure)] // doesn't actually work that way due to borrowchk - probably a bug initialized_app_and_terminal_with_closure(fixture_paths, |p| fixture(p)) diff --git a/src/traverse.rs b/src/traverse.rs index 0aba859..f221ace 100644 --- a/src/traverse.rs +++ b/src/traverse.rs @@ -143,14 +143,14 @@ pub struct RunningTraversal { } #[derive(PartialEq)] -pub enum ProcessEventResult { - NoOp, +pub enum TraversalProcessingEvent { + None, UpdateIsReady, Finished, } impl RunningTraversal { - pub fn new( + pub fn start( root_idx: TreeIndex, walk_options: &WalkOptions, input: Vec, @@ -213,7 +213,7 @@ impl RunningTraversal { &mut self, t: &mut Traversal, event: TraversalEvent, - ) -> ProcessEventResult { + ) -> TraversalProcessingEvent { match event { TraversalEvent::Entry(entry, root_path, device_id) => { t.entries_traversed += 1; @@ -335,7 +335,7 @@ impl RunningTraversal { if let Some(throttle) = &self.throttle { if throttle.can_update() { - return ProcessEventResult::UpdateIsReady; + return TraversalProcessingEvent::UpdateIsReady; } } } @@ -370,10 +370,10 @@ impl RunningTraversal { t.total_bytes = Some(root_size); t.elapsed = Some(t.start.elapsed()); - return ProcessEventResult::Finished; + return TraversalProcessingEvent::Finished; } } - ProcessEventResult::NoOp + TraversalProcessingEvent::None } } -- cgit v1.2.3