Skip to content

Commit

Permalink
test/core: fixes for checked arith
Browse files Browse the repository at this point in the history
  • Loading branch information
tzemanovic committed Apr 22, 2024
1 parent 4433612 commit a560edb
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 30 deletions.
57 changes: 53 additions & 4 deletions crates/core/src/dec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,11 +459,60 @@ impl Debug for Dec {

/// Helpers for testing.
#[cfg(any(test, feature = "testing"))]
#[allow(clippy::arithmetic_side_effects, clippy::cast_lossless)]
pub mod testing {
use proptest::prelude::*;

use super::*;

impl std::ops::Add<Dec> for Dec {
type Output = Dec;

fn add(self, rhs: Dec) -> Self::Output {
self.checked_add(&rhs).unwrap()
}
}

impl std::ops::Mul<Dec> for Dec {
type Output = Dec;

fn mul(self, rhs: Dec) -> Self::Output {
self.checked_mul(&rhs).unwrap()
}
}

impl std::ops::Div<Dec> for Dec {
type Output = Self;

fn div(self, rhs: Dec) -> Self::Output {
self.trunc_div(&rhs).unwrap()
}
}

impl std::ops::Mul<token::Amount> for Dec {
type Output = token::Amount;

fn mul(self, rhs: token::Amount) -> Self::Output {
if !self.is_negative() {
(rhs * self.0.abs()) / 10u64.pow(POS_DECIMAL_PRECISION as u32)
} else {
panic!(
"Dec is negative and cannot produce a valid Amount output"
);
}
}
}

impl std::ops::Mul<token::Change> for Dec {
type Output = token::Change;

fn mul(self, rhs: token::Change) -> Self::Output {
let tot = rhs * self.0;
let denom = Uint::from(10u64.pow(POS_DECIMAL_PRECISION as u32));
tot.checked_div(&I256(denom)).unwrap()
}
}

/// Generate an arbitrary non-negative `Dec`
pub fn arb_non_negative_dec() -> impl Strategy<Value = Dec> {
(any::<u64>(), 0_u8..POS_DECIMAL_PRECISION).prop_map(
Expand Down Expand Up @@ -593,13 +642,13 @@ mod test_dec {
let dec = Dec::from_str("2.76").unwrap();

debug_assert_eq!(
Dec::from(amt),
Dec::try_from(amt).unwrap(),
Dec::new(1018, 6).expect("Test failed")
);
debug_assert_eq!(dec * amt, token::Amount::from(2809u64));

let chg = -amt.change();
debug_assert_eq!(dec * chg, Change::from(-2809i64));
debug_assert_eq!(dec * chg, token::Change::from(-2809i64));
}

#[test]
Expand Down Expand Up @@ -689,12 +738,12 @@ mod test_dec {
fn test_ceiling() {
let neg = Dec::from_str("-2.4").expect("Test failed");
assert_eq!(
neg.ceil(),
neg.ceil().unwrap(),
Dec::from_str("-2").expect("Test failed").to_i256()
);
let pos = Dec::from_str("2.4").expect("Test failed");
assert_eq!(
pos.ceil(),
pos.ceil().unwrap(),
Dec::from_str("3").expect("Test failed").to_i256()
);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/keccak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ mod tests {
let mut hash = KeccakHash([0; 32]);

for i in 0..32 {
hash.0[i] = i as u8;
hash.0[i] = u8::try_from(i).unwrap();
}

let serialized = serde_json::to_string(&hash).unwrap();
Expand Down
52 changes: 44 additions & 8 deletions crates/core/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,7 @@ pub enum AmountError {

#[cfg(any(test, feature = "testing"))]
/// Testing helpers and strategies for tokens
#[allow(clippy::arithmetic_side_effects)]
pub mod testing {
use proptest::option;
use proptest::prelude::*;
Expand All @@ -926,6 +927,41 @@ pub mod testing {
arb_established_address, arb_non_internal_address,
};

impl std::ops::Add for Amount {
type Output = Self;

fn add(self, rhs: Self) -> Self::Output {
self.checked_add(rhs).unwrap()
}
}

impl std::ops::Sub for Amount {
type Output = Self;

fn sub(self, rhs: Self) -> Self::Output {
self.checked_sub(rhs).unwrap()
}
}

impl std::ops::Mul<Uint> for Amount {
type Output = Amount;

fn mul(mut self, rhs: Uint) -> Self::Output {
self.raw *= rhs;
self
}
}

impl std::ops::Div<u64> for Amount {
type Output = Self;

fn div(self, rhs: u64) -> Self::Output {
Self {
raw: self.raw / Uint::from(rhs),
}
}
}

prop_compose! {
/// Generate an arbitrary denomination
pub fn arb_denomination()(denom in 0u8..) -> Denomination {
Expand Down Expand Up @@ -1047,9 +1083,9 @@ mod tests {

#[test]
fn test_amount_checked_sub() {
let max = Amount::native_whole(u64::MAX);
let one = Amount::native_whole(1);
let zero = Amount::native_whole(0);
let max = Amount::native_whole(u64::MAX).unwrap();
let one = Amount::native_whole(1).unwrap();
let zero = Amount::native_whole(0).unwrap();

assert_eq!(zero.checked_sub(zero), Some(zero));
assert_eq!(zero.checked_sub(one), None);
Expand Down Expand Up @@ -1077,8 +1113,8 @@ mod tests {
fn test_amount_checked_add() {
let max = Amount::max();
let max_signed = Amount::max_signed();
let one = Amount::native_whole(1);
let zero = Amount::native_whole(0);
let one = Amount::native_whole(1).unwrap();
let zero = Amount::native_whole(0).unwrap();

assert_eq!(zero.checked_add(zero), Some(zero));
assert_eq!(zero.checked_signed_add(zero), Some(zero));
Expand Down Expand Up @@ -1165,9 +1201,9 @@ mod tests {
let two = Amount::from(2);
let three = Amount::from(3);
let dec = Dec::from_str("0.34").unwrap();
assert_eq!(one.mul_ceil(dec), one);
assert_eq!(two.mul_ceil(dec), one);
assert_eq!(three.mul_ceil(dec), two);
assert_eq!(one.mul_ceil(dec), Some(one));
assert_eq!(two.mul_ceil(dec), Some(one));
assert_eq!(three.mul_ceil(dec), Some(two));
}

#[test]
Expand Down
78 changes: 61 additions & 17 deletions crates/core/src/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,55 @@ impl TryFrom<I256> for i128 {
}
}

#[cfg(any(test, feature = "testing"))]
/// Testing helpers
pub mod testing {
use super::*;

impl Uint {
/// Returns a pair `((self * num) / denom, (self * num) % denom)`.
///
/// # Panics
///
/// Panics if `denom` is zero.
pub fn mul_div(&self, num: Self, denom: Self) -> (Self, Self) {
self.checked_mul_div(num, denom).unwrap()
}
}

impl std::ops::Add<I256> for I256 {
type Output = Self;

fn add(self, rhs: I256) -> Self::Output {
self.checked_add(&rhs).unwrap()
}
}

impl std::ops::Sub<I256> for I256 {
type Output = Self;

fn sub(self, rhs: I256) -> Self::Output {
self.checked_sub(&rhs).unwrap()
}
}

impl std::ops::Mul<I256> for I256 {
type Output = Self;

fn mul(self, rhs: I256) -> Self::Output {
self.checked_mul(&rhs).unwrap()
}
}

impl std::ops::Neg for I256 {
type Output = Self;

fn neg(self) -> Self::Output {
self.checked_neg().unwrap()
}
}
}

#[cfg(test)]
mod test_uint {
use std::str::FromStr;
Expand Down Expand Up @@ -870,17 +919,11 @@ mod test_uint {
);
}

/// Test that adding one to the max signed
/// value gives zero.
/// Test that checked add and sub stays below max signed value
#[test]
fn test_max_signed_value() {
let signed = I256::try_from(MAX_SIGNED_VALUE).expect("Test failed");
let one = I256::try_from(Uint::from(1u64)).expect("Test failed");
let overflow = signed + one;
assert_eq!(
overflow,
I256::try_from(Uint::zero()).expect("Test failed")
);
assert!(signed.checked_add(&one).is_none());
assert!((-signed).checked_sub(&one).is_none());
}
Expand All @@ -896,7 +939,7 @@ mod test_uint {
assert_eq!(larger, MINUS_ZERO);
assert!(I256::try_from(MINUS_ZERO).is_err());
let zero = Uint::zero();
assert_eq!(zero, zero.negate());
assert_eq!(zero, zero.negate().0);
}

/// Test that we correctly reserve the right bit for indicating the
Expand Down Expand Up @@ -973,8 +1016,8 @@ mod test_uint {
/// Test that ordering is correctly implemented
#[test]
fn test_ord() {
let this = Amount::from_uint(1, 0).unwrap().change();
let that = Amount::native_whole(1000).change();
let this = token::Amount::from_uint(1, 0).unwrap().change();
let that = token::Amount::native_whole(1000).unwrap().change();
assert!(this <= that);
assert!(-this <= that);
assert!(-this >= -that);
Expand All @@ -1001,15 +1044,16 @@ mod test_uint {
let one = I256::from(1);
let two = I256::from(2);
let dec = Dec::from_str("0.25").unwrap();
assert_eq!(one.mul_ceil(dec), one);
assert_eq!(two.mul_ceil(dec), one);
assert_eq!(I256::from(4).mul_ceil(dec), one);
assert_eq!(I256::from(5).mul_ceil(dec), two);
let neg_dec = dec.checked_neg().unwrap();
assert_eq!(one.mul_ceil(dec), Some(one));
assert_eq!(two.mul_ceil(dec), Some(one));
assert_eq!(I256::from(4).mul_ceil(dec), Some(one));
assert_eq!(I256::from(5).mul_ceil(dec), Some(two));

assert_eq!((-one).mul_ceil(-dec), one);
assert_eq!((-one).mul_ceil(neg_dec), Some(one));

assert_eq!((-one).mul_ceil(dec), I256::zero());
assert_eq!(one.mul_ceil(-dec), I256::zero());
assert_eq!((-one).mul_ceil(dec), Some(I256::zero()));
assert_eq!(one.mul_ceil(neg_dec), Some(I256::zero()));
}

#[test]
Expand Down

0 comments on commit a560edb

Please sign in to comment.