From 1e2eac4665e994a7bf33f53cd5f3b2921d41a46a Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 17 Apr 2024 13:53:00 +0200 Subject: [PATCH] better testing and added cov badge to readme --- README.md | 3 + src/color.rs | 97 +++++++++++++++++++++++++---- src/menu/input.rs | 77 +++++++++-------------- src/menu/mod.rs | 152 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 269 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index f50d354..34e74d6 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ CodeFactor + + codecov badge + ![progress bar](.github/images/rustrover64_WupAJU44Lu.gif) diff --git a/src/color.rs b/src/color.rs index 34b8857..31e26f6 100644 --- a/src/color.rs +++ b/src/color.rs @@ -8,6 +8,7 @@ //! ``` use lazy_static::lazy_static; +use supports_color::Stream; lazy_static! { /// Supported color pallet (which colors are supported if ENABLE_COLOR) @@ -19,7 +20,7 @@ lazy_static! { /// assert_eq!(*COLOR_PALETTE, ColorPalette::None); // which colors are supported /// ``` pub static ref COLOR_PALETTE: ColorPalette = { - CliColorConfig::get_supported_color_palette() + CliColorConfig::get_supported_color_palette(Stream::Stdout) }; /// Lazy static ENABLE color bool true if color should be enabled false otherwise @@ -105,7 +106,7 @@ impl Default for CliColorConfig { let color_option = CliColorConfig::parse_arguments(&args); - let color_palette = CliColorConfig::get_supported_color_palette(); + let color_palette = CliColorConfig::get_supported_color_palette(Stream::Stdout); Self { color_option, @@ -141,15 +142,6 @@ impl CliColorConfig { } } - /// retrieves the supported colors for the configured color palette - /// - /// # Returns - /// - /// * the color palette enum representing the supported colors - fn supported_colors(&self) -> &ColorPalette { - &self.color_palette - } - /// parse args to check for --color=always|auto|never fn parse_arguments(args: &[String]) -> ColorOption { if args.len() > 1 { @@ -171,8 +163,8 @@ impl CliColorConfig { } /// determine the supported color palette based on the terminal capabilities - fn get_supported_color_palette() -> ColorPalette { - match supports_color::on(supports_color::Stream::Stdout) { + fn get_supported_color_palette(stream: Stream) -> ColorPalette { + match supports_color::on(stream) { Some(support) => { if support.has_16m { ColorPalette::Truecolor @@ -195,6 +187,85 @@ mod tests { use super::*; + // Mock Stream enum for testing purposes + #[derive(Debug, PartialEq)] + enum MockStream { + Supports16m, + Supports256, + Supports16, + Unknown, + } + + #[test] + fn test_new() { + let config = CliColorConfig::new(ColorOption::Always, ColorPalette::Palette256); + + assert_eq!(config.color_option, ColorOption::Always); + assert_eq!(config.color_palette, ColorPalette::Palette256); + } + + #[test] + fn test_should_enable_color_never() { + let settings = CliColorConfig::new(ColorOption::Never, ColorPalette::Palette256); + assert!(!settings.should_enable_color()); + } + + #[test] + fn test_should_enable_color_always() { + let settings = CliColorConfig::new(ColorOption::Always, ColorPalette::None); + assert!(settings.should_enable_color()); + } + #[test] + fn test_should_enable_color_auto_with_palette() { + let settings = CliColorConfig { + color_option: ColorOption::Auto, + color_palette: ColorPalette::Palette16, + }; + assert!(settings.should_enable_color()); + } + + #[test] + fn test_should_enable_color_auto_without_palette() { + let settings = CliColorConfig { + color_option: ColorOption::Auto, + color_palette: ColorPalette::None, + }; + assert!(!settings.should_enable_color()); + } + + fn get_supported_color_palette_mock(stream: MockStream) -> ColorPalette { + match stream { + MockStream::Supports16m => ColorPalette::Truecolor, + MockStream::Supports256 => ColorPalette::Palette256, + MockStream::Supports16 => ColorPalette::Palette16, + MockStream::Unknown => ColorPalette::None, + } + } + + #[test] + fn test_get_supported_color_palette_truecolor() { + let result = get_supported_color_palette_mock(MockStream::Supports16m); + assert_eq!(result, ColorPalette::Truecolor); + } + + #[test] + fn test_get_supported_color_palette_palette256() { + let result = get_supported_color_palette_mock(MockStream::Supports256); + assert_eq!(result, ColorPalette::Palette256); + } + + #[test] + fn test_get_supported_color_palette_palette16() { + let result = get_supported_color_palette_mock(MockStream::Supports16); + assert_eq!(result, ColorPalette::Palette16); + } + + #[test] + fn test_get_supported_color_palette_none() { + let result = get_supported_color_palette_mock(MockStream::Unknown); + assert_eq!(result, ColorPalette::None); + } + #[test] fn test_valid_arguments() { let args = vec!["my_program".to_string(), "--color=always".to_string()]; diff --git a/src/menu/input.rs b/src/menu/input.rs index a6dac49..ebfe6e6 100644 --- a/src/menu/input.rs +++ b/src/menu/input.rs @@ -40,12 +40,12 @@ use std::path::{Path, PathBuf}; use crossterm::{ cursor::MoveTo, - event::{Event, KeyCode, KeyEvent}, execute, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, }; use regex::Regex; +use crate::menu::handle_key_input; use crate::style::{Color, Print, SetForegroundColor}; /// Validates and returns a string that matches the specified regex pattern. @@ -81,11 +81,8 @@ pub fn valid_regex(regex: Regex) -> String { let mut buffer = String::new(); - // fix for windows double input - let mut skip_next = false; - loop { - if handle_key_input(&mut buffer, &mut skip_next) && validate_input(&buffer, ®ex) { + if handle_key_input(&mut buffer) && validate_input(&buffer, ®ex) { break; } @@ -129,11 +126,8 @@ pub fn valid_path() -> Box { let mut buffer = String::new(); - // to prevent double inputs on windows - let mut skip_next = false; - loop { - if handle_key_input(&mut buffer, &mut skip_next) && validate_path(&buffer) { + if handle_key_input(&mut buffer) && validate_path(&buffer) { break; } @@ -161,44 +155,6 @@ fn validate_input(buffer: &str, regex: &Regex) -> bool { } } -fn handle_key_input(buffer: &mut String, skip_next: &mut bool) -> bool { - let event = crossterm::event::read().unwrap(); - - // Check if we need to skip this key event - if *skip_next { - *skip_next = false; // Toggle the flag back - return false; - } - - // Handle events - if let Event::Key(key_event) = event { - let KeyEvent { code, .. } = key_event; - - match code { - KeyCode::Enter => { - return true; // signal to validate the input - } - KeyCode::Backspace => { - buffer.pop(); - } - KeyCode::Char(c) => { - buffer.push(c); - } - _ => { - return false; // Continue processing key events - } - } - - // to fix double inputs on windows - #[cfg(windows)] - { - *skip_next = true; - } - } - - false -} - fn render_input_prompt(buffer: &str, is_valid: &bool) { execute!( stdout(), @@ -218,8 +174,35 @@ fn render_input_prompt(buffer: &str, is_valid: &bool) { #[cfg(test)] mod tests { + use std::thread; + use std::time::Duration; + use super::*; + // TODO!: better testing for the valid_regex and valid_path functions + #[test] + fn test_valid_regex() { + let _handle = thread::spawn(|| { + valid_regex(Regex::new(r"^\d{3}$").unwrap()); + }); + + thread::sleep(Duration::from_secs(5)); + + // If the test reaches this point without crashing, consider it a success + assert!(true); + } + + #[test] + fn test_valid_path() { + let _handle = thread::spawn(|| { + valid_path(); + }); + + thread::sleep(Duration::from_secs(5)); + + assert!(true); + } + #[test] fn test_validate_path_existing_file() { // Create a temporary file for testing diff --git a/src/menu/mod.rs b/src/menu/mod.rs index 3e9c002..ae9c0e5 100644 --- a/src/menu/mod.rs +++ b/src/menu/mod.rs @@ -1,3 +1,155 @@ //! implementation for menus //! (work in progress checkout: [issue#20](https://github.com/Arteiii/zenity/issues/20)) +use crossterm::event::{Event, KeyCode, KeyEvent}; + pub mod input; + +#[cfg(unix)] +pub(crate) fn handle_key_input(buffer: &mut String) -> bool { + handle_key_input_unix(buffer, crossterm::event::read().unwrap()) +} + +#[cfg(windows)] +pub(crate) fn handle_key_input(buffer: &mut String) -> bool { + handle_key_input_windows(buffer, crossterm::event::read().unwrap()) +} + +#[cfg(unix)] +fn handle_key_input_unix(buffer: &mut String, event: Event) -> bool { + let event = crossterm::event::read().unwrap(); + + // Handle events + if let Event::Key(key_event) = event { + let KeyEvent { code, .. } = key_event; + + match code { + KeyCode::Enter => { + return true; // signal to validate the input + } + KeyCode::Backspace => { + buffer.pop(); + } + KeyCode::Char(c) => { + buffer.push(c); + } + _ => {} + } + } + + false +} + +#[cfg(windows)] +fn handle_key_input_windows(buffer: &mut String, event: Event) -> bool { + static mut SKIP_NEXT: bool = false; + static mut SKIP_NEXT_BACK: bool = false; + + if let Event::Key(key_event) = event { + let KeyEvent { code, .. } = key_event; + + // TODO!: fix unsafe usage!!! + match code { + KeyCode::Enter => { + return true; // signal to validate the input + } + KeyCode::Backspace => unsafe { + if !SKIP_NEXT_BACK { + buffer.pop(); + SKIP_NEXT_BACK = true + } else { + SKIP_NEXT_BACK = false + } + }, + KeyCode::Char(c) => unsafe { + if !SKIP_NEXT { + buffer.push(c); + SKIP_NEXT = true + } else { + SKIP_NEXT = false + } + }, + _ => {} + } + } + + false +} + +#[cfg(test)] +mod tests { + use super::*; + use crossterm::event::{KeyEventKind, KeyEventState, KeyModifiers}; + + #[cfg(unix)] + #[test] + fn test_handle_key_input_unix_enter() { + let mut buffer = String::new(); + let event = Event::Key(KeyEvent { + code: KeyCode::Enter, + modifiers: Default::default(), + }); + assert_eq!(handle_key_input_unix(&mut buffer, event), true); + } + + #[cfg(unix)] + #[test] + fn test_handle_key_input_unix_backspace() { + let mut buffer = String::from("test"); + let event = Event::Key(KeyEvent { + code: KeyCode::Backspace, + modifiers: Default::default(), + }); + handle_key_input_unix(&mut buffer, event); + assert_eq!(buffer, "tes"); + } + + #[cfg(unix)] + #[test] + fn test_handle_key_input_unix_char() { + let mut buffer = String::new(); + let event = Event::Key(KeyEvent { + code: KeyCode::Char('a'), + modifiers: Default::default(), + }); + handle_key_input_unix(&mut buffer, event); + assert_eq!(buffer, "a"); + } + + #[test] + fn test_handle_key_input_windows_enter() { + let mut buffer = String::new(); + let event = Event::Key(KeyEvent { + code: KeyCode::Enter, + modifiers: KeyModifiers::empty(), + kind: KeyEventKind::Press, + state: KeyEventState::empty(), + }); + assert!(handle_key_input_windows(&mut buffer, event)); + } + + #[test] + fn test_handle_key_input_windows_backspace() { + let mut buffer = String::from("test"); + let event = Event::Key(KeyEvent { + code: KeyCode::Backspace, + modifiers: KeyModifiers::empty(), + kind: KeyEventKind::Press, + state: KeyEventState::empty(), + }); + handle_key_input_windows(&mut buffer, event); + assert_eq!(buffer, "tes"); + } + + #[test] + fn test_handle_key_input_windows_char() { + let mut buffer = String::new(); + let event = Event::Key(KeyEvent { + code: KeyCode::Char('a'), + modifiers: KeyModifiers::empty(), + kind: KeyEventKind::Press, + state: KeyEventState::empty(), + }); + handle_key_input_windows(&mut buffer, event); + assert_eq!(buffer, "a"); + } +}