use fxrank_core::frontend::{Frontend, SourceFile};
use fxrank_core::model::{Report, Scope};
use fxrank_lang_python::PythonFrontend;
fn analyze_dogfood(include_tests: bool) -> Report {
let path = format!("{}/tests/fixtures/dogfood.py", env!("CARGO_MANIFEST_DIR"));
let text = std::fs::read_to_string(&path).expect("dogfood.py fixture exists");
let output = PythonFrontend { include_tests }.analyze(&[SourceFile {
path: "dogfood.py".into(),
text,
}]);
let scope = Scope {
input: "dogfood.py".into(),
files: 1,
parsed: 1usize.saturating_sub(output.diagnostics.iter().filter(|d| !d.parsed).count()),
functions: output.functions.len(),
skipped_tests: output.skipped_tests,
skipped_excluded: 0,
risk_features: output.module_risks,
};
Report::build(scope, output.functions, output.diagnostics, None)
}
fn summarize(hs: &fxrank_core::model::Hotspot) -> serde_json::Value {
serde_json::json!({
"symbol": hs.symbol,
"max_class": hs.max_class,
"own_score": hs.own_score,
"risk_weight": hs.risk_weight,
"confidence": hs.confidence,
"async_boundary": hs.async_boundary,
"await_count": hs.await_count,
"effects": hs.effects.iter().map(|e| serde_json::json!({
"kind": e.kind.wire(),
"class": e.class,
"discounted_to": e.discounted_to,
"tier": format!("{:?}", e.tier).to_lowercase(),
"hidden": e.hidden,
})).collect::<Vec<_>>(),
"risk_features": hs.risk_features.iter().map(|r| serde_json::json!({
"kind": r.kind.wire(),
"class": r.class,
})).collect::<Vec<_>>(),
})
}
fn find_fn<'a>(report: &'a Report, symbol: &str) -> &'a fxrank_core::model::Hotspot {
report
.hotspots
.iter()
.find(|h| h.symbol == symbol)
.unwrap_or_else(|| {
let syms: Vec<_> = report.hotspots.iter().map(|h| h.symbol.as_str()).collect();
panic!("no function `{symbol}` in report; found: {syms:?}")
})
}
#[test]
fn snapshot_dogfood_report() {
let report = analyze_dogfood(false);
let io_world = find_fn(&report, "io_world");
assert_eq!(
io_world.max_class, 7,
"io_world must have max_class 7 (net.fs.db from open + requests.get)"
);
assert_eq!(
report.hotspots[0].symbol, "io_world",
"io_world must be the top-ranked hotspot"
);
let typed_local = find_fn(&report, "typed_local");
let lm = typed_local
.effects
.iter()
.find(|e| e.kind.wire() == "local.mutation")
.expect("typed_local must have a local.mutation effect");
assert_eq!(
lm.discounted_to,
Some(0),
"typed_local: Full coverage must discount local.mutation to class 0"
);
assert_eq!(
typed_local.own_score, 0.0,
"typed_local: own_score must be 0.0 (fully discounted)"
);
let update = find_fn(&report, "update");
let tm = update
.effects
.iter()
.find(|e| e.kind.wire() == "this.mutation")
.expect("update must have a this.mutation effect");
assert_eq!(
tm.discounted_to, None,
"update: this.mutation must NOT be discounted (receiver state escapes)"
);
assert_eq!(
update.max_class, 3,
"update: max_class must be 3 (this.mutation class)"
);
let dynamic = find_fn(&report, "dynamic");
assert!(
dynamic
.risk_features
.iter()
.any(|r| r.kind.wire() == "dynamic.code"),
"dynamic must carry a dynamic.code risk_feature (from eval)"
);
assert!(
!report.hotspots.iter().any(|h| h.symbol == "test_helper"),
"test_helper must be skipped when include_tests=false"
);
assert!(
report.scope.skipped_tests >= 1,
"skipped_tests must be >= 1 when test_helper is skipped"
);
let lambda = report
.hotspots
.iter()
.find(|h| h.symbol.starts_with("<lambda@"))
.expect("transform lambda must appear in the report");
assert_eq!(lambda.own_score, 0.0, "pure lambda must have own_score 0.0");
let snapshot = serde_json::json!({
"hotspots": report.hotspots.iter().map(summarize).collect::<Vec<_>>(),
"summary": {
"max_class": report.summary.max_class,
"own_score": report.summary.own_score,
"risk_weight": report.summary.risk_weight,
"confidence": report.summary.confidence,
},
"scope": {
"skipped_tests": report.scope.skipped_tests,
},
});
insta::assert_json_snapshot!("dogfood_report", snapshot);
}