properties.rs (3934B)
1 mod common; 2 3 use radroots_core::{Currency, Decimal, Money, Quantity, Unit}; 4 5 const VALUES: [&str; 8] = ["-100", "-1.25", "-0.01", "0", "0.01", "1.25", "42", "100"]; 6 const NON_NEGATIVE_VALUES: [&str; 6] = ["0", "0.01", "1", "1.25", "42", "100"]; 7 8 #[test] 9 fn checked_decimal_arithmetic_obeys_ring_laws_on_representative_values() { 10 for left in VALUES.map(common::dec) { 11 assert_eq!(left.checked_add(Decimal::ZERO).unwrap(), left); 12 assert_eq!(left.checked_mul(Decimal::ONE).unwrap(), left); 13 for right in VALUES.map(common::dec) { 14 assert_eq!( 15 left.checked_add(right).unwrap(), 16 right.checked_add(left).unwrap() 17 ); 18 assert_eq!( 19 left.checked_mul(right).unwrap(), 20 right.checked_mul(left).unwrap() 21 ); 22 assert_eq!( 23 left.checked_add(right).unwrap().checked_sub(right).unwrap(), 24 left 25 ); 26 } 27 } 28 } 29 30 #[test] 31 fn checked_decimal_multiplication_distributes_over_addition() { 32 for left in VALUES.map(common::dec) { 33 for middle in VALUES.map(common::dec) { 34 for right in VALUES.map(common::dec) { 35 let combined = left 36 .checked_add(middle) 37 .unwrap() 38 .checked_mul(right) 39 .unwrap(); 40 let distributed = left 41 .checked_mul(right) 42 .unwrap() 43 .checked_add(middle.checked_mul(right).unwrap()) 44 .unwrap(); 45 assert_eq!(combined, distributed); 46 } 47 } 48 } 49 } 50 51 #[test] 52 fn checked_money_addition_is_commutative_and_associative() { 53 for first in NON_NEGATIVE_VALUES.map(common::dec) { 54 for second in NON_NEGATIVE_VALUES.map(common::dec) { 55 for third in NON_NEGATIVE_VALUES.map(common::dec) { 56 let first = Money::try_new(first, Currency::USD).unwrap(); 57 let second = Money::try_new(second, Currency::USD).unwrap(); 58 let third = Money::try_new(third, Currency::USD).unwrap(); 59 assert_eq!(first.checked_add(&second), second.checked_add(&first)); 60 assert_eq!( 61 first.checked_add(&second).unwrap().checked_add(&third), 62 first.checked_add(&second.checked_add(&third).unwrap()) 63 ); 64 } 65 } 66 } 67 } 68 69 #[test] 70 fn checked_quantity_addition_preserves_unit_and_is_associative() { 71 for unit in [Unit::Each, Unit::MassG, Unit::VolumeMl] { 72 for first in NON_NEGATIVE_VALUES.map(common::dec) { 73 for second in NON_NEGATIVE_VALUES.map(common::dec) { 74 let first = Quantity::try_new(first, unit).unwrap(); 75 let second = Quantity::try_new(second, unit).unwrap(); 76 let sum = first.try_add(&second).unwrap(); 77 assert_eq!(sum.unit(), unit); 78 assert_eq!( 79 sum.amount(), 80 first.amount().checked_add(second.amount()).unwrap() 81 ); 82 assert_eq!(first.try_add(&second), second.try_add(&first)); 83 } 84 } 85 } 86 } 87 88 #[test] 89 fn canonical_unit_conversion_is_idempotent() { 90 for (unit, canonical) in [ 91 (Unit::Each, Unit::Each), 92 (Unit::MassKg, Unit::MassG), 93 (Unit::MassG, Unit::MassG), 94 (Unit::MassOz, Unit::MassG), 95 (Unit::MassLb, Unit::MassG), 96 (Unit::VolumeL, Unit::VolumeMl), 97 (Unit::VolumeMl, Unit::VolumeMl), 98 ] { 99 for amount in NON_NEGATIVE_VALUES.map(common::dec) { 100 let quantity = Quantity::try_new(amount, unit).unwrap(); 101 let once = quantity.to_canonical().unwrap(); 102 let twice = once.to_canonical().unwrap(); 103 assert_eq!(once, twice); 104 assert_eq!(once.unit(), canonical); 105 } 106 } 107 }