From 678fc71b887f858d78c20a7399f1903c8e107611 Mon Sep 17 00:00:00 2001 From: Shing Him Ng Date: Sun, 15 Dec 2024 19:19:06 -0600 Subject: [PATCH] Implement Arbitrary for units types --- api/units/all-features.txt | 14 +++++++++++++ units/src/amount/mod.rs | 18 +++++++++++++++++ units/src/block.rs | 26 ++++++++++++++++++++++++ units/src/fee_rate.rs | 22 +++++++++++++-------- units/src/locktime/absolute.rs | 36 ++++++++++++++++++++++++++++++++++ units/src/locktime/relative.rs | 28 ++++++++++++++++++++++++++ 6 files changed, 136 insertions(+), 8 deletions(-) diff --git a/api/units/all-features.txt b/api/units/all-features.txt index 66adfa66c..0d51f112c 100644 --- a/api/units/all-features.txt +++ b/api/units/all-features.txt @@ -606,7 +606,14 @@ impl serde::ser::Serialize for bitcoin_units::locktime::relative::Time impl serde::ser::Serialize for bitcoin_units::weight::Weight impl<'a> arbitrary::Arbitrary<'a> for bitcoin_units::Amount impl<'a> arbitrary::Arbitrary<'a> for bitcoin_units::SignedAmount +impl<'a> arbitrary::Arbitrary<'a> for bitcoin_units::amount::Denomination +impl<'a> arbitrary::Arbitrary<'a> for bitcoin_units::block::BlockHeight +impl<'a> arbitrary::Arbitrary<'a> for bitcoin_units::block::BlockInterval impl<'a> arbitrary::Arbitrary<'a> for bitcoin_units::fee_rate::FeeRate +impl<'a> arbitrary::Arbitrary<'a> for bitcoin_units::locktime::absolute::Height +impl<'a> arbitrary::Arbitrary<'a> for bitcoin_units::locktime::absolute::Time +impl<'a> arbitrary::Arbitrary<'a> for bitcoin_units::locktime::relative::Height +impl<'a> arbitrary::Arbitrary<'a> for bitcoin_units::locktime::relative::Time impl<'a> arbitrary::Arbitrary<'a> for bitcoin_units::weight::Weight impl<'a> core::iter::traits::accum::Sum<&'a bitcoin_units::Amount> for bitcoin_units::Amount impl<'a> core::iter::traits::accum::Sum<&'a bitcoin_units::SignedAmount> for bitcoin_units::SignedAmount @@ -849,6 +856,7 @@ pub fn bitcoin_units::SignedAmount::unchecked_add(self, rhs: bitcoin_units::Sign pub fn bitcoin_units::SignedAmount::unchecked_sub(self, rhs: bitcoin_units::SignedAmount) -> bitcoin_units::SignedAmount pub fn bitcoin_units::SignedAmount::unsigned_abs(self) -> bitcoin_units::Amount pub fn bitcoin_units::amount::CheckedSum::checked_sum(self) -> core::option::Option +pub fn bitcoin_units::amount::Denomination::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result pub fn bitcoin_units::amount::Denomination::clone(&self) -> bitcoin_units::amount::Denomination pub fn bitcoin_units::amount::Denomination::eq(&self, other: &bitcoin_units::amount::Denomination) -> bool pub fn bitcoin_units::amount::Denomination::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result @@ -936,6 +944,7 @@ pub fn bitcoin_units::amount::serde::as_str::opt::deserialize<'d, A: bitcoin_uni pub fn bitcoin_units::amount::serde::as_str::opt::serialize(a: &core::option::Option, s: S) -> core::result::Result<::Ok, ::Error> pub fn bitcoin_units::amount::serde::as_str::serialize(a: &A, s: S) -> core::result::Result<::Ok, ::Error> pub fn bitcoin_units::block::BlockHeight::add(self, rhs: bitcoin_units::block::BlockInterval) -> Self::Output +pub fn bitcoin_units::block::BlockHeight::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result pub fn bitcoin_units::block::BlockHeight::clone(&self) -> bitcoin_units::block::BlockHeight pub fn bitcoin_units::block::BlockHeight::cmp(&self, other: &bitcoin_units::block::BlockHeight) -> core::cmp::Ordering pub fn bitcoin_units::block::BlockHeight::deserialize<__D>(__deserializer: __D) -> core::result::Result::Error> where __D: serde::de::Deserializer<'de> @@ -954,6 +963,7 @@ pub fn bitcoin_units::block::BlockHeight::try_from(s: alloc::boxed::Box) -> pub fn bitcoin_units::block::BlockHeight::try_from(s: alloc::string::String) -> core::result::Result pub fn bitcoin_units::block::BlockInterval::add(self, rhs: bitcoin_units::block::BlockInterval) -> Self::Output pub fn bitcoin_units::block::BlockInterval::add_assign(&mut self, rhs: bitcoin_units::block::BlockInterval) +pub fn bitcoin_units::block::BlockInterval::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result pub fn bitcoin_units::block::BlockInterval::clone(&self) -> bitcoin_units::block::BlockInterval pub fn bitcoin_units::block::BlockInterval::cmp(&self, other: &bitcoin_units::block::BlockInterval) -> core::cmp::Ordering pub fn bitcoin_units::block::BlockInterval::default() -> bitcoin_units::block::BlockInterval @@ -1007,6 +1017,7 @@ pub fn bitcoin_units::locktime::absolute::ConversionError::clone(&self) -> bitco pub fn bitcoin_units::locktime::absolute::ConversionError::eq(&self, other: &bitcoin_units::locktime::absolute::ConversionError) -> bool pub fn bitcoin_units::locktime::absolute::ConversionError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result pub fn bitcoin_units::locktime::absolute::ConversionError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +pub fn bitcoin_units::locktime::absolute::Height::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result pub fn bitcoin_units::locktime::absolute::Height::clone(&self) -> bitcoin_units::locktime::absolute::Height pub fn bitcoin_units::locktime::absolute::Height::cmp(&self, other: &bitcoin_units::locktime::absolute::Height) -> core::cmp::Ordering pub fn bitcoin_units::locktime::absolute::Height::deserialize(deserializer: D) -> core::result::Result::Error> where D: serde::de::Deserializer<'de> @@ -1029,6 +1040,7 @@ pub fn bitcoin_units::locktime::absolute::ParseTimeError::clone(&self) -> bitcoi pub fn bitcoin_units::locktime::absolute::ParseTimeError::eq(&self, other: &bitcoin_units::locktime::absolute::ParseTimeError) -> bool pub fn bitcoin_units::locktime::absolute::ParseTimeError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result pub fn bitcoin_units::locktime::absolute::ParseTimeError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +pub fn bitcoin_units::locktime::absolute::Time::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result pub fn bitcoin_units::locktime::absolute::Time::clone(&self) -> bitcoin_units::locktime::absolute::Time pub fn bitcoin_units::locktime::absolute::Time::cmp(&self, other: &bitcoin_units::locktime::absolute::Time) -> core::cmp::Ordering pub fn bitcoin_units::locktime::absolute::Time::deserialize(deserializer: D) -> core::result::Result::Error> where D: serde::de::Deserializer<'de> @@ -1042,6 +1054,7 @@ pub fn bitcoin_units::locktime::absolute::Time::serialize(&self, serializer: pub fn bitcoin_units::locktime::absolute::Time::try_from(s: &str) -> core::result::Result pub fn bitcoin_units::locktime::absolute::Time::try_from(s: alloc::boxed::Box) -> core::result::Result pub fn bitcoin_units::locktime::absolute::Time::try_from(s: alloc::string::String) -> core::result::Result +pub fn bitcoin_units::locktime::relative::Height::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result pub fn bitcoin_units::locktime::relative::Height::clone(&self) -> bitcoin_units::locktime::relative::Height pub fn bitcoin_units::locktime::relative::Height::cmp(&self, other: &bitcoin_units::locktime::relative::Height) -> core::cmp::Ordering pub fn bitcoin_units::locktime::relative::Height::default() -> bitcoin_units::locktime::relative::Height @@ -1057,6 +1070,7 @@ pub fn bitcoin_units::locktime::relative::Height::try_from(h: bitcoin_units::blo pub fn bitcoin_units::locktime::relative::Height::try_from(s: &str) -> core::result::Result pub fn bitcoin_units::locktime::relative::Height::try_from(s: alloc::boxed::Box) -> core::result::Result pub fn bitcoin_units::locktime::relative::Height::try_from(s: alloc::string::String) -> core::result::Result +pub fn bitcoin_units::locktime::relative::Time::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result pub fn bitcoin_units::locktime::relative::Time::clone(&self) -> bitcoin_units::locktime::relative::Time pub fn bitcoin_units::locktime::relative::Time::cmp(&self, other: &bitcoin_units::locktime::relative::Time) -> core::cmp::Ordering pub fn bitcoin_units::locktime::relative::Time::default() -> bitcoin_units::locktime::relative::Time diff --git a/units/src/amount/mod.rs b/units/src/amount/mod.rs index c25e78a1a..025a86616 100644 --- a/units/src/amount/mod.rs +++ b/units/src/amount/mod.rs @@ -20,6 +20,9 @@ use core::cmp::Ordering; use core::fmt; use core::str::FromStr; +#[cfg(feature = "arbitrary")] +use arbitrary::{Arbitrary, Unstructured}; + use self::error::{MissingDigitsKind, ParseAmountErrorInner, ParseErrorInner}; #[rustfmt::skip] // Keep public re-exports separate. @@ -599,3 +602,18 @@ mod sealed { impl Sealed for T where T: Iterator {} impl Sealed for T where T: Iterator {} } + +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for Denomination { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let choice = u.int_in_range(0..=5)?; + match choice { + 0 => Ok(Denomination::Bitcoin), + 1 => Ok(Denomination::CentiBitcoin), + 2 => Ok(Denomination::MilliBitcoin), + 3 => Ok(Denomination::MicroBitcoin), + 4 => Ok(Denomination::Bit), + _ => Ok(Denomination::Satoshi), + } + } +} diff --git a/units/src/block.rs b/units/src/block.rs index bdbf886b0..f0abe5c48 100644 --- a/units/src/block.rs +++ b/units/src/block.rs @@ -13,6 +13,8 @@ use core::{fmt, ops}; +#[cfg(feature = "arbitrary")] +use arbitrary::{Arbitrary, Unstructured}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -248,6 +250,30 @@ impl<'a> core::iter::Sum<&'a BlockInterval> for BlockInterval { } } +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for BlockHeight { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let choice = u.int_in_range(0..=2)?; + match choice { + 0 => Ok(BlockHeight::MIN), + 1 => Ok(BlockHeight::MAX), + _ => Ok(BlockHeight::from_u32(u32::arbitrary(u)?)), + } + } +} + +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for BlockInterval { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let choice = u.int_in_range(0..=2)?; + match choice { + 0 => Ok(BlockInterval::MIN), + 1 => Ok(BlockInterval::MAX), + _ => Ok(BlockInterval::from_u32(u32::arbitrary(u)?)), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/units/src/fee_rate.rs b/units/src/fee_rate.rs index d6b4c33cb..0c329ac5a 100644 --- a/units/src/fee_rate.rs +++ b/units/src/fee_rate.rs @@ -162,14 +162,6 @@ impl FeeRate { } } -#[cfg(feature = "arbitrary")] -impl<'a> Arbitrary<'a> for FeeRate { - fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - let f = u64::arbitrary(u)?; - Ok(FeeRate(f)) - } -} - /// Alternative will display the unit. impl fmt::Display for FeeRate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -294,6 +286,20 @@ impl<'a> core::iter::Sum<&'a FeeRate> for FeeRate { crate::impl_parse_str_from_int_infallible!(FeeRate, u64, from_sat_per_kwu); +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for FeeRate { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let choice = u.int_in_range(0..=4)?; + match choice { + 0 => Ok(FeeRate::MIN), + 1 => Ok(FeeRate::BROADCAST_MIN), + 2 => Ok(FeeRate::DUST), + 3 => Ok(FeeRate::MAX), + _ => Ok(FeeRate(u64::arbitrary(u)?)), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/units/src/locktime/absolute.rs b/units/src/locktime/absolute.rs index 4ee23e3dd..b9a0a9e0e 100644 --- a/units/src/locktime/absolute.rs +++ b/units/src/locktime/absolute.rs @@ -4,6 +4,8 @@ use core::fmt; +#[cfg(feature = "arbitrary")] +use arbitrary::{Arbitrary, Unstructured}; use internals::error::InputString; use crate::parse::{self, ParseIntError}; @@ -384,6 +386,40 @@ impl From for ParseError { fn from(value: ConversionError) -> Self { Self::Conversion(value.input.into()) } } +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for Height { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let choice = u.int_in_range(0..=2)?; + match choice { + 0 => Ok(Height::MIN), + 1 => Ok(Height::MAX), + _ => { + let min = Height::MIN.to_consensus_u32(); + let max = Height::MAX.to_consensus_u32(); + let h = u.int_in_range(min..=max)?; + Ok(Height::from_consensus(h).unwrap()) + } + } + } +} + +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for Time { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let choice = u.int_in_range(0..=2)?; + match choice { + 0 => Ok(Time::MIN), + 1 => Ok(Time::MAX), + _ => { + let min = Time::MIN.to_consensus_u32(); + let max = Time::MAX.to_consensus_u32(); + let t = u.int_in_range(min..=max)?; + Ok(Time::from_consensus(t).unwrap()) + } + } + } +} + #[cfg(test)] mod tests { #[cfg(feature = "serde")] diff --git a/units/src/locktime/relative.rs b/units/src/locktime/relative.rs index 401ac2ced..39796ab6b 100644 --- a/units/src/locktime/relative.rs +++ b/units/src/locktime/relative.rs @@ -4,6 +4,8 @@ use core::fmt; +#[cfg(feature = "arbitrary")] +use arbitrary::{Arbitrary, Unstructured}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -159,6 +161,32 @@ impl fmt::Display for TimeOverflowError { #[cfg(feature = "std")] impl std::error::Error for TimeOverflowError {} +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for Height { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let choice = u.int_in_range(0..=2)?; + + match choice { + 0 => Ok(Height::MIN), + 1 => Ok(Height::MAX), + _ => Ok(Height::from_height(u16::arbitrary(u)?)), + } + } +} + +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for Time { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let choice = u.int_in_range(0..=2)?; + + match choice { + 0 => Ok(Time::MIN), + 1 => Ok(Time::MAX), + _ => Ok(Time::from_512_second_intervals(u16::arbitrary(u)?)), + } + } +} + #[cfg(test)] mod tests { use super::*;