diff --git a/api/units/all-features.txt b/api/units/all-features.txt index d0c77286c..23ad868e2 100644 --- a/api/units/all-features.txt +++ b/api/units/all-features.txt @@ -624,7 +624,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 @@ -894,6 +901,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 @@ -981,6 +989,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> @@ -999,6 +1008,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 @@ -1052,6 +1062,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> @@ -1074,6 +1085,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> @@ -1087,6 +1099,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 @@ -1102,6 +1115,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 9a364fd08..cd16a8552 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. @@ -600,3 +603,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 10e9208d1..9e8efa521 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 9f84a766c..3429aa44a 100644 --- a/units/src/fee_rate.rs +++ b/units/src/fee_rate.rs @@ -161,14 +161,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 { @@ -245,6 +237,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 1ab4b82cf..25de4f797 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}; @@ -386,6 +388,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 283ec1aaa..25c875ee0 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::*; diff --git a/units/tests/api.rs b/units/tests/api.rs index 20746a3a8..44eee3d9c 100644 --- a/units/tests/api.rs +++ b/units/tests/api.rs @@ -25,6 +25,8 @@ use bitcoin_units::{ amount, block, fee_rate, locktime, parse, weight, Amount, BlockHeight, BlockInterval, FeeRate, SignedAmount, Weight, }; +#[cfg(feature = "arbitrary")] +use arbitrary::{Arbitrary, Unstructured}; /// A struct that includes all public non-error enums. #[derive(Debug)] // All public types implement Debug (C-DEBUG). @@ -263,3 +265,42 @@ fn regression_default() { }; assert_eq!(got, want); } + +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for Types { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let a = Types { a: Enums::arbitrary(u)?, b: Structs::arbitrary(u)? }; + Ok(a) + } +} + +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for Structs { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let a = Structs { + a: Amount::arbitrary(u)?, + // Skip the `Display` type. + b: Amount::MAX.display_in(amount::Denomination::Bitcoin), + c: SignedAmount::arbitrary(u)?, + d: BlockHeight::arbitrary(u)?, + e: BlockInterval::arbitrary(u)?, + f: FeeRate::arbitrary(u)?, + g: absolute::Height::arbitrary(u)?, + h: absolute::Time::arbitrary(u)?, + i: relative::Height::arbitrary(u)?, + j: relative::Time::arbitrary(u)?, + k: Weight::arbitrary(u)?, + }; + Ok(a) + } +} + +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for Enums { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let a = Enums { + a: amount::Denomination::arbitrary(u)?, + }; + Ok(a) + } +}