use super::*;
#[test]
fn prepared_graph_reuses_reverse_csr_for_multiple_paths() {
let attrs = dummy_attrs(5);
let files = vec!["a.c".to_string()];
let descs: Vec<String> = (0..5).map(|i| format!("stmt{i}")).collect();
let edge_offsets = vec![0, 2, 3, 4, 4, 4];
let edge_targets = vec![1u32, 2, 3, 4];
let edge_kind_mask = vec![edge_kind::ASSIGNMENT; 4];
let graph = prepare_witness_graph(
&edge_offsets,
&edge_targets,
&edge_kind_mask,
&attrs,
&files,
&descs,
"c-c11",
);
let source_reach = vec![0b1_1111];
let seed_a = PathSeed {
source_file: "a.c".to_string(),
source_node: 0,
sink_file: "a.c".to_string(),
sink_node: 3,
};
let seed_b = PathSeed {
source_file: "a.c".to_string(),
source_node: 0,
sink_file: "a.c".to_string(),
sink_node: 4,
};
let path_a = extract_path_prepared(&seed_a, &source_reach, &[], &graph)
.expect("first prepared path must reconstruct");
let path_b = extract_path_prepared(&seed_b, &source_reach, &[], &graph)
.expect("second prepared path must reconstruct");
assert_eq!(
path_a
.statements
.iter()
.map(|s| s.node_id)
.collect::<Vec<_>>(),
vec![0, 1, 3]
);
assert_eq!(
path_b
.statements
.iter()
.map(|s| s.node_id)
.collect::<Vec<_>>(),
vec![0, 2, 4]
);
}
#[test]
fn prepared_batch_extracts_many_paths_without_rebuilding_reverse_csr() {
let attrs = dummy_attrs(5);
let files = vec!["a.c".to_string()];
let descs: Vec<String> = (0..5).map(|i| format!("stmt{i}")).collect();
let edge_offsets = vec![0, 2, 3, 4, 4, 4];
let edge_targets = vec![1u32, 2, 3, 4];
let edge_kind_mask = vec![edge_kind::ASSIGNMENT; 4];
let graph = prepare_witness_graph(
&edge_offsets,
&edge_targets,
&edge_kind_mask,
&attrs,
&files,
&descs,
"c-c11",
);
let source_reach = vec![0b1_1111];
let seed_a = PathSeed {
source_file: "a.c".to_string(),
source_node: 0,
sink_file: "a.c".to_string(),
sink_node: 3,
};
let seed_b = PathSeed {
source_file: "a.c".to_string(),
source_node: 0,
sink_file: "a.c".to_string(),
sink_node: 4,
};
let requests = [
PathExtractionRequest {
seed: &seed_a,
source_reach: &source_reach,
sanitizer_mask: &[],
},
PathExtractionRequest {
seed: &seed_b,
source_reach: &source_reach,
sanitizer_mask: &[],
},
];
let (paths, stats) = extract_paths_prepared(&requests, &graph)
.expect("prepared witness batch must reserve result slots");
assert_eq!(
stats,
PreparedWitnessBatchStats {
requests: 2,
successes: 2,
errors: 0,
prepared_graph_reuses: 2,
prepared_node_count: 5,
prepared_reverse_edges: 4,
}
);
assert_eq!(graph.node_count(), 5);
assert_eq!(graph.reverse_offset_count(), 6);
assert_eq!(graph.reverse_edge_count(), 4);
assert_eq!(
paths[0]
.as_ref()
.expect("first path must reconstruct")
.statements
.iter()
.map(|s| s.node_id)
.collect::<Vec<_>>(),
vec![0, 1, 3]
);
assert_eq!(
paths[1]
.as_ref()
.expect("second path must reconstruct")
.statements
.iter()
.map(|s| s.node_id)
.collect::<Vec<_>>(),
vec![0, 2, 4]
);
let mut reused_results = Vec::with_capacity(2);
let repeat_stats = extract_paths_prepared_into(&requests, &graph, &mut reused_results)
.expect("prepared witness batch into must reuse caller storage");
assert_eq!(repeat_stats, stats);
assert_eq!(reused_results, paths);
}