diff --git a/Cargo.toml b/Cargo.toml index 7f9c7b3..d5ffb3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] clap = { version = "4.4.6", features = ["derive", "cargo"] } +fake = { version = "2.8.0", features = ["dummy", "derive"] } [dev-dependencies] pretty_assertions = "1.4.0" @@ -14,3 +15,6 @@ assert_float_eq = "1.1.3" claim = "0.5.0" fake = "2.8.0" proptest = "1.3.1" +quickcheck = "1.0.3" +quickcheck_macros = "1.0.0" +rand = "0.8.5" diff --git a/src/investment.rs b/src/investment.rs index 94b313f..f9987b1 100644 --- a/src/investment.rs +++ b/src/investment.rs @@ -76,6 +76,8 @@ impl Investment { } } +#[derive(Debug, Clone, Copy)] +#[cfg_attr(test, derive(fake::Dummy))] pub struct InvestmentStatus { year: usize, deposited: PositiveFloat, @@ -141,6 +143,30 @@ impl InvestmentStatus { mod test { use super::{Investment, InvestmentStatus, PositiveFloat}; use assert_float_eq::{afe_is_f64_near, afe_near_error_msg, assert_f64_near}; + use fake::{Fake, Faker}; + + impl quickcheck::Arbitrary for InvestmentStatus { + fn arbitrary(_g: &mut quickcheck::Gen) -> Self { + Faker.fake() + } + } + + #[quickcheck_macros::quickcheck] + fn test_investment_status_interest(status: InvestmentStatus) -> bool { + // Interest < balance + status.interest() < status.balance + } + + #[quickcheck_macros::quickcheck] + fn test_investment_status_gross_profit(status: InvestmentStatus) -> bool { + // Gross profit > interest + status.gross_profit() > status.interest() + } + #[quickcheck_macros::quickcheck] + fn test_investment_status_net_profit(status: InvestmentStatus) -> bool { + // Net profit < Gross profit + status.net_profit() <= status.gross_profit() + } #[test] fn test_positive_profit_computation() { diff --git a/src/types.rs b/src/types.rs index e154d18..fef624d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -19,36 +19,54 @@ impl TryFrom for PositiveFloat { } } +#[cfg(test)] +mod faker { + use super::PositiveFloat; + use fake::{Dummy, Faker}; + + impl Dummy for PositiveFloat { + fn dummy_with_rng(_: &Faker, rng: &mut R) -> Self { + Self(rng.gen_range(0.0..100000000000000000.0)) + } + } +} + #[cfg(test)] mod test { use super::PositiveFloat; - use claim::{assert_err, assert_ok_eq}; - use proptest::num::f64::{NEGATIVE, POSITIVE}; - use proptest::test_runner::TestRunner; - - #[test] - fn test_positive_float_creation() { - let mut runner = TestRunner::default(); - - runner - .run(&POSITIVE, |val| { - let positive_float = PositiveFloat::try_from(val); - assert_ok_eq!(positive_float, PositiveFloat(val)); - Ok(()) - }) - .unwrap(); + use claim::assert_ok_eq; + use rand::Rng; + + #[derive(Clone, Debug)] + struct ValidNumberFixture(pub f64); + + #[derive(Clone, Debug)] + struct InvalidNumberFixture(pub f64); + + impl quickcheck::Arbitrary for ValidNumberFixture { + fn arbitrary(_g: &mut quickcheck::Gen) -> Self { + let mut rng = rand::thread_rng(); + Self(rng.gen_range(0.0..10000000000.0)) + } + } + + impl quickcheck::Arbitrary for InvalidNumberFixture { + fn arbitrary(_g: &mut quickcheck::Gen) -> Self { + let mut rng = rand::thread_rng(); + Self(rng.gen_range(-10000000000.0..-0.000001)) + } + } + + #[quickcheck_macros::quickcheck] + fn test_positive_float_creation(valid_number: ValidNumberFixture) -> bool { + let positive_float = PositiveFloat::try_from(valid_number.0); + assert_ok_eq!(positive_float, PositiveFloat(valid_number.0)); + positive_float.is_ok() } - #[test] - fn test_positive_float_error() { - let mut runner = TestRunner::default(); - - runner - .run(&NEGATIVE, |val| { - let invalid_float = PositiveFloat::try_from(val); - assert_err!(invalid_float); - Ok(()) - }) - .unwrap(); + #[quickcheck_macros::quickcheck] + fn test_positive_float_error(invalid_number: InvalidNumberFixture) -> bool { + let invalid_float = PositiveFloat::try_from(invalid_number.0); + invalid_float.is_err() } }