diff options
author | Tim Oram <dev@mitmaro.ca> | 2022-02-16 08:47:10 -0330 |
---|---|---|
committer | Tim Oram <dev@mitmaro.ca> | 2022-02-26 13:15:19 -0330 |
commit | 3d508664b8fae035f3e126902213c4415a1ae828 (patch) | |
tree | b8a79a18644026c7de428e9e45604060d7c8e107 | |
parent | 03bacf4672dfe9ccce9ea012e8658e572c64206f (diff) |
Move event handling too a separate thread
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | src/core/src/module/modules.rs | 45 | ||||
-rw-r--r-- | src/core/src/modules/external_editor/tests.rs | 160 | ||||
-rw-r--r-- | src/core/src/modules/show_commit/tests.rs | 4 | ||||
-rw-r--r-- | src/core/src/process/mod.rs | 43 | ||||
-rw-r--r-- | src/core/src/process/tests.rs | 630 | ||||
-rw-r--r-- | src/core/src/run.rs | 27 | ||||
-rw-r--r-- | src/core/src/tests.rs | 29 | ||||
-rw-r--r-- | src/core/src/testutil/mod.rs | 7 | ||||
-rw-r--r-- | src/core/src/testutil/module_test.rs | 15 | ||||
-rw-r--r-- | src/core/src/testutil/test_module.rs | 32 | ||||
-rw-r--r-- | src/input/Cargo.toml | 2 | ||||
-rw-r--r-- | src/input/src/event_action.rs | 9 | ||||
-rw-r--r-- | src/input/src/event_handler.rs | 138 | ||||
-rw-r--r-- | src/input/src/lib.rs | 5 | ||||
-rw-r--r-- | src/input/src/sender.rs | 90 | ||||
-rw-r--r-- | src/input/src/testutil.rs | 133 | ||||
-rw-r--r-- | src/input/src/thread.rs | 190 | ||||
-rw-r--r-- | src/view/src/testutil.rs | 2 |
19 files changed, 808 insertions, 755 deletions
@@ -233,8 +233,10 @@ version = "1.0.0" dependencies = [ "anyhow", "bitflags", + "crossbeam-channel", "crossterm", "girt-config", + "parking_lot 0.12.0", "rstest 0.12.0", "rustc_version", ] diff --git a/src/core/src/module/modules.rs b/src/core/src/module/modules.rs index 75aaf38..03d9e37 100644 --- a/src/core/src/module/modules.rs +++ b/src/core/src/module/modules.rs @@ -1,18 +1,20 @@ use std::collections::HashMap; -use input::EventHandler; +use input::{EventHandler, Sender as EventSender}; use todo_file::TodoFile; use view::{RenderContext, ViewData, ViewSender}; use super::{Module, ProcessResult, State}; pub(crate) struct Modules<'m> { + event_handler: EventHandler, modules: HashMap<State, Box<dyn Module + 'm>>, } impl<'m> Modules<'m> { - pub(crate) fn new() -> Self { + pub(crate) fn new(event_handler: EventHandler) -> Self { Self { + event_handler, modules: HashMap::new(), } } @@ -28,6 +30,13 @@ impl<'m> Modules<'m> { .unwrap_or_else(|| panic!("Invalid module for provided state: {:?}. Please report.", state)) } + #[allow(clippy::borrowed_box, clippy::panic)] + fn get_module(&self, state: State) -> &Box<dyn Module + 'm> { + self.modules + .get(&state) + .unwrap_or_else(|| panic!("Invalid module for provided state: {:?}", state)) + } + pub(crate) fn activate(&mut self, state: State, rebase_todo: &TodoFile, previous_state: State) -> ProcessResult { self.get_mut_module(state).activate(rebase_todo, previous_state) } @@ -48,16 +57,18 @@ impl<'m> Modules<'m> { pub(crate) fn handle_event( &mut self, state: State, - event_handler: &EventHandler, + event_sender: &mut EventSender, view_sender: &ViewSender, rebase_todo: &mut TodoFile, ) -> ProcessResult { - let module = self.get_mut_module(state); + let module = self.get_module(state); let input_options = module.input_options(); - let event = event_handler.read_event(input_options, |event, key_bindings| { - module.read_event(event, key_bindings) - }); - module.handle_event(event, view_sender, rebase_todo) + let event = self + .event_handler + .read_event(event_sender.read_event(), input_options, |event, key_bindings| { + module.read_event(event, key_bindings) + }); + self.get_mut_module(state).handle_event(event, view_sender, rebase_todo) } pub(crate) fn error(&mut self, state: State, error: &anyhow::Error) { @@ -117,7 +128,7 @@ mod tests { #[test] fn module_lifecycle() { module_test(&["pick aaa comment"], &[Event::Meta(MetaEvent::Exit)], |mut context| { - let mut modules = Modules::new(); + let mut modules = Modules::new(context.event_handler_context.event_handler); let trace = Rc::new(RefCell::new(Vec::new())); let test_module = TestModule::new(Rc::clone(&trace)); modules.register_module(State::List, test_module); @@ -125,7 +136,7 @@ mod tests { let _ = modules.activate(State::List, &context.rebase_todo_file, State::Insert); let _ = modules.handle_event( State::List, - &context.event_handler_context.event_handler, + &mut context.event_handler_context.sender, &context.view_sender_context.sender, &mut context.rebase_todo_file, ); @@ -140,11 +151,13 @@ mod tests { #[test] fn error() { - let mut modules = Modules::new(); - let trace = Rc::new(RefCell::new(Vec::new())); - let test_module = TestModule::new(Rc::clone(&trace)); - modules.register_module(State::Error, test_module); - modules.error(State::Error, &anyhow!("Test Error")); - assert_eq!((*trace).borrow().join(","), "Test Error"); + module_test(&["pick aaa comment"], &[Event::Meta(MetaEvent::Exit)], |context| { + let mut modules = Modules::new(context.event_handler_context.event_handler); + let trace = Rc::new(RefCell::new(Vec::new())); + let test_module = TestModule::new(Rc::clone(&trace)); + modules.register_module(State::Error, test_module); + modules.error(State::Error, &anyhow!("Test Error")); + assert_eq!((*trace).borrow().join(","), "Test Error"); + }); } } diff --git a/src/core/src/modules/external_editor/tests.rs b/src/core/src/modules/external_editor/tests.rs index 2a6ba3b..29dea36 100644 --- a/src/core/src/modules/external_editor/tests.rs +++ b/src/core/src/modules/external_editor/tests.rs @@ -107,55 +107,56 @@ fn deactivate() { #[test] fn edit_success() { - module_test(&["pick aaa comment"], &[], |mut test_context| { - test_context - .event_handler_context - .event_handler - .push_event(Event::from(MetaEvent::ExternalCommandSuccess)); - let mut module = ExternalEditor::new("editor"); - let _ = test_context.activate(&mut module, State::List); - let view_data = test_context.build_view_data(&mut module); - assert_rendered_output!(view_data, "{TITLE}", "{LEADING}", "{Normal}Editing..."); - assert_process_result!( - test_context.handle_event(&mut module), - event = Event::from(MetaEvent::ExternalCommandSuccess), - state = State::List - ); - assert_external_editor_state_eq!(module.state, ExternalEditorState::Active); - }); + module_test( + &["pick aaa comment"], + &[Event::from(MetaEvent::ExternalCommandSuccess)], + |mut test_context| { + let mut module = ExternalEditor::new("editor"); + let _ = test_context.activate(&mut module, State::List); + let view_data = test_context.build_view_data(&mut module); + assert_rendered_output!(view_data, "{TITLE}", "{LEADING}", "{Normal}Editing..."); + assert_process_result!( + test_context.handle_event(&mut module), + event = Event::from(MetaEvent::ExternalCommandSuccess), + state = State::List + ); + assert_external_editor_state_eq!(module.state, ExternalEditorState::Active); + }, + ); } #[test] fn empty_edit_error() { - module_test(&["pick aaa comment"], &[Event::from('1')], |mut test_context| { - test_context - .event_handler_context - .event_handler - .push_event(Event::from(MetaEvent::ExternalCommandSuccess)); - let mut module = ExternalEditor::new("editor"); - let _ = test_context.activate(&mut module, State::List); - test_context.rebase_todo_file.set_lines(vec![]); - test_context.rebase_todo_file.write_file().unwrap(); - assert_process_result!( - test_context.handle_event(&mut module), - event = Event::from(MetaEvent::ExternalCommandSuccess) - ); - assert_external_editor_state_eq!(module.state, ExternalEditorState::Empty); - let view_data = test_context.build_view_data(&mut module); - assert_rendered_output!( - view_data, - "{TITLE}", - "{LEADING}", - "{Normal}The rebase file is empty.", - "", - "{BODY}", - "{Normal}1) Abort rebase", - "{Normal}2) Edit rebase file", - "{Normal}3) Undo modifications and edit rebase file", - "", - "{IndicatorColor}Please choose an option." - ); - }); + module_test( + &["pick aaa comment"], + &[Event::from('1'), Event::from(MetaEvent::ExternalCommandSuccess)], + |mut test_context| { + let mut module = ExternalEditor::new("editor"); + let _ = test_context.activate(&mut module, State::List); + test_context.rebase_todo_file.set_lines(vec![]); + test_context.rebase_todo_file.write_file().unwrap(); + let _ = test_context.handle_event(&mut module); + assert_process_result!( + test_context.handle_event(&mut module), + event = Event::from(MetaEvent::ExternalCommandSuccess) + ); + assert_external_editor_state_eq!(module.state, ExternalEditorState::Empty); + let view_data = test_context.build_view_data(&mut module); + assert_rendered_output!( + view_data, + "{TITLE}", + "{LEADING}", + "{Normal}The rebase file is empty.", + "", + "{BODY}", + "{Normal}1) Abort rebase", + "{Normal}2) Edit rebase file", + "{Normal}3) Undo modifications and edit rebase file", + "", + "{IndicatorColor}Please choose an option." + ); + }, + ); } #[test] @@ -252,53 +253,50 @@ fn no_editor_set() { #[test] fn editor_non_zero_exit() { - module_test(&["pick aaa comment"], &[], |mut test_context| { - let mut module = ExternalEditor::new("editor"); - test_context - .event_handler_context - .event_handler - .push_event(Event::from(MetaEvent::ExternalCommandError)); - let _ = test_context.activate(&mut module, State::List); - assert_process_result!( - test_context.handle_event(&mut module), - event = Event::from(MetaEvent::ExternalCommandError) - ); - assert_external_editor_state_eq!( - module.state, - ExternalEditorState::Error(anyhow!("Editor returned a non-zero exit status")) - ); - let view_data = test_context.build_view_data(&mut module); - assert_rendered_output!( - view_data, - "{TITLE}", - "{LEADING}", - "{Normal}Editor returned a non-zero exit status", - "", - "{BODY}", - "{Normal}1) Abort rebase", - "{Normal}2) Edit rebase file", - "{Normal}3) Restore rebase file and abort edit", - "{Normal}4) Undo modifications and edit rebase file", - "", - "{IndicatorColor}Please choose an option." - ); - }); + module_test( + &["pick aaa comment"], + &[Event::from(MetaEvent::ExternalCommandError)], + |mut test_context| { + let mut module = ExternalEditor::new("editor"); + let _ = test_context.activate(&mut module, State::List); + assert_process_result!( + test_context.handle_event(&mut module), + event = Event::from(MetaEvent::ExternalCommandError) + ); + assert_external_editor_state_eq!( + module.state, + ExternalEditorState::Error(anyhow!("Editor returned a non-zero exit status")) + ); + let view_data = test_context.build_view_data(&mut module); + assert_rendered_output!( + view_data, + "{TITLE}", + "{LEADING}", + "{Normal}Editor returned a non-zero exit status", + "", + "{BODY}", + "{Normal}1) Abort rebase", + "{Normal}2) Edit rebase file", + "{Normal}3) Restore rebase file and abort edit", + "{Normal}4) Undo modifications and edit rebase file", + "", + "{IndicatorColor}Please choose an option." + ); + }, + ); } #[test] fn editor_reload_error() { module_test( &["pick aaa comment"], - &[Event::from(KeyCode::Up)], + &[Event::from(KeyCode::Up), Event::from(MetaEvent::ExternalCommandSuccess)], |mut test_context| { let todo_path = test_context.get_todo_file_path(); let mut module = ExternalEditor::new("editor"); - test_context - .event_handler_context - .event_handler - .push_event(Event::from(MetaEvent::ExternalCommandSuccess)); let _ = test_context.activate(&mut module, State::List); test_context.delete_todo_file(); + let _ = test_context.handle_event(&mut module); assert_process_result!( test_context.handle_event(&mut module), event = Event::from(MetaEvent::ExternalCommandSuccess) diff --git a/src/core/src/modules/show_commit/tests.rs b/src/core/src/modules/show_commit/tests.rs index 2e020ae..37f3c57 100644 --- a/src/core/src/modules/show_commit/tests.rs +++ b/src/core/src/modules/show_commit/tests.rs @@ -1133,7 +1133,7 @@ fn handle_event_toggle_overview_to_diff() { with_temp_repository(|repo| { module_test( &["pick 0123456789abcdef0123456789abcdef c1"], - &[Event::from(MetaEvent::ShowDiff)], + &[Event::from('d')], |mut test_context| { let mut module = ShowCommit::new(&Config::new(), &repo); module @@ -1211,7 +1211,7 @@ fn handle_help_event_hide() { with_temp_repository(|repo| { module_test( &["pick aaa c1"], - &[Event::from(MetaEvent::Help), Event::from(MetaEvent::Help)], + &[Event::from(MetaEvent::Help), Event::from('?')], |mut test_context| { let mut module = ShowCommit::new(&Config::new(), &repo); let _ = test_context.handle_all_events(&mut module); diff --git a/src/core/src/process/mod.rs b/src/core/src/process/mod.rs index b9dbd72..652eb92 100644 --- a/src/core/src/process/mod.rs +++ b/src/core/src/process/mod.rs @@ -4,44 +4,42 @@ mod tests; use std::{process::Command, thread}; use anyhow::{anyhow, Result}; -use display::Tui; -use input::{Event, EventHandler, MetaEvent}; +use display::{CrossTerm, Tui}; +use input::{spawn_event_thread, Event, MetaEvent, Sender as EventSender}; use todo_file::TodoFile; use view::{spawn_view_thread, RenderContext, View, ViewSender}; use crate::module::{ExitStatus, Modules, ProcessResult, State}; pub(crate) struct Process { - event_handler: EventHandler, exit_status: Option<ExitStatus>, rebase_todo: TodoFile, render_context: RenderContext, state: State, threads: Vec<thread::JoinHandle<()>>, view_sender: ViewSender, + event_sender: EventSender, } impl Process { - pub(crate) fn new<C: Tui + Send + 'static>( - rebase_todo: TodoFile, - event_handler: EventHandler, - view: View<C>, - ) -> Self { + pub(crate) fn new<C: Tui + Send + 'static>(rebase_todo: TodoFile, view: View<C>) -> Self { #[allow(deprecated)] let view_size = view.get_view_size(); let mut threads = vec![]; let (view_sender, view_thread) = spawn_view_thread(view); threads.push(view_thread); + let (event_sender, event_thread) = spawn_event_thread(CrossTerm::read_event); + threads.push(event_thread); Self { - event_handler, exit_status: None, rebase_todo, render_context: RenderContext::new(view_size.width() as u16, view_size.height() as u16), state: State::List, threads, view_sender, + event_sender, } } @@ -58,6 +56,7 @@ impl Process { self.render_context.height() as u16, )), ); + self.activate(&mut modules, State::List); while self.exit_status.is_none() { let view_data = modules.build_view_data(self.state, &self.render_context, &self.rebase_todo); @@ -72,11 +71,15 @@ impl Process { } let result = modules.handle_event( self.state, - &self.event_handler, + &mut self.event_sender, &self.view_sender, &mut self.rebase_todo, ); + if self.exit_status.is_some() { + break; + } + if let Some(event) = result.event { if event != Event::None { self.handle_process_result(&mut modules, &result); @@ -94,7 +97,9 @@ impl Process { } } - if self.view_sender.end().is_err() { + let (view_end_result, event_end_result) = (self.view_sender.end(), self.event_sender.end()); + + if view_end_result.is_err() || event_end_result.is_err() { return Ok(ExitStatus::StateError); } @@ -151,7 +156,11 @@ impl Process { if let Some(ref external_command) = result.external_command { match self.run_command(external_command) { - Ok(meta_event) => self.event_handler.push_event(Event::from(meta_event)), + Ok(meta_event) => { + self.event_sender + .enqueue_event(Event::from(meta_event)) + .expect("Enqueue event failed"); + }, Err(err) => { self.handle_process_result( modules, @@ -192,10 +201,12 @@ impl Process { fn activate(&mut self, modules: &mut Modules<'_>, previous_state: State) { let result = modules.activate(self.state, &self.rebase_todo, previous_state); // always trigger a resize on activate, for modules that track size - self.event_handler.push_event(Event::Resize( - self.render_context.width() as u16, - self.render_context.height() as u16, - )); + self.event_sender + .enqueue_event(Event::Resize( + self.render_context.width() as u16, + self.render_context.height() as u16, + )) + .expect("Enqueue Resize event failed"); self.handle_process_result(modules, &result); } } diff --git a/src/core/src/process/tests.rs b/src/core/src/process/tests.rs index 3a86acf..e0e3984 100644 --- a/src/core/src/process/tests.rs +++ b/src/core/src/process/tests.rs @@ -1,45 +1,18 @@ -use std::{path::Path, sync::atomic::Ordering}; +use std::{io::Write, path::Path, sync::atomic::Ordering}; use anyhow::anyhow; use config::Theme; use display::{testutil::CrossTerm, Display, Size}; -use input::InputOptions; -use view::{assert_rendered_output, render_line, ViewData}; +use input::{testutil::create_test_keybindings, EventHandler}; +use tempfile::{Builder, NamedTempFile}; +use view::{assert_rendered_output, render_line}; use super::*; use crate::{ - module::Module, modules::{Error, WindowSizeError}, - testutil::module_test, + testutil::TestModule, }; -struct TestModule { - event_callback: Box<dyn Fn(Event, &ViewSender, &mut TodoFile) -> ProcessResult>, - view_data: ViewData, - view_data_callback: Box<dyn Fn(&mut ViewData)>, -} - -impl TestModule { - fn new() -> Self { - Self { - event_callback: Box::new(|_, _, _| ProcessResult::new().event(Event::from(MetaEvent::Kill))), - view_data: ViewData::new(|_| {}), - view_data_callback: Box::new(|_| {}), - } - } -} - -impl Module for TestModule { - fn build_view_data(&mut self, _render_context: &RenderContext, _rebase_todo: &TodoFile) -> &ViewData { - (self.view_data_callback)(&mut self.view_data); - &self.view_data - } - - fn handle_event(&mut self, event: Event, view_sender: &ViewSender, todo_file: &mut TodoFile) -> ProcessResult { - (self.event_callback)(event, view_sender, todo_file) - } -} - fn create_crossterm() -> CrossTerm { let mut crossterm = CrossTerm::new(); crossterm.set_size(Size::new(100, 300)); @@ -47,438 +20,325 @@ fn create_crossterm() -> CrossTerm { } fn create_modules() -> Modules<'static> { - let mut modules = Modules::new(); + let mut modules = Modules::new(EventHandler::new(create_test_keybindings())); modules.register_module(State::Error, Error::new()); modules.register_module(State::WindowSizeError, WindowSizeError::new()); + modules.register_module(State::List, TestModule::new()); modules } +fn create_todo_file() -> (TodoFile, NamedTempFile) { + let todo_file_path = Builder::new() + .prefix("git-rebase-todo-scratch") + .suffix("") + .tempfile() + .unwrap(); + write!(todo_file_path.as_file(), "pick aaa comment").unwrap(); + let mut todo_file = TodoFile::new(todo_file_path.path().to_str().unwrap(), 1, "#"); + todo_file.load_file().unwrap(); + (todo_file, todo_file_path) +} + +fn create_shadow_todo_file(todo_file: &TodoFile) -> TodoFile { + TodoFile::new(todo_file.get_filepath(), 1, "#") +} + +fn create_process(rebase_todo_file: TodoFile, events: &[Event]) -> Process { + let crossterm = create_crossterm(); + let display = Display::new(crossterm, &Theme::new()); + let view = View::new(display, "~", "?"); + let process = Process::new(rebase_todo_file, view); + for event in events { + process.event_sender.enqueue_event(*event).unwrap(); + } + process + .event_sender + .enqueue_event(Event::from(MetaEvent::Kill)) + .unwrap(); + process +} + #[test] fn view_start_error() { - module_test(&["pick aaa comment"], &[], |test_context| { - let crossterm = create_crossterm(); - let display = Display::new(crossterm, &Theme::new()); - let view = View::new(display, "~", "?"); - let mut process = Process::new( - test_context.rebase_todo_file, - test_context.event_handler_context.event_handler, - view, - ); - let mut modules = create_modules(); - modules.register_module(State::List, TestModule::new()); - while process.view_sender.end().is_ok() {} - assert_eq!(process.run(modules).unwrap(), ExitStatus::StateError); - }); + let (rebase_todo_file, _file_path) = create_todo_file(); + let mut process = create_process(rebase_todo_file, &[]); + let modules = create_modules(); + while process.view_sender.end().is_ok() {} + assert_eq!(process.run(modules).unwrap(), ExitStatus::StateError); } #[test] fn window_too_small_on_start() { - module_test(&["pick aaa comment"], &[Event::from(MetaEvent::Exit)], |test_context| { - let mut crossterm = create_crossterm(); - crossterm.set_size(Size::new(1, 1)); - let display = Display::new(crossterm, &Theme::new()); - let view = View::new(display, "~", "?"); - let mut process = Process::new( - test_context.rebase_todo_file, - test_context.event_handler_context.event_handler, - view, - ); - let modules = create_modules(); - let _ = process.run(modules).unwrap(); - assert_eq!(process.state, State::WindowSizeError); - }); + let (rebase_todo_file, _file_path) = create_todo_file(); + let mut process = create_process(rebase_todo_file, &[Event::Resize(1, 1)]); + let modules = create_modules(); + let _ = process.run(modules).unwrap(); + assert_eq!(process.state, State::WindowSizeError); } #[test] fn render_error() { - module_test(&["pick aaa comment"], &[Event::from(MetaEvent::Exit)], |test_context| { - let crossterm = create_crossterm(); - let display = Display::new(crossterm, &Theme::new()); - let view = View::new(display, "~", "?"); - let mut process = Process::new( - test_context.rebase_todo_file, - test_context.event_handler_context.event_handler, - view, - ); - let mut modules = create_modules(); - let mut test_module = TestModule::new(); - let sender = process.view_sender.clone(); - test_module.view_data_callback = Box::new(move |_| while sender.end().is_ok() {}); - modules.register_module(State::List, test_module); - assert_eq!(process.run(modules).unwrap(), ExitStatus::StateError); - }); + let (rebase_todo_file, _file_path) = create_todo_file(); + let mut process = create_process(rebase_todo_file, &[]); + let mut modules = create_modules(); + let sender = process.view_sender.clone(); + let mut test_module = TestModule::new(); + test_module.view_data_callback = Box::new(move |_| while sender.end().is_ok() {}); + modules.register_module(State::List, test_module); + assert_eq!(process.run(modules).unwrap(), ExitStatus::StateError); } #[test] fn view_sender_is_poisoned() { - module_test(&["pick aaa comment"], &[Event::from(MetaEvent::Exit)], |test_context| { - let crossterm = create_crossterm(); - let display = Display::new(crossterm, &Theme::new()); - let view = View::new(display, "~", "?"); - let mut process = Process::new( - test_context.rebase_todo_file, - test_context.event_handler_context.event_handler, - view, - ); - let mut modules = create_modules(); - let test_module = TestModule::new(); - process.view_sender.clone_poisoned().store(true, Ordering::Release); - modules.register_module(State::List, test_module); - assert_eq!(process.run(modules).unwrap(), ExitStatus::StateError); - }); + let (rebase_todo_file, _file_path) = create_todo_file(); + let mut process = create_process(rebase_todo_file, &[Event::from(MetaEvent::Exit)]); + let modules = create_modules(); + process.view_sender.clone_poisoned().store(true, Ordering::Release); + assert_eq!(process.run(modules).unwrap(), ExitStatus::StateError); } #[test] fn stop_error() { - module_test(&["pick aaa comment"], &[Event::from(MetaEvent::Exit)], |test_context| { - let crossterm = create_crossterm(); - let display = Display::new(crossterm, &Theme::new()); - let view = View::new(display, "~", "?"); - let mut process = Process::new( - test_context.rebase_todo_file, - test_context.event_handler_context.event_handler, - view, - ); - let mut modules = create_modules(); - let mut test_module = TestModule::new(); - let sender = process.view_sender.clone(); - test_module.event_callback = Box::new(move |_, _, _| { - while sender.end().is_ok() {} - ProcessResult::new().event(Event::from(MetaEvent::Exit)) - }); - modules.register_module(State::List, test_module); - assert_eq!(process.run(modules).unwrap(), ExitStatus::StateError); + let (rebase_todo_file, _file_path) = create_todo_file(); + let mut process = create_process(rebase_todo_file, &[Event::from(MetaEvent::Exit)]); + let mut modules = create_modules(); + let mut test_module = TestModule::new(); + let sender = process.view_sender.clone(); + test_module.event_callback = Box::new(move |event, _, _| { + while sender.end().is_ok() {} + ProcessResult::from(event) }); + modules.register_module(State::List, test_module); + assert_eq!(process.run(modules).unwrap(), ExitStatus::StateError); } #[test] fn handle_exit_event_that_is_not_kill() { - module_test(&["pick aaa comment"], &[], |mut test_context| { - test_context.rebase_todo_file.write_file().unwrap(); - test_context.rebase_todo_file.set_lines(vec![]); - let mut shadow_rebase_file = test_context.new_todo_file(); - let crossterm = create_crossterm(); - let display = Display::new(crossterm, &Theme::new()); - let view = View::new(display, "~", "?"); - let mut process = Process::new( - test_context.rebase_todo_file, - test_context.event_handler_context.event_handler, - view, - ); - let mut modules = create_modules(); - let mut test_module = TestModule::new(); - test_module.event_callback = Box::new(|_, _, _| { - ProcessResult::new() - .event(Event::from(MetaEvent::Rebase)) - .exit_status(ExitStatus::Good) - }); - modules.register_module(State::List, test_module); - assert_eq!(process.run(modules).unwrap(), ExitStatus::Good); - shadow_rebase_file.load_file().unwrap(); - assert!(shadow_rebase_file.is_empty()); + let (mut rebase_todo_file, _file_path) = create_todo_file(); + rebase_todo_file.set_lines(vec![]); + let mut shadow_rebase_file = create_shadow_todo_file(&rebase_todo_file); + let mut process = create_process(rebase_todo_file, &[Event::from(MetaEvent::Exit)]); + let mut modules = create_modules(); + let mut test_module = TestModule::new(); + test_module.event_callback = Box::new(|_, _, _| { + ProcessResult::new() + .event(Event::from(MetaEvent::Rebase)) + .exit_status(ExitStatus::Good) }); + modules.register_module(State::List, test_module); + assert_eq!(process.run(modules).unwrap(), ExitStatus::Good); + shadow_rebase_file.load_file().unwrap(); + assert!(shadow_rebase_file.is_empty()); } #[test] fn handle_exit_event_that_is_kill() { - module_test(&["pick aaa comment"], &[], |mut test_context| { - test_context.rebase_todo_file.write_file().unwrap(); - test_context.rebase_todo_file.set_lines(vec![]); - let mut shadow_rebase_file = test_context.new_todo_file(); - let crossterm = create_crossterm(); - let display = Display::new(crossterm, &Theme::new()); - let view = View::new(display, "~", "?"); - let mut process = Process::new( - test_context.rebase_todo_file, - test_context.event_handler_context.event_handler, - view, - ); - let mut modules = create_modules(); - let mut test_module = TestModule::new(); - test_module.event_callback = Box::new(|_, _, _| { - ProcessResult::new() - .event(Event::from(MetaEvent::Kill)) - .exit_status(ExitStatus::Kill) - }); - modules.register_module(State::List, test_module); - assert_eq!(process.run(modules).unwrap(), ExitStatus::Kill); - shadow_rebase_file.load_file().unwrap(); - assert!(!shadow_rebase_file.is_empty()); + let (mut rebase_todo_file, _file_path) = create_todo_file(); + rebase_todo_file.set_lines(vec![]); + let mut shadow_rebase_file = create_shadow_todo_file(&rebase_todo_file); + let mut process = create_process(rebase_todo_file, &[]); + let mut modules = create_modules(); + let mut test_module = TestModule::new(); + test_module.event_callback = Box::new(|_, _, _| { + ProcessResult::new() + .event(Event::from(MetaEvent::Kill)) + .exit_status(ExitStatus::Kill) }); + modules.register_module(State::List, test_module); + assert_eq!(process.run(modules).unwrap(), ExitStatus::Kill); + shadow_rebase_file.load_file().unwrap(); + assert!(!shadow_rebase_file.is_empty()); } #[test] fn handle_process_result_error() { - module_test(&["pick aaa comment"], &[], |test_context| { - let crossterm = create_crossterm(); - let display = Display::new(crossterm, &Theme::new()); - let view = View::new(display, "~", "?"); - let mut process = Process::new( - test_context.rebase_todo_file, - test_context.event_handler_context.event_handler, - view, - ); - let mut modules = create_modules(); - let result = ProcessResult::new().error(anyhow!("Test error")); - process.handle_process_result(&mut modules, &result); - assert_eq!(process.state, State::Error); - }); + let (rebase_todo_file, _file_path) = create_todo_file(); + let mut process = create_process(rebase_todo_file, &[]); + let mut modules = create_modules(); + let result = ProcessResult::new().error(anyhow!("Test error")); + process.handle_process_result(&mut modules, &result); + assert_eq!(process.state, State::Error); } #[test] fn handle_process_result_new_state() { - module_test(&["pick aaa comment"], &[], |test_context| { - let crossterm = create_crossterm(); - let display = Display::new(crossterm, &Theme::new()); - let view = View::new(display, "~", "?"); - let mut process = Process::new( - test_context.rebase_todo_file, - test_context.event_handler_context.event_handler, - view, - ); - let mut modules = create_modules(); - modules.register_module(State::List, TestModule::new()); - let result = ProcessResult::new().state(State::Error); - process.handle_process_result(&mut modules, &result); - assert_eq!(process.state, State::Error); - }); + let (rebase_todo_file, _file_path) = create_todo_file(); + let mut process = create_process(rebase_todo_file, &[]); + let mut modules = create_modules(); + let result = ProcessResult::new().state(State::Error); + process.handle_process_result(&mut modules, &result); + assert_eq!(process.state, State::Error); } #[test] fn handle_process_result_state_same() { - module_test(&["pick aaa comment"], &[], |test_context| { - let crossterm = create_crossterm(); - let display = Display::new(crossterm, &Theme::new()); - let view = View::new(display, "~", "?"); - let mut process = Process::new( - test_context.rebase_todo_file, - test_context.event_handler_context.event_handler, - view, - ); - let mut modules = create_modules(); - modules.register_module(State::List, TestModule::new()); - let result = ProcessResult::new().state(State::List); - process.handle_process_result(&mut modules, &result); - assert_eq!(process.state, State::List); - }); + let (rebase_todo_file, _file_path) = create_todo_file(); + let mut process = create_process(rebase_todo_file, &[]); + let mut modules = create_modules(); + let result = ProcessResult::new().state(State::List); + process.handle_process_result(&mut modules, &result); + assert_e |