Make debugging easier on proptests with large vectors (#2232)
* Restore SummaryDebug on arbitrary chains And also add it to some more proptest vectors. * Reduce most arbitrary vectors from 10 to 4 This makes debugging easier * Make SummaryDebug generic over collections and exact size iterators * Document DisplayToDebug
This commit is contained in:
parent
db0cdb74ff
commit
35f097995b
|
@ -7,6 +7,7 @@ use std::sync::Arc;
|
|||
|
||||
use crate::{
|
||||
block,
|
||||
fmt::SummaryDebug,
|
||||
parameters::{Network, NetworkUpgrade, GENESIS_PREVIOUS_BLOCK_HASH},
|
||||
serialization,
|
||||
work::{difficulty::CompactDifficulty, equihash},
|
||||
|
@ -249,7 +250,7 @@ impl Block {
|
|||
pub fn partial_chain_strategy(
|
||||
mut current: LedgerState,
|
||||
count: usize,
|
||||
) -> BoxedStrategy<Vec<Arc<Self>>> {
|
||||
) -> BoxedStrategy<SummaryDebug<Vec<Arc<Self>>>> {
|
||||
let mut vec = Vec::with_capacity(count);
|
||||
|
||||
// generate block strategies with the correct heights
|
||||
|
@ -267,7 +268,7 @@ impl Block {
|
|||
}
|
||||
previous_block_hash = Some(block.hash());
|
||||
}
|
||||
vec.into_iter().map(Arc::new).collect()
|
||||
SummaryDebug(vec.into_iter().map(Arc::new).collect())
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
//! Format wrappers for Zebra
|
||||
|
||||
use std::fmt;
|
||||
use std::{fmt, ops};
|
||||
|
||||
/// Wrapper to override `Debug`, redirecting it to the `Display` impl.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DisplayToDebug<T>(pub T);
|
||||
|
||||
impl<T> fmt::Debug for DisplayToDebug<T>
|
||||
|
@ -13,16 +15,65 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub struct SummaryDebug<T>(pub T);
|
||||
impl<T> ops::Deref for DisplayToDebug<T> {
|
||||
type Target = T;
|
||||
|
||||
impl<T> fmt::Debug for SummaryDebug<Vec<T>> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}, len={}", std::any::type_name::<T>(), self.0.len())
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for SummaryDebug<&Vec<T>> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}, len={}", std::any::type_name::<T>(), self.0.len())
|
||||
impl<T> From<T> for DisplayToDebug<T> {
|
||||
fn from(t: T) -> Self {
|
||||
Self(t)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper to override `Debug` to display a shorter summary of the type.
|
||||
///
|
||||
/// For collections and exact size iterators, it only displays the
|
||||
/// collection/iterator type, the item type, and the length.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct SummaryDebug<CollectionOrIter>(pub CollectionOrIter);
|
||||
|
||||
impl<CollectionOrIter> fmt::Debug for SummaryDebug<CollectionOrIter>
|
||||
where
|
||||
CollectionOrIter: IntoIterator + Clone,
|
||||
<CollectionOrIter as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}<{}>, len={}",
|
||||
std::any::type_name::<CollectionOrIter>(),
|
||||
std::any::type_name::<<CollectionOrIter as IntoIterator>::Item>(),
|
||||
self.0.clone().into_iter().len()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<CollectionOrIter> ops::Deref for SummaryDebug<CollectionOrIter> {
|
||||
type Target = CollectionOrIter;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<CollectionOrIter> From<CollectionOrIter> for SummaryDebug<CollectionOrIter> {
|
||||
fn from(collection: CollectionOrIter) -> Self {
|
||||
Self(collection)
|
||||
}
|
||||
}
|
||||
|
||||
impl<CollectionOrIter> IntoIterator for SummaryDebug<CollectionOrIter>
|
||||
where
|
||||
CollectionOrIter: IntoIterator,
|
||||
{
|
||||
type Item = <CollectionOrIter as IntoIterator>::Item;
|
||||
type IntoIter = <CollectionOrIter as IntoIterator>::IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,12 +21,18 @@ use itertools::Itertools;
|
|||
use super::{FieldNotPresent, JoinSplitData, LockTime, Memo, Transaction};
|
||||
use sapling::{AnchorVariant, PerSpendAnchor, SharedAnchor};
|
||||
|
||||
/// The maximum number of arbitrary transactions, inputs, or outputs.
|
||||
///
|
||||
/// This size is chosen to provide interesting behaviour, but not be too large
|
||||
/// for debugging.
|
||||
pub const MAX_ARBITRARY_ITEMS: usize = 4;
|
||||
|
||||
impl Transaction {
|
||||
/// Generate a proptest strategy for V1 Transactions
|
||||
pub fn v1_strategy(ledger_state: LedgerState) -> BoxedStrategy<Self> {
|
||||
(
|
||||
transparent::Input::vec_strategy(ledger_state, 10),
|
||||
vec(any::<transparent::Output>(), 0..10),
|
||||
transparent::Input::vec_strategy(ledger_state, MAX_ARBITRARY_ITEMS),
|
||||
vec(any::<transparent::Output>(), 0..MAX_ARBITRARY_ITEMS),
|
||||
any::<LockTime>(),
|
||||
)
|
||||
.prop_map(|(inputs, outputs, lock_time)| Transaction::V1 {
|
||||
|
@ -40,8 +46,8 @@ impl Transaction {
|
|||
/// Generate a proptest strategy for V2 Transactions
|
||||
pub fn v2_strategy(ledger_state: LedgerState) -> BoxedStrategy<Self> {
|
||||
(
|
||||
transparent::Input::vec_strategy(ledger_state, 10),
|
||||
vec(any::<transparent::Output>(), 0..10),
|
||||
transparent::Input::vec_strategy(ledger_state, MAX_ARBITRARY_ITEMS),
|
||||
vec(any::<transparent::Output>(), 0..MAX_ARBITRARY_ITEMS),
|
||||
any::<LockTime>(),
|
||||
option::of(any::<JoinSplitData<Bctv14Proof>>()),
|
||||
)
|
||||
|
@ -59,8 +65,8 @@ impl Transaction {
|
|||
/// Generate a proptest strategy for V3 Transactions
|
||||
pub fn v3_strategy(ledger_state: LedgerState) -> BoxedStrategy<Self> {
|
||||
(
|
||||
transparent::Input::vec_strategy(ledger_state, 10),
|
||||
vec(any::<transparent::Output>(), 0..10),
|
||||
transparent::Input::vec_strategy(ledger_state, MAX_ARBITRARY_ITEMS),
|
||||
vec(any::<transparent::Output>(), 0..MAX_ARBITRARY_ITEMS),
|
||||
any::<LockTime>(),
|
||||
any::<block::Height>(),
|
||||
option::of(any::<JoinSplitData<Bctv14Proof>>()),
|
||||
|
@ -80,8 +86,8 @@ impl Transaction {
|
|||
/// Generate a proptest strategy for V4 Transactions
|
||||
pub fn v4_strategy(ledger_state: LedgerState) -> BoxedStrategy<Self> {
|
||||
(
|
||||
transparent::Input::vec_strategy(ledger_state, 10),
|
||||
vec(any::<transparent::Output>(), 0..10),
|
||||
transparent::Input::vec_strategy(ledger_state, MAX_ARBITRARY_ITEMS),
|
||||
vec(any::<transparent::Output>(), 0..MAX_ARBITRARY_ITEMS),
|
||||
any::<LockTime>(),
|
||||
any::<block::Height>(),
|
||||
option::of(any::<JoinSplitData<Groth16Proof>>()),
|
||||
|
@ -113,8 +119,8 @@ impl Transaction {
|
|||
NetworkUpgrade::branch_id_strategy(),
|
||||
any::<LockTime>(),
|
||||
any::<block::Height>(),
|
||||
transparent::Input::vec_strategy(ledger_state, 10),
|
||||
vec(any::<transparent::Output>(), 0..10),
|
||||
transparent::Input::vec_strategy(ledger_state, MAX_ARBITRARY_ITEMS),
|
||||
vec(any::<transparent::Output>(), 0..MAX_ARBITRARY_ITEMS),
|
||||
option::of(any::<sapling::ShieldedData<sapling::SharedAnchor>>()),
|
||||
option::of(any::<orchard::ShieldedData>()),
|
||||
)
|
||||
|
@ -202,7 +208,7 @@ impl<P: ZkSnarkProof + Arbitrary + 'static> Arbitrary for JoinSplitData<P> {
|
|||
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
|
||||
(
|
||||
any::<sprout::JoinSplit<P>>(),
|
||||
vec(any::<sprout::JoinSplit<P>>(), 0..10),
|
||||
vec(any::<sprout::JoinSplit<P>>(), 0..MAX_ARBITRARY_ITEMS),
|
||||
array::uniform32(any::<u8>()),
|
||||
vec(any::<u8>(), 64),
|
||||
)
|
||||
|
@ -256,8 +262,11 @@ impl Arbitrary for sapling::TransferData<PerSpendAnchor> {
|
|||
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
|
||||
// TODO: add an extra spend or output using Either, and stop using filter_map
|
||||
(
|
||||
vec(any::<sapling::Spend<PerSpendAnchor>>(), 0..10),
|
||||
vec(any::<sapling::Output>(), 0..10),
|
||||
vec(
|
||||
any::<sapling::Spend<PerSpendAnchor>>(),
|
||||
0..MAX_ARBITRARY_ITEMS,
|
||||
),
|
||||
vec(any::<sapling::Output>(), 0..MAX_ARBITRARY_ITEMS),
|
||||
)
|
||||
.prop_filter_map(
|
||||
"arbitrary v4 transfers with no spends and no outputs",
|
||||
|
@ -290,8 +299,11 @@ impl Arbitrary for sapling::TransferData<SharedAnchor> {
|
|||
// TODO: add an extra spend or output using Either, and stop using filter_map
|
||||
(
|
||||
any::<sapling::tree::Root>(),
|
||||
vec(any::<sapling::Spend<SharedAnchor>>(), 0..10),
|
||||
vec(any::<sapling::Output>(), 0..10),
|
||||
vec(
|
||||
any::<sapling::Spend<SharedAnchor>>(),
|
||||
0..MAX_ARBITRARY_ITEMS,
|
||||
),
|
||||
vec(any::<sapling::Output>(), 0..MAX_ARBITRARY_ITEMS),
|
||||
)
|
||||
.prop_filter_map(
|
||||
"arbitrary v5 transfers with no spends and no outputs",
|
||||
|
@ -326,7 +338,10 @@ impl Arbitrary for orchard::ShieldedData {
|
|||
any::<Amount>(),
|
||||
any::<orchard::tree::Root>(),
|
||||
any::<Halo2Proof>(),
|
||||
vec(any::<orchard::shielded_data::AuthorizedAction>(), 1..10),
|
||||
vec(
|
||||
any::<orchard::shielded_data::AuthorizedAction>(),
|
||||
1..MAX_ARBITRARY_ITEMS,
|
||||
),
|
||||
any::<Signature<Binding>>(),
|
||||
)
|
||||
.prop_map(
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use zebra_test::prelude::*;
|
||||
|
||||
use crate::{block, LedgerState};
|
||||
use crate::{block, fmt::SummaryDebug, transaction::arbitrary::MAX_ARBITRARY_ITEMS, LedgerState};
|
||||
|
||||
use super::Input;
|
||||
|
||||
|
@ -27,11 +27,10 @@ fn coinbase_has_height() -> Result<()> {
|
|||
fn input_coinbase_vecs_only_have_coinbase_input() -> Result<()> {
|
||||
zebra_test::init();
|
||||
|
||||
let max_size = 100;
|
||||
let strategy = LedgerState::coinbase_strategy(None)
|
||||
.prop_flat_map(|ledger_state| Input::vec_strategy(ledger_state, max_size));
|
||||
.prop_flat_map(|ledger_state| Input::vec_strategy(ledger_state, MAX_ARBITRARY_ITEMS));
|
||||
|
||||
proptest!(|(inputs in strategy)| {
|
||||
proptest!(|(inputs in strategy.prop_map(SummaryDebug))| {
|
||||
let len = inputs.len();
|
||||
for (ind, input) in inputs.into_iter().enumerate() {
|
||||
let is_coinbase = matches!(input, Input::Coinbase { .. });
|
||||
|
|
|
@ -5,7 +5,7 @@ use proptest::{
|
|||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
use zebra_chain::{block::Block, parameters::NetworkUpgrade::Nu5, LedgerState};
|
||||
use zebra_chain::{block::Block, fmt::SummaryDebug, parameters::NetworkUpgrade::Nu5, LedgerState};
|
||||
use zebra_test::prelude::*;
|
||||
|
||||
use crate::tests::Prepare;
|
||||
|
@ -16,14 +16,14 @@ const MAX_PARTIAL_CHAIN_BLOCKS: usize = 102;
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct PreparedChainTree {
|
||||
chain: Arc<Vec<PreparedBlock>>,
|
||||
chain: Arc<SummaryDebug<Vec<PreparedBlock>>>,
|
||||
count: BinarySearch,
|
||||
network: Network,
|
||||
}
|
||||
|
||||
impl ValueTree for PreparedChainTree {
|
||||
type Value = (
|
||||
Arc<Vec<PreparedBlock>>,
|
||||
Arc<SummaryDebug<Vec<PreparedBlock>>>,
|
||||
<BinarySearch as ValueTree>::Value,
|
||||
Network,
|
||||
);
|
||||
|
@ -44,7 +44,7 @@ impl ValueTree for PreparedChainTree {
|
|||
#[derive(Debug, Default)]
|
||||
pub struct PreparedChain {
|
||||
// the proptests are threaded (not async), so we want to use a threaded mutex here
|
||||
chain: std::sync::Mutex<Option<(Network, Arc<Vec<PreparedBlock>>)>>,
|
||||
chain: std::sync::Mutex<Option<(Network, Arc<SummaryDebug<Vec<PreparedBlock>>>)>>,
|
||||
}
|
||||
|
||||
impl Strategy for PreparedChain {
|
||||
|
@ -67,12 +67,14 @@ impl Strategy for PreparedChain {
|
|||
.prop_map(|(network, vec)| {
|
||||
(
|
||||
network,
|
||||
vec.into_iter().map(|blk| blk.prepare()).collect::<Vec<_>>(),
|
||||
vec.iter()
|
||||
.map(|blk| blk.clone().prepare())
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.new_tree(runner)?
|
||||
.current();
|
||||
*chain = Some((network, Arc::new(blocks)));
|
||||
*chain = Some((network, Arc::new(SummaryDebug(blocks))));
|
||||
}
|
||||
|
||||
let chain = chain.clone().expect("should be generated");
|
||||
|
|
Loading…
Reference in New Issue