If possible, use --exact flag when running tests

This commit is contained in:
Kirill Bulatov 2020-02-15 01:06:14 +02:00
parent 9ba801befd
commit 426c0f26fe
4 changed files with 101 additions and 32 deletions

View file

@ -71,7 +71,7 @@ pub use crate::{
references::{ references::{
Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope,
}, },
runnables::{Runnable, RunnableKind}, runnables::{Runnable, RunnableKind, TestId},
source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
syntax_highlighting::HighlightedRange, syntax_highlighting::HighlightedRange,
}; };

View file

@ -1,6 +1,6 @@
//! FIXME: write short doc here //! FIXME: write short doc here
use hir::InFile; use hir::{InFile, SourceBinder};
use itertools::Itertools; use itertools::Itertools;
use ra_db::SourceDatabase; use ra_db::SourceDatabase;
use ra_ide_db::RootDatabase; use ra_ide_db::RootDatabase;
@ -10,6 +10,7 @@ use ra_syntax::{
}; };
use crate::FileId; use crate::FileId;
use std::fmt::Display;
#[derive(Debug)] #[derive(Debug)]
pub struct Runnable { pub struct Runnable {
@ -17,39 +18,87 @@ pub struct Runnable {
pub kind: RunnableKind, 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)] #[derive(Debug)]
pub enum RunnableKind { pub enum RunnableKind {
Test { name: String }, Test { test_id: TestId },
TestMod { path: String }, TestMod { path: String },
Bench { name: String }, Bench { test_id: TestId },
Bin, Bin,
} }
pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
let parse = db.parse(file_id); 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<Runnable> { fn runnable(
db: &RootDatabase,
source_binder: &mut SourceBinder<RootDatabase>,
file_id: FileId,
item: SyntaxNode,
) -> Option<Runnable> {
match_ast! { match_ast! {
match item { match item {
ast::FnDef(it) => { runnable_fn(it) }, ast::FnDef(it) => { runnable_fn(db, source_binder, file_id, it) },
ast::Module(it) => { runnable_mod(db, file_id, it) }, ast::Module(it) => { runnable_mod(db, source_binder, file_id, it) },
_ => { None }, _ => { None },
} }
} }
} }
fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> { fn runnable_fn(
let name = fn_def.name()?.text().clone(); db: &RootDatabase,
let kind = if name == "main" { source_binder: &mut SourceBinder<RootDatabase>,
file_id: FileId,
fn_def: ast::FnDef,
) -> Option<Runnable> {
let name_string = fn_def.name()?.text().to_string();
let kind = if name_string == "main" {
RunnableKind::Bin 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 { } 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 }) 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")) .any(|attribute_text| attribute_text.contains("test"))
} }
fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> { fn runnable_mod(
db: &RootDatabase,
source_binder: &mut SourceBinder<RootDatabase>,
file_id: FileId,
module: ast::Module,
) -> Option<Runnable> {
let has_test_function = module let has_test_function = module
.item_list()? .item_list()?
.items() .items()
@ -76,13 +130,12 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti
ast::ModuleItem::FnDef(it) => Some(it), ast::ModuleItem::FnDef(it) => Some(it),
_ => None, _ => None,
}) })
.any(|f| f.has_atom_attr("test")); .any(|f| has_test_related_attribute(&f));
if !has_test_function { if !has_test_function {
return None; return None;
} }
let range = module.syntax().text_range(); let range = module.syntax().text_range();
let mut sb = hir::SourceBinder::new(db); let module = source_binder.to_def(InFile::new(file_id.into(), module))?;
let module = sb.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("::"); let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::");
Some(Runnable { range, kind: RunnableKind::TestMod { path } }) Some(Runnable { range, kind: RunnableKind::TestMod { path } })
@ -121,13 +174,17 @@ mod tests {
Runnable { Runnable {
range: [22; 46), range: [22; 46),
kind: Test { kind: Test {
name: "test_foo", test_id: Name(
"test_foo",
),
}, },
}, },
Runnable { Runnable {
range: [47; 81), range: [47; 81),
kind: Test { kind: Test {
name: "test_foo", test_id: Name(
"test_foo",
),
}, },
}, },
] ]
@ -160,7 +217,9 @@ mod tests {
Runnable { Runnable {
range: [28; 57), range: [28; 57),
kind: Test { kind: Test {
name: "test_foo1", test_id: Path(
"test_mod::test_foo1",
),
}, },
}, },
] ]
@ -195,7 +254,9 @@ mod tests {
Runnable { Runnable {
range: [46; 79), range: [46; 79),
kind: Test { kind: Test {
name: "test_foo1", test_id: Path(
"foo::test_mod::test_foo1",
),
}, },
}, },
] ]
@ -232,7 +293,9 @@ mod tests {
Runnable { Runnable {
range: [68; 105), range: [68; 105),
kind: Test { kind: Test {
name: "test_foo1", test_id: Path(
"foo::bar::test_mod::test_foo1",
),
}, },
}, },
] ]

View file

@ -1,6 +1,6 @@
//! FIXME: write short doc here //! FIXME: write short doc here
use ra_ide::{FileId, RunnableKind}; use ra_ide::{FileId, RunnableKind, TestId};
use ra_project_model::{self, ProjectWorkspace, TargetKind}; use ra_project_model::{self, ProjectWorkspace, TargetKind};
use crate::{world::WorldSnapshot, Result}; use crate::{world::WorldSnapshot, Result};
@ -13,13 +13,16 @@ pub(crate) fn runnable_args(
let spec = CargoTargetSpec::for_file(world, file_id)?; let spec = CargoTargetSpec::for_file(world, file_id)?;
let mut res = Vec::new(); let mut res = Vec::new();
match kind { match kind {
RunnableKind::Test { name } => { RunnableKind::Test { test_id } => {
res.push("test".to_string()); res.push("test".to_string());
if let Some(spec) = spec { if let Some(spec) = spec {
spec.push_to(&mut res); spec.push_to(&mut res);
} }
res.push("--".to_string()); 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()); res.push("--nocapture".to_string());
} }
RunnableKind::TestMod { path } => { RunnableKind::TestMod { path } => {
@ -31,13 +34,16 @@ pub(crate) fn runnable_args(
res.push(path.to_string()); res.push(path.to_string());
res.push("--nocapture".to_string()); res.push("--nocapture".to_string());
} }
RunnableKind::Bench { name } => { RunnableKind::Bench { test_id } => {
res.push("bench".to_string()); res.push("bench".to_string());
if let Some(spec) = spec { if let Some(spec) = spec {
spec.push_to(&mut res); spec.push_to(&mut res);
} }
res.push("--".to_string()); 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()); res.push("--nocapture".to_string());
} }
RunnableKind::Bin => { RunnableKind::Bin => {

View file

@ -918,9 +918,9 @@ fn to_lsp_runnable(
let args = runnable_args(world, file_id, &runnable.kind)?; let args = runnable_args(world, file_id, &runnable.kind)?;
let line_index = world.analysis().file_line_index(file_id)?; let line_index = world.analysis().file_line_index(file_id)?;
let label = match &runnable.kind { 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::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(), RunnableKind::Bin => "run binary".to_string(),
}; };
Ok(req::Runnable { Ok(req::Runnable {