use std::collections::HashSet;
use std::fs;
use std::path::Path;
fn extract_test_functions(path: &Path) -> Vec<String> {
let content = fs::read_to_string(path).expect("failed to read file");
let mut names = Vec::new();
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("pub async fn test_") {
if let Some(name) = trimmed
.strip_prefix("pub async fn ")
.and_then(|s| s.split('(').next())
{
names.push(name.to_string());
}
}
}
names
}
fn extract_referenced_tests(path: &Path) -> HashSet<String> {
let content = fs::read_to_string(path).expect("failed to read consumer file");
let mut refs = HashSet::new();
for line in content.lines() {
let haystack = line.trim();
if let Some(idx) = haystack.find("::test_") {
let after = &haystack[idx + 2..]; if let Some(name) = after.split('(').next() {
refs.insert(name.to_string());
}
}
}
refs
}
#[test]
fn all_shared_tests_are_consumed() {
let suite_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mem_suite = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("rustvello-mem/tests/suite.rs");
let sqlite_suite = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("rustvello-sqlite/tests/suite.rs");
let mut all_tests = Vec::new();
let modules = [
"broker.rs",
"orchestrator.rs",
"state_backend.rs",
"trigger.rs",
"client_data_store.rs",
"lifecycle.rs",
];
for module in &modules {
let path = suite_dir.join(module);
if path.exists() {
let tests = extract_test_functions(&path);
for t in tests {
all_tests.push((module.to_string(), t));
}
}
}
let mut all_refs = HashSet::new();
for consumer in [&mem_suite, &sqlite_suite] {
if consumer.exists() {
all_refs.extend(extract_referenced_tests(consumer));
}
}
let mut macro_tests = HashSet::new();
for module in &modules {
let path = suite_dir.join(module);
if path.exists() {
let content = fs::read_to_string(&path).unwrap();
for line in content.lines() {
if line.contains("$crate::") && line.contains("::test_") {
if let Some(idx) = line.find("::test_") {
let after = &line[idx + 2..];
if let Some(name) = after.split('(').next() {
macro_tests.insert(name.to_string());
}
}
}
}
}
}
let mut missing = Vec::new();
for (module, test_name) in &all_tests {
let is_referenced = all_refs.contains(test_name.as_str());
let is_in_macro = macro_tests.contains(test_name.as_str());
if !is_referenced && !is_in_macro {
missing.push(format!("{module}::{test_name}"));
}
}
assert!(
missing.is_empty(),
"The following shared test functions are not consumed by any backend crate \
or referenced in any suite macro:\n {}\n\n\
Add them to the appropriate suite macro or wire them manually in \
rustvello-mem/tests/suite.rs and/or rustvello-sqlite/tests/suite.rs.",
missing.join("\n ")
);
assert!(
all_tests.len() >= 30,
"Expected at least 30 shared test functions, found {}. \
Check that the test suite source files exist.",
all_tests.len()
);
}