From 133d9b1193ce6d7b91df428267fd70942986d241 Mon Sep 17 00:00:00 2001 From: Tim Oram Date: Sat, 19 Jun 2021 12:54:55 -0230 Subject: Refactor process module testutils Break apart the process module test utils and some minor renaming. --- src/process/modules.rs | 2 +- src/process/testutil.rs | 277 -------------------------- src/process/testutil/assert_process_result.rs | 153 ++++++++++++++ src/process/testutil/handle_event_test.rs | 21 ++ src/process/testutil/mod.rs | 9 + src/process/testutil/process_module_test.rs | 136 +++++++++++++ 6 files changed, 320 insertions(+), 278 deletions(-) delete mode 100644 src/process/testutil.rs create mode 100644 src/process/testutil/assert_process_result.rs create mode 100644 src/process/testutil/handle_event_test.rs create mode 100644 src/process/testutil/mod.rs create mode 100644 src/process/testutil/process_module_test.rs (limited to 'src/process') diff --git a/src/process/modules.rs b/src/process/modules.rs index da3a0c9..ee692a0 100644 --- a/src/process/modules.rs +++ b/src/process/modules.rs @@ -124,7 +124,7 @@ mod tests { modules.handle_input( State::List, &context.event_handler_context.event_handler, - &context.event_handler_context.view_sender, + &context.view_sender_context.sender, &mut context.rebase_todo_file, ); modules.build_view_data(State::List, &RenderContext::new(100, 100), &context.rebase_todo_file); diff --git a/src/process/testutil.rs b/src/process/testutil.rs deleted file mode 100644 index 34cebe3..0000000 --- a/src/process/testutil.rs +++ /dev/null @@ -1,277 +0,0 @@ -use std::{cell::Cell, path::Path}; - -use anyhow::Error; -use tempfile::{Builder, NamedTempFile}; - -use crate::{ - config::{testutil::create_config, Config}, - input::{ - testutil::{with_event_handler, TestContext as EventHandlerTestContext}, - Event, - }, - process::{ExitStatus, ProcessModule, ProcessResult, State}, - todo_file::{Line, TodoFile}, - view::{RenderContext, ViewData}, -}; - -pub struct TestContext<'t> { - pub config: &'t Config, - pub event_handler_context: EventHandlerTestContext, - pub rebase_todo_file: TodoFile, - pub render_context: RenderContext, - todo_file: Cell, -} - -impl<'t> TestContext<'t> { - fn get_build_data<'tc>(&self, module: &'tc mut dyn ProcessModule) -> &'tc ViewData { - module.build_view_data(&self.render_context, &self.rebase_todo_file) - } - - pub fn activate(&self, module: &'_ mut dyn ProcessModule, state: State) -> ProcessResult { - module.activate(&self.rebase_todo_file, state) - } - - #[allow(clippy::unused_self)] - pub fn deactivate(&mut self, module: &'_ mut dyn ProcessModule) { - module.deactivate(); - } - - pub fn build_view_data<'tc>(&self, module: &'tc mut dyn ProcessModule) -> &'tc ViewData { - self.get_build_data(module) - } - - pub fn handle_event(&mut self, module: &'_ mut dyn ProcessModule) -> ProcessResult { - module.handle_events( - &self.event_handler_context.event_handler, - &self.event_handler_context.view_sender, - &mut self.rebase_todo_file, - ) - } - - pub fn handle_n_events(&mut self, module: &'_ mut dyn ProcessModule, n: usize) -> Vec { - let mut results = vec![]; - for _ in 0..n { - results.push(module.handle_events( - &self.event_handler_context.event_handler, - &self.event_handler_context.view_sender, - &mut self.rebase_todo_file, - )); - } - results - } - - pub fn handle_all_events(&mut self, module: &'_ mut dyn ProcessModule) -> Vec { - let mut results = vec![]; - for _ in 0..self.event_handler_context.number_events { - results.push(module.handle_events( - &self.event_handler_context.event_handler, - &self.event_handler_context.view_sender, - &mut self.rebase_todo_file, - )); - } - results - } - - pub fn new_todo_file(&self) -> TodoFile { - TodoFile::new(self.get_todo_file_path().as_str(), 1, "#") - } - - pub fn get_todo_file_path(&self) -> String { - let t = self.todo_file.replace(NamedTempFile::new().unwrap()); - let path = t.path().to_str().unwrap().to_owned(); - self.todo_file.replace(t); - path - } - - pub fn delete_todo_file(&self) { - self.todo_file - .replace(Builder::new().tempfile().unwrap()) - .close() - .unwrap(); - } - - pub fn set_todo_file_readonly(&self) { - let t = self.todo_file.replace(NamedTempFile::new().unwrap()); - let todo_file = t.as_file(); - let mut permissions = todo_file.metadata().unwrap().permissions(); - permissions.set_readonly(true); - todo_file.set_permissions(permissions).unwrap(); - self.todo_file.replace(t); - } -} - -fn format_process_result( - event: Option, - state: Option, - exit_status: Option, - error: &Option, - external_command: &Option<(String, Vec)>, -) -> String { - format!( - "ExitStatus({}), State({}), Event({}), Error({}), ExternalCommand({})", - exit_status.map_or("None", |exit_status| { - match exit_status { - ExitStatus::Abort => "Abort", - ExitStatus::ConfigError => "ConfigError", - ExitStatus::FileReadError => "FileReadError", - ExitStatus::FileWriteError => "FileWriteError", - ExitStatus::Good => "Good", - ExitStatus::StateError => "StateError", - ExitStatus::Kill => "Kill", - } - }), - state.map_or("None", |state| { - match state { - State::ConfirmAbort => "ConfirmAbort", - State::ConfirmRebase => "ConfirmRebase", - State::Error => "Error", - State::ExternalEditor => "ExternalEditor", - State::Insert => "Insert", - State::List => "List", - State::ShowCommit => "ShowCommit", - State::WindowSizeError => "WindowSizeError", - } - }), - event.map_or(String::from("None"), |evt| format!("{:?}", evt)), - error - .as_ref() - .map_or(String::from("None"), |error| format!("{:#}", error)), - external_command.as_ref().map_or(String::from("None"), |command| { - format!("{} {}", command.0, command.1.join(",")) - }) - ) -} - -pub fn _assert_process_result( - actual: &ProcessResult, - event: Option, - state: Option, - exit_status: Option, - error: &Option, - external_command: &Option<(String, Vec)>, -) { - let error_compare_fn = |expected| { - actual - .error - .as_ref() - .map_or(false, |actual| format!("{:#}", expected) == format!("{:#}", actual)) - }; - let external_command_compare_fn = |expected| { - actual - .external_command - .as_ref() - .map_or(false, |actual| actual == expected) - }; - - if !(exit_status.map_or(actual.exit_status.is_none(), |expected| { - actual.exit_status.map_or(false, |actual| expected == actual) - }) && state.map_or(actual.state.is_none(), |expected| { - actual.state.map_or(false, |actual| expected == actual) - }) && event.map_or(actual.event.is_none(), |expected| { - actual.event.map_or(false, |actual| expected == actual) - }) && error.as_ref().map_or(actual.error.is_none(), error_compare_fn) - && external_command - .as_ref() - .map_or(actual.external_command.is_none(), external_command_compare_fn)) - { - panic!( - "{}", - vec![ - "\n", - "ProcessResult does not match", - "==========", - "Expected State:", - format_process_result(event, state, exit_status, error, external_command).as_str(), - "Actual:", - format_process_result( - actual.event, - actual.state, - actual.exit_status, - &actual.error, - &actual.external_command - ) - .as_str(), - "==========\n" - ] - .join("\n") - ); - } -} - -#[macro_export] -macro_rules! assert_process_result { - ($actual:expr) => { - crate::process::testutil::_assert_process_result(&$actual, None, None, None, &None, &None) - }; - ($actual:expr, error = $error:expr, exit_status = $exit_status:expr) => { - crate::process::testutil::_assert_process_result(&$actual, None, None, Some($exit_status), &Some($error), &None) - }; - ($actual:expr, state = $state:expr) => { - crate::process::testutil::_assert_process_result(&$actual, None, Some($state), None, &None, &None) - }; - ($actual:expr, state = $state:expr, external_command = $external_command:expr) => { - crate::process::testutil::_assert_process_result( - &$actual, - None, - Some($state), - None, - &None, - &Some($external_command), - ) - }; - ($actual:expr, state = $state:expr, error = $error:expr) => { - crate::process::testutil::_assert_process_result(&$actual, None, Some($state), None, &Some($error), &None) - }; - ($actual:expr, event = $event:expr) => { - crate::process::testutil::_assert_process_result(&$actual, Some($event), None, None, &None, &None) - }; - ($actual:expr, event = $event:expr, state = $state:expr) => { - crate::process::testutil::_assert_process_result(&$actual, Some($event), Some($state), None, &None, &None) - }; - ($actual:expr, event = $event:expr, exit_status = $exit_status:expr) => { - crate::process::testutil::_assert_process_result(&$actual, Some($event), None, Some($exit_status), &None, &None) - }; - ($actual:expr, event = $event:expr, external_command = $external_command:expr) => { - crate::process::testutil::_assert_process_result( - &$actual, - Some($event), - None, - None, - &None, - &Some($external_command), - ) - }; - - ($actual:expr, external_command = $external_command:expr) => { - crate::process::testutil::_assert_process_result(&$actual, None, None, None, &None, &Some($external_command)) - }; -} - -pub fn process_module_test(lines: &[&str], events: &[Event], callback: C) -where C: for<'p> FnOnce(TestContext<'p>) { - with_event_handler(events, |event_handler_context| { - let git_repo_dir = Path::new(env!("CARGO_MANIFEST_DIR")) - .join("test") - .join("fixtures") - .join("simple") - .to_str() - .unwrap() - .to_owned(); - let todo_file = Builder::new() - .prefix("git-rebase-todo-scratch") - .suffix("") - .tempfile_in(git_repo_dir.as_str()) - .unwrap(); - - let mut rebase_todo_file = TodoFile::new(todo_file.path().to_str().unwrap(), 1, "#"); - rebase_todo_file.set_lines(lines.iter().map(|l| Line::new(l).unwrap()).collect()); - - callback(TestContext { - config: &create_config(), - event_handler_context, - rebase_todo_file, - todo_file: Cell::new(todo_file), - render_context: RenderContext::new(300, 120), - }); - }); -} diff --git a/src/process/testutil/assert_process_result.rs b/src/process/testutil/assert_process_result.rs new file mode 100644 index 0000000..e5436ff --- /dev/null +++ b/src/process/testutil/assert_process_result.rs @@ -0,0 +1,153 @@ +use anyhow::Error; + +use crate::{ + input::Event, + process::{exit_status::ExitStatus, process_result::ProcessResult, state::State}, +}; + +fn format_process_result( + event: Option, + state: Option, + exit_status: Option, + error: &Option, + external_command: &Option<(String, Vec)>, +) -> String { + format!( + "ExitStatus({}), State({}), Event({}), Error({}), ExternalCommand({})", + exit_status.map_or("None", |exit_status| { + match exit_status { + ExitStatus::Abort => "Abort", + ExitStatus::ConfigError => "ConfigError", + ExitStatus::FileReadError => "FileReadError", + ExitStatus::FileWriteError => "FileWriteError", + ExitStatus::Good => "Good", + ExitStatus::StateError => "StateError", + ExitStatus::Kill => "Kill", + } + }), + state.map_or("None", |state| { + match state { + State::ConfirmAbort => "ConfirmAbort", + State::ConfirmRebase => "ConfirmRebase", + State::Error => "Error", + State::ExternalEditor => "ExternalEditor", + State::Insert => "Insert", + State::List => "List", + State::ShowCommit => "ShowCommit", + State::WindowSizeError => "WindowSizeError", + } + }), + event.map_or(String::from("None"), |evt| format!("{:?}", evt)), + error + .as_ref() + .map_or(String::from("None"), |error| format!("{:#}", error)), + external_command.as_ref().map_or(String::from("None"), |command| { + format!("{} {}", command.0, command.1.join(",")) + }) + ) +} + +pub fn _assert_process_result( + actual: &ProcessResult, + event: Option, + state: Option, + exit_status: Option, + error: &Option, + external_command: &Option<(String, Vec)>, +) { + let error_compare_fn = |expected| { + actual + .error + .as_ref() + .map_or(false, |actual| format!("{:#}", expected) == format!("{:#}", actual)) + }; + let external_command_compare_fn = |expected| { + actual + .external_command + .as_ref() + .map_or(false, |actual| actual == expected) + }; + + if !(exit_status.map_or(actual.exit_status.is_none(), |expected| { + actual.exit_status.map_or(false, |actual| expected == actual) + }) && state.map_or(actual.state.is_none(), |expected| { + actual.state.map_or(false, |actual| expected == actual) + }) && event.map_or(actual.event.is_none(), |expected| { + actual.event.map_or(false, |actual| expected == actual) + }) && error.as_ref().map_or(actual.error.is_none(), error_compare_fn) + && external_command + .as_ref() + .map_or(actual.external_command.is_none(), external_command_compare_fn)) + { + panic!( + "{}", + vec![ + "\n", + "ProcessResult does not match", + "==========", + "Expected State:", + format_process_result(event, state, exit_status, error, external_command).as_str(), + "Actual:", + format_process_result( + actual.event, + actual.state, + actual.exit_status, + &actual.error, + &actual.external_command + ) + .as_str(), + "==========\n" + ] + .join("\n") + ); + } +} + +#[macro_export] +macro_rules! assert_process_result { + ($actual:expr) => { + crate::process::testutil::_assert_process_result(&$actual, None, None, None, &None, &None) + }; + ($actual:expr, error = $error:expr, exit_status = $exit_status:expr) => { + crate::process::testutil::_assert_process_result(&$actual, None, None, Some($exit_status), &Some($error), &None) + }; + ($actual:expr, state = $state:expr) => { + crate::process::testutil::_assert_process_result(&$actual, None, Some($state), None, &None, &None) + }; + ($actual:expr, state = $state:expr, external_command = $external_command:expr) => { + crate::process::testutil::_assert_process_result( + &$actual, + None, + Some($state), + None, + &None, + &Some($external_command), + ) + }; + ($actual:expr, state = $state:expr, error = $error:expr) => { + crate::process::testutil::_assert_process_result(&$actual, None, Some($state), None, &Some($error), &None) + }; + ($actual:expr, event = $event:expr) => { + crate::process::testutil::_assert_process_result(&$actual, Some($event), None, None, &None, &None) + }; + ($actual:expr, event = $event:expr, state = $state:expr) => { + crate::process::testutil::_assert_process_result(&$actual, Some($event), Some($state), None, &None, &None) + }; + ($actual:expr, event = $event:expr, exit_status = $exit_status:expr) => { + crate::process::testutil::_assert_process_result(&$actual, Some($event), None, Some($exit_status), &None, &None) + }; + ($actual:expr, event = $event:expr, external_command = $external_command:expr) => { + crate::process::testutil::_assert_process_result( + &$actual, + Some($event), + None, + None, + &None, + &Some($external_command), + ) + }; + + ($actual:expr, external_command = $external_command:expr) => { + crate::process::testutil::_assert_process_result(&$actual, None, None, None, &None, &Some($external_command)) + }; +} diff --git a/src/process/testutil/handle_event_test.rs b/src/process/testutil/handle_event_test.rs new file mode 100644 index 0000000..268570e --- /dev/null +++ b/src/process/testutil/handle_event_test.rs @@ -0,0 +1,21 @@ +use crate::{ + input::{testutil::with_event_handler, Event, EventHandler}, + view::{testutil::with_view_sender, ViewSender}, +}; + +pub struct TestContext { + pub event_handler: EventHandler, + pub view_sender: ViewSender, +} + +pub fn handle_event_test(events: &[Event], callback: C) +where C: FnOnce(TestContext) { + with_view_sender(|view_sender_context| { + with_event_handler(events, |event_handler_context| { + callback(TestContext { + event_handler: event_handler_context.event_handler, + view_sender: view_sender_context.sender, + }); + }); + }); +} diff --git a/src/process/testutil/mod.rs b/src/process/testutil/mod.rs new file mode 100644 index 0000000..0288d33 --- /dev/null +++ b/src/process/testutil/mod.rs @@ -0,0 +1,9 @@ +mod assert_process_result; +mod handle_event_test; +mod process_module_test; + +pub use self::{ + assert_process_result::_assert_process_result, + handle_event_test::handle_event_test, + process_module_test::process_module_test, +}; diff --git a/src/process/testutil/process_module_test.rs b/src/process/testutil/process_module_test.rs new file mode 100644 index 0000000..fe2d63e --- /dev/null +++ b/src/process/testutil/process_module_test.rs @@ -0,0 +1,136 @@ +use std::{cell::Cell, path::Path}; + +use tempfile::{Builder, NamedTempFile}; + +use crate::{ + config::{testutil::create_config, Config}, + input::{ + testutil::{with_event_handler, TestContext as EventHandlerTestContext}, + Event, + }, + process::{process_module::ProcessModule, process_result::ProcessResult, state::State}, + todo_file::{Line, TodoFile}, + view::{ + testutil::{with_view_sender, TestContext as ViewSenderContext}, + RenderContext, + ViewData, + }, +}; + +pub struct TestContext<'t> { + pub config: &'t Config, + pub event_handler_context: EventHandlerTestContext, + pub view_sender_context: ViewSenderContext, + pub rebase_todo_file: TodoFile, + pub render_context: RenderContext, + todo_file: Cell, +} + +impl<'t> TestContext<'t> { + fn get_build_data<'tc>(&self, module: &'tc mut dyn ProcessModule) -> &'tc ViewData { + module.build_view_data(&self.render_context, &self.rebase_todo_file) + } + + pub fn activate(&self, module: &'_ mut dyn ProcessModule, state: State) -> ProcessResult { + module.activate(&self.rebase_todo_file, state) + } + + #[allow(clippy::unused_self)] + pub fn deactivate(&mut self, module: &'_ mut dyn ProcessModule) { + module.deactivate(); + } + + pub fn build_view_data<'tc>(&self, module: &'tc mut dyn ProcessModule) -> &'tc ViewData { + self.get_build_data(module) + } + + pub fn handle_event(&mut self, module: &'_ mut dyn ProcessModule) -> ProcessResult { + module.handle_events( + &self.event_handler_context.event_handler, + &self.view_sender_context.sender, + &mut self.rebase_todo_file, + ) + } + + pub fn handle_n_events(&mut self, module: &'_ mut dyn ProcessModule, n: usize) -> Vec { + let mut results = vec![]; + for _ in 0..n { + results.push(module.handle_events( + &self.event_handler_context.event_handler, + &self.view_sender_context.sender, + &mut self.rebase_todo_file, + )); + } + results + } + + pub fn handle_all_events(&mut self, module: &'_ mut dyn ProcessModule) -> Vec { + let mut results = vec![]; + for _ in 0..self.event_handler_context.number_events { + results.push(module.handle_events( + &self.event_handler_context.event_handler, + &self.view_sender_context.sender, + &mut self.rebase_todo_file, + )); + } + results + } + + pub fn new_todo_file(&self) -> TodoFile { + TodoFile::new(self.get_todo_file_path().as_str(), 1, "#") + } + + pub fn get_todo_file_path(&self) -> String { + let t = self.todo_file.replace(NamedTempFile::new().unwrap()); + let path = t.path().to_str().unwrap().to_owned(); + self.todo_file.replace(t); + path + } + + pub fn delete_todo_file(&self) { + self.todo_file + .replace(Builder::new().tempfile().unwrap()) + .close() + .unwrap(); + } + + pub fn set_todo_file_readonly(&self) { + let t = self.todo_file.replace(NamedTempFile::new().unwrap()); + let todo_file = t.as_file(); + let mut permissions = todo_file.metadata().unwrap().permissions(); + permissions.set_readonly(true); + todo_file.set_permissions(permissions).unwrap(); + self.todo_file.replace(t); + } +} + +pub fn process_module_test(lines: &[&str], events: &[Event], callback: C) +where C: for<'p> FnOnce(TestContext<'p>) { + with_event_handler(events, |event_handler_context| { + with_view_sender(|view_sender_context| { + let git_repo_dir = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("test") + .join("fixtures") + .join("simple") + .to_str() + .unwrap() + .to_owned(); + let todo_file = Builder::new() + .prefix("git-rebase-todo-scratch") + .suffix("") + .tempfile_in(git_repo_dir.as_str()) + .unwrap(); + + let mut rebase_todo_file = TodoFile::new(todo_file.path().to_str().unwrap(), 1, "#"); + rebase_todo_file.set_lines(lines.iter().map(|l| Line::new(l).unwrap()).collect()); + callback(TestContext { + config: &create_config(), + event_handler_context, + rebase_todo_file, + view_sender_context, + todo_file: Cell::new(todo_file), + render_context: RenderContext::new(300, 120), + }); + }); + }); +} -- cgit v1.2.3