From 426c0f26fe90e7d4fcf5950e217a64843e651fe9 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 15 Feb 2020 01:06:14 +0200 Subject: [PATCH] If possible, use --exact flag when running tests --- crates/ra_ide/src/lib.rs | 2 +- crates/ra_ide/src/runnables.rs | 111 ++++++++++++++---- crates/ra_lsp_server/src/cargo_target_spec.rs | 16 ++- .../ra_lsp_server/src/main_loop/handlers.rs | 4 +- 4 files changed, 101 insertions(+), 32 deletions(-) diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 689921f3f2..9d66c365b1 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -71,7 +71,7 @@ pub use crate::{ references::{ Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, }, - runnables::{Runnable, RunnableKind}, + runnables::{Runnable, RunnableKind, TestId}, source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, syntax_highlighting::HighlightedRange, }; diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index b6b0c70f9d..863b855915 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -use hir::InFile; +use hir::{InFile, SourceBinder}; use itertools::Itertools; use ra_db::SourceDatabase; use ra_ide_db::RootDatabase; @@ -10,6 +10,7 @@ use ra_syntax::{ }; use crate::FileId; +use std::fmt::Display; #[derive(Debug)] pub struct Runnable { @@ -17,39 +18,87 @@ pub struct Runnable { pub kind: RunnableKind, } +#[derive(Debug)] +pub enum TestId { + Name(String), + Path(String), +} + +impl Display for TestId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + TestId::Name(name) => write!(f, "{}", name), + TestId::Path(path) => write!(f, "{}", path), + } + } +} + #[derive(Debug)] pub enum RunnableKind { - Test { name: String }, + Test { test_id: TestId }, TestMod { path: String }, - Bench { name: String }, + Bench { test_id: TestId }, Bin, } pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { let parse = db.parse(file_id); - parse.tree().syntax().descendants().filter_map(|i| runnable(db, file_id, i)).collect() + let mut sb = SourceBinder::new(db); + parse.tree().syntax().descendants().filter_map(|i| runnable(db, &mut sb, file_id, i)).collect() } -fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNode) -> Option { +fn runnable( + db: &RootDatabase, + source_binder: &mut SourceBinder, + file_id: FileId, + item: SyntaxNode, +) -> Option { match_ast! { match item { - ast::FnDef(it) => { runnable_fn(it) }, - ast::Module(it) => { runnable_mod(db, file_id, it) }, + ast::FnDef(it) => { runnable_fn(db, source_binder, file_id, it) }, + ast::Module(it) => { runnable_mod(db, source_binder, file_id, it) }, _ => { None }, } } } -fn runnable_fn(fn_def: ast::FnDef) -> Option { - let name = fn_def.name()?.text().clone(); - let kind = if name == "main" { +fn runnable_fn( + db: &RootDatabase, + source_binder: &mut SourceBinder, + file_id: FileId, + fn_def: ast::FnDef, +) -> Option { + let name_string = fn_def.name()?.text().to_string(); + + let kind = if name_string == "main" { RunnableKind::Bin - } else if has_test_related_attribute(&fn_def) { - RunnableKind::Test { name: name.to_string() } - } else if fn_def.has_atom_attr("bench") { - RunnableKind::Bench { name: name.to_string() } } else { - return None; + let test_id = if let Some(module) = fn_def + .syntax() + .ancestors() + .find_map(ast::Module::cast) + .and_then(|module| source_binder.to_def(InFile::new(file_id.into(), module))) + { + let path = module + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .map(|name| name.to_string()) + .chain(std::iter::once(name_string)) + .join("::"); + TestId::Path(path) + } else { + TestId::Name(name_string) + }; + + if has_test_related_attribute(&fn_def) { + RunnableKind::Test { test_id } + } else if fn_def.has_atom_attr("bench") { + RunnableKind::Bench { test_id } + } else { + return None; + } }; Some(Runnable { range: fn_def.syntax().text_range(), kind }) } @@ -68,7 +117,12 @@ fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { .any(|attribute_text| attribute_text.contains("test")) } -fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option { +fn runnable_mod( + db: &RootDatabase, + source_binder: &mut SourceBinder, + file_id: FileId, + module: ast::Module, +) -> Option { let has_test_function = module .item_list()? .items() @@ -76,13 +130,12 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti ast::ModuleItem::FnDef(it) => Some(it), _ => None, }) - .any(|f| f.has_atom_attr("test")); + .any(|f| has_test_related_attribute(&f)); if !has_test_function { return None; } let range = module.syntax().text_range(); - let mut sb = hir::SourceBinder::new(db); - let module = sb.to_def(InFile::new(file_id.into(), module))?; + let module = source_binder.to_def(InFile::new(file_id.into(), module))?; let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); Some(Runnable { range, kind: RunnableKind::TestMod { path } }) @@ -121,13 +174,17 @@ mod tests { Runnable { range: [22; 46), kind: Test { - name: "test_foo", + test_id: Name( + "test_foo", + ), }, }, Runnable { range: [47; 81), kind: Test { - name: "test_foo", + test_id: Name( + "test_foo", + ), }, }, ] @@ -160,7 +217,9 @@ mod tests { Runnable { range: [28; 57), kind: Test { - name: "test_foo1", + test_id: Path( + "test_mod::test_foo1", + ), }, }, ] @@ -195,7 +254,9 @@ mod tests { Runnable { range: [46; 79), kind: Test { - name: "test_foo1", + test_id: Path( + "foo::test_mod::test_foo1", + ), }, }, ] @@ -232,7 +293,9 @@ mod tests { Runnable { range: [68; 105), kind: Test { - name: "test_foo1", + test_id: Path( + "foo::bar::test_mod::test_foo1", + ), }, }, ] diff --git a/crates/ra_lsp_server/src/cargo_target_spec.rs b/crates/ra_lsp_server/src/cargo_target_spec.rs index 594caffe22..5fd1e7b6b3 100644 --- a/crates/ra_lsp_server/src/cargo_target_spec.rs +++ b/crates/ra_lsp_server/src/cargo_target_spec.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -use ra_ide::{FileId, RunnableKind}; +use ra_ide::{FileId, RunnableKind, TestId}; use ra_project_model::{self, ProjectWorkspace, TargetKind}; use crate::{world::WorldSnapshot, Result}; @@ -13,13 +13,16 @@ pub(crate) fn runnable_args( let spec = CargoTargetSpec::for_file(world, file_id)?; let mut res = Vec::new(); match kind { - RunnableKind::Test { name } => { + RunnableKind::Test { test_id } => { res.push("test".to_string()); if let Some(spec) = spec { spec.push_to(&mut res); } res.push("--".to_string()); - res.push(name.to_string()); + res.push(test_id.to_string()); + if let TestId::Path(_) = test_id { + res.push("--exact".to_string()); + } res.push("--nocapture".to_string()); } RunnableKind::TestMod { path } => { @@ -31,13 +34,16 @@ pub(crate) fn runnable_args( res.push(path.to_string()); res.push("--nocapture".to_string()); } - RunnableKind::Bench { name } => { + RunnableKind::Bench { test_id } => { res.push("bench".to_string()); if let Some(spec) = spec { spec.push_to(&mut res); } res.push("--".to_string()); - res.push(name.to_string()); + res.push(test_id.to_string()); + if let TestId::Path(_) = test_id { + res.push("--exact".to_string()); + } res.push("--nocapture".to_string()); } RunnableKind::Bin => { diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 2e598fdcdf..5879a1f7ad 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -918,9 +918,9 @@ fn to_lsp_runnable( let args = runnable_args(world, file_id, &runnable.kind)?; let line_index = world.analysis().file_line_index(file_id)?; let label = match &runnable.kind { - RunnableKind::Test { name } => format!("test {}", name), + RunnableKind::Test { test_id } => format!("test {}", test_id), RunnableKind::TestMod { path } => format!("test-mod {}", path), - RunnableKind::Bench { name } => format!("bench {}", name), + RunnableKind::Bench { test_id } => format!("bench {}", test_id), RunnableKind::Bin => "run binary".to_string(), }; Ok(req::Runnable {