176 lines
5.9 KiB
Rust
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"));
|
|
}
|