3153: When a single test is run, do not run others with overlapping names r=matklad a=SomeoneToIgnore



Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
This commit is contained in:
bors[bot] 2020-02-17 08:52:45 +00:00 committed by GitHub
commit c9989a524c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 100 additions and 33 deletions

View file

@ -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,
};

View file

@ -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,85 @@ 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<Runnable> {
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 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<Runnable> {
let name = fn_def.name()?.text().clone();
let kind = if name == "main" {
fn runnable_fn(
db: &RootDatabase,
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
} 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) = source_binder
.to_def(InFile::new(file_id.into(), fn_def.clone()))
.map(|def| def.module(db))
{
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 +115,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<Runnable> {
fn runnable_mod(
db: &RootDatabase,
source_binder: &mut SourceBinder<RootDatabase>,
file_id: FileId,
module: ast::Module,
) -> Option<Runnable> {
let has_test_function = module
.item_list()?
.items()
@ -76,13 +128,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 +172,17 @@ mod tests {
Runnable {
range: [22; 46),
kind: Test {
name: "test_foo",
test_id: Path(
"test_foo",
),
},
},
Runnable {
range: [47; 81),
kind: Test {
name: "test_foo",
test_id: Path(
"test_foo",
),
},
},
]
@ -160,7 +215,9 @@ mod tests {
Runnable {
range: [28; 57),
kind: Test {
name: "test_foo1",
test_id: Path(
"test_mod::test_foo1",
),
},
},
]
@ -195,7 +252,9 @@ mod tests {
Runnable {
range: [46; 79),
kind: Test {
name: "test_foo1",
test_id: Path(
"foo::test_mod::test_foo1",
),
},
},
]
@ -232,7 +291,9 @@ mod tests {
Runnable {
range: [68; 105),
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
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 => {

View file

@ -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 {

View file

@ -147,7 +147,7 @@ fn main() {}
},
json!([
{
"args": [ "test", "--package", "foo", "--test", "spam", "--", "test_eggs", "--nocapture" ],
"args": [ "test", "--package", "foo", "--test", "spam", "--", "test_eggs", "--exact", "--nocapture" ],
"bin": "cargo",
"env": { "RUST_BACKTRACE": "short" },
"label": "test test_eggs",