mirror of
https://github.com/zaphar/merkle-dag.git
synced 2025-07-21 18:19:58 -04:00
Complex DAG generation strategies
This commit is contained in:
parent
4e18982d0c
commit
e52034ad4a
7
proptest-regressions/proptest.txt
Normal file
7
proptest-regressions/proptest.txt
Normal file
File diff suppressed because one or more lines are too long
@ -47,6 +47,7 @@ pub enum EdgeError {
|
||||
///
|
||||
/// A merkle DAG instance is tied to a specific implementation of the HashWriter interface to ensure
|
||||
/// that all hash identifiers are of the same hash algorithm.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DAG<N, HW, const HASH_LEN: usize>
|
||||
where
|
||||
N: ByteEncoder,
|
||||
|
@ -25,7 +25,7 @@ use crate::hash::{ByteEncoder, HashWriter};
|
||||
/// Nodes are tied to a specific implementation of the HashWriter trait which is itself tied
|
||||
/// to the DAG they are stored in guaranteeing that the same Hashing implementation is used
|
||||
/// for each node in the DAG.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Node<N, HW, const HASH_LEN: usize>
|
||||
where
|
||||
N: ByteEncoder,
|
||||
|
@ -14,25 +14,61 @@
|
||||
use proptest::prelude::*;
|
||||
use std::collections::{hash_map::DefaultHasher, BTreeMap, BTreeSet};
|
||||
|
||||
use crate::DAG;
|
||||
use crate::{NodeCompare, DAG};
|
||||
|
||||
fn edge_strategy(nodes_count: usize) -> impl Strategy<Value = (Vec<String>, BTreeSet<usize>)> {
|
||||
fn simple_edge_strategy(
|
||||
nodes_count: usize,
|
||||
) -> impl Strategy<Value = (Vec<String>, BTreeSet<usize>)> {
|
||||
prop::collection::vec(".*", 4..nodes_count).prop_flat_map(|payloads| {
|
||||
let nodes_len = payloads.len();
|
||||
// select a random set of payloads to be roots.
|
||||
// select a random set of non root payloads to be dependencies
|
||||
(
|
||||
// our total list of nodes.
|
||||
Just(payloads),
|
||||
// our random list of roots.
|
||||
// our list of roots.
|
||||
prop::collection::btree_set(1..nodes_len, 1..(nodes_len / 2)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn complex_dag_strategy(
|
||||
nodes_count: usize,
|
||||
depth: usize,
|
||||
branch: usize,
|
||||
) -> impl Strategy<Value = DAG<String, std::collections::hash_map::DefaultHasher, 8>> {
|
||||
prop::collection::vec(".*", depth..nodes_count).prop_flat_map(move |payloads| {
|
||||
let nodes_len = payloads.len();
|
||||
let mut dag = DAG::<String, std::collections::hash_map::DefaultHasher, 8>::new();
|
||||
// partition the payloads into depth pieces
|
||||
let mut id_stack: Vec<[u8; 8]> = Vec::new();
|
||||
for chunk in payloads.chunks(nodes_len / depth) {
|
||||
// loop through the partions adding each partions nodes to the dag.
|
||||
let dep_sets: Vec<BTreeSet<[u8; 8]>> = if id_stack.is_empty() {
|
||||
vec![BTreeSet::new()]
|
||||
} else {
|
||||
let mut dep_sets = Vec::new();
|
||||
for id_chunk in id_stack.chunks(branch) {
|
||||
let id_set = id_chunk.iter().fold(BTreeSet::new(), |mut acc, item| {
|
||||
acc.insert(item.clone());
|
||||
acc
|
||||
});
|
||||
dep_sets.push(id_set);
|
||||
}
|
||||
dep_sets
|
||||
};
|
||||
let dep_set_len = dep_sets.len();
|
||||
for (idx, p) in chunk.iter().enumerate() {
|
||||
let dep_idx = idx % dep_set_len;
|
||||
let dep_set = dep_sets[dep_idx].clone();
|
||||
id_stack.push(dag.add_node(p.clone(), dep_set).unwrap().clone());
|
||||
}
|
||||
}
|
||||
Just(dag)
|
||||
})
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_dag_add_node_properties((nodes, parent_idxs) in edge_strategy(100)) {
|
||||
fn test_dag_add_node_properties((nodes, parent_idxs) in simple_edge_strategy(100)) {
|
||||
// TODO implement the tests now
|
||||
let mut dag = DAG::<String, DefaultHasher, 8>::new();
|
||||
let parent_count = parent_idxs.len();
|
||||
@ -58,3 +94,37 @@ proptest! {
|
||||
assert!(dag.get_nodes().len() == node_set.len());
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_complex_dag_node_properties(dag in complex_dag_strategy(100, 10, 3)) {
|
||||
// TODO(jwall): We can assert much more about the DAG if we get more clever in what we return.
|
||||
let nodes = dag.get_nodes();
|
||||
assert!(nodes.len() <= 100);
|
||||
|
||||
let roots = dag.get_roots();
|
||||
assert!(roots.len() < dag.get_nodes().len());
|
||||
|
||||
for node_id in nodes.keys() {
|
||||
let mut is_descendant = false;
|
||||
if roots.contains(node_id) {
|
||||
continue;
|
||||
}
|
||||
for root in roots.iter() {
|
||||
if let NodeCompare::After = dag.compare(root, node_id) {
|
||||
// success
|
||||
is_descendant = true;
|
||||
}
|
||||
}
|
||||
assert!(is_descendant);
|
||||
}
|
||||
// Check that every root node is uncomparable.
|
||||
for left_root in roots.iter() {
|
||||
for right_root in roots.iter() {
|
||||
if left_root != right_root {
|
||||
assert_eq!(dag.compare(left_root, right_root), NodeCompare::Uncomparable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user