Add **Ignore Test** assist

This commit is contained in:
Aleksey Kladov 2020-11-17 14:22:04 +01:00
parent 10e3a9879c
commit 9a30707281
6 changed files with 80 additions and 18 deletions

View file

@ -0,0 +1,34 @@
use syntax::{ast, AstNode};
use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists};
// Assist: ignore_test
//
// Adds `#[ignore]` attribute to the test.
//
// ```
// <|>#[test]
// fn arithmetics {
// assert_eq!(2 + 2, 5);
// }
// ```
// ->
// ```
// #[test]
// #[ignore]
// fn arithmetics {
// assert_eq!(2 + 2, 5);
// }
// ```
pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let attr: ast::Attr = ctx.find_node_at_offset()?;
let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
let attr = test_related_attribute(&func)?;
acc.add(
AssistId("ignore_test", AssistKind::None),
"Ignore this test",
attr.syntax().text_range(),
|builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")),
)
}

View file

@ -141,6 +141,7 @@ mod handlers {
mod generate_function;
mod generate_impl;
mod generate_new;
mod ignore_test;
mod infer_function_return_type;
mod inline_local_variable;
mod introduce_named_lifetime;
@ -189,6 +190,7 @@ mod handlers {
generate_function::generate_function,
generate_impl::generate_impl,
generate_new::generate_new,
ignore_test::ignore_test,
infer_function_return_type::infer_function_return_type,
inline_local_variable::inline_local_variable,
introduce_named_lifetime::introduce_named_lifetime,

View file

@ -473,6 +473,26 @@ impl<T: Clone> Ctx<T> {
)
}
#[test]
fn doctest_ignore_test() {
check_doc_test(
"ignore_test",
r#####"
<|>#[test]
fn arithmetics {
assert_eq!(2 + 2, 5);
}
"#####,
r#####"
#[test]
#[ignore]
fn arithmetics {
assert_eq!(2 + 2, 5);
}
"#####,
)
}
#[test]
fn doctest_infer_function_return_type() {
check_doc_test(

View file

@ -9,6 +9,7 @@ use ide_db::RootDatabase;
use itertools::Itertools;
use syntax::{
ast::edit::AstNodeEdit,
ast::AttrsOwner,
ast::NameOwner,
ast::{self, edit, make, ArgListOwner},
AstNode, Direction,
@ -82,6 +83,23 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
None
}
/// 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.
pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
fn_def.attrs().find_map(|attr| {
let path = attr.path()?;
if path.syntax().text().to_string().contains("test") {
Some(attr)
} else {
None
}
})
}
#[derive(Copy, Clone, PartialEq)]
pub enum DefaultMethods {
Only,

View file

@ -1,11 +1,12 @@
//! This module implements a methods and free functions search in the specified file.
//! We have to skip tests, so cannot reuse file_structure module.
use assists::utils::test_related_attribute;
use hir::Semantics;
use ide_db::RootDatabase;
use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode};
use crate::{runnables::has_test_related_attribute, FileId, FileRange};
use crate::{FileId, FileRange};
pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> {
let sema = Semantics::new(db);
@ -15,7 +16,7 @@ pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRa
fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> {
ast::Fn::cast(item).and_then(|fn_def| {
if has_test_related_attribute(&fn_def) {
if test_related_attribute(&fn_def).is_some() {
None
} else {
fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() })

View file

@ -1,5 +1,6 @@
use std::fmt;
use assists::utils::test_related_attribute;
use cfg::CfgExpr;
use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
use ide_db::RootDatabase;
@ -156,7 +157,7 @@ fn runnable_fn(
None => TestId::Name(name_string),
};
if has_test_related_attribute(&fn_def) {
if test_related_attribute(&fn_def).is_some() {
let attr = TestAttr::from_fn(&fn_def);
RunnableKind::Test { test_id, attr }
} else if fn_def.has_atom_attr("bench") {
@ -235,20 +236,6 @@ impl TestAttr {
}
}
/// 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.
pub(crate) fn has_test_related_attribute(fn_def: &ast::Fn) -> bool {
fn_def
.attrs()
.filter_map(|attr| attr.path())
.map(|path| path.syntax().to_string().to_lowercase())
.any(|attribute_text| attribute_text.contains("test"))
}
const RUSTDOC_FENCE: &str = "```";
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
&["", "rust", "should_panic", "edition2015", "edition2018"];
@ -307,7 +294,7 @@ fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool {
for item in item_list.items() {
match item {
ast::Item::Fn(f) => {
if has_test_related_attribute(&f) {
if test_related_attribute(&f).is_some() {
return true;
}
}