weir/test_harness/
csr_factory.rs1use std::collections::{HashSet, VecDeque};
4
5use rand::{Rng, SeedableRng};
6
7#[derive(Clone, Debug)]
8pub struct CsrGraphFixture {
9 pub node_count: u32,
10 pub edge_offsets: Vec<u32>,
11 pub edge_targets: Vec<u32>,
12 pub edge_kind_mask: Vec<u32>,
13 pub expected_reachability: Vec<HashSet<u32>>,
14}
15
16pub struct CsrFactory;
17
18impl CsrFactory {
19 pub fn random_valid(node_count: u32, edge_probability: f64, seed: u64) -> CsrGraphFixture {
20 let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
21 let probability = edge_probability.clamp(0.0, 1.0);
22 let mut adjacency = vec![Vec::new(); node_count as usize];
23 for src in 0..node_count {
24 for dst in 0..node_count {
25 if rng.random::<f64>() < probability {
26 adjacency[src as usize].push(dst);
27 }
28 }
29 adjacency[src as usize].sort_unstable();
30 adjacency[src as usize].dedup();
31 }
32 fixture_from_adjacency(adjacency)
33 }
34
35 pub fn adversarial_empty_rows() -> CsrGraphFixture {
36 fixture_from_adjacency(vec![vec![1], Vec::new(), vec![0]])
37 }
38
39 pub fn adversarial_non_monotonic_offsets() -> CsrGraphFixture {
40 CsrGraphFixture {
41 node_count: 3,
42 edge_offsets: vec![0, 2, 1, 3],
43 edge_targets: vec![1, 2, 0],
44 edge_kind_mask: vec![u32::MAX; 3],
45 expected_reachability: Vec::new(),
46 }
47 }
48
49 pub fn adversarial_oob_targets() -> CsrGraphFixture {
50 CsrGraphFixture {
51 node_count: 3,
52 edge_offsets: vec![0, 1, 2, 3],
53 edge_targets: vec![1, 99, 0],
54 edge_kind_mask: vec![u32::MAX; 3],
55 expected_reachability: Vec::new(),
56 }
57 }
58
59 pub fn adversarial_terminal_mismatch() -> CsrGraphFixture {
60 CsrGraphFixture {
61 node_count: 3,
62 edge_offsets: vec![0, 1, 2, 4],
63 edge_targets: vec![1, 2, 0],
64 edge_kind_mask: vec![u32::MAX; 3],
65 expected_reachability: Vec::new(),
66 }
67 }
68}
69
70fn fixture_from_adjacency(adjacency: Vec<Vec<u32>>) -> CsrGraphFixture {
71 let node_count = adjacency.len() as u32;
72 let mut edge_offsets = Vec::with_capacity(adjacency.len() + 1);
73 let mut edge_targets = Vec::new();
74 edge_offsets.push(0);
75 for targets in &adjacency {
76 edge_targets.extend(targets.iter().copied());
77 edge_offsets.push(edge_targets.len() as u32);
78 }
79 let edge_kind_mask = vec![u32::MAX; edge_targets.len()];
80 let expected_reachability = reachability_sets(&adjacency);
81 CsrGraphFixture {
82 node_count,
83 edge_offsets,
84 edge_targets,
85 edge_kind_mask,
86 expected_reachability,
87 }
88}
89
90fn reachability_sets(adjacency: &[Vec<u32>]) -> Vec<HashSet<u32>> {
91 let mut out = Vec::with_capacity(adjacency.len());
92 for start in 0..adjacency.len() as u32 {
93 let mut seen = HashSet::new();
94 let mut queue = VecDeque::new();
95 seen.insert(start);
96 queue.push_back(start);
97 while let Some(node) = queue.pop_front() {
98 let Some(targets) = adjacency.get(node as usize) else {
99 continue;
100 };
101 for &target in targets {
102 if (target as usize) < adjacency.len() && seen.insert(target) {
103 queue.push_back(target);
104 }
105 }
106 }
107 out.push(seen);
108 }
109 out
110}