diff --git a/src/dpi.rs b/src/dpi.rs index 027f5d44e6..912056e6f2 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -581,3 +581,421 @@ impl From> for Position { Position::Logical(position.cast()) } } + +#[cfg(test)] +mod tests { + use crate::dpi; + use std::collections::HashSet; + + macro_rules! test_pixel_int_impl { + ($($name:ident => $ty:ty),*) => {$( + #[test] + fn $name() { + use dpi::Pixel; + + assert_eq!( + <$ty as Pixel>::from_f64(37.0), + 37, + ); + assert_eq!( + <$ty as Pixel>::from_f64(37.4), + 37, + ); + assert_eq!( + <$ty as Pixel>::from_f64(37.5), + 38, + ); + assert_eq!( + <$ty as Pixel>::from_f64(37.9), + 38, + ); + + assert_eq!( + <$ty as Pixel>::cast::(37), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37), + 37, + ); + } + )*}; + } + + test_pixel_int_impl! { + test_pixel_int_u8 => u8, + test_pixel_int_u16 => u16, + test_pixel_int_u32 => u32, + test_pixel_int_i8 => i8, + test_pixel_int_i16 => i16 + } + + macro_rules! assert_approx_eq { + ($a:expr, $b:expr $(,)?) => { + assert!( + ($a - $b).abs() < 0.001, + "{} is not approximately equal to {}", + $a, + $b + ); + }; + } + + macro_rules! test_pixel_float_impl { + ($($name:ident => $ty:ty),*) => {$( + #[test] + fn $name() { + use dpi::Pixel; + + assert_approx_eq!( + <$ty as Pixel>::from_f64(37.0), + 37.0, + ); + assert_approx_eq!( + <$ty as Pixel>::from_f64(37.4), + 37.4, + ); + assert_approx_eq!( + <$ty as Pixel>::from_f64(37.5), + 37.5, + ); + assert_approx_eq!( + <$ty as Pixel>::from_f64(37.9), + 37.9, + ); + + assert_eq!( + <$ty as Pixel>::cast::(37.0), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37.4), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37.5), + 38, + ); + + assert_eq!( + <$ty as Pixel>::cast::(37.0), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37.4), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37.5), + 38, + ); + + assert_eq!( + <$ty as Pixel>::cast::(37.0), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37.4), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37.5), + 38, + ); + + assert_eq!( + <$ty as Pixel>::cast::(37.0), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37.4), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37.5), + 38, + ); + + assert_eq!( + <$ty as Pixel>::cast::(37.0), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37.4), + 37, + ); + assert_eq!( + <$ty as Pixel>::cast::(37.5), + 38, + ); + } + )*}; +} + + test_pixel_float_impl! { + test_pixel_float_f32 => f32, + test_pixel_float_f64 => f64 + } + + #[test] + fn test_validate_scale_factor() { + assert!(dpi::validate_scale_factor(1.0)); + assert!(dpi::validate_scale_factor(2.0)); + assert!(dpi::validate_scale_factor(3.0)); + assert!(dpi::validate_scale_factor(1.5)); + assert!(dpi::validate_scale_factor(0.5)); + + assert!(!dpi::validate_scale_factor(0.0)); + assert!(!dpi::validate_scale_factor(-1.0)); + assert!(!dpi::validate_scale_factor(f64::INFINITY)); + assert!(!dpi::validate_scale_factor(f64::NAN)); + assert!(!dpi::validate_scale_factor(f64::NEG_INFINITY)); + } + + #[test] + fn test_logical_position() { + let log_pos = dpi::LogicalPosition::new(1.0, 2.0); + assert_eq!( + log_pos.to_physical::(1.0), + dpi::PhysicalPosition::new(1, 2) + ); + assert_eq!( + log_pos.to_physical::(2.0), + dpi::PhysicalPosition::new(2, 4) + ); + assert_eq!(log_pos.cast::(), dpi::LogicalPosition::new(1, 2)); + assert_eq!( + log_pos, + dpi::LogicalPosition::from_physical(dpi::PhysicalPosition::new(1.0, 2.0), 1.0) + ); + assert_eq!( + log_pos, + dpi::LogicalPosition::from_physical(dpi::PhysicalPosition::new(2.0, 4.0), 2.0) + ); + assert_eq!( + dpi::LogicalPosition::from((2.0, 2.0)), + dpi::LogicalPosition::new(2.0, 2.0) + ); + assert_eq!( + dpi::LogicalPosition::from([2.0, 3.0]), + dpi::LogicalPosition::new(2.0, 3.0) + ); + + let x: (f64, f64) = log_pos.into(); + assert_eq!(x, (1.0, 2.0)); + let x: [f64; 2] = log_pos.into(); + assert_eq!(x, [1.0, 2.0]); + } + + #[test] + fn test_physical_position() { + assert_eq!( + dpi::PhysicalPosition::from_logical(dpi::LogicalPosition::new(1.0, 2.0), 1.0), + dpi::PhysicalPosition::new(1, 2) + ); + assert_eq!( + dpi::PhysicalPosition::from_logical(dpi::LogicalPosition::new(2.0, 4.0), 0.5), + dpi::PhysicalPosition::new(1, 2) + ); + assert_eq!( + dpi::PhysicalPosition::from((2.0, 2.0)), + dpi::PhysicalPosition::new(2.0, 2.0) + ); + assert_eq!( + dpi::PhysicalPosition::from([2.0, 3.0]), + dpi::PhysicalPosition::new(2.0, 3.0) + ); + + let x: (f64, f64) = dpi::PhysicalPosition::new(1, 2).into(); + assert_eq!(x, (1.0, 2.0)); + let x: [f64; 2] = dpi::PhysicalPosition::new(1, 2).into(); + assert_eq!(x, [1.0, 2.0]); + } + + #[test] + fn test_logical_size() { + let log_size = dpi::LogicalSize::new(1.0, 2.0); + assert_eq!( + log_size.to_physical::(1.0), + dpi::PhysicalSize::new(1, 2) + ); + assert_eq!( + log_size.to_physical::(2.0), + dpi::PhysicalSize::new(2, 4) + ); + assert_eq!(log_size.cast::(), dpi::LogicalSize::new(1, 2)); + assert_eq!( + log_size, + dpi::LogicalSize::from_physical(dpi::PhysicalSize::new(1.0, 2.0), 1.0) + ); + assert_eq!( + log_size, + dpi::LogicalSize::from_physical(dpi::PhysicalSize::new(2.0, 4.0), 2.0) + ); + assert_eq!( + dpi::LogicalSize::from((2.0, 2.0)), + dpi::LogicalSize::new(2.0, 2.0) + ); + assert_eq!( + dpi::LogicalSize::from([2.0, 3.0]), + dpi::LogicalSize::new(2.0, 3.0) + ); + + let x: (f64, f64) = log_size.into(); + assert_eq!(x, (1.0, 2.0)); + let x: [f64; 2] = log_size.into(); + assert_eq!(x, [1.0, 2.0]); + } + + #[test] + fn test_physical_size() { + assert_eq!( + dpi::PhysicalSize::from_logical(dpi::LogicalSize::new(1.0, 2.0), 1.0), + dpi::PhysicalSize::new(1, 2) + ); + assert_eq!( + dpi::PhysicalSize::from_logical(dpi::LogicalSize::new(2.0, 4.0), 0.5), + dpi::PhysicalSize::new(1, 2) + ); + assert_eq!( + dpi::PhysicalSize::from((2.0, 2.0)), + dpi::PhysicalSize::new(2.0, 2.0) + ); + assert_eq!( + dpi::PhysicalSize::from([2.0, 3.0]), + dpi::PhysicalSize::new(2.0, 3.0) + ); + + let x: (f64, f64) = dpi::PhysicalSize::new(1, 2).into(); + assert_eq!(x, (1.0, 2.0)); + let x: [f64; 2] = dpi::PhysicalSize::new(1, 2).into(); + assert_eq!(x, [1.0, 2.0]); + } + + #[test] + fn test_size() { + assert_eq!( + dpi::Size::new(dpi::PhysicalSize::new(1, 2)), + dpi::Size::Physical(dpi::PhysicalSize::new(1, 2)) + ); + assert_eq!( + dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)), + dpi::Size::Logical(dpi::LogicalSize::new(1.0, 2.0)) + ); + + assert_eq!( + dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_logical::(1.0), + dpi::LogicalSize::new(1.0, 2.0) + ); + assert_eq!( + dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_logical::(2.0), + dpi::LogicalSize::new(0.5, 1.0) + ); + assert_eq!( + dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_logical::(1.0), + dpi::LogicalSize::new(1.0, 2.0) + ); + + assert_eq!( + dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_physical::(1.0), + dpi::PhysicalSize::new(1, 2) + ); + assert_eq!( + dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_physical::(2.0), + dpi::PhysicalSize::new(1, 2) + ); + assert_eq!( + dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_physical::(1.0), + dpi::PhysicalSize::new(1, 2) + ); + assert_eq!( + dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_physical::(2.0), + dpi::PhysicalSize::new(2, 4) + ); + + let small = dpi::Size::Physical((1, 2).into()); + let medium = dpi::Size::Logical((3, 4).into()); + let medium_physical = dpi::Size::new(medium.to_physical::(1.0)); + let large = dpi::Size::Physical((5, 6).into()); + assert_eq!(dpi::Size::clamp(medium, small, large, 1.0), medium_physical); + assert_eq!(dpi::Size::clamp(small, medium, large, 1.0), medium_physical); + assert_eq!(dpi::Size::clamp(large, small, medium, 1.0), medium_physical); + } + + #[test] + fn test_position() { + assert_eq!( + dpi::Position::new(dpi::PhysicalPosition::new(1, 2)), + dpi::Position::Physical(dpi::PhysicalPosition::new(1, 2)) + ); + assert_eq!( + dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)), + dpi::Position::Logical(dpi::LogicalPosition::new(1.0, 2.0)) + ); + + assert_eq!( + dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_logical::(1.0), + dpi::LogicalPosition::new(1.0, 2.0) + ); + assert_eq!( + dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_logical::(2.0), + dpi::LogicalPosition::new(0.5, 1.0) + ); + assert_eq!( + dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_logical::(1.0), + dpi::LogicalPosition::new(1.0, 2.0) + ); + + assert_eq!( + dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_physical::(1.0), + dpi::PhysicalPosition::new(1, 2) + ); + assert_eq!( + dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_physical::(2.0), + dpi::PhysicalPosition::new(1, 2) + ); + assert_eq!( + dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_physical::(1.0), + dpi::PhysicalPosition::new(1, 2) + ); + assert_eq!( + dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_physical::(2.0), + dpi::PhysicalPosition::new(2, 4) + ); + } + + // Eat coverage for the Debug impls et al + #[test] + fn ensure_attrs_do_not_panic() { + let _ = format!("{:?}", dpi::LogicalPosition::::default().clone()); + HashSet::new().insert(dpi::LogicalPosition::::default()); + + let _ = format!("{:?}", dpi::PhysicalPosition::::default().clone()); + HashSet::new().insert(dpi::PhysicalPosition::::default()); + + let _ = format!("{:?}", dpi::LogicalSize::::default().clone()); + HashSet::new().insert(dpi::LogicalSize::::default()); + + let _ = format!("{:?}", dpi::PhysicalSize::::default().clone()); + HashSet::new().insert(dpi::PhysicalSize::::default()); + + let _ = format!("{:?}", dpi::Size::Physical((1, 2).into()).clone()); + let _ = format!("{:?}", dpi::Position::Physical((1, 2).into()).clone()); + } +} diff --git a/src/error.rs b/src/error.rs index 7324742d4b..42a3f43cf9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -109,3 +109,25 @@ impl error::Error for OsError {} impl error::Error for ExternalError {} impl error::Error for NotSupportedError {} impl error::Error for RunLoopError {} + +#[cfg(test)] +mod tests { + #![allow(clippy::redundant_clone)] + + use super::*; + + // Eat attributes for testing + #[test] + fn ensure_fmt_does_not_panic() { + let _ = format!( + "{:?}, {}", + NotSupportedError::new(), + NotSupportedError::new().clone() + ); + let _ = format!( + "{:?}, {}", + ExternalError::NotSupported(NotSupportedError::new()), + ExternalError::NotSupported(NotSupportedError::new()) + ); + } +} diff --git a/src/event.rs b/src/event.rs index e24b0a9c91..80d57544e0 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1102,3 +1102,206 @@ impl PartialEq for InnerSizeWriter { self.new_inner_size.as_ptr() == other.new_inner_size.as_ptr() } } + +#[cfg(test)] +mod tests { + use crate::event; + use std::collections::{BTreeSet, HashSet}; + + macro_rules! foreach_event { + ($closure:expr) => {{ + #[allow(unused_mut)] + let mut x = $closure; + let did = unsafe { event::DeviceId::dummy() }; + + #[allow(deprecated)] + { + use crate::event::{Event::*, Ime::Enabled, WindowEvent::*}; + use crate::window::WindowId; + + // Mainline events. + let wid = unsafe { WindowId::dummy() }; + x(UserEvent(())); + x(NewEvents(event::StartCause::Init)); + x(RedrawRequested(wid)); + x(AboutToWait); + x(LoopExiting); + x(Suspended); + x(Resumed); + + // Window events. + let with_window_event = |wev| { + x(WindowEvent { + window_id: wid, + event: wev, + }) + }; + + with_window_event(CloseRequested); + with_window_event(Destroyed); + with_window_event(Focused(true)); + with_window_event(Moved((0, 0).into())); + with_window_event(Resized((0, 0).into())); + with_window_event(DroppedFile("x.txt".into())); + with_window_event(HoveredFile("x.txt".into())); + with_window_event(HoveredFileCancelled); + with_window_event(Ime(Enabled)); + with_window_event(CursorMoved { + device_id: did, + position: (0, 0).into(), + }); + with_window_event(ModifiersChanged(event::Modifiers::default())); + with_window_event(CursorEntered { device_id: did }); + with_window_event(CursorLeft { device_id: did }); + with_window_event(MouseWheel { + device_id: did, + delta: event::MouseScrollDelta::LineDelta(0.0, 0.0), + phase: event::TouchPhase::Started, + }); + with_window_event(MouseInput { + device_id: did, + state: event::ElementState::Pressed, + button: event::MouseButton::Other(0), + }); + with_window_event(TouchpadMagnify { + device_id: did, + delta: 0.0, + phase: event::TouchPhase::Started, + }); + with_window_event(SmartMagnify { device_id: did }); + with_window_event(TouchpadRotate { + device_id: did, + delta: 0.0, + phase: event::TouchPhase::Started, + }); + with_window_event(TouchpadPressure { + device_id: did, + pressure: 0.0, + stage: 0, + }); + with_window_event(AxisMotion { + device_id: did, + axis: 0, + value: 0.0, + }); + with_window_event(Touch(event::Touch { + device_id: did, + phase: event::TouchPhase::Started, + location: (0.0, 0.0).into(), + id: 0, + force: Some(event::Force::Normalized(0.0)), + })); + with_window_event(ThemeChanged(crate::window::Theme::Light)); + with_window_event(Occluded(true)); + } + + #[allow(deprecated)] + { + use event::DeviceEvent::*; + + let with_device_event = |dev_ev| { + x(event::Event::DeviceEvent { + device_id: did, + event: dev_ev, + }) + }; + + with_device_event(Added); + with_device_event(Removed); + with_device_event(MouseMotion { + delta: (0.0, 0.0).into(), + }); + with_device_event(MouseWheel { + delta: event::MouseScrollDelta::LineDelta(0.0, 0.0), + }); + with_device_event(Motion { + axis: 0, + value: 0.0, + }); + with_device_event(Button { + button: 0, + state: event::ElementState::Pressed, + }); + with_device_event(Text { codepoint: 'a' }); + } + }}; + } + + #[allow(clippy::redundant_clone)] + #[test] + fn test_event_clone() { + foreach_event!(|event: event::Event<()>| { + let event2 = event.clone(); + assert_eq!(event, event2); + }) + } + + #[test] + fn test_map_nonuser_event() { + foreach_event!(|event: event::Event<()>| { + let is_user = matches!(event, event::Event::UserEvent(())); + let event2 = event.map_nonuser_event::<()>(); + if is_user { + assert_eq!(event2, Err(event::Event::UserEvent(()))); + } else { + assert!(event2.is_ok()); + } + }) + } + + #[test] + fn test_force_normalize() { + let force = event::Force::Normalized(0.0); + assert_eq!(force.normalized(), 0.0); + + let force2 = event::Force::Calibrated { + force: 5.0, + max_possible_force: 2.5, + altitude_angle: None, + }; + assert_eq!(force2.normalized(), 2.0); + + let force3 = event::Force::Calibrated { + force: 5.0, + max_possible_force: 2.5, + altitude_angle: Some(std::f64::consts::PI / 2.0), + }; + assert_eq!(force3.normalized(), 2.0); + } + + #[allow(clippy::clone_on_copy)] + #[test] + fn ensure_attrs_do_not_panic() { + foreach_event!(|event: event::Event<()>| { + let _ = format!("{:?}", event); + }); + let _ = event::StartCause::Init.clone(); + + let did = unsafe { crate::event::DeviceId::dummy() }.clone(); + HashSet::new().insert(did); + let mut set = [did, did, did]; + set.sort_unstable(); + let mut set2 = BTreeSet::new(); + set2.insert(did); + set2.insert(did); + + HashSet::new().insert(event::TouchPhase::Started.clone()); + HashSet::new().insert(event::MouseButton::Left.clone()); + HashSet::new().insert(event::Ime::Enabled); + + let _ = event::Touch { + device_id: did, + phase: event::TouchPhase::Started, + location: (0.0, 0.0).into(), + id: 0, + force: Some(event::Force::Normalized(0.0)), + } + .clone(); + let _ = event::Force::Calibrated { + force: 0.0, + max_possible_force: 0.0, + altitude_angle: None, + } + .clone(); + } +}