#![cfg(feature = "semantic")]
#![cfg(feature = "checks")]
use pedant_core::ir::DataFlowKind;
use crate::fixtures::{dataflow_lib_path, dataflow_workspace_root};
#[path = "semantic_support/workspace_load.rs"]
mod workspace_load;
#[path = "semantic_support/semantic_context_fixture.rs"]
mod semantic_context_fixture;
#[path = "semantic_support/fixtures.rs"]
mod fixtures;
#[path = "semantic_support/context_cases.rs"]
mod context_cases;
#[path = "semantic_support/extraction_cases.rs"]
mod extraction_cases;
#[path = "semantic_support/flow_cases.rs"]
mod flow_cases;
#[path = "semantic_support/analyze_cases.rs"]
mod analyze_cases;
#[path = "semantic_support/detector_quality_cases.rs"]
mod detector_quality_cases;
#[path = "semantic_support/detector_performance_cases.rs"]
mod detector_performance_cases;
#[path = "semantic_support/detector_concurrency_cases.rs"]
mod detector_concurrency_cases;
#[path = "semantic_support/tier_cases.rs"]
mod tier_cases;
#[path = "semantic_support/cache_cases.rs"]
mod cache_cases;
#[test]
fn test_dataflow_file_analysis_is_shared() {
let first = crate::fixtures::dataflow_file_analysis();
let second = crate::fixtures::dataflow_file_analysis();
assert!(
std::sync::Arc::ptr_eq(&first, &second),
"the shared fixture analysis should be constructed once"
);
}
#[test]
fn test_call_graph_direct_call() {
let root = dataflow_workspace_root();
let ctx =
crate::fixtures::load_semantic_context(&root).expect("dataflow workspace should load");
let file = dataflow_lib_path();
let analysis = ctx
.analyze_file(&file)
.expect("should produce file analysis");
assert!(
analysis.is_line_reachable(5),
"private fn fetch is reachable only through the run → fetch call"
);
}
#[test]
fn test_call_graph_no_calls() {
let root = dataflow_workspace_root();
let ctx =
crate::fixtures::load_semantic_context(&root).expect("dataflow workspace should load");
let file = dataflow_lib_path();
let analysis = ctx
.analyze_file(&file)
.expect("should produce file analysis");
assert!(
analysis.is_line_reachable(15),
"pub fn no_calls is an entry point"
);
assert!(
!analysis.is_line_reachable(25),
"no_calls reaches nothing, so the private unreachable fn stays unreachable"
);
}
#[test]
fn test_semantic_public_queries_match_pre_cache_behavior() {
let root = dataflow_workspace_root();
let ctx = crate::fixtures::load_semantic_context(&root).expect("workspace should load");
let file = dataflow_lib_path();
let analysis = ctx
.analyze_file(&file)
.expect("should produce file analysis");
assert!(
analysis.is_line_reachable(5),
"the run → fetch call should keep private fn fetch reachable"
);
let summary = analysis.function("leak_env").expect("should find leak_env");
let taints = summary.taint_flows();
assert_eq!(taints.len(), 1, "leak_env should have one taint flow");
let summary = analysis
.function("dead_store")
.expect("should find dead_store");
let quality = summary.quality_issues();
assert!(
quality.iter().any(|f| f.kind == DataFlowKind::DeadStore),
"dead_store should produce DeadStore finding"
);
let summary = analysis
.function("repeated_call_same_args")
.expect("should find repeated_call_same_args");
let perf = summary.performance_issues();
assert!(
perf.iter().any(|f| f.kind == DataFlowKind::RepeatedCall),
"repeated_call_same_args should produce RepeatedCall finding"
);
let summary = analysis
.function("unobserved_thread_spawn")
.expect("should find unobserved_thread_spawn");
let conc = summary.concurrency_issues();
assert!(
conc.iter().any(|f| f.kind == DataFlowKind::UnobservedSpawn),
"unobserved_thread_spawn should produce UnobservedSpawn finding"
);
assert!(
analysis.is_line_reachable(20),
"pub fn reachable_network should be reachable"
);
assert!(
!analysis.is_line_reachable(25),
"private fn unreachable_private should not be reachable"
);
assert!(
analysis
.data_flows()
.iter()
.any(|f| f.kind == DataFlowKind::InconsistentLockOrder),
"should detect inconsistent lock ordering"
);
}
#[test]
fn test_semantic_file_analysis_public_queries_match_existing_behavior() {
let root = dataflow_workspace_root();
let ctx =
crate::fixtures::load_semantic_context(&root).expect("dataflow workspace should load");
let file = dataflow_lib_path();
let analysis = ctx
.analyze_file(&file)
.expect("should produce file analysis");
assert!(
analysis.is_line_reachable(5),
"the run → fetch call should keep private fn fetch reachable"
);
assert!(
analysis.is_line_reachable(20),
"pub fn reachable_network should be reachable"
);
assert!(
!analysis.is_line_reachable(25),
"private fn unreachable_private should not be reachable"
);
let has_taint = analysis.data_flows().iter().any(|f| {
f.source_capability == Some(pedant_types::Capability::EnvAccess)
&& f.sink_capability == Some(pedant_types::Capability::Network)
});
assert!(
has_taint,
"should contain EnvAccess→Network taint flow, got: {:?}",
analysis.data_flows()
);
}
#[test]
fn test_call_graph_and_reachability_reuse_cached_state() {
let root = dataflow_workspace_root();
let ctx = crate::fixtures::load_semantic_context(&root).expect("workspace should load");
let file = dataflow_lib_path();
let analysis = ctx
.analyze_file(&file)
.expect("should produce file analysis");
let count_after_first = ctx.file_setup_count();
let _reachable = analysis.is_line_reachable(20);
let _batch = analysis.check_reachability_batch(&[20, 25]);
let _analysis2 = ctx
.analyze_file(&file)
.expect("should produce file analysis");
let count_after_all = ctx.file_setup_count();
assert_eq!(
count_after_first, count_after_all,
"repeated queries and analyze_file calls should not trigger additional file setup"
);
}