Skip to content

Commit 95b0c4c

Browse files
committed
Make modules with tests runnable
Fixes #154
1 parent 46cce4f commit 95b0c4c

File tree

4 files changed

+144
-62
lines changed

4 files changed

+144
-62
lines changed

crates/ra_analysis/src/lib.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ extern crate rayon;
55
extern crate relative_path;
66
extern crate rustc_hash;
77
extern crate salsa;
8+
#[cfg(test)]
9+
extern crate test_utils as test_utils;
810

911
mod input;
1012
mod db;
@@ -13,6 +15,7 @@ mod imp;
1315
mod symbol_index;
1416
mod completion;
1517
mod syntax_ptr;
18+
mod runnables;
1619

1720
use std::{
1821
fmt,
@@ -30,11 +33,11 @@ use crate::{
3033

3134
pub use crate::{
3235
descriptors::FnDescriptor,
33-
input::{FileId, FileResolver, CrateGraph, CrateId}
36+
input::{FileId, FileResolver, CrateGraph, CrateId},
37+
runnables::{Runnable, RunnableKind}
3438
};
3539
pub use ra_editor::{
36-
CompletionItem, FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable,
37-
RunnableKind, StructureNode,
40+
CompletionItem, FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, StructureNode,
3841
};
3942

4043
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -268,7 +271,7 @@ impl Analysis {
268271
}
269272
pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
270273
let file = self.imp.file_syntax(file_id);
271-
Ok(ra_editor::runnables(&file))
274+
Ok(runnables::runnables(&file))
272275
}
273276
pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
274277
let file = self.imp.file_syntax(file_id);

crates/ra_analysis/src/runnables.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
2+
use ra_syntax::{
3+
ast::{self, AstNode, NameOwner, ModuleItemOwner},
4+
File, TextRange,
5+
SyntaxNodeRef
6+
};
7+
8+
#[derive(Debug)]
9+
pub struct Runnable {
10+
pub range: TextRange,
11+
pub kind: RunnableKind,
12+
}
13+
14+
#[derive(Debug)]
15+
pub enum RunnableKind {
16+
Test { name: String },
17+
TestMod { path: String },
18+
Bin,
19+
}
20+
21+
pub fn runnables(file: &File) -> Vec<Runnable> {
22+
file.syntax()
23+
.descendants()
24+
.filter_map(runnable)
25+
.collect()
26+
}
27+
28+
fn runnable<'a>(item: SyntaxNodeRef<'a>) -> Option<Runnable> {
29+
if let Some(f) = ast::FnDef::cast(item) {
30+
let name = f.name()?.text();
31+
let kind = if name == "main" {
32+
RunnableKind::Bin
33+
} else if f.has_atom_attr("test") {
34+
RunnableKind::Test {
35+
name: name.to_string(),
36+
}
37+
} else {
38+
return None;
39+
};
40+
Some(Runnable {
41+
range: f.syntax().range(),
42+
kind,
43+
})
44+
} else if let Some(m) = ast::Module::cast(item) {
45+
let name = m.name()?.text();
46+
if m.item_list()?
47+
.items()
48+
.map(ast::ModuleItem::syntax)
49+
.filter_map(ast::FnDef::cast)
50+
.any(|f| f.has_atom_attr("test")) {
51+
Some(Runnable {
52+
range: m.syntax().range(),
53+
kind: RunnableKind::TestMod {
54+
path: name.to_string()
55+
}
56+
})
57+
}else {
58+
None
59+
}
60+
}else {
61+
None
62+
}
63+
}
64+
65+
#[cfg(test)]
66+
mod tests {
67+
use super::*;
68+
use crate::test_utils::{assert_eq_dbg};
69+
70+
#[test]
71+
fn test_runnables() {
72+
let file = File::parse(
73+
r#"
74+
fn main() {}
75+
76+
#[test]
77+
fn test_foo() {}
78+
79+
#[test]
80+
#[ignore]
81+
fn test_foo() {}
82+
"#,
83+
);
84+
let runnables = runnables(&file);
85+
assert_eq_dbg(
86+
r#"[Runnable { range: [1; 13), kind: Bin },
87+
Runnable { range: [15; 39), kind: Test { name: "test_foo" } },
88+
Runnable { range: [41; 75), kind: Test { name: "test_foo" } }]"#,
89+
&runnables,
90+
)
91+
}
92+
93+
#[test]
94+
fn test_runnables_module() {
95+
let file = File::parse(
96+
r#"
97+
mod test_mod {
98+
#[test]
99+
fn test_foo1() {}
100+
}
101+
"#,
102+
);
103+
let runnables = runnables(&file);
104+
assert_eq_dbg(
105+
r#"[Runnable { range: [1; 51), kind: TestMod { path: "test_mod" } },
106+
Runnable { range: [20; 49), kind: Test { name: "test_foo1" } }]"#,
107+
&runnables,
108+
)
109+
}
110+
111+
#[test]
112+
fn test_runnables_no_test_function_in_module() {
113+
let file = File::parse(
114+
r#"
115+
mod test_mod {
116+
fn foo1() {}
117+
}
118+
"#,
119+
);
120+
let runnables = runnables(&file);
121+
assert_eq_dbg(
122+
r#"[]"#,
123+
&runnables,
124+
)
125+
}
126+
127+
}

crates/ra_editor/src/lib.rs

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,6 @@ pub struct Diagnostic {
5050
pub msg: String,
5151
}
5252

53-
#[derive(Debug)]
54-
pub struct Runnable {
55-
pub range: TextRange,
56-
pub kind: RunnableKind,
57-
}
58-
59-
#[derive(Debug)]
60-
pub enum RunnableKind {
61-
Test { name: String },
62-
Bin,
63-
}
64-
6553
pub fn matching_brace(file: &File, offset: TextUnit) -> Option<TextUnit> {
6654
const BRACES: &[SyntaxKind] = &[
6755
L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE,
@@ -116,29 +104,6 @@ pub fn syntax_tree(file: &File) -> String {
116104
::ra_syntax::utils::dump_tree(file.syntax())
117105
}
118106

119-
pub fn runnables(file: &File) -> Vec<Runnable> {
120-
file.syntax()
121-
.descendants()
122-
.filter_map(ast::FnDef::cast)
123-
.filter_map(|f| {
124-
let name = f.name()?.text();
125-
let kind = if name == "main" {
126-
RunnableKind::Bin
127-
} else if f.has_atom_attr("test") {
128-
RunnableKind::Test {
129-
name: name.to_string(),
130-
}
131-
} else {
132-
return None;
133-
};
134-
Some(Runnable {
135-
range: f.syntax().range(),
136-
kind,
137-
})
138-
})
139-
.collect()
140-
}
141-
142107
pub fn find_node_at_offset<'a, N: AstNode<'a>>(
143108
syntax: SyntaxNodeRef<'a>,
144109
offset: TextUnit,
@@ -187,29 +152,6 @@ fn main() {}
187152
);
188153
}
189154

190-
#[test]
191-
fn test_runnables() {
192-
let file = File::parse(
193-
r#"
194-
fn main() {}
195-
196-
#[test]
197-
fn test_foo() {}
198-
199-
#[test]
200-
#[ignore]
201-
fn test_foo() {}
202-
"#,
203-
);
204-
let runnables = runnables(&file);
205-
assert_eq_dbg(
206-
r#"[Runnable { range: [1; 13), kind: Bin },
207-
Runnable { range: [15; 39), kind: Test { name: "test_foo" } },
208-
Runnable { range: [41; 75), kind: Test { name: "test_foo" } }]"#,
209-
&runnables,
210-
)
211-
}
212-
213155
#[test]
214156
fn test_matching_brace() {
215157
fn do_check(before: &str, after: &str) {

crates/ra_lsp_server/src/main_loop/handlers.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ pub fn handle_runnables(
256256
range: runnable.range.conv_with(&line_index),
257257
label: match &runnable.kind {
258258
RunnableKind::Test { name } => format!("test {}", name),
259+
RunnableKind::TestMod { path } => format!("test-mod {}", path),
259260
RunnableKind::Bin => "run binary".to_string(),
260261
},
261262
bin: "cargo".to_string(),
@@ -303,6 +304,15 @@ pub fn handle_runnables(
303304
res.push(name.to_string());
304305
res.push("--nocapture".to_string());
305306
}
307+
RunnableKind::TestMod { path } => {
308+
res.push("test".to_string());
309+
if let Some(spec) = spec {
310+
spec.push_to(&mut res);
311+
}
312+
res.push("--".to_string());
313+
res.push(path.to_string());
314+
res.push("--nocapture".to_string());
315+
}
306316
RunnableKind::Bin => {
307317
res.push("run".to_string());
308318
if let Some(spec) = spec {

0 commit comments

Comments
 (0)