diff --git a/crates/foundation/apsis-units/src/angle.rs b/crates/foundation/apsis-units/src/angle.rs index 6a53863..a9f264b 100644 --- a/crates/foundation/apsis-units/src/angle.rs +++ b/crates/foundation/apsis-units/src/angle.rs @@ -129,65 +129,3 @@ impl fmt::Display for Degrees { impl_quantity_ops!(Radians); impl_quantity_ops!(Degrees); - -#[cfg(test)] -mod tests { - use super::{Degrees, Radians}; - use crate::dimension::Angle; - use apsis_core::{Quantity, Unit}; - use core::f64::consts::PI; - - #[test] - fn direct_conversions_are_consistent() { - assert!((Degrees::new(180.0).to_radians().value() - PI).abs() < 1e-12); - assert!((Radians::new(PI).to_degrees().value() - 180.0).abs() < 1e-12); - assert!((Degrees::new(90.0).to_radians().value() - PI / 2.0).abs() < 1e-12); - } - - #[test] - fn generic_conversion_matches_direct_conversion() { - let angle = Degrees::new(45.0); - let via_trait: Radians = angle.to_unit(); - assert!((via_trait.value() - angle.to_radians().value()).abs() < 1e-12); - } - - #[test] - fn conversion_round_trips() { - let original = Degrees::new(137.5); - let round_tripped: Degrees = original.to_unit::().to_unit(); - assert!((round_tripped.value() - original.value()).abs() < 1e-12); - } - - #[test] - fn wrap_places_angles_in_the_canonical_range() { - for turns in -3..=3 { - let angle = Radians::new(PI + f64::from(turns) * 2.0 * PI); - let wrapped = angle.wrap(); - assert!(wrapped.value() >= -PI); - assert!(wrapped.value() < PI); - // Wrapping shifts by whole turns, so the angle is preserved - // modulo two pi. - let difference = angle.value() - wrapped.value(); - assert!((difference / (2.0 * PI)).fract().abs() < 1e-12); - } - assert!((Radians::new(0.0).wrap().value()).abs() < 1e-12); - assert!((Radians::new(PI).wrap().value() + PI).abs() < 1e-12); - } - - #[test] - fn unit_metadata_is_available() { - assert_eq!(Radians::symbol(), "rad"); - assert_eq!(Degrees::symbol(), "deg"); - assert_eq!(Radians::factor_to_base(), 1.0); - assert!((Degrees::factor_to_base() - PI / 180.0).abs() < 1e-15); - fn assert_angle>() {} - assert_angle::(); - assert_angle::(); - } - - #[test] - fn display_includes_the_symbol() { - assert_eq!(Radians::new(1.0).to_string(), "1 rad"); - assert_eq!(Degrees::new(90.0).to_string(), "90 deg"); - } -} diff --git a/crates/foundation/apsis-units/src/frequency.rs b/crates/foundation/apsis-units/src/frequency.rs index 6b1090e..f914e14 100644 --- a/crates/foundation/apsis-units/src/frequency.rs +++ b/crates/foundation/apsis-units/src/frequency.rs @@ -63,34 +63,3 @@ impl fmt::Display for Hertz { } impl_quantity_ops!(Hertz); - -#[cfg(test)] -mod tests { - use super::Hertz; - use crate::dimension::Frequency; - use apsis_core::Unit; - - #[test] - fn stores_the_given_value() { - assert_eq!(Hertz::new(1_575.42).value(), 1_575.42); - } - - #[test] - fn period_is_the_reciprocal() { - assert!((Hertz::new(2.0).period() - 0.5).abs() < 1e-12); - assert!(Hertz::new(0.0).period().is_infinite()); - } - - #[test] - fn unit_metadata_is_available() { - assert_eq!(Hertz::symbol(), "Hz"); - assert_eq!(Hertz::factor_to_base(), 1.0); - fn assert_frequency>() {} - assert_frequency::(); - } - - #[test] - fn display_includes_the_symbol() { - assert_eq!(Hertz::new(1_000.0).to_string(), "1000 Hz"); - } -} diff --git a/crates/foundation/apsis-units/src/length.rs b/crates/foundation/apsis-units/src/length.rs index bb47c19..32d9395 100644 --- a/crates/foundation/apsis-units/src/length.rs +++ b/crates/foundation/apsis-units/src/length.rs @@ -113,72 +113,3 @@ impl fmt::Display for Kilometers { impl_quantity_ops!(Meters); impl_quantity_ops!(Kilometers); - -#[cfg(test)] -mod tests { - use super::{Kilometers, Meters}; - use crate::dimension::Length; - use apsis_core::{Quantity, Unit}; - - #[test] - fn stores_the_given_value() { - assert_eq!(Meters::new(12.5).value(), 12.5); - assert_eq!(Kilometers::new(-3.0).value(), -3.0); - } - - #[test] - fn direct_conversions_are_consistent() { - assert_eq!(Meters::new(2_500.0).to_kilometers().value(), 2.5); - assert_eq!(Kilometers::new(2.5).to_meters().value(), 2_500.0); - } - - #[test] - fn generic_conversion_matches_direct_conversion() { - let distance = Meters::new(2_500.0); - let via_trait: Kilometers = distance.to_unit(); - assert_eq!(via_trait, distance.to_kilometers()); - } - - #[test] - fn conversion_round_trips() { - let original = Kilometers::new(42.195); - let round_tripped: Kilometers = original.to_unit::().to_unit(); - assert!((round_tripped.value() - original.value()).abs() < 1e-12); - } - - #[test] - fn units_report_their_metadata() { - assert_eq!(Meters::symbol(), "m"); - assert_eq!(Kilometers::symbol(), "km"); - assert_eq!(Meters::factor_to_base(), 1.0); - assert_eq!(Kilometers::factor_to_base(), 1_000.0); - // Both units measure the same dimension, which is what makes the - // generic conversion above type check. - fn assert_length>() {} - assert_length::(); - assert_length::(); - } - - #[test] - fn arithmetic_operates_on_the_stored_value() { - let sum = Meters::new(1.5) + Meters::new(2.5); - assert_eq!(sum.value(), 4.0); - let difference = Meters::new(4.0) - Meters::new(1.0); - assert_eq!(difference.value(), 3.0); - let scaled = Meters::new(3.0) * 2.0; - assert_eq!(scaled.value(), 6.0); - let straddled = 2.0 * Meters::new(3.0); - assert_eq!(straddled.value(), 6.0); - let halved = Meters::new(3.0) / 2.0; - assert_eq!(halved.value(), 1.5); - let ratio = Meters::new(3.0) / Meters::new(1.5); - assert_eq!(ratio, 2.0); - assert_eq!((-Meters::new(1.0)).value(), -1.0); - } - - #[test] - fn display_includes_the_symbol() { - assert_eq!(Meters::new(1.0).to_string(), "1 m"); - assert_eq!(Kilometers::new(2.0).to_string(), "2 km"); - } -} diff --git a/crates/foundation/apsis-units/src/temperature.rs b/crates/foundation/apsis-units/src/temperature.rs index d45d6a6..781b514 100644 --- a/crates/foundation/apsis-units/src/temperature.rs +++ b/crates/foundation/apsis-units/src/temperature.rs @@ -77,46 +77,3 @@ impl fmt::Display for Kelvin { } impl_quantity_ops!(Kelvin); - -#[cfg(test)] -mod tests { - use super::Kelvin; - use crate::dimension::Temperature; - use apsis_core::Unit; - use apsis_core::constant::ZERO_CELSIUS_IN_KELVIN; - - #[test] - fn stores_the_given_value() { - assert_eq!(Kelvin::new(290.0).value(), 290.0); - } - - #[test] - fn celsius_conversion_uses_the_exact_offset() { - let freezing = Kelvin::from_celsius(0.0); - assert_eq!(freezing.value(), ZERO_CELSIUS_IN_KELVIN); - assert_eq!(freezing.to_celsius(), 0.0); - let boiling = Kelvin::from_celsius(100.0); - assert!((boiling.to_celsius() - 100.0).abs() < 1e-12); - } - - #[test] - fn celsius_conversion_round_trips() { - let original = Kelvin::new(273.15 + 21.5); - assert!( - (Kelvin::from_celsius(original.to_celsius()).value() - original.value()).abs() < 1e-12 - ); - } - - #[test] - fn unit_metadata_is_available() { - assert_eq!(Kelvin::symbol(), "K"); - assert_eq!(Kelvin::factor_to_base(), 1.0); - fn assert_temperature>() {} - assert_temperature::(); - } - - #[test] - fn display_includes_the_symbol() { - assert_eq!(Kelvin::new(300.0).to_string(), "300 K"); - } -} diff --git a/crates/foundation/apsis-units/src/time.rs b/crates/foundation/apsis-units/src/time.rs index 19e93c3..1aa93b8 100644 --- a/crates/foundation/apsis-units/src/time.rs +++ b/crates/foundation/apsis-units/src/time.rs @@ -55,37 +55,3 @@ impl fmt::Display for Seconds { } impl_quantity_ops!(Seconds); - -#[cfg(test)] -mod tests { - use super::Seconds; - use crate::dimension::Time; - use apsis_core::Unit; - - #[test] - fn stores_the_given_value() { - assert_eq!(Seconds::new(0.5).value(), 0.5); - } - - #[test] - fn unit_metadata_is_available() { - assert_eq!(Seconds::symbol(), "s"); - assert_eq!(Seconds::factor_to_base(), 1.0); - fn assert_time>() {} - assert_time::(); - } - - #[test] - fn arithmetic_operates_on_the_stored_value() { - assert_eq!((Seconds::new(1.0) + Seconds::new(2.0)).value(), 3.0); - assert_eq!((Seconds::new(3.0) - Seconds::new(2.0)).value(), 1.0); - assert_eq!((Seconds::new(3.0) * 2.0).value(), 6.0); - assert_eq!((Seconds::new(3.0) / 3.0).value(), 1.0); - assert_eq!(Seconds::new(6.0) / Seconds::new(3.0), 2.0); - } - - #[test] - fn display_includes_the_symbol() { - assert_eq!(Seconds::new(1.5).to_string(), "1.5 s"); - } -} diff --git a/crates/foundation/apsis-units/tests/angle.rs b/crates/foundation/apsis-units/tests/angle.rs new file mode 100644 index 0000000..8898255 --- /dev/null +++ b/crates/foundation/apsis-units/tests/angle.rs @@ -0,0 +1,60 @@ +//! Tests for plane angle quantities. + +use apsis_core::{Quantity, Unit}; +use apsis_units::dimension::Angle; +use apsis_units::{Degrees, Radians}; +use core::f64::consts::PI; + +#[test] +fn direct_conversions_are_consistent() { + assert!((Degrees::new(180.0).to_radians().value() - PI).abs() < 1e-12); + assert!((Radians::new(PI).to_degrees().value() - 180.0).abs() < 1e-12); + assert!((Degrees::new(90.0).to_radians().value() - PI / 2.0).abs() < 1e-12); +} + +#[test] +fn generic_conversion_matches_direct_conversion() { + let angle = Degrees::new(45.0); + let via_trait: Radians = angle.to_unit(); + assert!((via_trait.value() - angle.to_radians().value()).abs() < 1e-12); +} + +#[test] +fn conversion_round_trips() { + let original = Degrees::new(137.5); + let round_tripped: Degrees = original.to_unit::().to_unit(); + assert!((round_tripped.value() - original.value()).abs() < 1e-12); +} + +#[test] +fn wrap_places_angles_in_the_canonical_range() { + for turns in -3..=3 { + let angle = Radians::new(PI + f64::from(turns) * 2.0 * PI); + let wrapped = angle.wrap(); + assert!(wrapped.value() >= -PI); + assert!(wrapped.value() < PI); + // Wrapping shifts by whole turns, so the angle is preserved + // modulo two pi. + let difference = angle.value() - wrapped.value(); + assert!((difference / (2.0 * PI)).fract().abs() < 1e-12); + } + assert!((Radians::new(0.0).wrap().value()).abs() < 1e-12); + assert!((Radians::new(PI).wrap().value() + PI).abs() < 1e-12); +} + +#[test] +fn unit_metadata_is_available() { + assert_eq!(Radians::symbol(), "rad"); + assert_eq!(Degrees::symbol(), "deg"); + assert_eq!(Radians::factor_to_base(), 1.0); + assert!((Degrees::factor_to_base() - PI / 180.0).abs() < 1e-15); + fn assert_angle>() {} + assert_angle::(); + assert_angle::(); +} + +#[test] +fn display_includes_the_symbol() { + assert_eq!(Radians::new(1.0).to_string(), "1 rad"); + assert_eq!(Degrees::new(90.0).to_string(), "90 deg"); +} diff --git a/crates/foundation/apsis-units/tests/frequency.rs b/crates/foundation/apsis-units/tests/frequency.rs new file mode 100644 index 0000000..4094bf4 --- /dev/null +++ b/crates/foundation/apsis-units/tests/frequency.rs @@ -0,0 +1,29 @@ +//! Tests for frequency quantities. + +use apsis_core::Unit; +use apsis_units::Hertz; +use apsis_units::dimension::Frequency; + +#[test] +fn stores_the_given_value() { + assert_eq!(Hertz::new(1_575.42).value(), 1_575.42); +} + +#[test] +fn period_is_the_reciprocal() { + assert!((Hertz::new(2.0).period() - 0.5).abs() < 1e-12); + assert!(Hertz::new(0.0).period().is_infinite()); +} + +#[test] +fn unit_metadata_is_available() { + assert_eq!(Hertz::symbol(), "Hz"); + assert_eq!(Hertz::factor_to_base(), 1.0); + fn assert_frequency>() {} + assert_frequency::(); +} + +#[test] +fn display_includes_the_symbol() { + assert_eq!(Hertz::new(1_000.0).to_string(), "1000 Hz"); +} diff --git a/crates/foundation/apsis-units/tests/length.rs b/crates/foundation/apsis-units/tests/length.rs new file mode 100644 index 0000000..5e9c2c6 --- /dev/null +++ b/crates/foundation/apsis-units/tests/length.rs @@ -0,0 +1,67 @@ +//! Tests for length quantities. + +use apsis_core::{Quantity, Unit}; +use apsis_units::dimension::Length; +use apsis_units::{Kilometers, Meters}; + +#[test] +fn stores_the_given_value() { + assert_eq!(Meters::new(12.5).value(), 12.5); + assert_eq!(Kilometers::new(-3.0).value(), -3.0); +} + +#[test] +fn direct_conversions_are_consistent() { + assert_eq!(Meters::new(2_500.0).to_kilometers().value(), 2.5); + assert_eq!(Kilometers::new(2.5).to_meters().value(), 2_500.0); +} + +#[test] +fn generic_conversion_matches_direct_conversion() { + let distance = Meters::new(2_500.0); + let via_trait: Kilometers = distance.to_unit(); + assert_eq!(via_trait, distance.to_kilometers()); +} + +#[test] +fn conversion_round_trips() { + let original = Kilometers::new(42.195); + let round_tripped: Kilometers = original.to_unit::().to_unit(); + assert!((round_tripped.value() - original.value()).abs() < 1e-12); +} + +#[test] +fn units_report_their_metadata() { + assert_eq!(Meters::symbol(), "m"); + assert_eq!(Kilometers::symbol(), "km"); + assert_eq!(Meters::factor_to_base(), 1.0); + assert_eq!(Kilometers::factor_to_base(), 1_000.0); + // Both units measure the same dimension, which is what makes the + // generic conversion above type check. + fn assert_length>() {} + assert_length::(); + assert_length::(); +} + +#[test] +fn arithmetic_operates_on_the_stored_value() { + let sum = Meters::new(1.5) + Meters::new(2.5); + assert_eq!(sum.value(), 4.0); + let difference = Meters::new(4.0) - Meters::new(1.0); + assert_eq!(difference.value(), 3.0); + let scaled = Meters::new(3.0) * 2.0; + assert_eq!(scaled.value(), 6.0); + let straddled = 2.0 * Meters::new(3.0); + assert_eq!(straddled.value(), 6.0); + let halved = Meters::new(3.0) / 2.0; + assert_eq!(halved.value(), 1.5); + let ratio = Meters::new(3.0) / Meters::new(1.5); + assert_eq!(ratio, 2.0); + assert_eq!((-Meters::new(1.0)).value(), -1.0); +} + +#[test] +fn display_includes_the_symbol() { + assert_eq!(Meters::new(1.0).to_string(), "1 m"); + assert_eq!(Kilometers::new(2.0).to_string(), "2 km"); +} diff --git a/crates/foundation/apsis-units/tests/temperature.rs b/crates/foundation/apsis-units/tests/temperature.rs new file mode 100644 index 0000000..abcdbe6 --- /dev/null +++ b/crates/foundation/apsis-units/tests/temperature.rs @@ -0,0 +1,39 @@ +//! Tests for thermodynamic temperature quantities. + +use apsis_core::Unit; +use apsis_core::constant::ZERO_CELSIUS_IN_KELVIN; +use apsis_units::Kelvin; +use apsis_units::dimension::Temperature; + +#[test] +fn stores_the_given_value() { + assert_eq!(Kelvin::new(290.0).value(), 290.0); +} + +#[test] +fn celsius_conversion_uses_the_exact_offset() { + let freezing = Kelvin::from_celsius(0.0); + assert_eq!(freezing.value(), ZERO_CELSIUS_IN_KELVIN); + assert_eq!(freezing.to_celsius(), 0.0); + let boiling = Kelvin::from_celsius(100.0); + assert!((boiling.to_celsius() - 100.0).abs() < 1e-12); +} + +#[test] +fn celsius_conversion_round_trips() { + let original = Kelvin::new(273.15 + 21.5); + assert!((Kelvin::from_celsius(original.to_celsius()).value() - original.value()).abs() < 1e-12); +} + +#[test] +fn unit_metadata_is_available() { + assert_eq!(Kelvin::symbol(), "K"); + assert_eq!(Kelvin::factor_to_base(), 1.0); + fn assert_temperature>() {} + assert_temperature::(); +} + +#[test] +fn display_includes_the_symbol() { + assert_eq!(Kelvin::new(300.0).to_string(), "300 K"); +} diff --git a/crates/foundation/apsis-units/tests/time.rs b/crates/foundation/apsis-units/tests/time.rs new file mode 100644 index 0000000..12b6d2b --- /dev/null +++ b/crates/foundation/apsis-units/tests/time.rs @@ -0,0 +1,32 @@ +//! Tests for the duration quantity. + +use apsis_core::Unit; +use apsis_units::Seconds; +use apsis_units::dimension::Time; + +#[test] +fn stores_the_given_value() { + assert_eq!(Seconds::new(0.5).value(), 0.5); +} + +#[test] +fn unit_metadata_is_available() { + assert_eq!(Seconds::symbol(), "s"); + assert_eq!(Seconds::factor_to_base(), 1.0); + fn assert_time>() {} + assert_time::(); +} + +#[test] +fn arithmetic_operates_on_the_stored_value() { + assert_eq!((Seconds::new(1.0) + Seconds::new(2.0)).value(), 3.0); + assert_eq!((Seconds::new(3.0) - Seconds::new(2.0)).value(), 1.0); + assert_eq!((Seconds::new(3.0) * 2.0).value(), 6.0); + assert_eq!((Seconds::new(3.0) / 3.0).value(), 1.0); + assert_eq!(Seconds::new(6.0) / Seconds::new(3.0), 2.0); +} + +#[test] +fn display_includes_the_symbol() { + assert_eq!(Seconds::new(1.5).to_string(), "1.5 s"); +}