Skip to content

Commit

Permalink
test(chain): introduce proptest for CheckPoint::range
Browse files Browse the repository at this point in the history
Ensure that `CheckPoint::range` returns expected values by comparing
against values returned from a primitive approach.

I think it is a good idea to start writing proptests for this crate.
  • Loading branch information
evanlinjin committed Apr 11, 2024
1 parent 62619d3 commit 446b045
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/workflows/cont_integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
cargo update -p zstd-sys --precise "2.0.8+zstd.1.5.5"
cargo update -p time --precise "0.3.20"
cargo update -p home --precise "0.5.5"
cargo update -p proptest --precise "1.2.0"
- name: Build
run: cargo build ${{ matrix.features }}
- name: Test
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ cargo update -p time --precise "0.3.20"
cargo update -p jobserver --precise "0.1.26"
# home 0.5.9 has MSRV 1.70.0
cargo update -p home --precise "0.5.5"
# proptest 1.4.0 has MSRV 1.65.0
cargo update -p proptest --precise "1.2.0"
```

## License
Expand Down
1 change: 1 addition & 0 deletions crates/chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ miniscript = { version = "10.0.0", optional = true, default-features = false }

[dev-dependencies]
rand = "0.8"
proptest = "1.2.0"

[features]
default = ["std"]
Expand Down
48 changes: 48 additions & 0 deletions crates/chain/tests/test_local_chain.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::ops::{Bound, RangeBounds};

use bdk_chain::{
local_chain::{
AlterCheckPointError, ApplyHeaderError, CannotConnectError, ChangeSet, CheckPoint,
Expand All @@ -6,6 +8,7 @@ use bdk_chain::{
BlockId,
};
use bitcoin::{block::Header, hashes::Hash, BlockHash};
use proptest::prelude::*;

#[macro_use]
mod common;
Expand Down Expand Up @@ -725,3 +728,48 @@ fn local_chain_apply_header_connected_to() {
assert_eq!(result, exp_result, "[{}:{}] unexpected result", i, t.name);
}
}

fn generate_height_range_bounds(
height_upper_bound: u32,
) -> impl Strategy<Value = (Bound<u32>, Bound<u32>)> {
fn generate_height_bound(height_upper_bound: u32) -> impl Strategy<Value = Bound<u32>> {
prop_oneof![
(0..height_upper_bound).prop_map(Bound::Included),
(0..height_upper_bound).prop_map(Bound::Excluded),
Just(Bound::Unbounded),
]
}
(
generate_height_bound(height_upper_bound),
generate_height_bound(height_upper_bound),
)
}

fn generate_checkpoints(max_height: u32, max_count: usize) -> impl Strategy<Value = CheckPoint> {
proptest::collection::btree_set(1..max_height, 0..max_count).prop_map(|mut heights| {
heights.insert(0); // must have genesis
CheckPoint::from_block_ids(heights.into_iter().map(|height| {
let hash = bitcoin::hashes::Hash::hash(height.to_le_bytes().as_slice());
BlockId { height, hash }
}))
.expect("blocks must be in order as it comes from btreeset")
})
}

proptest! {
#![proptest_config(ProptestConfig {
..Default::default()
})]

/// Ensure that [`CheckPoint::range`] returns the expected checkpoint heights by comparing it
/// against a more primitive approach.
#[test]
fn checkpoint_range(
range in generate_height_range_bounds(21_000),
cp in generate_checkpoints(21_000, 2100)
) {
let exp_heights = cp.iter().map(|cp| cp.height()).filter(|h| range.contains(h)).collect::<Vec<u32>>();
let heights = cp.range(range).map(|cp| cp.height()).collect::<Vec<u32>>();
prop_assert_eq!(heights, exp_heights);
}
}

0 comments on commit 446b045

Please sign in to comment.