Use `proptest` instead of `quickcheck` for `zcash_history` tests.

This commit is contained in:
Kris Nuttycombe 2023-04-13 17:06:13 -06:00
parent 47596b08ff
commit 5511bf3c92
3 changed files with 123 additions and 112 deletions

View File

@ -11,12 +11,16 @@ categories = ["cryptography::cryptocurrencies"]
[dev-dependencies]
assert_matches = "1.3.0"
quickcheck = "0.9"
proptest = "1.0.0"
[dependencies]
primitive-types = "0.11"
byteorder = "1"
blake2 = { package = "blake2b_simd", version = "1" }
proptest = { version = "1.0.0", optional = true }
[features]
test-dependencies = ["proptest"]
[lib]
bench = false

View File

@ -216,39 +216,57 @@ impl V2 {
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for NodeData {
fn arbitrary<G: quickcheck::Gen>(gen: &mut G) -> Self {
let mut node_data = NodeData {
consensus_branch_id: 0,
..Default::default()
};
gen.fill_bytes(&mut node_data.subtree_commitment[..]);
node_data.start_time = gen.next_u32();
node_data.end_time = gen.next_u32();
node_data.start_target = gen.next_u32();
node_data.end_target = gen.next_u32();
gen.fill_bytes(&mut node_data.start_sapling_root[..]);
gen.fill_bytes(&mut node_data.end_sapling_root[..]);
let mut number = [0u8; 32];
gen.fill_bytes(&mut number[..]);
node_data.subtree_total_work = U256::from_little_endian(&number[..]);
node_data.start_height = gen.next_u64();
node_data.end_height = gen.next_u64();
node_data.sapling_tx = gen.next_u64();
#[cfg(any(test, feature = "test-dependencies"))]
pub mod testing {
use primitive_types::U256;
use proptest::array::uniform32;
use proptest::prelude::{any, prop_compose};
node_data
use super::NodeData;
prop_compose! {
pub fn arb_node_data()(
subtree_commitment in uniform32(any::<u8>()),
start_time in any::<u32>(),
end_time in any::<u32>(),
start_target in any::<u32>(),
end_target in any::<u32>(),
start_sapling_root in uniform32(any::<u8>()),
end_sapling_root in uniform32(any::<u8>()),
subtree_total_work in uniform32(any::<u8>()),
start_height in any::<u64>(),
end_height in any::<u64>(),
sapling_tx in any::<u64>(),
) -> NodeData {
NodeData {
consensus_branch_id: 0,
subtree_commitment,
start_time,
end_time,
start_target,
end_target,
start_sapling_root,
end_sapling_root,
subtree_total_work: U256::from_little_endian(&subtree_total_work[..]),
start_height,
end_height,
sapling_tx
}
}
}
}
#[cfg(test)]
mod tests {
use super::NodeData;
use quickcheck::{quickcheck, TestResult};
use super::testing::arb_node_data;
use proptest::prelude::*;
quickcheck! {
fn serialization_round_trip(node_data: NodeData) -> TestResult {
TestResult::from_bool(NodeData::from_bytes(0, node_data.to_bytes()).unwrap() == node_data)
use super::NodeData;
proptest! {
#[test]
fn serialization_round_trip(node_data in arb_node_data()) {
assert_eq!(NodeData::from_bytes(0, node_data.to_bytes()).unwrap(), node_data);
}
}
}

View File

@ -325,12 +325,11 @@ fn combine_nodes<'a, V: Version>(left: IndexedNode<'a, V>, right: IndexedNode<'a
#[cfg(test)]
mod tests {
use super::{Entry, EntryKind, EntryLink, Tree};
use crate::{node_data, NodeData, Version, V2};
use assert_matches::assert_matches;
use quickcheck::{quickcheck, TestResult};
use proptest::prelude::*;
fn leaf(height: u32) -> node_data::V2 {
node_data::V2 {
@ -640,100 +639,90 @@ mod tests {
assert_eq!(tree.len(), 4083); // 4095 - log2(4096)
}
quickcheck! {
fn there_and_back(number: u32) -> TestResult {
if number > 1024*1024 {
TestResult::discard()
} else {
let mut tree = initial();
for i in 0..number {
tree.append_leaf(leaf(i+3)).expect("Failed to append");
}
for _ in 0..number {
tree.truncate_leaf().expect("Failed to truncate");
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
TestResult::from_bool(matches!(tree.root(), EntryLink::Stored(2)))
#[test]
fn prop_there_and_back(number in 0u32..=(1024*1024)) {
let mut tree = initial();
for i in 0..number {
tree.append_leaf(leaf(i+3)).expect("Failed to append");
}
for _ in 0..number {
tree.truncate_leaf().expect("Failed to truncate");
}
assert_matches!(tree.root(), EntryLink::Stored(2));
}
#[test]
fn prop_leaf_count(number in 3u32..=(1024*1024)) {
let mut tree = initial();
for i in 1..(number-1) {
tree.append_leaf(leaf(i+2)).expect("Failed to append");
}
assert_eq!(tree.root_node().expect("no root").node.leaf_count(), number as u64);
}
#[test]
fn prop_parity(number in 3u32..=(2048*2048)) {
let mut tree = initial();
for i in 1..(number-1) {
tree.append_leaf(leaf(i+2)).expect("Failed to append");
}
if number & (number - 1) == 0 {
assert_matches!(tree.root(), EntryLink::Stored(_));
} else {
assert_matches!(tree.root(), EntryLink::Generated(_));
}
}
fn leaf_count(number: u32) -> TestResult {
if !(3..=1024 * 1024).contains(&number) {
TestResult::discard()
} else {
let mut tree = initial();
for i in 1..(number-1) {
tree.append_leaf(leaf(i+2)).expect("Failed to append");
}
TestResult::from_bool(
tree.root_node().expect("no root").node.leaf_count() == number as u64
)
}
}
fn parity(number: u32) -> TestResult {
if !(3..=2048 * 2048).contains(&number) {
TestResult::discard()
} else {
let mut tree = initial();
for i in 1..(number-1) {
tree.append_leaf(leaf(i+2)).expect("Failed to append");
}
TestResult::from_bool(
if number & (number - 1) == 0 {
matches!(tree.root(), EntryLink::Stored(_))
} else {
matches!(tree.root(), EntryLink::Generated(_))
}
)
}
}
fn parity_with_truncate(add: u32, delete: u32) -> TestResult {
#[test]
fn prop_parity_with_truncate(
add_and_delete in (0u32..=(2048*2048)).prop_flat_map(
|add| (Just(add), 0..=add)
)
) {
let (add, delete) = add_and_delete;
// First we add `add` number of leaves, then delete `delete` number of leaves
// What is left should be consistent with generated-stored structure
if add > 2048 * 2048 || add < delete {
TestResult::discard()
let mut tree = initial();
for i in 0..add {
tree.append_leaf(leaf(i+3)).expect("Failed to append");
}
for _ in 0..delete {
tree.truncate_leaf().expect("Failed to truncate");
}
let total = add - delete + 2;
if total & (total - 1) == 0 {
assert_matches!(tree.root(), EntryLink::Stored(_));
} else {
let mut tree = initial();
for i in 0..add {
tree.append_leaf(leaf(i+3)).expect("Failed to append");
}
for _ in 0..delete {
tree.truncate_leaf().expect("Failed to truncate");
}
let total = add - delete + 2;
TestResult::from_bool(
if total & (total - 1) == 0 {
matches!(tree.root(), EntryLink::Stored(_))
} else {
matches!(tree.root(), EntryLink::Generated(_))
}
)
assert_matches!(tree.root(), EntryLink::Generated(_));
}
}
// Length of tree is always less than number of leaves squared
fn stored_length(add: u32, delete: u32) -> TestResult {
if add > 2048 * 2048 || add < delete {
TestResult::discard()
} else {
let mut tree = initial();
for i in 0..add {
tree.append_leaf(leaf(i+3)).expect("Failed to append");
}
for _ in 0..delete {
tree.truncate_leaf().expect("Failed to truncate");
}
let total = add - delete + 2;
TestResult::from_bool(total * total > tree.len())
#[test]
fn prop_stored_length(
add_and_delete in (0u32..=(2048*2048)).prop_flat_map(
|add| (Just(add), 0..=add)
)
) {
let (add, delete) = add_and_delete;
let mut tree = initial();
for i in 0..add {
tree.append_leaf(leaf(i+3)).expect("Failed to append");
}
for _ in 0..delete {
tree.truncate_leaf().expect("Failed to truncate");
}
let total = add - delete + 2;
assert!(total * total > tree.len())
}
}
}