use std::collections::{HashMap, HashSet};
use weir::graph_layout::{CsrGraph, LinearDomain};
use weir::loop_sum::try_loop_summarize_with_count;
use weir::reachability_witness::{
extract_path, extract_path_prepared, try_prepare_witness_graph, NodeAttr, PathSeed,
};
use weir::ssa::{compute_dominators, try_compute_dominators, Block, Cfg};
fn empty_attrs(count: usize) -> Vec<NodeAttr> {
(0..count)
.map(|_| NodeAttr {
byte_start: 0,
byte_end: 0,
file_idx: 0,
})
.collect()
}
fn empty_strings(count: usize) -> Vec<String> {
(0..count).map(|_| String::new()).collect()
}
#[test]
fn ssa_try_compute_dominators_rejects_missing_entry() {
let mut blocks = HashMap::new();
blocks.insert(
1u32,
Block {
id: 1,
preds: vec![],
succs: vec![],
defs: HashSet::new(),
uses: HashSet::new(),
},
);
let err = try_compute_dominators(&Cfg { entry: 0, blocks }).expect_err("missing entry");
assert!(err.contains("entry block not found"), "{err}");
}
#[test]
fn ssa_compute_dominators_rejects_missing_entry() {
let mut blocks = HashMap::new();
blocks.insert(
1u32,
Block {
id: 1,
preds: vec![],
succs: vec![],
defs: HashSet::new(),
uses: HashSet::new(),
},
);
let err = compute_dominators(&Cfg { entry: 0, blocks }).expect_err("missing entry");
assert!(
err.contains("entry block not found") || err.contains("SSA dominator construction failed"),
"{err}"
);
}
#[test]
fn csr_graph_validate_rejects_nonzero_first_offset() {
let err = CsrGraph::new(2, &[1, 1, 1], &[0], &[0])
.validate("garden")
.expect_err("nonzero first offset");
assert!(err.contains("edge_offsets[0]"), "{err}");
}
#[test]
fn csr_graph_validate_rejects_zero_node_graph_with_edges() {
let err = CsrGraph::new(0, &[1], &[0], &[0])
.validate("garden")
.expect_err("zero-node with edges");
assert!(err.contains("edge_offsets[0]"), "{err}");
}
#[test]
fn csr_graph_validate_rejects_target_out_of_domain() {
let err = CsrGraph::new(2, &[0, 1, 1], &[2], &[0])
.validate("garden")
.expect_err("target OOB");
assert!(err.contains("targets node 2"), "{err}");
}
#[test]
fn csr_graph_validate_rejects_mismatched_mask_len() {
let err = CsrGraph::new(2, &[0, 1, 1], &[0], &[0, 0])
.validate("garden")
.expect_err("mask mismatch");
assert!(err.contains("edge_kind_mask"), "{err}");
}
#[test]
fn csr_graph_normalize_rejects_non_monotonic_offsets() {
let err = CsrGraph::new(3, &[0, 1, 0, 1], &[0], &[0])
.normalize("garden")
.expect_err("non-monotonic");
assert!(err.contains("not monotonic"), "{err}");
}
#[test]
fn csr_graph_normalize_rejects_target_out_of_bounds() {
let err = CsrGraph::new(2, &[0, 1, 1], &[2], &[0])
.normalize("garden")
.expect_err("target OOB");
assert!(err.contains("targets node 2"), "{err}");
}
#[test]
fn linear_domain_scalar_slots_rejects_overflow() {
let err = LinearDomain::new(u32::MAX)
.scalar_slots("garden", 2)
.expect_err("overflow");
assert!(err.contains("overflows"), "{err}");
}
#[test]
fn loop_summarize_with_count_rejects_overflow() {
let err = try_loop_summarize_with_count("cfg", "ranges", "out", 1 << 31)
.expect_err("overflowing loop summarize count must fail");
assert!(
err.contains("loop_summarize var_count=2147483648 overflows u32 buffer slots"),
"{err}"
);
}
#[test]
fn try_prepare_witness_graph_rejects_malformed_csr_legacy_panic_input() {
let attrs = empty_attrs(1);
let files = empty_strings(1);
let descs = empty_strings(1);
let err = try_prepare_witness_graph(&[0, 0], &[0], &[0], &attrs, &files, &descs, "test")
.expect_err("malformed CSR");
let msg = format!("{err:?}");
assert!(msg.contains("EdgeArrayLengthMismatch"), "{msg}");
}
#[test]
fn try_prepare_witness_graph_rejects_nonzero_first_offset_legacy_panic_input() {
let attrs = empty_attrs(1);
let files = empty_strings(1);
let descs = empty_strings(1);
let err = try_prepare_witness_graph(&[1, 1], &[0], &[0], &attrs, &files, &descs, "test")
.expect_err("nonzero first offset");
let msg = format!("{err:?}");
assert!(msg.contains("NonZeroInitialOffset"), "{msg}");
}
#[test]
fn try_prepare_witness_graph_rejects_non_monotonic_offsets_legacy_panic_input() {
let attrs = empty_attrs(2);
let files = empty_strings(1);
let descs = empty_strings(2);
let err = try_prepare_witness_graph(&[0, 2, 1], &[0, 1], &[0, 0], &attrs, &files, &descs, "test")
.expect_err("non-monotonic offsets");
let msg = format!("{err:?}");
assert!(msg.contains("NonMonotonicOffsets"), "{msg}");
}
#[test]
fn try_prepare_witness_graph_rejects_invalid_offset_count() {
let attrs = empty_attrs(1);
let files = empty_strings(1);
let descs = empty_strings(1);
let err = try_prepare_witness_graph(&[0], &[0], &[0], &attrs, &files, &descs, "test")
.expect_err("invalid offsets");
let msg = format!("{err:?}");
assert!(msg.contains("InvalidOffsetCount"), "{msg}");
}
#[test]
fn try_prepare_witness_graph_rejects_nonzero_first_offset() {
let attrs = empty_attrs(1);
let files = empty_strings(1);
let descs = empty_strings(1);
let err = try_prepare_witness_graph(&[1, 1], &[0], &[0], &attrs, &files, &descs, "test")
.expect_err("nonzero first");
let msg = format!("{err:?}");
assert!(msg.contains("NonZeroInitialOffset"), "{msg}");
}
#[test]
fn try_prepare_witness_graph_rejects_non_monotonic_offsets() {
let attrs = empty_attrs(3);
let files = empty_strings(1);
let descs = empty_strings(3);
let err = try_prepare_witness_graph(&[0, 1, 0, 1], &[0], &[0], &attrs, &files, &descs, "test")
.expect_err("non-monotonic");
let msg = format!("{err:?}");
assert!(msg.contains("NonMonotonicOffsets"), "{msg}");
}
#[test]
fn try_prepare_witness_graph_rejects_target_out_of_bounds() {
let attrs = empty_attrs(2);
let files = empty_strings(1);
let descs = empty_strings(2);
let err = try_prepare_witness_graph(&[0, 1, 1], &[2], &[0], &attrs, &files, &descs, "test")
.expect_err("target OOB");
let msg = format!("{err:?}");
assert!(msg.contains("TargetOutOfBounds"), "{msg}");
}
#[test]
fn try_prepare_witness_graph_rejects_edge_array_mismatch() {
let attrs = empty_attrs(2);
let files = empty_strings(1);
let descs = empty_strings(2);
let err = try_prepare_witness_graph(&[0, 1, 1], &[0], &[0, 0], &attrs, &files, &descs, "test")
.expect_err("mismatch");
let msg = format!("{err:?}");
assert!(msg.contains("EdgeArrayLengthMismatch"), "{msg}");
}
#[test]
fn try_prepare_witness_graph_rejects_terminal_offset_mismatch() {
let attrs = empty_attrs(2);
let files = empty_strings(1);
let descs = empty_strings(2);
let err = try_prepare_witness_graph(&[0, 1, 2], &[0], &[0], &attrs, &files, &descs, "test")
.expect_err("terminal mismatch");
let msg = format!("{err:?}");
assert!(msg.contains("TerminalOffsetMismatch"), "{msg}");
}
#[test]
fn extract_path_rejects_node_out_of_bounds_source() {
let attrs = empty_attrs(2);
let files = empty_strings(1);
let descs = empty_strings(2);
let graph = try_prepare_witness_graph(&[0, 1, 1], &[0], &[0], &attrs, &files, &descs, "test")
.expect("valid graph");
let seed = PathSeed {
source_file: String::new(),
source_node: 5,
sink_file: String::new(),
sink_node: 0,
};
let err = extract_path_prepared(&seed, &[1, 0], &[0, 0], &graph).expect_err("OOB source");
assert!(
matches!(
err,
weir::reachability_witness::PathError::NodeOutOfBounds { node: 5, .. }
),
"{err:?}"
);
}
#[test]
fn extract_path_rejects_node_out_of_bounds_sink() {
let attrs = empty_attrs(2);
let files = empty_strings(1);
let descs = empty_strings(2);
let graph = try_prepare_witness_graph(&[0, 1, 1], &[0], &[0], &attrs, &files, &descs, "test")
.expect("valid graph");
let seed = PathSeed {
source_file: String::new(),
source_node: 0,
sink_file: String::new(),
sink_node: 5,
};
let err = extract_path_prepared(&seed, &[1, 0], &[0, 0], &graph).expect_err("OOB sink");
assert!(
matches!(
err,
weir::reachability_witness::PathError::NodeOutOfBounds { node: 5, .. }
),
"{err:?}"
);
}
#[test]
fn extract_path_rejects_no_path() {
let attrs = empty_attrs(2);
let files = empty_strings(1);
let descs = empty_strings(2);
let graph = try_prepare_witness_graph(&[0, 1, 1], &[0], &[0], &attrs, &files, &descs, "test")
.expect("valid graph");
let seed = PathSeed {
source_file: String::new(),
source_node: 0,
sink_file: String::new(),
sink_node: 1,
};
let err = extract_path_prepared(&seed, &[0, 0], &[0, 0], &graph).expect_err("no path");
assert!(
matches!(err, weir::reachability_witness::PathError::NoPath),
"{err:?}"
);
}
#[test]
fn extract_path_rejects_sanitizer_at_source() {
let attrs = empty_attrs(2);
let files = empty_strings(1);
let descs = empty_strings(2);
let graph = try_prepare_witness_graph(&[0, 1, 1], &[0], &[0], &attrs, &files, &descs, "test")
.expect("valid graph");
let seed = PathSeed {
source_file: String::new(),
source_node: 0,
sink_file: String::new(),
sink_node: 0,
};
let err =
extract_path_prepared(&seed, &[1, 0], &[1, 0], &graph).expect_err("sanitizer at source");
assert!(
matches!(err, weir::reachability_witness::PathError::NoPath),
"{err:?}"
);
}
#[test]
fn extract_path_rejects_sanitizer_at_sink() {
let attrs = empty_attrs(2);
let files = empty_strings(1);
let descs = empty_strings(2);
let graph = try_prepare_witness_graph(&[0, 1, 1], &[1], &[0], &attrs, &files, &descs, "test")
.expect("valid graph");
let seed = PathSeed {
source_file: String::new(),
source_node: 0,
sink_file: String::new(),
sink_node: 1,
};
let err =
extract_path_prepared(&seed, &[1, 1], &[0, 1], &graph).expect_err("sanitizer at sink");
assert!(
matches!(err, weir::reachability_witness::PathError::NoPath),
"{err:?}"
);
}
#[test]
fn extract_path_rejects_malformed_graph_via_extract_path() {
let attrs = empty_attrs(1);
let files = empty_strings(1);
let descs = empty_strings(1);
let seed = PathSeed {
source_file: String::new(),
source_node: 0,
sink_file: String::new(),
sink_node: 0,
};
let err = extract_path(
&seed,
&[1],
&[0],
&[1, 1],
&[0],
&[0],
&attrs,
&files,
&descs,
"test",
)
.expect_err("malformed");
let msg = format!("{err:?}");
assert!(
msg.contains("NonZeroInitialOffset") || msg.contains("edge_offsets[0]"),
"{msg}"
);
}