aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/handlers/add_turbo_fish.rs12
-rw-r--r--crates/ra_assists/src/marks.rs2
-rw-r--r--crates/test_utils/src/lib.rs2
-rw-r--r--crates/test_utils/src/mark.rs78
4 files changed, 86 insertions, 8 deletions
diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs
index a0363bc78..26acf81f2 100644
--- a/crates/ra_assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs
@@ -1,11 +1,11 @@
1use ra_ide_db::defs::{classify_name_ref, Definition, NameRefClass}; 1use ra_ide_db::defs::{classify_name_ref, Definition, NameRefClass};
2use ra_syntax::{ast, AstNode, SyntaxKind, T}; 2use ra_syntax::{ast, AstNode, SyntaxKind, T};
3use test_utils::mark;
3 4
4use crate::{ 5use crate::{
5 assist_context::{AssistContext, Assists}, 6 assist_context::{AssistContext, Assists},
6 AssistId, 7 AssistId,
7}; 8};
8use test_utils::tested_by;
9 9
10// Assist: add_turbo_fish 10// Assist: add_turbo_fish
11// 11//
@@ -28,7 +28,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
28 let ident = ctx.find_token_at_offset(SyntaxKind::IDENT)?; 28 let ident = ctx.find_token_at_offset(SyntaxKind::IDENT)?;
29 let next_token = ident.next_token()?; 29 let next_token = ident.next_token()?;
30 if next_token.kind() == T![::] { 30 if next_token.kind() == T![::] {
31 tested_by!(add_turbo_fish_one_fish_is_enough); 31 mark::hit!(add_turbo_fish_one_fish_is_enough);
32 return None; 32 return None;
33 } 33 }
34 let name_ref = ast::NameRef::cast(ident.parent())?; 34 let name_ref = ast::NameRef::cast(ident.parent())?;
@@ -42,7 +42,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
42 }; 42 };
43 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); 43 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
44 if generics.is_empty() { 44 if generics.is_empty() {
45 tested_by!(add_turbo_fish_non_generic); 45 mark::hit!(add_turbo_fish_non_generic);
46 return None; 46 return None;
47 } 47 }
48 acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| { 48 acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| {
@@ -58,7 +58,7 @@ mod tests {
58 use crate::tests::{check_assist, check_assist_not_applicable}; 58 use crate::tests::{check_assist, check_assist_not_applicable};
59 59
60 use super::*; 60 use super::*;
61 use test_utils::covers; 61 use test_utils::mark;
62 62
63 #[test] 63 #[test]
64 fn add_turbo_fish_function() { 64 fn add_turbo_fish_function() {
@@ -106,7 +106,7 @@ fn main() {
106 106
107 #[test] 107 #[test]
108 fn add_turbo_fish_one_fish_is_enough() { 108 fn add_turbo_fish_one_fish_is_enough() {
109 covers!(add_turbo_fish_one_fish_is_enough); 109 mark::check!(add_turbo_fish_one_fish_is_enough);
110 check_assist_not_applicable( 110 check_assist_not_applicable(
111 add_turbo_fish, 111 add_turbo_fish,
112 r#" 112 r#"
@@ -120,7 +120,7 @@ fn main() {
120 120
121 #[test] 121 #[test]
122 fn add_turbo_fish_non_generic() { 122 fn add_turbo_fish_non_generic() {
123 covers!(add_turbo_fish_non_generic); 123 mark::check!(add_turbo_fish_non_generic);
124 check_assist_not_applicable( 124 check_assist_not_applicable(
125 add_turbo_fish, 125 add_turbo_fish,
126 r#" 126 r#"
diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs
index 722f3c6a4..525ec4abc 100644
--- a/crates/ra_assists/src/marks.rs
+++ b/crates/ra_assists/src/marks.rs
@@ -10,6 +10,4 @@ test_utils::marks![
10 test_not_applicable_if_variable_unused 10 test_not_applicable_if_variable_unused
11 change_visibility_field_false_positive 11 change_visibility_field_false_positive
12 test_add_from_impl_already_exists 12 test_add_from_impl_already_exists
13 add_turbo_fish_one_fish_is_enough
14 add_turbo_fish_non_generic
15]; 13];
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index b1e3c328f..4e05d464f 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -8,6 +8,8 @@
8 8
9#[macro_use] 9#[macro_use]
10pub mod marks; 10pub mod marks;
11#[macro_use]
12pub mod mark;
11 13
12use std::{ 14use std::{
13 fs, 15 fs,
diff --git a/crates/test_utils/src/mark.rs b/crates/test_utils/src/mark.rs
new file mode 100644
index 000000000..7c309a894
--- /dev/null
+++ b/crates/test_utils/src/mark.rs
@@ -0,0 +1,78 @@
1//! This module implements manually tracked test coverage, which is useful for
2//! quickly finding a test responsible for testing a particular bit of code.
3//!
4//! See <https://matklad.github.io/2018/06/18/a-trick-for-test-maintenance.html>
5//! for details, but the TL;DR is that you write your test as
6//!
7//! ```
8//! #[test]
9//! fn test_foo() {
10//! mark::check!(test_foo);
11//! }
12//! ```
13//!
14//! and in the code under test you write
15//!
16//! ```
17//! # use test_utils::mark;
18//! # fn some_condition() -> bool { true }
19//! fn foo() {
20//! if some_condition() {
21//! mark::hit!(test_foo);
22//! }
23//! }
24//! ```
25//!
26//! This module then checks that executing the test indeed covers the specified
27//! function. This is useful if you come back to the `foo` function ten years
28//! later and wonder where the test are: now you can grep for `test_foo`.
29use std::sync::atomic::{AtomicUsize, Ordering};
30
31#[macro_export]
32macro_rules! _hit {
33 ($ident:ident) => {{
34 #[cfg(test)]
35 {
36 extern "C" {
37 #[no_mangle]
38 static $ident: std::sync::atomic::AtomicUsize;
39 }
40 unsafe {
41 $ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
42 }
43 }
44 }};
45}
46pub use _hit as hit;
47
48#[macro_export]
49macro_rules! _check {
50 ($ident:ident) => {
51 #[no_mangle]
52 static $ident: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
53 let _checker = $crate::mark::MarkChecker::new(&$ident);
54 };
55}
56pub use _check as check;
57
58pub struct MarkChecker {
59 mark: &'static AtomicUsize,
60 value_on_entry: usize,
61}
62
63impl MarkChecker {
64 pub fn new(mark: &'static AtomicUsize) -> MarkChecker {
65 let value_on_entry = mark.load(Ordering::SeqCst);
66 MarkChecker { mark, value_on_entry }
67 }
68}
69
70impl Drop for MarkChecker {
71 fn drop(&mut self) {
72 if std::thread::panicking() {
73 return;
74 }
75 let value_on_exit = self.mark.load(Ordering::SeqCst);
76 assert!(value_on_exit > self.value_on_entry, "mark was not hit")
77 }
78}