rust_analyzer/cli/
run_tests.rs1use hir::{Crate, Module};
4use hir_ty::db::HirDatabase;
5use ide_db::{base_db::SourceDatabase, line_index};
6use profile::StopWatch;
7use project_model::{CargoConfig, RustLibSource};
8use syntax::TextRange;
9
10use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at};
11
12use crate::cli::{Result, flags, full_name_of_item};
13
14impl flags::RunTests {
15 pub fn run(self) -> Result<()> {
16 let cargo_config = CargoConfig {
17 sysroot: Some(RustLibSource::Discover),
18 all_targets: true,
19 set_test: true,
20 ..Default::default()
21 };
22 let load_cargo_config = LoadCargoConfig {
23 load_out_dirs_from_check: true,
24 with_proc_macro_server: ProcMacroServerChoice::Sysroot,
25 prefill_caches: false,
26 num_worker_threads: 1,
27 proc_macro_processes: 1,
28 };
29 let (ref db, _vfs, _proc_macro) =
30 load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
31
32 let tests = all_modules(db)
33 .into_iter()
34 .flat_map(|x| x.declarations(db))
35 .filter_map(|x| match x {
36 hir::ModuleDef::Function(f) => Some(f),
37 _ => None,
38 })
39 .filter(|x| x.is_test(db));
40 let span_formatter = |file_id, text_range: TextRange| {
41 let line_col = match line_index(db, file_id).try_line_col(text_range.start()) {
42 None => " (unknown line col)".to_owned(),
43 Some(x) => format!("#{}:{}", x.line + 1, x.col),
44 };
45 let source_root = db.file_source_root(file_id).source_root_id(db);
46 let source_root = db.source_root(source_root).source_root(db);
47
48 let path = source_root.path_for_file(&file_id).map(|x| x.to_string());
49 let path = path.as_deref().unwrap_or("<unknown file>");
50 format!("file://{path}{line_col}")
51 };
52 let mut pass_count = 0;
53 let mut ignore_count = 0;
54 let mut fail_count = 0;
55 let mut sw_all = StopWatch::start();
56 for test in tests {
57 let full_name = full_name_of_item(db, test.module(db), test.name(db));
58 println!("test {full_name}");
59 if test.is_ignore(db) {
60 println!("ignored");
61 ignore_count += 1;
62 continue;
63 }
64 let mut sw_one = StopWatch::start();
65 let result = test.eval(db, span_formatter);
66 match &result {
67 Ok(result) if result.trim() == "pass" => pass_count += 1,
68 _ => fail_count += 1,
69 }
70 println!("{result:?}");
71 eprintln!("{:<20} {}", format!("test {}", full_name), sw_one.elapsed());
72 }
73 println!("{pass_count} passed, {fail_count} failed, {ignore_count} ignored");
74 eprintln!("{:<20} {}", "All tests", sw_all.elapsed());
75 Ok(())
76 }
77}
78
79fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
80 let mut worklist: Vec<_> = Crate::all(db)
81 .into_iter()
82 .filter(|x| x.origin(db).is_local())
83 .map(|krate| krate.root_module(db))
84 .collect();
85 let mut modules = Vec::new();
86
87 while let Some(module) = worklist.pop() {
88 modules.push(module);
89 worklist.extend(module.children(db));
90 }
91
92 modules
93}