From 2991eeac9187808457d491ed87211fc24629ad96 Mon Sep 17 00:00:00 2001 From: sinYa Iwasaki Date: Wed, 6 Mar 2024 15:24:27 +0900 Subject: [PATCH] Validator, Value Object, and rstest #44 --- src/config.rs | 126 ++++++++++++++++++++++++++++++++++++++--------- src/lib.rs | 1 + src/utils.rs | 16 +++--- src/validator.rs | 51 +++++++++++++++++++ src/window.rs | 12 ++--- 5 files changed, 170 insertions(+), 36 deletions(-) create mode 100644 src/validator.rs diff --git a/src/config.rs b/src/config.rs index 2568c4f..2d8fab4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,55 +1,135 @@ use crate::constants::{UNIT_LENGTH, UNIT_SIZE, WINDOW_TITLE}; +use crate::validator; + +pub struct Title(String); + +impl Title { + pub fn new(title: &str) -> Result { + validator::validate_string_length(title, 1, 50)?; + Ok(Self(title.to_string())) + } + + pub fn value(&self) -> &str { + &self.0 + } +} + +pub struct Width(u32); + +impl Width { + pub fn new(width: u32) -> Result { + validator::validate_numeric_range(width, 1, 1000)?; + Ok(Self(width)) + } + + pub fn value(&self) -> u32 { + self.0 + } +} + +pub struct Height(u32); + +impl Height { + pub fn new(height: u32) -> Result { + validator::validate_numeric_range(height, 1, 1000)?; + Ok(Self(height)) + } + + pub fn value(&self) -> u32 { + self.0 + } +} pub struct WindowConfig { - pub title: String, - pub width: u32, - pub height: u32, + pub title: Title, + pub width: Width, + pub height: Height, } impl WindowConfig { - pub fn new(title: String, width: u32, height: u32) -> WindowConfig { - Self { - title, - width, - height, - } + pub fn new(title: &str, width: u32, height: u32) -> Result { + Ok(Self { + title: Title::new(title)?, + width: Width::new(width)?, + height: Height::new(height)?, + }) } fn calculate_window_size() -> (u32, u32) { (UNIT_SIZE.0 * UNIT_LENGTH.0, UNIT_SIZE.1 * UNIT_LENGTH.1) } - fn create_with_default_size(title: &str) -> WindowConfig { + fn create_with_default_size(title: &str) -> Result { let (width, height) = Self::calculate_window_size(); - Self::new(title.to_string(), width, height) + Self::new(title, width, height) } } -pub fn create_window_config() -> WindowConfig { +pub fn create_window_config() -> Result { WindowConfig::create_with_default_size(WINDOW_TITLE) } #[cfg(test)] mod tests { use super::*; + use rstest::rstest; - #[test] - fn test_window_config_creation() { - let title = "Test Window".to_string(); - let width = 800; - let height = 600; - let window_config = WindowConfig::new(title.clone(), width, height); + #[rstest] + #[case::min_1("T")] + #[case::max_50("12345678901234567890123456789012345678901234567890")] + #[case::normal("Test Window")] + #[should_panic] + #[case::min_0("")] + #[should_panic] + #[case::max_51("123456789012345678901234567890123456789012345678901")] + fn test_title_new(#[case] title: &str) { + assert!(Title::new(title).is_ok()); + } + + #[rstest] + #[case::min_1(1)] + #[case::max_1000(1000)] + #[case::normal(50)] + #[should_panic] + #[case::min_0(0)] + #[should_panic] + #[case::max_1001(1001)] + fn test_width_new(#[case] width: u32) { + assert!(Width::new(width).is_ok()); + } + + #[rstest] + #[case::min_1(1)] + #[case::max_1000(1000)] + #[case::normal(50)] + #[should_panic] + #[case::min_0(0)] + #[should_panic] + #[case::max_1001(1001)] + fn test_height_new(#[case] height: u32) { + assert!(Height::new(height).is_ok()); + } + + #[rstest] + #[case::normal("Test Window", 800, 600)] + #[case::min_1("T", 1, 1)] + #[case::max_1000("12345678901234567890123456789012345678901234567890", 1000, 1000)] + fn test_window_config_creation(#[case] title: &str, #[case] width: u32, #[case] height: u32) { + let window_config = WindowConfig::new(title, width, height).unwrap(); - assert_eq!(window_config.title, title); - assert_eq!(window_config.width, width); - assert_eq!(window_config.height, height); + assert_eq!(window_config.title.value(), title); + assert_eq!(window_config.width.value(), width); + assert_eq!(window_config.height.value(), height); } #[test] fn test_default_window_size() { - let window_config = WindowConfig::create_with_default_size("Default Size Window"); + let window_config = WindowConfig::create_with_default_size("Default Size Window").unwrap(); let expected_size = WindowConfig::calculate_window_size(); - assert_eq!((window_config.width, window_config.height), expected_size); + assert_eq!( + (window_config.width.value(), window_config.height.value()), + expected_size + ); } } diff --git a/src/lib.rs b/src/lib.rs index 7586f02..08ec666 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ mod sprite; mod text; mod text_resources; mod utils; +mod validator; mod window; pub fn run() { diff --git a/src/utils.rs b/src/utils.rs index c592070..5f35aa9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -13,17 +13,19 @@ pub fn unit_size() -> Vec2 { #[cfg(test)] mod tests { use super::*; + use rstest::rstest; - #[test] - fn test_vec2_from_tuple() { - let tuple = (100, 50); - let result = vec2_from_tuple(tuple); - assert_eq!(result, Vec2::new(100.0, 50.0)); + #[rstest] + #[case::tuple((100, 50), Vec2::new(100.0, 50.0))] + #[case::tuple((200, 100), Vec2::new(200.0, 100.0))] + fn test_vec2_from_tuple(#[case] tuple: (u32, u32), #[case] expected: Vec2) { + let actual = vec2_from_tuple(tuple); + assert_eq!(actual, expected); } #[test] fn test_unit_size() { - let result = unit_size(); - assert_eq!(result, vec2_from_tuple(constants::UNIT_SIZE)); + let actual = unit_size(); + assert_eq!(actual, vec2_from_tuple(constants::UNIT_SIZE)); } } diff --git a/src/validator.rs b/src/validator.rs new file mode 100644 index 0000000..3d43a62 --- /dev/null +++ b/src/validator.rs @@ -0,0 +1,51 @@ +pub fn validate_string_length(value: &str, min: usize, max: usize) -> Result<(), String> { + if (min..=max).contains(&value.len()) { + Ok(()) + } else { + Err(format!( + "Value must be between {} and {} characters long: {}", + min, + max, + value.len() + )) + } +} + +pub fn validate_numeric_range(value: u32, min: u32, max: u32) -> Result<(), String> { + if (min..=max).contains(&value) { + Ok(()) + } else { + Err(format!( + "Value must be between {} and {}: {}", + min, max, value + )) + } +} + +#[cfg(test)] +mod test { + use super::*; + use rstest::rstest; + + #[rstest] + #[case::min_1("T", 1, 10)] + #[case::max_50("12345678901234567890123456789012345678901234567890", 1, 50)] + #[should_panic] + #[case::min_1_empty("", 1, 10)] + #[should_panic] + #[case::max_50_over("123456789012345678901234567890123456789012345678901", 1, 50)] + fn test_validate_string_length(#[case] value: &str, #[case] min: usize, #[case] max: usize) { + assert!(validate_string_length(value, min, max).is_ok()); + } + + #[rstest] + #[case::min_1(1, 1, 10)] + #[case::max_50(50, 1, 50)] + #[should_panic] + #[case::min_0(0, 1, 10)] + #[should_panic] + #[case::max_51(51, 1, 50)] + fn test_validate_numeric_range(#[case] value: u32, #[case] min: u32, #[case] max: u32) { + assert!(validate_numeric_range(value, min, max).is_ok()); + } +} diff --git a/src/window.rs b/src/window.rs index 373bae7..fb949e4 100644 --- a/src/window.rs +++ b/src/window.rs @@ -2,21 +2,21 @@ use bevy::prelude::*; use crate::{config, utils::unit_size}; -fn create_window(title: String, width: u32, height: u32) -> Window { +fn create_window(title: &str, width: u32, height: u32) -> Window { Window { - title, + title: title.to_string(), resolution: (width as f32, height as f32).into(), ..default() } } pub fn init_window() -> Window { - let window_config = config::create_window_config(); + let window_config = config::create_window_config().unwrap(); create_window( - window_config.title, - window_config.width, - window_config.height, + window_config.title.value(), + window_config.width.value(), + window_config.height.value(), ) }