Skip to content

Commit 13b7985

Browse files
committed
Make modules with tests runnable
Fixes #154
1 parent b26ab36 commit 13b7985

File tree

9 files changed

+293
-67
lines changed

9 files changed

+293
-67
lines changed

Cargo.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_analysis/src/imp.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,28 @@ impl AnalysisImpl {
184184
};
185185
Ok(query.search(&buf))
186186
}
187+
188+
pub(crate) fn module_path(&self, position: FilePosition) -> Cancelable<String> {
189+
let descr = match source_binder::module_from_position(&*self.db, position)? {
190+
None => return Ok("".to_string()),
191+
Some(it) => it,
192+
};
193+
let name = match descr.name() {
194+
None => return Ok("".to_string()),
195+
Some(it) => it.to_string(),
196+
};
197+
198+
let modules = descr.path_to_root();
199+
200+
let path = modules
201+
.into_iter()
202+
.filter_map(|s| s.name())
203+
.skip(1)
204+
.fold(name, |path, it| format!("{}::{}", it, path));
205+
206+
Ok(path.to_string())
207+
}
208+
187209
/// This returns `Vec` because a module may be included from several places. We
188210
/// don't handle this case yet though, so the Vec has length at most one.
189211
pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {

crates/ra_analysis/src/lib.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod imp;
1515
mod completion;
1616
mod symbol_index;
1717
pub mod mock_analysis;
18+
mod runnables;
1819

1920
use std::{fmt, sync::Arc};
2021

@@ -29,10 +30,12 @@ use crate::{
2930
symbol_index::SymbolIndex,
3031
};
3132

32-
pub use crate::completion::{CompletionItem, CompletionItemKind, InsertText};
33+
pub use crate::{
34+
completion::{CompletionItem, CompletionItemKind, InsertText},
35+
runnables::{Runnable, RunnableKind}
36+
};
3337
pub use ra_editor::{
34-
FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode,
35-
Severity
38+
FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity
3639
};
3740
pub use hir::FnSignatureInfo;
3841

@@ -336,6 +339,9 @@ impl Analysis {
336339
pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
337340
self.imp.parent_module(position)
338341
}
342+
pub fn module_path(&self, position: FilePosition) -> Cancelable<String> {
343+
self.imp.module_path(position)
344+
}
339345
pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
340346
self.imp.crate_for(file_id)
341347
}
@@ -344,7 +350,7 @@ impl Analysis {
344350
}
345351
pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
346352
let file = self.imp.file_syntax(file_id);
347-
Ok(ra_editor::runnables(&file))
353+
Ok(runnables::runnables(self, &file, file_id))
348354
}
349355
pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
350356
let file = self.imp.file_syntax(file_id);

crates/ra_analysis/src/runnables.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use ra_syntax::{
2+
ast::{self, AstNode, NameOwner, ModuleItemOwner},
3+
SourceFileNode, TextRange, SyntaxNodeRef,
4+
TextUnit,
5+
};
6+
use crate::{
7+
Analysis, FileId, FilePosition
8+
};
9+
10+
#[derive(Debug)]
11+
pub struct Runnable {
12+
pub range: TextRange,
13+
pub kind: RunnableKind,
14+
}
15+
16+
#[derive(Debug)]
17+
pub enum RunnableKind {
18+
Test { name: String },
19+
TestMod { path: String },
20+
Bin,
21+
}
22+
23+
pub fn runnables(
24+
analysis: &Analysis,
25+
file_node: &SourceFileNode,
26+
file_id: FileId,
27+
) -> Vec<Runnable> {
28+
file_node
29+
.syntax()
30+
.descendants()
31+
.filter_map(|i| runnable(analysis, i, file_id))
32+
.collect()
33+
}
34+
35+
fn runnable<'a>(analysis: &Analysis, item: SyntaxNodeRef<'a>, file_id: FileId) -> Option<Runnable> {
36+
if let Some(f) = ast::FnDef::cast(item) {
37+
let name = f.name()?.text();
38+
let kind = if name == "main" {
39+
RunnableKind::Bin
40+
} else if f.has_atom_attr("test") {
41+
RunnableKind::Test {
42+
name: name.to_string(),
43+
}
44+
} else {
45+
return None;
46+
};
47+
Some(Runnable {
48+
range: f.syntax().range(),
49+
kind,
50+
})
51+
} else if let Some(m) = ast::Module::cast(item) {
52+
if m.item_list()?
53+
.items()
54+
.map(ast::ModuleItem::syntax)
55+
.filter_map(ast::FnDef::cast)
56+
.any(|f| f.has_atom_attr("test"))
57+
{
58+
let postition = FilePosition {
59+
file_id: file_id,
60+
offset: m.syntax().range().start() + TextUnit::from_usize(1),
61+
};
62+
let path = analysis.module_path(postition).unwrap();
63+
Some(Runnable {
64+
range: m.syntax().range(),
65+
kind: RunnableKind::TestMod { path: path },
66+
})
67+
} else {
68+
None
69+
}
70+
} else {
71+
None
72+
}
73+
}

crates/ra_analysis/tests/runnables.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
extern crate ra_analysis;
2+
extern crate ra_editor;
3+
extern crate ra_syntax;
4+
extern crate relative_path;
5+
extern crate rustc_hash;
6+
extern crate test_utils;
7+
8+
use test_utils::assert_eq_dbg;
9+
10+
use ra_analysis::{
11+
mock_analysis::{analysis_and_position},
12+
};
13+
14+
#[test]
15+
fn test_runnables() {
16+
let (analysis, pos) = analysis_and_position(
17+
r#"
18+
//- /lib.rs
19+
<|> //empty
20+
fn main() {}
21+
22+
#[test]
23+
fn test_foo() {}
24+
25+
#[test]
26+
#[ignore]
27+
fn test_foo() {}
28+
"#,
29+
);
30+
let runnables = analysis.runnables(pos.file_id).unwrap();
31+
assert_eq_dbg(
32+
r#"[Runnable { range: [1; 21), kind: Bin },
33+
Runnable { range: [22; 46), kind: Test { name: "test_foo" } },
34+
Runnable { range: [47; 81), kind: Test { name: "test_foo" } }]"#,
35+
&runnables,
36+
)
37+
}
38+
39+
#[test]
40+
fn test_runnables_module() {
41+
let (analysis, pos) = analysis_and_position(
42+
r#"
43+
//- /lib.rs
44+
<|> //empty
45+
mod test_mod {
46+
#[test]
47+
fn test_foo1() {}
48+
}
49+
"#,
50+
);
51+
let runnables = analysis.runnables(pos.file_id).unwrap();
52+
assert_eq_dbg(
53+
r#"[Runnable { range: [1; 59), kind: TestMod { path: "test_mod" } },
54+
Runnable { range: [28; 57), kind: Test { name: "test_foo1" } }]"#,
55+
&runnables,
56+
)
57+
}
58+
59+
#[test]
60+
fn test_runnables_one_depth_layer_module() {
61+
let (analysis, pos) = analysis_and_position(
62+
r#"
63+
//- /lib.rs
64+
<|> //empty
65+
mod foo {
66+
mod test_mod {
67+
#[test]
68+
fn test_foo1() {}
69+
}
70+
}
71+
"#,
72+
);
73+
let runnables = analysis.runnables(pos.file_id).unwrap();
74+
assert_eq_dbg(
75+
r#"[Runnable { range: [23; 85), kind: TestMod { path: "foo::test_mod" } },
76+
Runnable { range: [46; 79), kind: Test { name: "test_foo1" } }]"#,
77+
&runnables,
78+
)
79+
}
80+
81+
#[test]
82+
fn test_runnables_multiple_depth_module() {
83+
let (analysis, pos) = analysis_and_position(
84+
r#"
85+
//- /lib.rs
86+
<|> //empty
87+
mod foo {
88+
mod bar {
89+
mod test_mod {
90+
#[test]
91+
fn test_foo1() {}
92+
}
93+
}
94+
}
95+
"#,
96+
);
97+
let runnables = analysis.runnables(pos.file_id).unwrap();
98+
assert_eq_dbg(
99+
r#"[Runnable { range: [41; 115), kind: TestMod { path: "foo::bar::test_mod" } },
100+
Runnable { range: [68; 105), kind: Test { name: "test_foo1" } }]"#,
101+
&runnables,
102+
)
103+
}
104+
105+
#[test]
106+
fn test_runnables_no_test_function_in_module() {
107+
let (analysis, pos) = analysis_and_position(
108+
r#"
109+
//- /lib.rs
110+
<|> //empty
111+
mod test_mod {
112+
fn foo1() {}
113+
}
114+
"#,
115+
);
116+
let runnables = analysis.runnables(pos.file_id).unwrap();
117+
assert_eq_dbg(r#"[]"#, &runnables)
118+
}

crates/ra_analysis/tests/tests.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,56 @@ fn test_resolve_parent_module_for_inline() {
131131
);
132132
}
133133

134+
#[test]
135+
fn test_path_one_layer() {
136+
let (analysis, pos) = analysis_and_position(
137+
"
138+
//- /lib.rs
139+
mod foo;
140+
//- /foo/mod.rs
141+
mod bla;
142+
//- /foo/bla.rs
143+
<|> //empty
144+
",
145+
);
146+
let symbols = analysis.module_path(pos).unwrap();
147+
assert_eq!("foo::bla", &symbols);
148+
}
149+
150+
#[test]
151+
fn test_path_two_layer() {
152+
let (analysis, pos) = analysis_and_position(
153+
"
154+
//- /lib.rs
155+
mod foo;
156+
//- /foo/mod.rs
157+
mod bla;
158+
//- /foo/bla/mod.rs
159+
mod more;
160+
//- /foo/bla/more.rs
161+
<|> //empty
162+
",
163+
);
164+
let symbols = analysis.module_path(pos).unwrap();
165+
assert_eq!("foo::bla::more", &symbols);
166+
}
167+
168+
#[test]
169+
fn test_path_in_file_mod() {
170+
let (analysis, pos) = analysis_and_position(
171+
"
172+
//- /lib.rs
173+
mod foo;
174+
//- /foo.rs
175+
mod bar {
176+
<|> //empty
177+
}
178+
",
179+
);
180+
let symbols = analysis.module_path(pos).unwrap();
181+
assert_eq!("foo::bar", &symbols);
182+
}
183+
134184
#[test]
135185
fn test_resolve_crate_root() {
136186
let mock = MockAnalysis::with_files(

0 commit comments

Comments
 (0)