Skip to content

Commit cdde466

Browse files
committed
Make modules with tests runnable
Fixes #154
1 parent 9aebd9e commit cdde466

File tree

7 files changed

+303
-60
lines changed

7 files changed

+303
-60
lines changed

crates/ra_analysis/src/imp.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,34 @@ impl AnalysisImpl {
220220
let source_root = self.db.file_source_root(file_id);
221221
self.db.module_tree(source_root)
222222
}
223+
pub(crate) fn module_path(&self, position: FilePosition) -> Cancelable<String> {
224+
let mut current_position = position;
225+
let mut path = "".to_string();
226+
227+
loop {
228+
println!("current_position {:?}", current_position);
229+
if let Some((file_id, file_symbol)) = self.parent_module(current_position)?.first() {
230+
println!("parent_module {:?} {:?}",file_id, file_symbol);
231+
let file = self.db.file_syntax(*file_id);
232+
let module = find_node_at_offset::<ast::Module>(file.syntax(),file_symbol.node_range.start()).unwrap().syntax().range().start();
233+
println!("{:?}", module);
234+
path = format!("{}::{}", file_symbol.name, path);
235+
current_position = FilePosition {
236+
file_id: *file_id,
237+
offset: if module == TextUnit::from_usize(0) {
238+
TextUnit::from_usize(0)
239+
} else {
240+
module - TextUnit::from_usize(1)
241+
},
242+
};
243+
} else {
244+
break;
245+
}
246+
}
247+
248+
Ok(path.to_string())
249+
}
250+
223251
pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
224252
let module_tree = self.module_tree(position.file_id)?;
225253
let file = self.db.file_syntax(position.file_id);
@@ -229,6 +257,7 @@ impl AnalysisImpl {
229257
_ => ModuleSource::SourceFile(position.file_id),
230258
};
231259

260+
println!("{:#?}", module_tree);
232261
let res = module_tree
233262
.modules_for_source(module_source)
234263
.into_iter()

crates/ra_analysis/src/lib.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ extern crate rayon;
88
extern crate relative_path;
99
extern crate rustc_hash;
1010
extern crate salsa;
11+
#[cfg(test)]
12+
extern crate test_utils as test_utils;
1113

1214
mod db;
1315
mod input;
@@ -17,6 +19,7 @@ mod descriptors;
1719
mod symbol_index;
1820
mod syntax_ptr;
1921
pub mod mock_analysis;
22+
mod runnables;
2023

2124
use std::{fmt, sync::Arc};
2225

@@ -33,9 +36,10 @@ pub use crate::{
3336
completion::CompletionItem,
3437
descriptors::function::FnDescriptor,
3538
input::{CrateGraph, CrateId, FileId, FileResolver},
39+
runnables::{Runnable, RunnableKind}
3640
};
3741
pub use ra_editor::{
38-
FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode,
42+
FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, StructureNode,
3943
};
4044

4145
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -268,6 +272,9 @@ impl Analysis {
268272
pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
269273
self.imp.parent_module(position)
270274
}
275+
pub fn module_path(&self, position: FilePosition) -> Cancelable<String> {
276+
self.imp.module_path(position)
277+
}
271278
pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
272279
self.imp.crate_for(file_id)
273280
}
@@ -276,7 +283,7 @@ impl Analysis {
276283
}
277284
pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
278285
let file = self.imp.file_syntax(file_id);
279-
Ok(ra_editor::runnables(&file))
286+
Ok(runnables::runnables(self, &file, file_id))
280287
}
281288
pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
282289
let file = self.imp.file_syntax(file_id);

crates/ra_analysis/src/runnables.rs

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

crates/ra_analysis/tests/runnables.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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+
let runnables = analysis.runnables(pos.file_id).unwrap();
30+
assert_eq_dbg(
31+
r#"[Runnable { range: [1; 21), kind: Bin },
32+
Runnable { range: [22; 46), kind: Test { name: "test_foo" } },
33+
Runnable { range: [47; 81), kind: Test { name: "test_foo" } }]"#,
34+
&runnables,
35+
)
36+
}
37+
38+
#[test]
39+
fn test_runnables_module() {
40+
let (analysis, pos) = analysis_and_position(
41+
r#"
42+
//- /lib.rs
43+
<|> //empty
44+
mod test_mod {
45+
#[test]
46+
fn test_foo1() {}
47+
}
48+
"#);
49+
let runnables = analysis.runnables(pos.file_id).unwrap();
50+
assert_eq_dbg(
51+
r#"[Runnable { range: [1; 59), kind: TestMod { path: "test_mod::test_mod" } },
52+
Runnable { range: [28; 57), kind: Test { name: "test_foo1" } }]"#,
53+
&runnables,
54+
)
55+
}
56+
57+
#[test]
58+
fn test_runnables_one_depth_layer_module() {
59+
let (analysis, pos) = analysis_and_position(
60+
r#"
61+
//- /lib.rs
62+
<|> //empty
63+
mod foo {
64+
mod test_mod {
65+
#[test]
66+
fn test_foo1() {}
67+
}
68+
}
69+
"#);
70+
let runnables = analysis.runnables(pos.file_id).unwrap();
71+
assert_eq_dbg(
72+
r#"[Runnable { range: [23; 85), kind: TestMod { path: "foo::test_mod" } },
73+
Runnable { range: [46; 79), kind: Test { name: "test_foo1" } }]"#,
74+
&runnables,
75+
)
76+
}
77+
78+
#[test]
79+
fn test_runnables_multiple_depth_module() {
80+
let (analysis, pos) = analysis_and_position(
81+
r#"
82+
//- /lib.rs
83+
<|> //empty
84+
mod foo {
85+
mod bar {
86+
mod test_mod {
87+
#[test]
88+
fn test_foo1() {}
89+
}
90+
}
91+
}
92+
"#);
93+
let runnables = analysis.runnables(pos.file_id).unwrap();
94+
assert_eq_dbg(
95+
r#"[Runnable { range: [41; 115), kind: TestMod { path: "foo::bar::test_mod" } },
96+
Runnable { range: [68; 105), kind: Test { name: "test_foo1" } }]"#,
97+
&runnables,
98+
)
99+
}
100+
101+
#[test]
102+
fn test_runnables_no_test_function_in_module() {
103+
let (analysis, pos) = analysis_and_position(
104+
r#"
105+
//- /lib.rs
106+
<|> //empty
107+
mod test_mod {
108+
fn foo1() {}
109+
}
110+
"#);
111+
let runnables = analysis.runnables(pos.file_id).unwrap();
112+
assert_eq_dbg(r#"[]"#, &runnables)
113+
}

crates/ra_analysis/tests/tests.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,79 @@ fn test_resolve_parent_module_for_inline() {
111111
);
112112
}
113113

114+
#[test]
115+
fn test_resolve_parent_module_for_inline_to_lib() {
116+
let (analysis, pos) = analysis_and_position(
117+
"
118+
//- /lib.rs
119+
mod foo {
120+
<|>
121+
}
122+
",
123+
);
124+
let symbols = analysis.parent_module(pos).unwrap();
125+
assert_eq_dbg(
126+
r#"[]"#,
127+
&symbols,
128+
);
129+
}
130+
131+
#[test]
132+
fn test_path_one_layer() {
133+
let (analysis, pos) = analysis_and_position(
134+
"
135+
//- /lib.rs
136+
mod foo;
137+
//- /foo/mod.rs
138+
mod bla;
139+
//- /foo/bla.rs
140+
<|> //empty
141+
");
142+
let symbols = analysis.module_path(pos).unwrap();
143+
assert_eq!(
144+
"foo::bla",
145+
&symbols,
146+
);
147+
}
148+
149+
#[test]
150+
fn test_path_two_layer() {
151+
let (analysis, pos) = analysis_and_position(
152+
"
153+
//- /lib.rs
154+
mod foo;
155+
//- /foo/mod.rs
156+
mod bla;
157+
//- /foo/bla/mod.rs
158+
mod more;
159+
//- /foo/bla/more.rs
160+
<|> //empty
161+
");
162+
let symbols = analysis.module_path(pos).unwrap();
163+
assert_eq!(
164+
"foo::bla::more",
165+
&symbols,
166+
);
167+
}
168+
169+
#[test]
170+
fn test_path_in_file_mod() {
171+
let (analysis, pos) = analysis_and_position(
172+
"
173+
//- /lib.rs
174+
mod foo;
175+
//- /foo.rs
176+
mod bar {
177+
<|> //empty
178+
}
179+
");
180+
let symbols = analysis.module_path(pos).unwrap();
181+
assert_eq!(
182+
"foo::bar",
183+
&symbols,
184+
);
185+
}
186+
114187
#[test]
115188
fn test_resolve_crate_root() {
116189
let mock = MockAnalysis::with_files(

0 commit comments

Comments
 (0)