blob: e1704199151a61f6b9a3fab14bd2c139a8cb56c7 [file] [log] [blame]
//! Run all tests in a project, similar to `cargo test`, but using the mir interpreter.
use hir::{Crate, Module};
use hir_ty::db::HirDatabase;
use ide_db::{base_db::SourceDatabaseExt, LineIndexDatabase};
use profile::StopWatch;
use project_model::{CargoConfig, RustLibSource};
use syntax::TextRange;
use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
use crate::cli::{flags, full_name_of_item, Result};
impl flags::RunTests {
pub fn run(self) -> Result<()> {
let mut cargo_config = CargoConfig::default();
cargo_config.sysroot = Some(RustLibSource::Discover);
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: true,
with_proc_macro_server: ProcMacroServerChoice::Sysroot,
prefill_caches: false,
};
let (host, _vfs, _proc_macro) =
load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
let db = host.raw_database();
let tests = all_modules(db)
.into_iter()
.flat_map(|x| x.declarations(db))
.filter_map(|x| match x {
hir::ModuleDef::Function(f) => Some(f),
_ => None,
})
.filter(|x| x.is_test(db));
let span_formatter = |file_id, text_range: TextRange| {
let line_col = match db.line_index(file_id).try_line_col(text_range.start()) {
None => " (unknown line col)".to_string(),
Some(x) => format!("#{}:{}", x.line + 1, x.col),
};
let path = &db
.source_root(db.file_source_root(file_id))
.path_for_file(&file_id)
.map(|x| x.to_string());
let path = path.as_deref().unwrap_or("<unknown file>");
format!("file://{path}{line_col}")
};
let mut pass_count = 0;
let mut ignore_count = 0;
let mut fail_count = 0;
let mut sw_all = StopWatch::start();
for test in tests {
let full_name = full_name_of_item(db, test.module(db), test.name(db));
println!("test {}", full_name);
if test.is_ignore(db) {
println!("ignored");
ignore_count += 1;
continue;
}
let mut sw_one = StopWatch::start();
let result = test.eval(db, span_formatter);
if result.trim() == "pass" {
pass_count += 1;
} else {
fail_count += 1;
}
println!("{}", result);
eprintln!("{:<20} {}", format!("test {}", full_name), sw_one.elapsed());
}
println!("{pass_count} passed, {fail_count} failed, {ignore_count} ignored");
eprintln!("{:<20} {}", "All tests", sw_all.elapsed());
Ok(())
}
}
fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
let mut worklist: Vec<_> = Crate::all(db)
.into_iter()
.filter(|x| x.origin(db).is_local())
.map(|krate| krate.root_module())
.collect();
let mut modules = Vec::new();
while let Some(module) = worklist.pop() {
modules.push(module);
worklist.extend(module.children(db));
}
modules
}