cstat/archive/overcomplicated/crates/cstat-cli/tests/extract_fixture.rs
Zachery Aaron Shores-Chmielewski cced49e3ed Archive overcomplicated workspace, restore legacy cstat at root
The milestone-1 multi-crate representation stack overshot the goal.
Move it to archive/overcomplicated/ to keep for reference, and bring
the original single-package cstat back to /workspace as the active
codebase. Also commit demo_glossary.rs and ignore the build binary
plus .loop/.stash/.

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
2026-07-12 11:00:45 +04:00

1959 lines
67 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! End-to-end scenario tests for `cstat extract` against the §13 fixture
//! and the unparseable-file contract fixture.
//!
//! These observe the JSON envelope only — they should survive any refactor
//! that preserves the spec §7 contract.
use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::OnceLock;
use cstat_schema::{LevelStatus, OracleOutput};
fn workspace_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("..")
.canonicalize()
.expect("workspace root resolves")
}
fn run_extract(project: &Path) -> OracleOutput {
run_extract_with(project, &[])
}
fn run_extract_with(project: &Path, extra_args: &[&str]) -> OracleOutput {
let bin = env!("CARGO_BIN_EXE_cstat");
let mut cmd = Command::new(bin);
cmd.args(["extract", "--path"]).arg(project);
cmd.args(extra_args);
let output = cmd.output().expect("invoke cstat binary");
assert!(
output.status.success(),
"cstat extract on {project:?} failed: status={:?} stderr={}",
output.status,
String::from_utf8_lossy(&output.stderr)
);
serde_json::from_slice(&output.stdout).expect("stdout parses as OracleOutput envelope")
}
/// Default (codegen-disabled) extract on the §13 fixture. Cached so the
/// many scenario tests that share this baseline don't each pay the
/// cost of the external-tool stages (clippy, rustdoc, cargo test).
fn run_extract_on_fixture() -> OracleOutput {
static CACHE: OnceLock<OracleOutput> = OnceLock::new();
CACHE
.get_or_init(|| {
let fixture = workspace_root().join("examples").join("single-file-lib");
assert!(fixture.is_dir(), "fixture missing: {fixture:?}");
run_extract(&fixture)
})
.clone()
}
/// Shared codegen-enabled extract. Cached so the tests don't trigger
/// repeated cargo builds (each ~3s) or contend on the fixture's
/// `target/cstat-*` lock when run in parallel.
fn run_extract_on_fixture_with_codegen() -> &'static OracleOutput {
static CACHE: OnceLock<OracleOutput> = OnceLock::new();
CACHE.get_or_init(|| {
let fixture = workspace_root().join("examples").join("single-file-lib");
assert!(fixture.is_dir(), "fixture missing: {fixture:?}");
run_extract_with(&fixture, &["--enable-codegen"])
})
}
fn run_extract_on_broken_fixture() -> OracleOutput {
let fixture = workspace_root().join("examples").join("broken-file");
assert!(fixture.is_dir(), "fixture missing: {fixture:?}");
run_extract(&fixture)
}
/// Shared extract on the `with-impls` fixture (lib with traits, an
/// inherent impl, two trait-impls, and a `Display` impl exercising the
/// orphan-rule quadrants). Cached so we don't shell out repeatedly.
fn run_extract_on_with_impls_fixture() -> &'static OracleOutput {
static CACHE: OnceLock<OracleOutput> = OnceLock::new();
CACHE.get_or_init(|| {
let fixture = workspace_root().join("examples").join("with-impls");
assert!(fixture.is_dir(), "fixture missing: {fixture:?}");
run_extract(&fixture)
})
}
/// Shared extract on the `with-graphs` fixture (lib with three modules
/// and a small intra-crate call graph), used by the Stage 9 graph tests.
/// Cached so we don't shell out repeatedly.
fn run_extract_on_with_graphs_fixture() -> &'static OracleOutput {
static CACHE: OnceLock<OracleOutput> = OnceLock::new();
CACHE.get_or_init(|| {
let fixture = workspace_root().join("examples").join("with-graphs");
assert!(fixture.is_dir(), "fixture missing: {fixture:?}");
run_extract(&fixture)
})
}
/// Stage-3 levels (spec §2.1 1–6 plus §2.9 #47) — always `ok` on the fixture.
const STAGE3_OK_LEVELS: &[&str] = &[
"fs.layout",
"fs.bytes",
"fs.source",
"tok.stream",
"tok.comments",
"fmt.whitespace",
"rustfmt",
];
/// Stage-4 levels that always report `ok` on a well-formed project.
/// Level 9 (`ast.expanded`) is conditional on cargo-expand and exercised
/// separately.
const STAGE4_OK_LEVELS: &[&str] = &["ast.cst", "ast.items"];
/// Stage-5 levels — spec §2.8 41–44. `cargo.lock` is conditional on a
/// generated `Cargo.lock` (which the fixture commits, so it's `ok` there).
const STAGE5_OK_LEVELS: &[&str] = &[
"cargo.workspace",
"cargo.lock",
"cargo.cfg",
"cargo.toolchain",
];
#[test]
fn every_required_level_is_present_with_a_documented_status() {
let envelope = run_extract_on_fixture();
let required_ids: Vec<&str> = cstat_orchestrate::REQUIRED_LEVELS
.iter()
.map(|d| d.id)
.collect();
assert_eq!(
envelope.levels.len(),
required_ids.len(),
"envelope levels count differs from required-levels table"
);
for id in required_ids {
let report = envelope
.levels
.get(id)
.unwrap_or_else(|| panic!("level {id} missing from envelope"));
match report.status {
LevelStatus::Ok => {}
LevelStatus::Skipped | LevelStatus::Failed => {
assert!(
report.reason.as_ref().is_some_and(|r| !r.is_empty()),
"non-ok level {id} has empty reason"
);
}
}
}
}
#[test]
fn envelope_carries_a_populated_toolchain_stamp_and_schema_version() {
let envelope = run_extract_on_fixture();
assert!(!envelope.schema_version.is_empty());
assert!(!envelope.cstat_version.is_empty());
assert!(!envelope.toolchain.version.is_empty(), "toolchain version");
assert!(!envelope.toolchain.channel.is_empty(), "toolchain channel");
}
#[test]
fn meta_levels_missing_count_matches_non_ok_level_count() {
let envelope = run_extract_on_fixture();
let observed_non_ok = envelope
.levels
.values()
.filter(|r| r.status != LevelStatus::Ok)
.count();
let missing = envelope
.vector
.get("meta.levels_missing.count")
.expect("meta.levels_missing.count present");
assert_eq!(
missing as usize, observed_non_ok,
"meta counter must agree with the levels map"
);
}
#[test]
fn stage_three_levels_report_ok_on_the_fixture() {
let envelope = run_extract_on_fixture();
for id in STAGE3_OK_LEVELS {
let report = envelope
.levels
.get(*id)
.unwrap_or_else(|| panic!("level {id} missing"));
assert_eq!(
report.status,
LevelStatus::Ok,
"stage-3 level {id} expected ok but got {:?} (reason: {:?})",
report.status,
report.reason
);
}
}
#[test]
fn fixture_vector_contains_the_completion_gate_metrics() {
let envelope = run_extract_on_fixture();
assert_eq!(
envelope.vector.get("fs.global.rust_file_count"),
Some(1.0),
"fixture has one .rs file"
);
let total_bytes = envelope
.vector
.get("fs.global.total_bytes")
.expect("fs.global.total_bytes populated");
assert!(total_bytes > 0.0, "total_bytes must reflect the .rs payload");
for key in [
"fs.layout.file_size_bytes.max",
"fs.layout.file_size_bytes.mean",
] {
let v = envelope
.vector
.get(key)
.unwrap_or_else(|| panic!("{key} missing"));
assert!(v >= 0.0, "{key} must be non-negative");
}
let blank_ratio = envelope
.vector
.get("fs.source.blank_line_ratio.mean")
.expect("blank_line_ratio.mean populated");
assert!(
(0.0..=1.0).contains(&blank_ratio),
"blank_line_ratio must be a fraction, got {blank_ratio}"
);
let unique_idents = envelope
.vector
.get("tok.global.unique_identifiers")
.expect("tok.global.unique_identifiers populated");
assert!(
unique_idents >= 5.0,
"fixture should have several distinct identifiers, got {unique_idents}"
);
assert_eq!(envelope.vector.get("tok.comments.todo_count"), Some(0.0));
assert_eq!(envelope.vector.get("tok.comments.fixme_count"), Some(0.0));
let conformant = envelope
.vector
.get("rustfmt.conformant")
.expect("rustfmt.conformant populated");
assert!(
conformant == 0.0 || conformant == 1.0,
"rustfmt.conformant is a 0/1 flag, got {conformant}"
);
}
#[test]
fn stage_four_ast_levels_report_ok_on_the_fixture() {
let envelope = run_extract_on_fixture();
for id in STAGE4_OK_LEVELS {
let report = envelope
.levels
.get(*id)
.unwrap_or_else(|| panic!("level {id} missing"));
assert_eq!(
report.status,
LevelStatus::Ok,
"stage-4 level {id} expected ok but got {:?} (reason: {:?})",
report.status,
report.reason
);
}
}
#[test]
fn ast_expanded_is_ok_or_skipped_with_a_reason() {
let envelope = run_extract_on_fixture();
let report = envelope.levels.get("ast.expanded").expect("ast.expanded present");
match report.status {
LevelStatus::Ok => {
assert!(
envelope.vector.contains_key("ast.expanded.token_count"),
"ok ast.expanded must populate its vector keys"
);
}
LevelStatus::Skipped => {
assert!(
report.reason.as_ref().is_some_and(|r| !r.is_empty()),
"skipped ast.expanded must explain itself"
);
assert!(
!envelope.vector.contains_key("ast.expanded.token_count"),
"skipped levels must omit their vector keys (spec §8)"
);
}
LevelStatus::Failed => panic!(
"ast.expanded failed on a well-formed fixture (reason: {:?})",
report.reason
),
}
}
#[test]
fn fixture_ast_metrics_reflect_the_two_pub_functions() {
let envelope = run_extract_on_fixture();
// §13: two top-level `pub fn`s. The exact value is the §13 anchor.
assert_eq!(
envelope.vector.get("ast.global.func_count"),
Some(2.0),
"fixture has exactly two functions"
);
let parsed = envelope
.vector
.get("ast.cst.parsed_file_count")
.expect("ast.cst.parsed_file_count populated");
assert_eq!(parsed, 1.0, "single-file fixture parses one file");
let pub_ratio = envelope
.vector
.get("ast.items.pub_ratio")
.expect("ast.items.pub_ratio populated");
assert!(
(0.0..=1.0).contains(&pub_ratio) && pub_ratio > 0.0,
"fixture's items are all `pub`, ratio must be > 0"
);
// The crate-root module must show up under entities.ast.module with the
// function count it owns.
let modules = envelope
.entities
.get("ast.module")
.expect("entities.ast.module populated");
assert!(
modules
.iter()
.any(|r| r.metrics.get("func_count").copied() == Some(2.0)),
"expected a module with two functions; got {modules:?}"
);
}
#[test]
fn meta_files_unparseable_count_is_zero_on_the_fixture() {
let envelope = run_extract_on_fixture();
assert_eq!(
envelope.vector.get("meta.files_unparseable.count"),
Some(0.0),
"well-formed fixture has no parse errors"
);
assert!(
envelope.entities.get("ast.file_errors").map_or(true, |v| v.is_empty()),
"well-formed fixture must not produce file_errors entries"
);
}
#[test]
fn unparseable_file_degrades_gracefully_per_spec_section_11() {
// Anchor test 6 (§11 row 4): one well-formed file + one broken file.
let envelope = run_extract_on_broken_fixture();
// The process exits 0 — already asserted by run_extract on success.
// AST levels still report ok despite one file failing to parse.
for id in ["ast.cst", "ast.items"] {
let report = envelope.levels.get(id).expect("ast level present");
assert_eq!(
report.status,
LevelStatus::Ok,
"{id} must stay ok in the face of one bad file (reason: {:?})",
report.reason
);
}
// The broken file shows up under entities.ast.file_errors.
let errors = envelope
.entities
.get("ast.file_errors")
.expect("entities.ast.file_errors populated");
assert_eq!(
errors.len(),
1,
"exactly one unparseable file expected, got {}",
errors.len()
);
let bad = &errors[0];
let file_id = bad
.ids
.file
.as_ref()
.expect("file_errors entry carries a FileId");
assert!(
file_id.as_str().ends_with("bad.rs"),
"file_errors should identify bad.rs, got {file_id:?}"
);
// meta counter agrees with the entity record count.
assert_eq!(
envelope.vector.get("meta.files_unparseable.count"),
Some(1.0),
"meta.files_unparseable.count must reflect entities.ast.file_errors"
);
// The good file's metrics still land: the surviving `pub fn good` shows up.
assert_eq!(
envelope.vector.get("ast.global.func_count"),
Some(1.0),
"good.rs's single function must still be counted"
);
assert_eq!(
envelope.vector.get("ast.cst.parsed_file_count"),
Some(1.0),
"exactly one .rs file parsed successfully"
);
assert_eq!(
envelope.vector.get("ast.cst.unparseable_file_count"),
Some(1.0),
"exactly one .rs file failed to parse"
);
}
#[test]
fn stage_five_cargo_metadata_levels_report_ok_on_the_fixture() {
let envelope = run_extract_on_fixture();
for id in STAGE5_OK_LEVELS {
let report = envelope
.levels
.get(*id)
.unwrap_or_else(|| panic!("level {id} missing"));
assert_eq!(
report.status,
LevelStatus::Ok,
"stage-5 level {id} expected ok but got {:?} (reason: {:?})",
report.status,
report.reason
);
}
}
#[test]
fn fixture_cargo_metadata_reflects_a_single_crate_with_no_deps() {
let envelope = run_extract_on_fixture();
// §13 fixture is a single package with no dependencies.
assert_eq!(
envelope.vector.get("cargo.workspace.member_count"),
Some(1.0),
"fixture is a single crate"
);
assert_eq!(
envelope.vector.get("cargo.workspace.direct_deps"),
Some(0.0),
"fixture declares no dependencies"
);
assert_eq!(
envelope.vector.get("cargo.workspace.dev_deps"),
Some(0.0),
"fixture declares no dev-dependencies"
);
assert_eq!(
envelope.vector.get("cargo.workspace.edition"),
Some(2021.0),
"fixture is edition 2021"
);
assert_eq!(
envelope.vector.get("cargo.workspace.is_virtual"),
Some(0.0),
"fixture is a real package, not a virtual workspace"
);
// Cargo.lock has one entry: the package itself, with no resolved deps.
assert_eq!(
envelope.vector.get("cargo.lock.package_count"),
Some(1.0),
"fixture lockfile has one package entry"
);
let edge_count = envelope
.vector
.get("cargo.lock.dep_edge_count")
.expect("cargo.lock.dep_edge_count populated");
assert_eq!(edge_count, 0.0, "fixture has no resolved deps");
let lockfile_version = envelope
.vector
.get("cargo.lock.lockfile_version")
.expect("cargo.lock.lockfile_version populated");
assert!(lockfile_version >= 3.0, "modern cargo writes v3+ lockfiles");
// rustc --print=cfg should yield at least a handful of entries on any
// supported host. Exact contents vary, so we only assert "non-empty".
let cfg_count = envelope
.vector
.get("cargo.cfg.entry_count")
.expect("cargo.cfg.entry_count populated");
assert!(
cfg_count > 0.0,
"rustc --print=cfg returned no entries (got {cfg_count})"
);
// Toolchain pin: fixture has no rust-toolchain.toml.
assert_eq!(
envelope.vector.get("cargo.toolchain.pinned"),
Some(0.0),
"fixture does not pin a toolchain"
);
}
#[test]
fn envelope_toolchain_stamp_classifies_the_active_channel() {
let envelope = run_extract_on_fixture();
let channel = envelope.toolchain.channel.as_str();
assert!(
matches!(channel, "stable" | "beta" | "nightly" | "dev"),
"channel {channel:?} is not one of the documented values"
);
assert!(!envelope.toolchain.version.is_empty());
assert!(!envelope.toolchain.host.is_empty());
}
#[test]
fn provenance_attributes_each_vector_key_to_exactly_one_level() {
let envelope = run_extract_on_fixture();
let level_ids: BTreeSet<&str> = cstat_orchestrate::REQUIRED_LEVELS
.iter()
.map(|d| d.id)
.chain(std::iter::once("meta"))
.collect();
for (key, _) in envelope.vector.iter() {
let owners = envelope
.provenance
.0
.get(key)
.unwrap_or_else(|| panic!("no provenance entry for vector key {key}"));
assert_eq!(
owners.len(),
1,
"vector key {key} has multiple owners {owners:?}; namespace cross-check failed"
);
let owner = owners[0].as_str();
assert!(
level_ids.contains(owner),
"vector key {key} attributed to unknown level {owner}"
);
}
}
// ───── Stage 6: codegen extractors ──────────────────────────────────────
/// The five codegen-level ids covered by Stage 6 (spec §2.5 20–24).
const STAGE6_LEVELS: &[&str] = &["llvm.preopt", "llvm.opt", "asm", "obj", "bin"];
#[test]
fn stage_six_codegen_levels_skip_cleanly_without_the_flag() {
// Default `cstat extract` (no --enable-codegen) must not drive a
// cargo build. All five Stage-6 levels report Skipped with a reason
// that explains how to turn them on. Spec §8 omission contract: no
// codegen vector keys leak into the envelope.
let envelope = run_extract_on_fixture();
for id in STAGE6_LEVELS {
let report = envelope.levels.get(*id).expect("level present");
assert_eq!(
report.status,
LevelStatus::Skipped,
"{id} should skip when codegen is disabled; got {:?}",
report.status
);
let reason = report.reason.as_deref().unwrap_or("");
assert!(
!reason.is_empty(),
"{id} skipped without a reason — user has no way to know how to enable it"
);
}
for key in envelope.vector.0.keys() {
for prefix in ["llvm.preopt.", "llvm.opt.", "asm.", "obj.", "bin."] {
assert!(
!key.starts_with(prefix),
"skipped codegen level emitted {key} (spec §8 says skipped levels omit their keys)"
);
}
}
}
#[test]
fn stage_six_codegen_levels_report_ok_when_enabled_on_the_fixture() {
let envelope = run_extract_on_fixture_with_codegen();
// The fixture is lib-only, so bin legitimately skips. Every other
// Stage-6 level must report ok.
for id in ["llvm.preopt", "llvm.opt", "asm", "obj"] {
let report = envelope.levels.get(id).expect("level present");
assert_eq!(
report.status,
LevelStatus::Ok,
"{id} expected ok with --enable-codegen; got {:?} (reason: {:?})",
report.status,
report.reason
);
}
let bin_report = envelope.levels.get("bin").expect("bin level present");
assert_eq!(
bin_report.status,
LevelStatus::Skipped,
"bin should skip on the lib-only fixture; got {:?} (reason: {:?})",
bin_report.status,
bin_report.reason
);
let bin_reason = bin_report.reason.as_deref().unwrap_or("");
assert!(
bin_reason.contains("bin") || bin_reason.contains("link"),
"bin skip reason should explain why; got {bin_reason:?}"
);
}
#[test]
fn fixture_codegen_per_function_records_show_add_and_classify() {
let envelope = run_extract_on_fixture_with_codegen();
// The fixture defines exactly two functions. Both should appear in
// the per-function LLVM IR records, pre and post optimization.
for kind in ["llvm.preopt_func", "llvm.opt_func"] {
let records = envelope
.entities
.get(kind)
.unwrap_or_else(|| panic!("entities.{kind} missing"));
assert_eq!(
records.len(),
2,
"fixture defines two functions but {kind} has {} records",
records.len()
);
for r in records {
assert!(
r.metrics.values().all(|v| v.is_finite() && *v >= 0.0),
"all per-function metrics must be finite non-negative; got {:?}",
r.metrics
);
}
}
// Summary scalars must agree with the per-function records.
let summary_count = envelope
.vector
.get("llvm.preopt.function_count")
.expect("llvm.preopt.function_count populated");
assert_eq!(summary_count, 2.0);
let bb_sum = envelope
.vector
.get("llvm.preopt.basic_block_count.sum")
.expect("llvm.preopt.basic_block_count.sum populated");
let per_func_bb_sum: f64 = envelope
.entities
.get("llvm.preopt_func")
.unwrap()
.iter()
.map(|r| r.metrics.get("preopt_basic_blocks").copied().unwrap_or(0.0))
.sum();
assert_eq!(
bb_sum, per_func_bb_sum,
"vector sum must equal sum-over-entities"
);
// Assembly: the fixture has two functions, so the .s file should
// declare at least two function symbols.
let asm_funcs = envelope
.vector
.get("asm.global.function_count")
.expect("asm.global.function_count populated");
assert!(
asm_funcs >= 2.0,
"assembly should declare ≥ 2 functions, got {asm_funcs}"
);
}
#[test]
fn fixture_codegen_object_section_artifacts_are_observed() {
let envelope = run_extract_on_fixture_with_codegen();
// The fixture is lib-only, so its build artifact is an .rlib. The
// obj level should observe at least one rlib with non-trivial size.
let rlib_count = envelope
.vector
.get("obj.global.rlib_file_count")
.expect("obj.global.rlib_file_count populated");
assert!(rlib_count >= 1.0, "expected at least one rlib, got {rlib_count}");
let rlib_bytes = envelope
.vector
.get("obj.global.rlib_total_bytes")
.expect("obj.global.rlib_total_bytes populated");
assert!(rlib_bytes > 0.0, "rlib should not be empty");
}
// ───── Stage 7: binary-artifact extractors + AST↔Symbol join ────────────
/// The six binary-artifact level ids covered by Stage 7 (spec §2.6 25–30).
const STAGE7_LEVELS: &[&str] = &[
"sym.table",
"sym.demangled",
"sym.sections",
"sym.relocs",
"dwarf",
"link.map",
];
#[test]
fn stage_seven_binary_levels_skip_cleanly_without_codegen() {
// Without --enable-codegen there are no object artifacts to read,
// so every Stage 7 level must skip with a reason and emit no keys.
let envelope = run_extract_on_fixture();
for id in STAGE7_LEVELS {
let report = envelope.levels.get(*id).expect("level present");
assert_eq!(
report.status,
LevelStatus::Skipped,
"{id} should skip without codegen; got {:?}",
report.status
);
let reason = report.reason.as_deref().unwrap_or("");
assert!(!reason.is_empty(), "{id} skipped without a reason");
}
for key in envelope.vector.0.keys() {
for prefix in ["sym.", "dwarf.", "link.map."] {
assert!(
!key.starts_with(prefix),
"skipped binary level emitted {key} (spec §8 says skipped levels omit their keys)"
);
}
}
}
#[test]
fn stage_seven_binary_levels_report_ok_with_codegen_on_the_lib_fixture() {
let envelope = run_extract_on_fixture_with_codegen();
// sym.table, sym.demangled, sym.sections, sym.relocs, dwarf must
// all be ok on the fixture (which has a real .rlib with DWARF).
// link.map legitimately skips: no -Wl,-Map flag was passed.
for id in ["sym.table", "sym.demangled", "sym.sections", "sym.relocs", "dwarf"] {
let report = envelope.levels.get(id).expect("level present");
assert_eq!(
report.status,
LevelStatus::Ok,
"{id} expected ok with codegen; got {:?} (reason: {:?})",
report.status,
report.reason
);
}
let link = envelope.levels.get("link.map").expect("link.map present");
assert_eq!(
link.status,
LevelStatus::Skipped,
"link.map should skip on the lib-only fixture; got {:?}",
link.status
);
let reason = link.reason.as_deref().unwrap_or("");
assert!(
reason.contains("link") || reason.contains("Map"),
"link.map skip reason should mention linking; got {reason:?}"
);
}
#[test]
fn fixture_symbol_table_reports_at_least_the_two_pub_functions() {
let envelope = run_extract_on_fixture_with_codegen();
let func_count = envelope
.vector
.get("sym.global.function_count")
.expect("sym.global.function_count populated");
assert!(
func_count >= 2.0,
"fixture's two pub fns must produce ≥ 2 function symbols, got {func_count}"
);
// Per-function size distribution: max ≥ 0, mean ≥ 0, sum ≥ 0.
for key in [
"sym.func.size_bytes.max",
"sym.func.size_bytes.mean",
"sym.func.size_bytes.sum",
] {
let v = envelope.vector.get(key).unwrap_or_else(|| panic!("{key} missing"));
assert!(v.is_finite() && v >= 0.0, "{key} must be a finite non-negative; got {v}");
}
// Per-symbol entity records: one per function symbol with a SymbolId.
let records = envelope
.entities
.get("sym.func")
.expect("entities.sym.func populated");
assert!(
records.len() >= 2,
"expected ≥ 2 function-symbol records, got {}",
records.len()
);
for r in records {
assert!(
r.ids.symbol.is_some(),
"every sym.func record must carry a SymbolId; got {r:?}"
);
}
}
#[test]
fn fixture_section_layout_observes_text_and_debug_bytes() {
let envelope = run_extract_on_fixture_with_codegen();
let text = envelope
.vector
.get("sym.sections.text_bytes")
.expect("sym.sections.text_bytes populated");
assert!(text > 0.0, "fixture's two fns must produce some .text");
let debug = envelope
.vector
.get("sym.sections.debug_bytes")
.expect("sym.sections.debug_bytes populated");
assert!(debug > 0.0, "dev-profile build must carry DWARF debug sections");
let text_ratio = envelope
.vector
.get("sym.sections.text_ratio")
.expect("text_ratio populated");
assert!(
(0.0..=1.0).contains(&text_ratio),
"text_ratio must be a fraction; got {text_ratio}"
);
}
#[test]
fn fixture_dwarf_subprograms_match_the_two_pub_functions() {
let envelope = run_extract_on_fixture_with_codegen();
let n = envelope
.vector
.get("dwarf.global.subprogram_count")
.expect("dwarf.global.subprogram_count populated");
assert!(
n >= 2.0,
"fixture's two pub fns must show up as DWARF subprograms; got {n}"
);
let with_link = envelope
.vector
.get("dwarf.global.subprograms_with_linkage_count")
.expect("subprograms_with_linkage_count populated");
assert!(with_link >= 2.0, "both fns must have DW_AT_linkage_name");
}
/// §13 worked-example anchor: the `classify` entity has both an
/// `AstFuncId` and a `SymbolId`. The DWARF level is what joins them
/// (via DW_AT_name → DW_AT_linkage_name).
#[test]
fn fixture_ast_symbol_join_populates_both_ids_on_classify() {
let envelope = run_extract_on_fixture_with_codegen();
let records = envelope
.entities
.get("ast.func")
.expect("entities.ast.func populated by the DWARF level");
let classify = records
.iter()
.find(|r| {
r.ids
.ast_func
.as_ref()
.is_some_and(|id| id.fq_path.ends_with("::classify"))
})
.expect("classify entity present in entities.ast.func");
// Spec §6 / §13: both IDs populated.
assert!(
classify.ids.ast_func.is_some(),
"classify must carry AstFuncId"
);
let symbol = classify
.ids
.symbol
.as_ref()
.expect("classify must carry SymbolId after the DWARF join");
assert!(
symbol.as_str().contains("classify"),
"joined SymbolId must mention the source name; got {symbol:?}"
);
// The join's success rate scalar should reflect that both fns
// joined.
let success = envelope
.vector
.get("dwarf.global.join_success_count")
.expect("join_success_count populated");
let attempts = envelope
.vector
.get("dwarf.global.join_attempt_count")
.expect("join_attempt_count populated");
assert!(
attempts >= 2.0,
"two AST fns means at least two join attempts; got {attempts}"
);
assert!(
success >= 2.0,
"both fixture fns have unambiguous DWARF names; got success={success}"
);
}
#[test]
fn fixture_source_to_symbol_ratio_is_one_with_no_generics() {
// Spec §13: source_to_symbol_ratio = 1.0 when no generics are
// monomorphized. The fixture has no generics.
let envelope = run_extract_on_fixture_with_codegen();
let ratio = envelope
.vector
.get("sym.global.source_to_symbol_ratio")
.expect("sym.global.source_to_symbol_ratio populated");
assert!(
(ratio - 1.0).abs() < 1e-9,
"expected source-to-symbol ratio = 1.0 (no generics); got {ratio}"
);
let generics = envelope
.vector
.get("sym.demangled.generic_instantiation_count")
.expect("generic_instantiation_count populated");
assert_eq!(generics, 0.0, "fixture has no generics");
}
// ───── Stage 8: type-system extractors (stable side) ────────────────────
/// All three Stage 8 level ids — `type.usage` (AST approximation),
/// `type.trait_impl`, and `type.coherence` (rustdoc-JSON-derived).
const STAGE8_LEVELS: &[&str] = &["type.usage", "type.trait_impl", "type.coherence"];
#[test]
fn stage_eight_levels_appear_with_a_documented_status() {
let envelope = run_extract_on_fixture();
for id in STAGE8_LEVELS {
let report = envelope
.levels
.get(*id)
.unwrap_or_else(|| panic!("level {id} missing"));
match report.status {
LevelStatus::Ok => {}
LevelStatus::Skipped | LevelStatus::Failed => {
assert!(
report.reason.as_ref().is_some_and(|r| !r.is_empty()),
"non-ok stage-8 level {id} has empty reason"
);
}
}
}
}
#[test]
fn fixture_type_usage_observes_the_two_pub_functions() {
// type.usage is AST-derived, so it must be `ok` on every well-formed
// fixture — no nightly prerequisite.
let envelope = run_extract_on_fixture();
let report = envelope.levels.get("type.usage").expect("type.usage present");
assert_eq!(
report.status,
LevelStatus::Ok,
"type.usage is AST-derived; should be ok (reason: {:?})",
report.reason
);
assert_eq!(
envelope.vector.get("type.usage.function_count"),
Some(2.0),
"fixture has exactly two functions"
);
// Both functions reference `i32`; `classify` also uses `str`. Distinct
// types overall is therefore at least 2.
let overall = envelope
.vector
.get("type.usage.distinct_types_overall")
.expect("type.usage.distinct_types_overall populated");
assert!(overall >= 2.0, "expected ≥ 2 distinct types overall, got {overall}");
let primitives = envelope
.vector
.get("type.usage.primitive_types_overall")
.expect("primitive_types_overall populated");
assert!(primitives >= 1.0, "expected i32 to register as a primitive");
// Per-entity records: one per function, each with an AstFuncId.
let records = envelope
.entities
.get("type.usage_func")
.expect("entities.type.usage_func populated");
assert_eq!(records.len(), 2, "two functions, two records");
for r in records {
assert!(
r.ids.ast_func.is_some(),
"type.usage_func record must carry an AstFuncId; got {r:?}"
);
let distinct = r
.metrics
.get("distinct_types")
.copied()
.expect("distinct_types metric");
assert!(distinct >= 1.0, "every fn touches at least one type");
}
}
#[test]
fn rustdoc_levels_are_ok_or_skipped_with_a_reason() {
let envelope = run_extract_on_fixture();
for id in ["type.trait_impl", "type.coherence"] {
let report = envelope.levels.get(id).expect("level present");
match report.status {
LevelStatus::Ok => {
// When ok, the level emits at least its impl_count /
// quadrant scalars (zero is fine; "populated" is the
// contract, "non-zero" is fixture-dependent).
let prefix = format!("{id}.");
let any = envelope.vector.0.keys().any(|k| k.starts_with(&prefix));
assert!(any, "ok {id} must emit at least one vector key");
}
LevelStatus::Skipped => {
assert!(
report.reason.as_ref().is_some_and(|r| !r.is_empty()),
"skipped {id} must explain itself"
);
let prefix = format!("{id}.");
let leaked = envelope.vector.0.keys().find(|k| k.starts_with(&prefix));
assert!(
leaked.is_none(),
"skipped level {id} leaked key {leaked:?} (spec §8)"
);
}
LevelStatus::Failed => panic!(
"{id} failed on a well-formed fixture (reason: {:?})",
report.reason
),
}
}
}
#[test]
fn with_impls_fixture_classifies_orphan_rule_quadrants() {
// The `with-impls` fixture is the load-bearing scenario for level 19:
// - 2 local-trait + local-type impls (Describe / Reverse for Counter)
// - 1 foreign-trait + local-type impl (Display for Counter)
// - 0 local-trait + foreign-type impls
// - 0 foreign-trait + foreign-type impls (no blankets that count)
let envelope = run_extract_on_with_impls_fixture();
let trait_impl = envelope.levels.get("type.trait_impl").expect("level present");
let coherence = envelope.levels.get("type.coherence").expect("level present");
// If nightly is unavailable both levels skip — only run the
// substantive checks when rustdoc actually ran. Either outcome is
// acceptable to spec §11.
if !matches!(trait_impl.status, LevelStatus::Ok) {
return;
}
assert_eq!(coherence.status, LevelStatus::Ok);
// Level 17: at least the 2 local traits + 1 inherent + 3 user impls
// are visible; rustdoc additionally synthesizes auto-trait + blanket
// impls, so the totals will be larger than the source-visible count.
let trait_count = envelope
.vector
.get("type.trait_impl.trait_count")
.expect("trait_count populated");
assert!(
trait_count >= 2.0,
"fixture defines two local traits; got {trait_count}"
);
let inherent = envelope
.vector
.get("type.trait_impl.inherent_impl_count")
.expect("inherent_impl_count populated");
assert!(
inherent >= 1.0,
"fixture defines an inherent impl on Counter; got {inherent}"
);
// Level 19: the user-written orphan-rule quadrants. We exclude
// synthetic and blanket impls so these counts reflect the source.
let llt = envelope
.vector
.get("type.coherence.local_trait_local_type")
.expect("local_trait_local_type populated");
let flt = envelope
.vector
.get("type.coherence.foreign_trait_local_type")
.expect("foreign_trait_local_type populated");
let lft = envelope
.vector
.get("type.coherence.local_trait_foreign_type")
.expect("local_trait_foreign_type populated");
let fft = envelope
.vector
.get("type.coherence.foreign_trait_foreign_type")
.expect("foreign_trait_foreign_type populated");
assert_eq!(llt, 2.0, "Describe + Reverse for Counter");
assert_eq!(flt, 1.0, "Display for Counter");
assert_eq!(lft, 0.0, "no local-trait + foreign-type impls in fixture");
assert_eq!(fft, 0.0, "no foreign-trait + foreign-type impls in fixture");
}
// ───── Stage 9: graph-derived extractors ────────────────────────────────
/// The eight graph-derived level ids covered by Stage 9 (spec §2.7
/// 31, 32, 34, 36–40). Levels 33 (mono call graph) and 35 (data-flow)
/// land in Stage 14.
const STAGE9_LEVELS: &[&str] = &[
"graph.mod",
"graph.call_static",
"graph.cfg",
"graph.type_usage",
"graph.sccs",
"graph.communities",
"graph.power_law",
"graph.self_sim",
];
#[test]
fn stage_nine_graph_levels_report_ok_on_a_well_formed_fixture() {
let envelope = run_extract_on_fixture();
for id in STAGE9_LEVELS {
let report = envelope
.levels
.get(*id)
.unwrap_or_else(|| panic!("level {id} missing"));
assert_eq!(
report.status,
LevelStatus::Ok,
"stage-9 level {id} expected ok; got {:?} (reason: {:?})",
report.status,
report.reason
);
// Each ok level must contribute at least one vector key.
let prefix = format!("{id}.");
let any = envelope.vector.0.keys().any(|k| k.starts_with(&prefix));
assert!(any, "ok level {id} emitted no vector keys");
}
}
#[test]
fn fixture_call_and_mod_graphs_are_empty_with_no_uses_or_calls() {
// The §13 fixture has a single file with two top-level fns that
// never call each other. Spec §13 anchor: graph.call.edge_count = 0.
let envelope = run_extract_on_fixture();
assert_eq!(
envelope.vector.get("graph.call_static.edge_count"),
Some(0.0),
"fixture's two fns don't call each other"
);
assert_eq!(
envelope.vector.get("graph.call_static.node_count"),
Some(2.0),
"fixture has exactly two function nodes"
);
// Single-file crate → one module node, no edges.
assert_eq!(
envelope.vector.get("graph.mod.edge_count"),
Some(0.0),
"single-file crate has no use-clause edges"
);
}
#[test]
fn with_graphs_fixture_observes_three_modules_and_call_edges() {
let envelope = run_extract_on_with_graphs_fixture();
// Module graph: three modules (`crate`, `crate::helpers`,
// `crate::compute`) with two use-driven edges (helpers→compute and
// compute→helpers).
let mod_nodes = envelope
.vector
.get("graph.mod.node_count")
.expect("graph.mod.node_count populated");
assert_eq!(mod_nodes, 3.0, "fixture has three modules; got {mod_nodes}");
let mod_edges = envelope
.vector
.get("graph.mod.edge_count")
.expect("graph.mod.edge_count populated");
assert!(
mod_edges >= 2.0,
"fixture's reciprocal `use` clauses must produce ≥ 2 edges; got {mod_edges}"
);
// Call graph: 5 functions, multiple calls. Edge count must be > 0
// and node count must be 5.
let call_nodes = envelope
.vector
.get("graph.call_static.node_count")
.expect("graph.call_static.node_count populated");
assert_eq!(call_nodes, 5.0, "fixture has 5 functions; got {call_nodes}");
let call_edges = envelope
.vector
.get("graph.call_static.edge_count")
.expect("graph.call_static.edge_count populated");
assert!(call_edges >= 4.0, "fixture has multiple calls; got {call_edges}");
}
#[test]
fn with_graphs_fixture_per_function_cfg_records_carry_ast_func_ids() {
let envelope = run_extract_on_with_graphs_fixture();
let records = envelope
.entities
.get("graph.cfg_func")
.expect("entities.graph.cfg_func populated");
// Five functions: run, clamp, boosted, scale, quadratic.
assert_eq!(records.len(), 5, "five fns → five CFG records");
for r in records {
assert!(
r.ids.ast_func.is_some(),
"every CFG record must carry an AstFuncId; got {r:?}"
);
let nodes = r
.metrics
.get("nodes")
.copied()
.expect("CFG record has node count");
assert!(nodes >= 1.0, "every fn has at least one basic block; got {nodes}");
}
// `clamp` has two `if`/`else if` branches → branch_count ≥ 2.
let clamp = records
.iter()
.find(|r| {
r.ids
.ast_func
.as_ref()
.is_some_and(|id| id.fq_path.ends_with("::clamp"))
})
.expect("clamp fn present");
let branches = clamp
.metrics
.get("branches")
.copied()
.expect("clamp.branches");
assert!(
branches >= 2.0,
"clamp has if/else-if/else; expected ≥ 2 branches, got {branches}"
);
}
#[test]
fn fixture_type_usage_graph_populates_function_and_type_counts() {
let envelope = run_extract_on_fixture();
let funcs = envelope
.vector
.get("graph.type_usage.function_count")
.expect("graph.type_usage.function_count populated");
let types = envelope
.vector
.get("graph.type_usage.type_count")
.expect("graph.type_usage.type_count populated");
let edges = envelope
.vector
.get("graph.type_usage.edge_count")
.expect("graph.type_usage.edge_count populated");
assert_eq!(funcs, 2.0, "fixture has two functions");
assert!(
types >= 2.0,
"fixture references at least i32 and str; got {types}"
);
assert!(
edges >= 2.0,
"fixture has at least 2 fn→type edges; got {edges}"
);
}
#[test]
fn fixture_scc_and_community_metrics_are_well_formed() {
let envelope = run_extract_on_with_graphs_fixture();
// Every fn-node is its own SCC (call graph has cycles only via
// recursion, which the fixture doesn't have). So SCC count == node count.
let n = envelope
.vector
.get("graph.call_static.node_count")
.expect("call_static node_count populated");
let scc_count = envelope
.vector
.get("graph.sccs.call_static.count")
.expect("graph.sccs.call_static.count populated");
assert_eq!(scc_count, n, "no recursion → one SCC per fn");
// No non-trivial SCCs (no cycles).
assert_eq!(
envelope
.vector
.get("graph.sccs.call_static.nontrivial_count"),
Some(0.0),
"fixture has no recursive calls"
);
// Community detection emits a modularity Q in [-0.5, 1.0]; check the
// value is finite and in-range.
let q = envelope
.vector
.get("graph.communities.call_static.modularity_q")
.expect("modularity_q populated");
assert!(q.is_finite(), "modularity Q must be finite; got {q}");
assert!(
(-0.5..=1.0).contains(&q),
"modularity Q must lie in [-0.5, 1.0]; got {q}"
);
}
#[test]
fn fixture_power_law_and_self_sim_emit_finite_scalars() {
let envelope = run_extract_on_with_graphs_fixture();
let alpha = envelope
.vector
.get("graph.power_law.call_static.degree_exponent")
.expect("degree_exponent populated");
assert!(alpha.is_finite(), "α must be finite; got {alpha}");
assert!(alpha >= 0.0, "α must be non-negative; got {alpha}");
let n = envelope
.vector
.get("graph.power_law.call_static.sample_count")
.expect("sample_count populated");
assert!(n >= 0.0, "sample_count must be non-negative; got {n}");
for key in [
"graph.self_sim.branch.func_vs_file.ks_distance",
"graph.self_sim.branch.func_vs_module.ks_distance",
"graph.self_sim.branch.file_vs_module.ks_distance",
] {
let d = envelope.vector.get(key).expect("ks distance populated");
assert!(d.is_finite(), "{key} must be finite; got {d}");
assert!(
(0.0..=1.0).contains(&d),
"{key} must be a KS distance in [0,1]; got {d}"
);
}
}
#[test]
fn graph_level_vector_keys_use_their_level_namespace() {
// Spec §5: vector keys are namespaced by their level's id. Anchor
// test 5 (no two levels claim the same key) is already exercised
// generally by the provenance test; this one specifically asserts
// that every Stage-9 emission lives under its level's prefix.
let envelope = run_extract_on_with_graphs_fixture();
for (key, _) in envelope.vector.iter() {
for id in STAGE9_LEVELS {
// If a key is claimed by `id`, it must start with `id.`.
let owner = envelope
.provenance
.0
.get(key)
.and_then(|v| v.first())
.map(|s| s.as_str())
.unwrap_or("");
if owner == *id {
let prefix = format!("{id}.");
assert!(
key.starts_with(&prefix),
"key {key:?} owned by {id} should live under {prefix:?}"
);
}
}
}
}
// ───── Stage 10: external-tool extractors ──────────────────────────────
/// Spec §2.9 levels covered by Stage 10. Each may be `ok` or
/// `skipped`; on a well-formed fixture none should be `failed`.
const STAGE10_LEVELS: &[&str] = &["clippy", "rustdoc", "tests", "doctests"];
#[test]
fn stage_ten_levels_appear_with_a_documented_status() {
let envelope = run_extract_on_fixture();
for id in STAGE10_LEVELS {
let report = envelope
.levels
.get(*id)
.unwrap_or_else(|| panic!("level {id} missing from envelope"));
match report.status {
LevelStatus::Ok | LevelStatus::Skipped => {
if report.status == LevelStatus::Skipped {
assert!(
report.reason.as_ref().is_some_and(|r| !r.is_empty()),
"skipped {id} must explain itself"
);
}
}
LevelStatus::Failed => panic!(
"stage-10 level {id} should not fail on a well-formed fixture (reason: {:?})",
report.reason
),
}
}
}
#[test]
fn stage_ten_levels_obey_the_omission_contract() {
// Spec §8: skipped/failed levels must not leak vector keys under
// their level-id prefix. Ok levels must emit at least one key.
let envelope = run_extract_on_fixture();
for id in STAGE10_LEVELS {
let report = envelope.levels.get(*id).expect("level present");
let prefix = format!("{id}.");
let any_key = envelope.vector.0.keys().any(|k| k.starts_with(&prefix));
match report.status {
LevelStatus::Ok => assert!(any_key, "ok level {id} emitted no keys"),
LevelStatus::Skipped => {
let leaked = envelope.vector.0.keys().find(|k| k.starts_with(&prefix));
assert!(
leaked.is_none(),
"skipped level {id} leaked key {leaked:?} (spec §8)"
);
}
LevelStatus::Failed => unreachable!("ruled out by previous test"),
}
}
}
#[test]
fn fixture_tests_and_doctests_inventories_show_zero_on_the_lib_fixture() {
// The §13 fixture has no `#[test]` functions and no doctests. Spec
// §8: ok levels with zero matter still must populate their summary
// keys (so the consumer can distinguish "no tests" from "level
// didn't run"). If the level is skipped on this host that's also
// ok — bail early.
let envelope = run_extract_on_fixture();
let tests_report = envelope.levels.get("tests").expect("tests level present");
if matches!(tests_report.status, LevelStatus::Ok) {
let count = envelope
.vector
.get("tests.test_count")
.expect("tests.test_count populated");
assert_eq!(count, 0.0, "fixture defines no tests; got {count}");
let binaries = envelope
.vector
.get("tests.binary_count")
.expect("tests.binary_count populated");
assert!(
binaries >= 1.0,
"even an empty lib produces one test binary; got {binaries}"
);
}
let dt_report = envelope
.levels
.get("doctests")
.expect("doctests level present");
if matches!(dt_report.status, LevelStatus::Ok) {
let count = envelope
.vector
.get("doctests.test_count")
.expect("doctests.test_count populated");
assert_eq!(count, 0.0, "fixture has no doctests; got {count}");
}
}
#[test]
fn fixture_clippy_level_yields_finite_lint_counts() {
// The §13 fixture is trivially clean — clippy may produce zero or a
// handful of pedantic lints depending on the host's clippy version.
// The contract: when ok, the summary scalars exist and are finite.
let envelope = run_extract_on_fixture();
let report = envelope.levels.get("clippy").expect("clippy level present");
if !matches!(report.status, LevelStatus::Ok) {
return;
}
for key in [
"clippy.message_count",
"clippy.lint_count",
"clippy.warning_count",
"clippy.error_count",
"clippy.distinct_lint_names",
] {
let v = envelope
.vector
.get(key)
.unwrap_or_else(|| panic!("{key} missing"));
assert!(v.is_finite() && v >= 0.0, "{key} must be a finite count; got {v}");
}
// The fixture compiles, so no errors.
assert_eq!(
envelope.vector.get("clippy.error_count"),
Some(0.0),
"fixture must compile cleanly under clippy"
);
}
#[test]
fn fixture_rustdoc_inventory_reports_two_local_functions_when_available() {
// Level 46 needs nightly to produce rustdoc JSON. When ok we expect
// the two pub fns (add + classify) to show up as `function` kind
// entries; when skipped the level explains itself.
let envelope = run_extract_on_fixture();
let report = envelope.levels.get("rustdoc").expect("rustdoc level present");
match report.status {
LevelStatus::Ok => {
let fns = envelope
.vector
.get("rustdoc.kind.function")
.expect("rustdoc.kind.function populated when level is ok");
assert!(
fns >= 2.0,
"fixture has two pub functions; rustdoc should list at least that many, got {fns}"
);
let local = envelope
.vector
.get("rustdoc.local_item_count")
.expect("rustdoc.local_item_count populated");
assert!(local >= 2.0, "local_item_count must include the two fns");
}
LevelStatus::Skipped => {
assert!(
report.reason.as_ref().is_some_and(|r| !r.is_empty()),
"skipped rustdoc level must explain itself"
);
}
LevelStatus::Failed => panic!("rustdoc failed on a clean fixture: {:?}", report.reason),
}
}
// ───── Stage 13: rustc-derived Level impls (nightly subprocess) ────────
/// The seven nightly-IR level ids covered by Stage 13 (spec §2.2 #10,
/// §2.3 #11–15, §2.4 #18).
const STAGE13_LEVELS: &[&str] = &[
"ast.resolved",
"hir",
"thir",
"mir.preopt",
"mir.opt",
"borrowck",
"type.mono",
];
#[test]
fn stage_thirteen_levels_skip_cleanly_without_the_flag() {
// Without --enable-rustc-internal the seven IR levels must all
// report Skipped with a reason that tells the user how to enable
// them. Spec §8 omission contract: no IR vector keys leak.
let envelope = run_extract_on_fixture();
for id in STAGE13_LEVELS {
let report = envelope.levels.get(*id).expect("level present");
assert_eq!(
report.status,
LevelStatus::Skipped,
"{id} should skip when rustc-internal extraction is disabled; got {:?}",
report.status
);
let reason = report.reason.as_deref().unwrap_or("");
assert!(
!reason.is_empty(),
"{id} skipped without a reason — user has no way to know how to enable it"
);
}
for key in envelope.vector.0.keys() {
for prefix in [
"ast.resolved.",
"hir.",
"thir.",
"mir.preopt.",
"mir.opt.",
"borrowck.",
"type.mono.",
] {
assert!(
!key.starts_with(prefix),
"skipped rustc-internal level emitted {key} (spec §8 says skipped levels omit their keys)"
);
}
}
}
/// Shared --enable-rustc-internal extract on the fixture. Cached so
/// the several scenario tests below don't each pay the cost of
/// building and running the nightly sub-binary.
fn run_extract_on_fixture_with_rustc_internal() -> &'static OracleOutput {
static CACHE: OnceLock<OracleOutput> = OnceLock::new();
CACHE.get_or_init(|| {
let fixture = workspace_root().join("examples").join("single-file-lib");
assert!(fixture.is_dir(), "fixture missing: {fixture:?}");
run_extract_with(&fixture, &["--enable-rustc-internal"])
})
}
#[test]
fn stage_thirteen_levels_appear_with_a_documented_status_when_enabled() {
// With --enable-rustc-internal each level is either ok (nightly
// present and the extractor ran) or skipped (nightly absent, or
// the sub-binary couldn't be located/built on this host). Failed
// is acceptable too — the spec §11 contract is "documented
// status with non-empty reason", not "guaranteed ok".
let envelope = run_extract_on_fixture_with_rustc_internal();
for id in STAGE13_LEVELS {
let report = envelope.levels.get(*id).expect("level present");
match report.status {
LevelStatus::Ok => {
let prefix = format!("{id}.");
let any = envelope.vector.0.keys().any(|k| k.starts_with(&prefix));
assert!(any, "ok {id} must emit at least one vector key");
}
LevelStatus::Skipped | LevelStatus::Failed => {
assert!(
report.reason.as_ref().is_some_and(|r| !r.is_empty()),
"{id} non-ok must explain itself"
);
let prefix = format!("{id}.");
let leaked = envelope.vector.0.keys().find(|k| k.starts_with(&prefix));
assert!(
leaked.is_none(),
"{id} {:?} leaked key {leaked:?} (spec §8)",
report.status
);
}
}
}
}
#[test]
fn stage_thirteen_levels_share_one_reason_when_nightly_missing() {
// If nightly isn't available, all seven IR levels must report the
// same reason — the single subprocess invocation skipped once and
// every level routed the same diagnostic back. This stops the
// user from seeing seven different complaints when there is only
// one underlying cause.
let envelope = run_extract_on_fixture_with_rustc_internal();
let reasons: BTreeSet<String> = STAGE13_LEVELS
.iter()
.map(|id| {
envelope
.levels
.get(*id)
.and_then(|r| r.reason.clone())
.unwrap_or_default()
})
.collect();
let any_ok = STAGE13_LEVELS.iter().any(|id| {
envelope
.levels
.get(*id)
.map(|r| r.status == LevelStatus::Ok)
.unwrap_or(false)
});
if !any_ok {
assert_eq!(
reasons.len(),
1,
"every IR level should share one reason when the cache fails once; got {reasons:?}"
);
}
}
#[test]
fn stage_thirteen_fixture_reflects_two_functions_when_ok() {
// When nightly is available and the sub-binary runs, the fixture
// (two pub fns: add + classify) must produce observable signals:
// ≥ 2 HIR fns, ≥ 2 THIR bodies, ≥ 2 MIR bodies (pre and opt), and
// ≥ 2 mono items. Exact values are rustc-version-sensitive; we
// lower-bound them.
let envelope = run_extract_on_fixture_with_rustc_internal();
let hir_status = &envelope.levels.get("hir").expect("hir present").status;
if !matches!(hir_status, LevelStatus::Ok) {
// Nightly unavailable or extractor couldn't run; the previous
// tests already cover that case.
return;
}
let hir_fns = envelope
.vector
.get("hir.fn_count")
.expect("hir.fn_count populated when ok");
assert!(
hir_fns >= 2.0,
"fixture has two pub fns; hir.fn_count must be ≥ 2, got {hir_fns}"
);
let thir_bodies = envelope
.vector
.get("thir.body_count")
.expect("thir.body_count populated when ok");
assert!(
thir_bodies >= 2.0,
"thir.body_count must reflect both fixture fns, got {thir_bodies}"
);
for level_id in ["mir.preopt", "mir.opt"] {
let key = format!("{level_id}.body_count");
let n = envelope
.vector
.get(&key)
.unwrap_or_else(|| panic!("{key} populated when ok"));
assert!(n >= 2.0, "{key} must be ≥ 2, got {n}");
// classify has two if-expressions → ≥ 4 basic blocks in MIR.
let bb_max_key = format!("{level_id}.basic_block_count.max");
let bb_max = envelope
.vector
.get(&bb_max_key)
.unwrap_or_else(|| panic!("{bb_max_key} populated when ok"));
assert!(
bb_max >= 4.0,
"{bb_max_key} must capture classify's branching (≥ 4); got {bb_max}"
);
}
let mono_items = envelope
.vector
.get("type.mono.item_count")
.expect("type.mono.item_count populated when ok");
assert!(
mono_items >= 2.0,
"fixture's two non-generic fns must produce ≥ 2 mono items, got {mono_items}"
);
// Non-generic fns yield exactly one instantiation each → no group
// has more than one instance.
let multi = envelope
.vector
.get("type.mono.multi_instance_group_count")
.expect("type.mono.multi_instance_group_count populated when ok");
assert_eq!(
multi, 0.0,
"fixture has no generics; multi-instance groups must be 0"
);
// HIR expr-kind histogram must include "if" with ≥ 2 (classify has
// two if-expressions).
let if_kinds = envelope
.vector
.get("hir.expr_kind.if")
.unwrap_or(0.0);
assert!(
if_kinds >= 2.0,
"hir.expr_kind.if should reflect classify's two ifs, got {if_kinds}"
);
// borrowck and ast.resolved levels emit at least their core scalars.
let region_sum = envelope
.vector
.get("borrowck.region_count.sum")
.expect("borrowck.region_count.sum populated when ok");
assert!(
region_sum.is_finite() && region_sum >= 0.0,
"borrowck.region_count.sum must be finite non-negative, got {region_sum}"
);
let imports = envelope
.vector
.get("ast.resolved.import_count")
.expect("ast.resolved.import_count populated when ok");
assert!(
imports.is_finite() && imports >= 0.0,
"ast.resolved.import_count must be finite non-negative, got {imports}"
);
}
// ───── Stage 14: derived cross-level extractors (33, 35) ────────────────
/// The two Stage-14 derived levels (spec §2.7 #33 + #35).
const STAGE14_LEVELS: &[&str] = &["graph.call_mono", "graph.dataflow"];
#[test]
fn stage_fourteen_levels_skip_cleanly_without_codegen() {
// The mono-call-graph and per-function dataflow levels both compose
// codegen-side artifacts. Without `--enable-codegen` they must skip
// with a documented reason and contribute no vector keys (spec §8).
let envelope = run_extract_on_fixture();
for id in STAGE14_LEVELS {
let report = envelope.levels.get(*id).expect("level present");
assert_eq!(
report.status,
LevelStatus::Skipped,
"{id} should skip without codegen; got {:?}",
report.status
);
let reason = report.reason.as_deref().unwrap_or("");
assert!(!reason.is_empty(), "{id} skipped without a reason");
}
for key in envelope.vector.0.keys() {
assert!(
!key.starts_with("graph.call_mono.") && !key.starts_with("graph.dataflow."),
"skipped Stage-14 level emitted {key} (spec §8 says skipped levels omit their keys)"
);
}
}
#[test]
fn stage_fourteen_levels_report_ok_with_codegen_on_the_fixture() {
let envelope = run_extract_on_fixture_with_codegen();
for id in STAGE14_LEVELS {
let report = envelope.levels.get(*id).expect("level present");
assert_eq!(
report.status,
LevelStatus::Ok,
"{id} expected ok with codegen; got {:?} (reason: {:?})",
report.status,
report.reason
);
}
}
#[test]
fn fixture_call_mono_graph_observes_at_least_the_two_pub_functions() {
// Spec §7 Stage 14 done-when: mono-call-graph edges and per-function
// dataflow density scalars appear in the vector for the fixture.
// The fixture has two top-level pub fns; the codegen build emits
// each as its own LLVM function, so the mono node count is ≥ 2 and
// every node carries both a SymbolId and a MonoId.
let envelope = run_extract_on_fixture_with_codegen();
let node_count = envelope
.vector
.get("graph.call_mono.node_count")
.expect("graph.call_mono.node_count populated");
assert!(
node_count >= 2.0,
"two pub fns must produce ≥ 2 mono nodes, got {node_count}"
);
// The fixture has no cross-fn calls, so the in-graph edge count is
// 0 and there are exactly as many leaves as nodes (every fn is a
// leaf in its own call graph). External calls (to panic helpers,
// intrinsics, etc.) may still register on the external side.
let edge_count = envelope
.vector
.get("graph.call_mono.edge_count")
.expect("graph.call_mono.edge_count populated");
assert!(
edge_count.is_finite() && edge_count >= 0.0,
"edge_count must be finite ≥ 0, got {edge_count}"
);
let leaf_count = envelope
.vector
.get("graph.call_mono.leaf_count")
.expect("graph.call_mono.leaf_count populated");
assert!(leaf_count >= 2.0, "≥ 2 leaves expected, got {leaf_count}");
let recursive = envelope
.vector
.get("graph.call_mono.recursive_node_count")
.expect("graph.call_mono.recursive_node_count populated");
assert_eq!(recursive, 0.0, "fixture has no recursion; got {recursive}");
// Degree summaries must be finite even on graphs with zero edges.
for key in [
"graph.call_mono.in_degree.max",
"graph.call_mono.in_degree.mean",
"graph.call_mono.out_degree.max",
"graph.call_mono.out_degree.mean",
"graph.call_mono.density",
] {
let v = envelope
.vector
.get(key)
.unwrap_or_else(|| panic!("{key} missing"));
assert!(v.is_finite() && v >= 0.0, "{key} must be finite ≥ 0; got {v}");
}
// Per-node entities: each carries SymbolId + MonoId per spec §6.
let records = envelope
.entities
.get("graph.call_mono_node")
.expect("entities.graph.call_mono_node populated when ok");
assert!(
records.len() >= 2,
"expected ≥ 2 mono-node records, got {}",
records.len()
);
for r in records {
assert!(
r.ids.symbol.is_some(),
"every mono-node record must carry a SymbolId; got {r:?}"
);
assert!(
r.ids.mono.is_some(),
"every mono-node record must carry a MonoId; got {r:?}"
);
}
}
#[test]
fn fixture_dataflow_density_is_a_finite_per_function_scalar() {
let envelope = run_extract_on_fixture_with_codegen();
let n = envelope
.vector
.get("graph.dataflow.function_count")
.expect("graph.dataflow.function_count populated");
assert!(n >= 2.0, "at least two functions expected, got {n}");
// Aggregate scalars must be finite and well-typed. density.mean is
// dimensionless (edges per instruction) but, in pathological cases
// with heavy reuse, can exceed 1 — only the finite+non-negative
// contract is load-bearing.
for key in [
"graph.dataflow.def_count.sum",
"graph.dataflow.def_count.mean",
"graph.dataflow.def_count.max",
"graph.dataflow.use_count.sum",
"graph.dataflow.use_count.mean",
"graph.dataflow.use_count.max",
"graph.dataflow.edge_count.sum",
"graph.dataflow.edge_count.mean",
"graph.dataflow.edge_count.max",
"graph.dataflow.density.mean",
"graph.dataflow.density.max",
] {
let v = envelope
.vector
.get(key)
.unwrap_or_else(|| panic!("{key} missing"));
assert!(v.is_finite() && v >= 0.0, "{key} must be finite ≥ 0; got {v}");
}
// At least one function in this fixture (classify) is non-trivial
// and must have a non-zero def-use edge count.
let edge_max = envelope
.vector
.get("graph.dataflow.edge_count.max")
.unwrap();
assert!(
edge_max > 0.0,
"fixture's classify body must have some def-use chain, got max={edge_max}"
);
// Per-function entity records: density present, finite, ≥ 0.
let records = envelope
.entities
.get("graph.dataflow_func")
.expect("entities.graph.dataflow_func populated when ok");
assert!(
records.len() >= 2,
"expected ≥ 2 dataflow records, got {}",
records.len()
);
for r in records {
let density = r
.metrics
.get("density")
.copied()
.unwrap_or_else(|| panic!("per-fn density missing: {r:?}"));
assert!(
density.is_finite() && density >= 0.0,
"per-fn density must be finite ≥ 0; got {density}"
);
assert!(
r.ids.symbol.is_some(),
"every dataflow record must carry a SymbolId; got {r:?}"
);
}
}
#[test]
fn stage_fourteen_keys_obey_their_level_namespace() {
// Spec §4 + §7: each level owns its key prefix; provenance must
// attribute each Stage-14 key to exactly its owning level.
let envelope = run_extract_on_fixture_with_codegen();
for (key, owners) in &envelope.provenance.0 {
if key.starts_with("graph.call_mono.") {
assert_eq!(
owners,
&vec!["graph.call_mono".to_string()],
"{key} should be owned only by graph.call_mono; got {owners:?}"
);
}
if key.starts_with("graph.dataflow.") {
assert_eq!(
owners,
&vec!["graph.dataflow".to_string()],
"{key} should be owned only by graph.dataflow; got {owners:?}"
);
}
}
}