2019-09-30 08:58:53 +00:00
|
|
|
//! FIXME: write short doc here
|
|
|
|
|
2019-11-28 09:50:26 +00:00
|
|
|
use hir::InFile;
|
2019-01-08 19:33:36 +00:00
|
|
|
use itertools::Itertools;
|
2019-07-04 20:05:17 +00:00
|
|
|
use ra_db::SourceDatabase;
|
2019-01-08 19:33:36 +00:00
|
|
|
use ra_syntax::{
|
2019-07-04 20:05:17 +00:00
|
|
|
ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
|
2019-10-30 20:09:16 +00:00
|
|
|
match_ast, SyntaxNode, TextRange,
|
2019-01-08 19:33:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{db::RootDatabase, FileId};
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Runnable {
|
|
|
|
pub range: TextRange,
|
|
|
|
pub kind: RunnableKind,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum RunnableKind {
|
|
|
|
Test { name: String },
|
|
|
|
TestMod { path: String },
|
2019-01-12 23:40:54 +00:00
|
|
|
Bench { name: String },
|
2019-01-08 19:33:36 +00:00
|
|
|
Bin,
|
|
|
|
}
|
|
|
|
|
2019-01-15 18:02:42 +00:00
|
|
|
pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
|
2019-07-12 16:41:13 +00:00
|
|
|
let parse = db.parse(file_id);
|
|
|
|
parse.tree().syntax().descendants().filter_map(|i| runnable(db, file_id, i)).collect()
|
2019-01-08 19:33:36 +00:00
|
|
|
}
|
|
|
|
|
2019-07-19 09:56:47 +00:00
|
|
|
fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNode) -> Option<Runnable> {
|
2019-10-30 20:09:16 +00:00
|
|
|
match_ast! {
|
|
|
|
match item {
|
|
|
|
ast::FnDef(it) => { runnable_fn(it) },
|
|
|
|
ast::Module(it) => { runnable_mod(db, file_id, it) },
|
|
|
|
_ => { None },
|
|
|
|
}
|
2019-01-08 19:33:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-19 09:56:47 +00:00
|
|
|
fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> {
|
|
|
|
let name = fn_def.name()?.text().clone();
|
2019-01-08 19:33:36 +00:00
|
|
|
let kind = if name == "main" {
|
|
|
|
RunnableKind::Bin
|
2020-01-23 11:39:14 +00:00
|
|
|
} else if has_test_related_attribute(&fn_def) {
|
2019-02-08 11:49:43 +00:00
|
|
|
RunnableKind::Test { name: name.to_string() }
|
2019-01-12 23:40:54 +00:00
|
|
|
} else if fn_def.has_atom_attr("bench") {
|
2019-02-08 11:49:43 +00:00
|
|
|
RunnableKind::Bench { name: name.to_string() }
|
2019-01-08 19:33:36 +00:00
|
|
|
} else {
|
|
|
|
return None;
|
|
|
|
};
|
2019-07-20 09:58:27 +00:00
|
|
|
Some(Runnable { range: fn_def.syntax().text_range(), kind })
|
2019-01-08 19:33:36 +00:00
|
|
|
}
|
|
|
|
|
2020-01-23 11:39:14 +00:00
|
|
|
/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
|
|
|
|
/// `#[test_case(...)]`, `#[tokio::test]` and similar.
|
|
|
|
/// Also a regular `#[test]` annotation is supported.
|
|
|
|
///
|
|
|
|
/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
|
|
|
|
/// but it's better than not to have the runnables for the tests at all.
|
|
|
|
fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool {
|
|
|
|
fn_def
|
|
|
|
.attrs()
|
|
|
|
.filter_map(|attr| attr.path())
|
|
|
|
.map(|path| path.syntax().to_string().to_lowercase())
|
|
|
|
.any(|attribute_text| attribute_text.contains("test"))
|
|
|
|
}
|
|
|
|
|
2019-07-19 09:56:47 +00:00
|
|
|
fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> {
|
2019-01-08 19:33:36 +00:00
|
|
|
let has_test_function = module
|
|
|
|
.item_list()?
|
|
|
|
.items()
|
2019-08-19 11:13:58 +00:00
|
|
|
.filter_map(|it| match it {
|
|
|
|
ast::ModuleItem::FnDef(it) => Some(it),
|
2019-01-08 19:33:36 +00:00
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.any(|f| f.has_atom_attr("test"));
|
|
|
|
if !has_test_function {
|
|
|
|
return None;
|
|
|
|
}
|
2019-07-20 09:58:27 +00:00
|
|
|
let range = module.syntax().text_range();
|
2020-01-16 16:33:07 +00:00
|
|
|
let mut sb = hir::SourceBinder::new(db);
|
|
|
|
let module = sb.to_def(InFile::new(file_id.into(), module))?;
|
2019-01-08 19:33:36 +00:00
|
|
|
|
2019-02-08 11:49:43 +00:00
|
|
|
let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::");
|
|
|
|
Some(Runnable { range, kind: RunnableKind::TestMod { path } })
|
2019-01-08 19:33:36 +00:00
|
|
|
}
|
2019-01-14 13:27:08 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2019-08-29 13:49:10 +00:00
|
|
|
use insta::assert_debug_snapshot;
|
2019-01-14 13:27:08 +00:00
|
|
|
|
|
|
|
use crate::mock_analysis::analysis_and_position;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_runnables() {
|
|
|
|
let (analysis, pos) = analysis_and_position(
|
|
|
|
r#"
|
|
|
|
//- /lib.rs
|
|
|
|
<|> //empty
|
|
|
|
fn main() {}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_foo() {}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
fn test_foo() {}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
let runnables = analysis.runnables(pos.file_id).unwrap();
|
2019-08-29 13:49:10 +00:00
|
|
|
assert_debug_snapshot!(&runnables,
|
2019-11-15 09:56:24 +00:00
|
|
|
@r###"
|
|
|
|
[
|
|
|
|
Runnable {
|
|
|
|
range: [1; 21),
|
|
|
|
kind: Bin,
|
|
|
|
},
|
|
|
|
Runnable {
|
|
|
|
range: [22; 46),
|
|
|
|
kind: Test {
|
|
|
|
name: "test_foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Runnable {
|
|
|
|
range: [47; 81),
|
|
|
|
kind: Test {
|
|
|
|
name: "test_foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
"###
|
2019-07-28 11:08:06 +00:00
|
|
|
);
|
2019-01-14 13:27:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_runnables_module() {
|
|
|
|
let (analysis, pos) = analysis_and_position(
|
|
|
|
r#"
|
|
|
|
//- /lib.rs
|
|
|
|
<|> //empty
|
|
|
|
mod test_mod {
|
|
|
|
#[test]
|
|
|
|
fn test_foo1() {}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
let runnables = analysis.runnables(pos.file_id).unwrap();
|
2019-08-29 13:49:10 +00:00
|
|
|
assert_debug_snapshot!(&runnables,
|
2019-11-15 09:56:24 +00:00
|
|
|
@r###"
|
|
|
|
[
|
|
|
|
Runnable {
|
|
|
|
range: [1; 59),
|
|
|
|
kind: TestMod {
|
|
|
|
path: "test_mod",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Runnable {
|
|
|
|
range: [28; 57),
|
|
|
|
kind: Test {
|
|
|
|
name: "test_foo1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
"###
|
2019-07-28 11:08:06 +00:00
|
|
|
);
|
2019-01-14 13:27:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_runnables_one_depth_layer_module() {
|
|
|
|
let (analysis, pos) = analysis_and_position(
|
|
|
|
r#"
|
|
|
|
//- /lib.rs
|
|
|
|
<|> //empty
|
|
|
|
mod foo {
|
|
|
|
mod test_mod {
|
|
|
|
#[test]
|
|
|
|
fn test_foo1() {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
let runnables = analysis.runnables(pos.file_id).unwrap();
|
2019-08-29 13:49:10 +00:00
|
|
|
assert_debug_snapshot!(&runnables,
|
2019-11-15 09:56:24 +00:00
|
|
|
@r###"
|
|
|
|
[
|
|
|
|
Runnable {
|
|
|
|
range: [23; 85),
|
|
|
|
kind: TestMod {
|
|
|
|
path: "foo::test_mod",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Runnable {
|
|
|
|
range: [46; 79),
|
|
|
|
kind: Test {
|
|
|
|
name: "test_foo1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
"###
|
2019-07-28 11:08:06 +00:00
|
|
|
);
|
2019-01-14 13:27:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_runnables_multiple_depth_module() {
|
|
|
|
let (analysis, pos) = analysis_and_position(
|
|
|
|
r#"
|
|
|
|
//- /lib.rs
|
|
|
|
<|> //empty
|
|
|
|
mod foo {
|
|
|
|
mod bar {
|
|
|
|
mod test_mod {
|
|
|
|
#[test]
|
|
|
|
fn test_foo1() {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
let runnables = analysis.runnables(pos.file_id).unwrap();
|
2019-08-29 13:49:10 +00:00
|
|
|
assert_debug_snapshot!(&runnables,
|
2019-11-15 09:56:24 +00:00
|
|
|
@r###"
|
|
|
|
[
|
|
|
|
Runnable {
|
|
|
|
range: [41; 115),
|
|
|
|
kind: TestMod {
|
|
|
|
path: "foo::bar::test_mod",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Runnable {
|
|
|
|
range: [68; 105),
|
|
|
|
kind: Test {
|
|
|
|
name: "test_foo1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
"###
|
2019-07-28 11:08:06 +00:00
|
|
|
);
|
2019-01-14 13:27:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_runnables_no_test_function_in_module() {
|
|
|
|
let (analysis, pos) = analysis_and_position(
|
|
|
|
r#"
|
|
|
|
//- /lib.rs
|
|
|
|
<|> //empty
|
|
|
|
mod test_mod {
|
|
|
|
fn foo1() {}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
let runnables = analysis.runnables(pos.file_id).unwrap();
|
|
|
|
assert!(runnables.is_empty())
|
|
|
|
}
|
|
|
|
}
|