use std::path::{Path, PathBuf};
use meta_ast::LangId;
use meta_ast::model::FileExtraction;
fn read_source(relative: &str) -> String {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(relative);
std::fs::read_to_string(&path)
.unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display()))
}
fn rust_sources(dir: &Path, out: &mut Vec<PathBuf>) {
for entry in std::fs::read_dir(dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.is_dir() {
rust_sources(&path, out);
} else if path.extension().is_some_and(|extension| extension == "rs") {
out.push(path);
}
}
}
fn repository_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
#[test]
fn file_extraction_is_constructed_through_one_constructor() {
let mut sources = Vec::new();
rust_sources(&repository_root().join("src"), &mut sources);
let mut literals = Vec::new();
for path in sources {
if path.ends_with("src/model/mod.rs") {
continue;
}
let source = std::fs::read_to_string(&path).unwrap();
let has_literal = source.lines().any(|line| line.trim() == "FileExtraction {");
if has_literal {
literals.push(path);
}
}
assert!(
literals.is_empty(),
"a file extraction must be built through FileExtraction::empty, found literals in {literals:?}"
);
let model = read_source("src/model/mod.rs");
assert_eq!(
model.matches("pub fn empty(").count(),
1,
"exactly one constructor builds a file extraction"
);
for (feature, field) in [
("metacall-deploy", "call_sites"),
("dataflow", "data_nodes"),
("dataflow", "flow_edges"),
] {
let marker = format!("#[cfg(feature = \"{feature}\")]\n {field}:");
assert!(
model.contains(&marker),
"the constructor must set the gated field {field}"
);
}
}
#[test]
fn gated_fields_start_empty() {
let extraction = FileExtraction::empty(PathBuf::from("a.py"), LangId::Python);
assert!(extraction.symbols.is_empty());
assert!(extraction.imports.is_empty());
assert!(extraction.references.is_empty());
assert!(extraction.diagnostics.is_empty());
assert_eq!(extraction.ast_node_count, 0);
#[cfg(feature = "metacall-deploy")]
assert!(extraction.call_sites.is_empty());
#[cfg(feature = "dataflow")]
{
assert!(extraction.data_nodes.is_empty());
assert!(extraction.flow_edges.is_empty());
}
}
#[test]
fn public_output_records_are_non_exhaustive() {
let graph_output = read_source("src/output/graph.rs");
for name in [
"pub struct GraphOutput {",
"pub struct GraphMetadata {",
"pub struct SerializedNode {",
"pub struct SerializedEdge {",
"pub struct SerializedScc {",
"pub struct DeployabilityStats {",
] {
let marker = format!("#[non_exhaustive]\n{name}");
assert!(
graph_output.contains(&marker),
"{name} must be non_exhaustive"
);
}
}