Merge rust-bitcoin/rust-bitcoin#3777: Implement Arbitrary for units types

04dfe8dd45 Add api test to check Arbitrary impls (Shing Him Ng)
678fc71b88 Implement Arbitrary for units types (Shing Him Ng)

Pull request description:

  Implement Arbitrary for the rest of the types in `units`. Also moved the implementation in `FeeRate` right before the `tests` module

  Closes #3705

ACKs for top commit:
  apoelstra:
    ACK 04dfe8dd45fae9b55dacfe9eb0d73ea306db14ba; successfully ran local tests
  tcharding:
    ACK 04dfe8dd45

Tree-SHA512: 156bd26d4de85d484711d476df1d2758805387125209f0307aa786dd1585ff9953dbe41b0864b00ae101419176647e3bde7994ed9257c18307d161463b1c8d2e
This commit is contained in:
merge-script 2024-12-21 17:30:19 +00:00
commit 1c405524e8
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
7 changed files with 177 additions and 8 deletions

View File

@ -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<R>
pub fn bitcoin_units::amount::Denomination::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
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: bitcoin_units::amount::serde::SerdeAmountForOpt, S: serde::ser::Serializer>(a: &core::option::Option<A>, s: S) -> core::result::Result<<S as serde::ser::Serializer>::Ok, <S as serde::ser::Serializer>::Error>
pub fn bitcoin_units::amount::serde::as_str::serialize<A: bitcoin_units::amount::serde::SerdeAmount, S: serde::ser::Serializer>(a: &A, s: S) -> core::result::Result<<S as serde::ser::Serializer>::Ok, <S as serde::ser::Serializer>::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<Self>
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<Self, <__D as serde::de::Deserializer>::Error> where __D: serde::de::Deserializer<'de>
@ -999,6 +1008,7 @@ pub fn bitcoin_units::block::BlockHeight::try_from(s: alloc::boxed::Box<str>) ->
pub fn bitcoin_units::block::BlockHeight::try_from(s: alloc::string::String) -> core::result::Result<Self, Self::Error>
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<Self>
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<Self>
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<D>(deserializer: D) -> core::result::Result<Self, <D as serde::de::Deserializer>::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<Self>
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<D>(deserializer: D) -> core::result::Result<Self, <D as serde::de::Deserializer>::Error> where D: serde::de::Deserializer<'de>
@ -1087,6 +1099,7 @@ pub fn bitcoin_units::locktime::absolute::Time::serialize<S>(&self, serializer:
pub fn bitcoin_units::locktime::absolute::Time::try_from(s: &str) -> core::result::Result<Self, Self::Error>
pub fn bitcoin_units::locktime::absolute::Time::try_from(s: alloc::boxed::Box<str>) -> core::result::Result<Self, Self::Error>
pub fn bitcoin_units::locktime::absolute::Time::try_from(s: alloc::string::String) -> core::result::Result<Self, Self::Error>
pub fn bitcoin_units::locktime::relative::Height::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
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<Self, Self::Error>
pub fn bitcoin_units::locktime::relative::Height::try_from(s: alloc::boxed::Box<str>) -> core::result::Result<Self, Self::Error>
pub fn bitcoin_units::locktime::relative::Height::try_from(s: alloc::string::String) -> core::result::Result<Self, Self::Error>
pub fn bitcoin_units::locktime::relative::Time::arbitrary(u: &mut arbitrary::unstructured::Unstructured<'a>) -> arbitrary::error::Result<Self>
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

View File

@ -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<T> Sealed<Amount> for T where T: Iterator<Item = Amount> {}
impl<T> Sealed<SignedAmount> for T where T: Iterator<Item = SignedAmount> {}
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Denomination {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
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),
}
}
}

View File

@ -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<Self> {
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<Self> {
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::*;

View File

@ -161,14 +161,6 @@ impl FeeRate {
}
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for FeeRate {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
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<Self> {
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::*;

View File

@ -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<ConversionError> 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<Self> {
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<Self> {
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")]

View File

@ -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<Self> {
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<Self> {
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::*;

View File

@ -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<Self> {
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<Self> {
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<Self> {
let a = Enums {
a: amount::Denomination::arbitrary(u)?,
};
Ok(a)
}
}