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
|
/// 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.
|
/// that all hash identifiers are of the same hash algorithm.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct DAG<N, HW, const HASH_LEN: usize>
|
pub struct DAG<N, HW, const HASH_LEN: usize>
|
||||||
where
|
where
|
||||||
N: ByteEncoder,
|
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
|
/// 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
|
/// to the DAG they are stored in guaranteeing that the same Hashing implementation is used
|
||||||
/// for each node in the DAG.
|
/// for each node in the DAG.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Node<N, HW, const HASH_LEN: usize>
|
pub struct Node<N, HW, const HASH_LEN: usize>
|
||||||
where
|
where
|
||||||
N: ByteEncoder,
|
N: ByteEncoder,
|
||||||
|
@ -14,25 +14,61 @@
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use std::collections::{hash_map::DefaultHasher, BTreeMap, BTreeSet};
|
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| {
|
prop::collection::vec(".*", 4..nodes_count).prop_flat_map(|payloads| {
|
||||||
let nodes_len = payloads.len();
|
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.
|
// our total list of nodes.
|
||||||
Just(payloads),
|
Just(payloads),
|
||||||
// our random list of roots.
|
// our list of roots.
|
||||||
prop::collection::btree_set(1..nodes_len, 1..(nodes_len / 2)),
|
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! {
|
proptest! {
|
||||||
#[test]
|
#[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
|
// TODO implement the tests now
|
||||||
let mut dag = DAG::<String, DefaultHasher, 8>::new();
|
let mut dag = DAG::<String, DefaultHasher, 8>::new();
|
||||||
let parent_count = parent_idxs.len();
|
let parent_count = parent_idxs.len();
|
||||||
@ -58,3 +94,37 @@ proptest! {
|
|||||||
assert!(dag.get_nodes().len() == node_set.len());
|
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