diff --git a/CHANGELOG.md b/CHANGELOG.md index a320581e..4296671a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,9 @@ availability of the type as an underlying storage type: `usize`, `u8`, `u16`, `u32`, `u64`, `isize`, `i8`, `i16`, `i32`, `i64`, `bigint`, `biguint`, `rational`, `rational32`, `rational64`, `bigrational`, `f32`, and `f64`. For compile time reasons only `f32` and `f64` are enabled by - default. + default. Tests are implemented for all underlying storage types but don't account for minimum or + maximum values and will fail for non-float types where the conversion factor overflows the + type's limits. A future release will correct this. * [#29](https://github.com/iliekturtles/uom/issues/29) [Breaking] Traits and structs generated by the `system!` macro have been significantly changed in order to support non-float underlying storage types. diff --git a/Cargo.toml b/Cargo.toml index 5163f81c..5b459167 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ clippy = { version = "^0", optional = true } compiletest_rs = { version = "^0", optional = true } [dev-dependencies] -quickcheck = "0.4.1" +quickcheck = "0.5.0" approx = "0.1.1" [features] diff --git a/src/tests.rs b/src/tests.rs index 73a6b054..ac6fd5e8 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -104,6 +104,60 @@ mod test_trait { } } +#[derive(Clone, Debug)] +struct A { + v: V, +} + +impl ::stdlib::ops::Deref for A { + type Target = V; + + fn deref(&self) -> &Self::Target { + &self.v + } +} + +mod a_struct { + storage_types! { + types: Float, PrimInt; + + use super::super::A; + + impl ::quickcheck::Arbitrary for A { + fn arbitrary(g: &mut G) -> Self + where + G: ::quickcheck::Gen, + { + A { v: V::arbitrary(g), } + } + } + } + + storage_types! { + types: BigInt, BigUint, Ratio; + + use num::FromPrimitive; + use super::super::A; + + impl ::quickcheck::Arbitrary for A { + fn arbitrary(g: &mut G) -> Self + where + G: ::quickcheck::Gen, + { + A { + v: loop { + let v = V::from_f64(::arbitrary(g)); + + if let Some(a) = v { + break a; + } + }, + } + } + } + } +} + mod quantity_macro { use tests::*; @@ -333,128 +387,128 @@ mod system_macro { quickcheck! { #[allow(trivial_casts)] - fn from_base(v: V) -> bool + fn from_base(v: A) -> bool { let km: V = >::conversion().value(); // meter -> meter. - Test::approx_eq(&v, - &::tests::from_base::(&v)) + Test::approx_eq(&*v, + &::tests::from_base::(&*v)) // kilometer -> kilometer. - && Test::approx_eq(&v, - &::tests::from_base::(&v)) + && Test::approx_eq(&*v, + &::tests::from_base::(&*v)) // meter -> kilometer. - && Test::approx_eq(&(&v / &km), - &::tests::from_base::(&v)) + && Test::approx_eq(&(&*v / &km), + &::tests::from_base::(&*v)) // kilometer -> meter. - && Test::approx_eq(&(&v * &km), - &::tests::from_base::(&v)) + && Test::approx_eq(&(&*v * &km), + &::tests::from_base::(&*v)) } #[allow(trivial_casts)] - fn to_base(v: V) -> bool + fn to_base(v: A) -> bool { let km: V = >::conversion().value(); // meter -> meter. - Test::approx_eq(&v, - &::tests::to_base::(&v)) + Test::approx_eq(&*v, + &::tests::to_base::(&*v)) // kilometer -> kilometer. - && Test::approx_eq(&v, - &::tests::to_base::(&v)) + && Test::approx_eq(&*v, + &::tests::to_base::(&*v)) // kilometer -> meter. - && Test::approx_eq(&(&v * &km), - &::tests::to_base::(&v)) + && Test::approx_eq(&(&*v * &km), + &::tests::to_base::(&*v)) // meter -> kilometer. - && Test::approx_eq(&(&v / &km), - &::tests::to_base::(&v)) + && Test::approx_eq(&(&*v / &km), + &::tests::to_base::(&*v)) } #[allow(trivial_casts)] - fn change_base(v: V) -> bool + fn change_base(v: A) -> bool { let km: V = >::conversion().value(); // meter -> meter. - Test::approx_eq(&v, - &::tests::change_base::(&v)) + Test::approx_eq(&*v, + &::tests::change_base::(&*v)) // kilometer -> kilometer. - && Test::approx_eq(&v, - &::tests::change_base::(&v)) + && Test::approx_eq(&*v, + &::tests::change_base::(&*v)) // kilometer -> meter. - && Test::approx_eq(&(&v * &km), - &::tests::change_base::(&v)) + && Test::approx_eq(&(&*v * &km), + &::tests::change_base::(&*v)) // meter -> kilometer. - && Test::approx_eq(&(&v / &km), - &::tests::change_base::(&v)) + && Test::approx_eq(&(&*v / &km), + &::tests::change_base::(&*v)) } #[allow(trivial_casts)] - fn add(l: V, r: V) -> bool { - Test::eq(&(&l + &r), - &(Length::new::((l).clone()) - + Length::new::((r).clone())).get(meter)) + fn add(l: A, r: A) -> bool { + Test::eq(&(&*l + &*r), + &(Length::new::((*l).clone()) + + Length::new::((*r).clone())).get(meter)) } #[allow(trivial_casts)] - fn sub(l: V, r: V) -> bool { - Test::eq(&(&l - &r), - &(Length::new::((l).clone()) - - Length::new::((r).clone())).get(meter)) + fn sub(l: A, r: A) -> bool { + Test::eq(&(&*l - &*r), + &(Length::new::((*l).clone()) + - Length::new::((*r).clone())).get(meter)) } #[allow(trivial_casts)] - fn mul_quantity(l: V, r: V) -> bool { + fn mul_quantity(l: A, r: A) -> bool { // TODO Use `.get(square_meter)` - Test::eq(&(&l * &r), - &(Length::new::((l).clone()) - * Length::new::((r).clone())).value) + Test::eq(&(&*l * &*r), + &(Length::new::((*l).clone()) + * Length::new::((*r).clone())).value) } #[allow(trivial_casts)] - fn mul_v(l: V, r: V) -> bool { - Test::eq(&(&l * &r), - &(Length::new::((l).clone()) * (r).clone()).get(meter)) - && Test::eq(&(&l * &r), - &((l).clone() * Length::new::((r).clone())).get(meter)) + fn mul_v(l: A, r: A) -> bool { + Test::eq(&(&*l * &*r), + &(Length::new::((*l).clone()) * (*r).clone()).get(meter)) + && Test::eq(&(&*l * &*r), + &((*l).clone() * Length::new::((*r).clone())).get(meter)) } #[allow(trivial_casts)] - fn div_quantity(l: V, r: V) -> TestResult { - if r == V::zero() { + fn div_quantity(l: A, r: A) -> TestResult { + if *r == V::zero() { return TestResult::discard(); } // TODO Use `.get(?)` TestResult::from_bool( - Test::eq(&(&l / &r), - &(Length::new::((l).clone()) / Length::new::((r).clone())).value)) + Test::eq(&(&*l / &*r), + &(Length::new::((*l).clone()) / Length::new::((*r).clone())).value)) } #[allow(trivial_casts)] - fn div_v(l: V, r: V) -> TestResult { - if r == V::zero() { + fn div_v(l: A, r: A) -> TestResult { + if *r == V::zero() { return TestResult::discard(); } // TODO Use `get(meter^-1)` TestResult::from_bool( - Test::eq(&(&l / &r), - &(Length::new::((l).clone()) / (r).clone()).get(meter)) - && Test::eq(&(&l / &r), - &((l).clone() / Length::new::((r).clone())).value)) + Test::eq(&(&*l / &*r), + &(Length::new::((*l).clone()) / (*r).clone()).get(meter)) + && Test::eq(&(&*l / &*r), + &((*l).clone() / Length::new::((*r).clone())).value)) } #[allow(trivial_casts)] - fn rem(l: V, r: V) -> TestResult { - if r == V::zero() { + fn rem(l: A, r: A) -> TestResult { + if *r == V::zero() { return TestResult::discard(); } TestResult::from_bool( - Test::eq(&(&l % &r), - &(Length::new::((l).clone()) - % Length::new::((r).clone())).get(meter))) + Test::eq(&(&*l % &*r), + &(Length::new::((*l).clone()) + % Length::new::((*r).clone())).get(meter))) } } } @@ -482,105 +536,105 @@ mod system_macro { quickcheck! { #[allow(trivial_casts)] - fn is_nan(v: V) -> bool { - v.is_nan() == Length::new::(v).is_nan() + fn is_nan(v: A) -> bool { + v.is_nan() == Length::new::(*v).is_nan() } #[allow(trivial_casts)] - fn is_infinite(v: V) -> bool { - v.is_infinite() == Length::new::(v).is_infinite() + fn is_infinite(v: A) -> bool { + v.is_infinite() == Length::new::(*v).is_infinite() } #[allow(trivial_casts)] - fn is_finite(v: V) -> bool { - v.is_finite() == Length::new::(v).is_finite() + fn is_finite(v: A) -> bool { + v.is_finite() == Length::new::(*v).is_finite() } #[allow(trivial_casts)] - fn is_normal(v: V) -> bool { - v.is_normal() == Length::new::(v).is_normal() + fn is_normal(v: A) -> bool { + v.is_normal() == Length::new::(*v).is_normal() } #[allow(trivial_casts)] - fn classify(v: V) -> bool { - v.classify() == Length::new::(v).classify() + fn classify(v: A) -> bool { + v.classify() == Length::new::(*v).classify() } #[allow(trivial_casts)] - fn cbrt(v: V) -> bool { + fn cbrt(v: A) -> bool { let l: Quantity, U, V> = Quantity::, U, V> { dimension: ::stdlib::marker::PhantomData, units: ::stdlib::marker::PhantomData, - value: v, + value: *v, }.cbrt(); Test::eq(&v.cbrt(), &l.value) } #[allow(trivial_casts)] - fn is_sign_positive(v: V) -> bool { - v.is_sign_positive() == Length::new::(v).is_sign_positive() + fn is_sign_positive(v: A) -> bool { + v.is_sign_positive() == Length::new::(*v).is_sign_positive() } #[allow(trivial_casts)] - fn is_sign_negative(v: V) -> bool { - v.is_sign_negative() == Length::new::(v).is_sign_negative() + fn is_sign_negative(v: A) -> bool { + v.is_sign_negative() == Length::new::(*v).is_sign_negative() } #[allow(trivial_casts)] - fn mul_add(s: V, a: V, b: V) -> bool { - let r: Quantity, U, V> = Length::new::(s).mul_add( - Length::new::(a), + fn mul_add(s: A, a: A, b: A) -> bool { + let r: Quantity, U, V> = Length::new::(*s).mul_add( + Length::new::(*a), Quantity::, U, V> { dimension: ::stdlib::marker::PhantomData, units: ::stdlib::marker::PhantomData, - value: b + value: *b }); - Test::eq(&s.mul_add(a, b), &r.value) + Test::eq(&s.mul_add(*a, *b), &r.value) } #[allow(trivial_casts)] - fn recip(v: V) -> bool { + fn recip(v: A) -> bool { let a: Quantity, U, V> = Quantity::, U, V> { dimension: ::stdlib::marker::PhantomData, units: ::stdlib::marker::PhantomData, - value: v, + value: *v, }.recip(); Test::eq(&v.recip(), &a.value) } #[allow(trivial_casts)] - fn powi(v: V) -> bool { - Test::eq(&v.powi(3), &Length::new::(v).powi(P3::new()).value) + fn powi(v: A) -> bool { + Test::eq(&v.powi(3), &Length::new::(*v).powi(P3::new()).value) } #[allow(trivial_casts)] - fn sqrt(v: V) -> TestResult { - if v < V::zero() { + fn sqrt(v: A) -> TestResult { + if *v < V::zero() { return TestResult::discard(); } let l: Quantity, U, V> = Quantity::, U, V> { dimension: ::stdlib::marker::PhantomData, units: ::stdlib::marker::PhantomData, - value: v, + value: *v, }.sqrt(); TestResult::from_bool(Test::eq(&v.sqrt(), &l.value)) } #[allow(trivial_casts)] - fn max(l: V, r: V) -> bool { - Test::eq(&l.max(r), - &Length::new::(l).max(Length::new::(r)).get(meter)) + fn max(l: A, r: A) -> bool { + Test::eq(&l.max(*r), + &Length::new::(*l).max(Length::new::(*r)).get(meter)) } #[allow(trivial_casts)] - fn min(l: V, r: V) -> bool { - Test::eq(&l.min(r), - &Length::new::(l).min(Length::new::(r)).get(meter)) + fn min(l: A, r: A) -> bool { + Test::eq(&l.min(*r), + &Length::new::(*l).min(Length::new::(*r)).get(meter)) } } } @@ -596,18 +650,18 @@ mod system_macro { quickcheck! { #[allow(trivial_casts)] - fn abs(v: V) -> bool { - Test::eq(&v.abs(), &Length::new::((v).clone()).abs().get(meter)) + fn abs(v: A) -> bool { + Test::eq(&v.abs(), &Length::new::((*v).clone()).abs().get(meter)) } #[allow(trivial_casts)] - fn signum(v: V) -> bool { - Test::eq(&v.signum(), &Length::new::((v).clone()).signum().get(meter)) + fn signum(v: A) -> bool { + Test::eq(&v.signum(), &Length::new::((*v).clone()).signum().get(meter)) } #[allow(trivial_casts)] - fn neg(l: V) -> bool { - Test::eq(&-(l).clone(), &-Length::new::((l).clone()).get(meter)) + fn neg(l: A) -> bool { + Test::eq(&-(*l).clone(), &-Length::new::((*l).clone()).get(meter)) } } } @@ -623,64 +677,64 @@ mod system_macro { quickcheck! { #[allow(trivial_casts)] - fn add_assign(l: V, r: V) -> bool { - let mut f = l; - let mut v = Length::new::(l); + fn add_assign(l: A, r: A) -> bool { + let mut f = *l; + let mut v = Length::new::(*l); - f += r; - v += Length::new::(r); + f += *r; + v += Length::new::(*r); Test::eq(&f, &v.get(meter)) } #[allow(trivial_casts)] - fn sub_assign(l: V, r: V) -> bool { - let mut f = l; - let mut v = Length::new::(l); + fn sub_assign(l: A, r: A) -> bool { + let mut f = *l; + let mut v = Length::new::(*l); - f -= r; - v -= Length::new::(r); + f -= *r; + v -= Length::new::(*r); Test::eq(&f, &v.get(meter)) } #[allow(trivial_casts)] - fn mul_assign(l: V, r: V) -> bool { - let mut f = l; - let mut v = Length::new::(l); + fn mul_assign(l: A, r: A) -> bool { + let mut f = *l; + let mut v = Length::new::(*l); - f *= r; - v *= r; + f *= *r; + v *= *r; Test::eq(&f, &v.get(meter)) } #[allow(trivial_casts)] - fn div_assign(l: V, r: V) -> TestResult { - if r == V::zero() { + fn div_assign(l: A, r: A) -> TestResult { + if *r == V::zero() { return TestResult::discard(); } - let mut f = l; - let mut v = Length::new::(l); + let mut f = *l; + let mut v = Length::new::(*l); - f /= r; - v /= r; + f /= *r; + v /= *r; TestResult::from_bool(Test::eq(&f, &v.get(meter))) } #[allow(trivial_casts)] - fn rem_assign(l: V, r: V) -> TestResult { - if r == V::zero() { + fn rem_assign(l: A, r: A) -> TestResult { + if *r == V::zero() { return TestResult::discard(); } - let mut f = l; - let mut v = Length::new::(l); + let mut f = *l; + let mut v = Length::new::(*l); - f %= r; - v %= Length::new::(r); + f %= *r; + v %= Length::new::(*r); TestResult::from_bool(Test::eq(&f, &v.get(meter))) } @@ -725,56 +779,56 @@ mod quantities_macro { quickcheck! { #[allow(trivial_casts)] - fn add(l: V, r: V) -> bool { - Test::approx_eq(&(&l + &r), - &(k::Length::new::((l).clone()) - + f::Length::new::((r).clone())).get(meter)) + fn add(l: A, r: A) -> bool { + Test::approx_eq(&(&*l + &*r), + &(k::Length::new::((*l).clone()) + + f::Length::new::((*r).clone())).get(meter)) } #[allow(trivial_casts)] - fn sub(l: V, r: V) -> bool { - Test::approx_eq(&(&l - &r), - &(k::Length::new::((l).clone()) - - f::Length::new::((r).clone())).get(meter)) + fn sub(l: A, r: A) -> bool { + Test::approx_eq(&(&*l - &*r), + &(k::Length::new::((*l).clone()) + - f::Length::new::((*r).clone())).get(meter)) } #[allow(trivial_casts)] - fn mul_quantity(l: V, r: V) -> bool { + fn mul_quantity(l: A, r: A) -> bool { // TODO Use `.get(square_meter)` - Test::approx_eq(&(&l * &r), - &(f::Length::new::((l).clone()) - * k::Length::new::((r).clone())).value) - && Test::approx_eq(&(&l * &r), - &(f::Length::new::((l).clone()) - * k::Mass::new::((r).clone())).value) - && Test::approx_eq(&(&l * &r), - &(k::Length::new::((l).clone()) - * f::Mass::new::((r).clone())).value) + Test::approx_eq(&(&*l * &*r), + &(f::Length::new::((*l).clone()) + * k::Length::new::((*r).clone())).value) + && Test::approx_eq(&(&*l * &*r), + &(f::Length::new::((*l).clone()) + * k::Mass::new::((*r).clone())).value) + && Test::approx_eq(&(&*l * &*r), + &(k::Length::new::((*l).clone()) + * f::Mass::new::((*r).clone())).value) } #[allow(trivial_casts)] - fn div_quantity(l: V, r: V) -> TestResult { - if r == V::zero() { + fn div_quantity(l: A, r: A) -> TestResult { + if *r == V::zero() { return TestResult::discard(); } // TODO Use `.get(?)` TestResult::from_bool( - Test::approx_eq(&(&l / &r), - &(k::Length::new::((l).clone()) - / f::Length::new::((r).clone())).value)) + Test::approx_eq(&(&*l / &*r), + &(k::Length::new::((*l).clone()) + / f::Length::new::((*r).clone())).value)) } #[allow(trivial_casts)] - fn rem(l: V, r: V) -> TestResult { - if r == V::zero() { + fn rem(l: A, r: A) -> TestResult { + if *r == V::zero() { return TestResult::discard(); } TestResult::from_bool( - Test::approx_eq(&(&l % &r), - &(k::Length::new::((l).clone()) - % f::Length::new::((r).clone())).get(meter))) + Test::approx_eq(&(&*l % &*r), + &(k::Length::new::((*l).clone()) + % f::Length::new::((*r).clone())).get(meter))) } } } @@ -791,38 +845,38 @@ mod quantities_macro { quickcheck! { #[allow(trivial_casts)] - fn add_assign(l: V, r: V) -> bool { - let mut f = l; - let mut v = k::Length::new::(l); + fn add_assign(l: A, r: A) -> bool { + let mut f = *l; + let mut v = k::Length::new::(*l); - f += r; - v += f::Length::new::(r); + f += *r; + v += f::Length::new::(*r); Test::approx_eq(&f, &v.get(meter)) } #[allow(trivial_casts)] - fn sub_assign(l: V, r: V) -> bool { - let mut f = l; - let mut v = k::Length::new::(l); + fn sub_assign(l: A, r: A) -> bool { + let mut f = *l; + let mut v = k::Length::new::(*l); - f -= r; - v -= f::Length::new::(r); + f -= *r; + v -= f::Length::new::(*r); Test::approx_eq(&f, &v.get(meter)) } #[allow(trivial_casts)] - fn rem_assign(l: V, r: V) -> TestResult { - if r == V::zero() { + fn rem_assign(l: A, r: A) -> TestResult { + if *r == V::zero() { return TestResult::discard(); } - let mut f = l; - let mut v = k::Length::new::(l); + let mut f = *l; + let mut v = k::Length::new::(*l); - f %= r; - v %= f::Length::new::(r); + f %= *r; + v %= f::Length::new::(*r); TestResult::from_bool(Test::approx_eq(&f, &v.get(meter))) }