Files
core/test/rust/native/inspection.rs
T
2026-11-03 03:32:07 +00:00

176 lines
5.9 KiB
Rust

#[path = "inspection/documentation.rs"]
mod documentation;
use super::Directory;
use dever::compiler::project::Project;
use std::process::Command;
#[path = "inspection/dispatch.rs"]
mod dispatch;
#[path = "inspection/flows.rs"]
mod flows;
#[path = "inspection/links.rs"]
mod links;
#[test]
fn inspection_preserves_multiple_callers_sites_and_utf16_columns() {
let directory = Directory::new();
let path = directory.0.join("main.dever");
let source = "label(name: Text) (text: Text) {\n text = name\n}\n\nfirst(name: Text) (text: Text) {\n text = \"😀\" + label(name) + label(\"另\")\n}\n\nsecond(name: Text) (text: Text) {\n text = label(name)\n}\n";
std::fs::write(&path, source).unwrap();
let project = Project::load(&path).unwrap();
let mut index = project.inspect().unwrap();
index.select("main.label", 1).unwrap();
let label = index
.symbols
.iter()
.find(|symbol| symbol.name == "main.label")
.unwrap();
let calls: Vec<_> = index
.calls
.iter()
.filter(|call| call.callee == label.id)
.collect();
assert_eq!(calls.len(), 3);
assert_eq!(
calls
.iter()
.map(|call| call.caller)
.collect::<std::collections::BTreeSet<_>>()
.len(),
2
);
let line = source.lines().nth(5).unwrap();
let first = calls.iter().find(|call| call.site.start.line == 5).unwrap();
assert_eq!(
first.site.start.character,
line[..line.find("label").unwrap()].encode_utf16().count()
);
assert_eq!(
first.site.end.character - first.site.start.character,
"label".len()
);
assert_eq!(index.files.len(), 1);
assert!(index.select("does.not.exist", 1).is_err());
}
#[test]
fn inspection_retains_clauses_and_distinguishes_callback_and_task_dispatch() {
let directory = Directory::new();
let path = directory.0.join("main.dever");
std::fs::write(&path, "label(value: true) (text: Text) {\n text = \"yes\"\n}\nlabel(value: other) (text: Text) {\n text = \"no\"\n}\nmain() (texts: List<Text>, text: Text) {\n texts = each(label, [true, false])\n task = run(label(true))\n text = wait(task)\n}\n").unwrap();
let index = Project::load(&path).unwrap().inspect().unwrap();
let label = index
.symbols
.iter()
.find(|symbol| symbol.name == "main.label")
.unwrap();
assert_eq!(label.declarations.len(), 2);
let calls: Vec<_> = index
.calls
.iter()
.filter(|call| call.callee == label.id)
.collect();
assert_eq!(calls.len(), 2);
assert!(calls.iter().any(|call| call.kind == "callback"));
assert!(calls.iter().any(|call| call.kind == "run"));
for call in calls {
assert_eq!(call.site.end.character - call.site.start.character, 5);
}
}
#[test]
fn inspection_cli_resolves_real_command_bindings_and_reports_failures() {
let directory = Directory::new();
let root = directory.0.join("app");
dever::compiler::project::create(&root).unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_dever"))
.args([
"inspect",
root.to_str().unwrap(),
"--symbol",
"hello.greeting.greet",
"--json",
])
.output()
.unwrap();
assert!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
let index: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(index["calls"].as_array().unwrap().len(), 1);
assert_eq!(index["calls"][0]["kind"], "command");
let failed = Command::new(env!("CARGO_BIN_EXE_dever"))
.args(["inspect", root.to_str().unwrap(), "--symbol", "missing"])
.output()
.unwrap();
assert!(!failed.status.success());
assert!(failed.stdout.is_empty());
assert!(String::from_utf8_lossy(&failed.stderr).contains("unknown symbol"));
}
#[test]
fn inspection_does_not_change_generated_rust_or_accept_invalid_source() {
let directory = Directory::new();
let path = directory.0.join("main.dever");
std::fs::write(&path, "main() (value: Int) {\n value = 42\n}\n").unwrap();
let project = Project::load(&path).unwrap();
let emit = || {
let program = project.check().unwrap();
dever::compiler::emit::generate(
&program,
&project.sources,
&std::collections::BTreeSet::from([0]),
)
};
let before = emit();
project.inspect().unwrap();
assert_eq!(emit(), before);
std::fs::write(&path, "main() (value: Int) {\n value = unknown()\n}\n").unwrap();
assert!(Project::load(&path).unwrap().inspect().is_err());
}
#[test]
fn inspection_keeps_job_dispatch_and_static_handler_specializations() {
let fixtures = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../test/rust/fixtures");
let jobs = Project::load(&fixtures.join("job"))
.unwrap()
.inspect()
.unwrap();
let publish = jobs
.symbols
.iter()
.find(|symbol| symbol.name == "demo.queue.job.publish")
.unwrap();
assert!(
jobs.calls
.iter()
.any(|call| call.callee == publish.id && call.kind == "enqueue")
);
assert!(
jobs.calls
.iter()
.any(|call| call.caller == publish.id && call.kind == "call")
);
let handlers = Project::load(&fixtures.join("handler.dever"))
.unwrap()
.inspect()
.unwrap();
let apply = handlers
.symbols
.iter()
.filter(|symbol| symbol.name == "main.apply")
.collect::<Vec<_>>();
assert_eq!(apply.len(), 1);
let targets: std::collections::BTreeSet<_> = handlers
.calls
.iter()
.filter(|call| call.caller == apply[0].id)
.map(|call| handlers.symbols[call.callee].name.as_str())
.collect();
assert!(targets.contains("main.increment"));
assert!(targets.contains("main.decrement"));
}