use crate::common::approx_eq;
use unitforge::{
Distance, DistanceUnit, Force, ForceDistanceUnit, ForceUnit, Mass, MassUnit, Quantity,
PhysicsQuantity, StiffnessUnit, Unit,
};
#[test]
fn add() {
let a = Quantity::MassQuantity(Mass::new(12.0, MassUnit::kg));
let b = Quantity::MassQuantity(Mass::new(10.0, MassUnit::kg));
let c = (a + b).unwrap();
assert!(approx_eq(
c.to(Unit::MassUnit(MassUnit::kg)).unwrap(),
22.0,
1e-10
));
}
#[test]
fn add_fail() {
let a = Quantity::MassQuantity(Mass::new(12.0, MassUnit::kg));
let b = Quantity::FloatQuantity(3.0);
assert!((a + b).is_err());
}
#[test]
fn sub() {
let a = Quantity::MassQuantity(Mass::new(12.0, MassUnit::kg));
let b = Quantity::MassQuantity(Mass::new(10.0, MassUnit::kg));
let c = (a - b).unwrap();
assert!(approx_eq(
c.to(Unit::MassUnit(MassUnit::kg)).unwrap(),
2.0,
1e-10
));
}
#[test]
fn sub_fail() {
let a = Quantity::MassQuantity(Mass::new(12.0, MassUnit::kg));
let b = Quantity::FloatQuantity(3.0);
assert!((a - b).is_err());
}
#[test]
fn mul() {
let a = Quantity::ForceQuantity(Force::new(3.0, ForceUnit::N));
let b = Quantity::DistanceQuantity(Distance::new(4.0, DistanceUnit::m));
let c = (a * b).unwrap();
assert!(approx_eq(
c.to(Unit::ForceDistanceUnit(ForceDistanceUnit::Nm))
.unwrap(),
12.0,
1e-10
));
}
#[test]
fn mul_with_float_quantity_is_commutative() {
let scalar = Quantity::FloatQuantity(3.0);
let distance = Quantity::DistanceQuantity(Distance::new(4.0, DistanceUnit::m));
let left = (scalar * distance).unwrap();
let right = (distance * scalar).unwrap();
assert!(approx_eq(
left.to(Unit::DistanceUnit(DistanceUnit::m)).unwrap(),
12.0,
1e-10
));
assert_eq!(left, right);
}
#[test]
fn div() {
let a = Quantity::ForceQuantity(Force::new(3.0, ForceUnit::N));
let b = Quantity::DistanceQuantity(Distance::new(4.0, DistanceUnit::m));
let c = (a / b).unwrap();
assert!(approx_eq(
c.to(Unit::StiffnessUnit(StiffnessUnit::N_m)).unwrap(),
3.0 / 4.0,
1e-10
));
}
#[test]
fn div_fail_for_unsupported_runtime_pair() {
let mass = Quantity::MassQuantity(Mass::new(3.0, MassUnit::kg));
let force = Quantity::ForceQuantity(Force::new(4.0, ForceUnit::N));
assert!((mass / force).is_err());
}
#[test]
fn abs() {
let value = Quantity::DistanceQuantity(Distance::new(-4.0, DistanceUnit::m));
assert!(approx_eq(
value.abs().to(Unit::DistanceUnit(DistanceUnit::m)).unwrap(),
4.0,
1e-10
));
}
#[test]
fn unit_name() {
let unit = Unit::DistanceUnit(DistanceUnit::m);
assert_eq!(unit.get_name(), "m");
}
#[test]
fn unit_to_quantity_wraps_runtime_value() {
let quantity = Unit::DistanceUnit(DistanceUnit::m).to_quantity(2.5);
assert!(approx_eq(
quantity.to(Unit::DistanceUnit(DistanceUnit::m)).unwrap(),
2.5,
1e-10
));
}
#[test]
fn float_quantity_round_trips_through_no_unit() {
let quantity = Unit::NoUnit.to_quantity(2.5);
assert_eq!(quantity, Quantity::FloatQuantity(2.5));
assert_eq!(quantity.to(Unit::NoUnit).unwrap(), 2.5);
}
#[test]
fn extract_float_only_works_for_float_quantity() {
assert_eq!(Quantity::FloatQuantity(2.5).extract_float().unwrap(), 2.5);
assert!(Quantity::DistanceQuantity(Distance::new(1.0, DistanceUnit::m))
.extract_float()
.is_err());
}
#[test]
fn to_with_mismatched_unit_returns_error() {
let quantity = Quantity::MassQuantity(Mass::new(2.0, MassUnit::kg));
assert!(quantity.to(Unit::DistanceUnit(DistanceUnit::m)).is_err());
}
#[test]
fn is_not_nan() {
let quantity = Quantity::DistanceQuantity(Distance::new(-4.0, DistanceUnit::m));
assert!(!quantity.is_nan());
}
#[test]
fn is_nan() {
let quantity = Quantity::DistanceQuantity(Distance::new(f64::NAN, DistanceUnit::m));
assert!(quantity.is_nan());
}
#[test]
fn float_quantity_sqrt_works() {
let quantity = Quantity::FloatQuantity(9.0);
assert_eq!(quantity.sqrt().unwrap().extract_float().unwrap(), 3.0);
}
#[test]
fn sqrt_rejects_non_square_quantity() {
let quantity = Quantity::ForceQuantity(Force::new(9.0, ForceUnit::N));
assert!(quantity.sqrt().is_err());
}
#[test]
fn inf_multiplication() {
let distance = Quantity::DistanceQuantity(Distance::new(1.0, DistanceUnit::m));
let inf = Quantity::FloatQuantity(f64::INFINITY);
let res = (distance * inf).unwrap();
let value = res.to(Unit::DistanceUnit(DistanceUnit::mm)).unwrap();
assert!(value.is_infinite());
assert!(value.is_sign_positive());
}
#[test]
fn neg_inf_multiplication() {
let distance = Quantity::DistanceQuantity(Distance::new(1.0, DistanceUnit::m));
let inf = Quantity::FloatQuantity(f64::NEG_INFINITY);
let res = (distance * inf).unwrap();
let value = res.to(Unit::DistanceUnit(DistanceUnit::mm)).unwrap();
assert!(value.is_infinite());
assert!(value.is_sign_negative());
}