mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 15:14:32 +00:00
Merge #3153
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:
commit
c9989a524c
5 changed files with 100 additions and 33 deletions
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
),
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue