diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-08-13 16:59:50 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-08-13 16:59:50 +0100 |
commit | 018a6cac072767dfd630c22e6d9ce134b7bb09af (patch) | |
tree | 4293492e643f9a604c5f30e051289bcea182694c /crates/ide/src/runnables.rs | |
parent | 00fb411f3edea72a1a9739f7df6f21cca045730b (diff) | |
parent | 6bc2633c90cedad057c5201d1ab7f67b57247004 (diff) |
Merge #5750
5750: Rename ra_ide -> ide
r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ide/src/runnables.rs')
-rw-r--r-- | crates/ide/src/runnables.rs | 883 |
1 files changed, 883 insertions, 0 deletions
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs new file mode 100644 index 000000000..c3e07c8de --- /dev/null +++ b/crates/ide/src/runnables.rs | |||
@@ -0,0 +1,883 @@ | |||
1 | use std::fmt; | ||
2 | |||
3 | use cfg::CfgExpr; | ||
4 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; | ||
5 | use ide_db::RootDatabase; | ||
6 | use itertools::Itertools; | ||
7 | use syntax::{ | ||
8 | ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner}, | ||
9 | match_ast, SyntaxNode, | ||
10 | }; | ||
11 | |||
12 | use crate::{display::ToNav, FileId, NavigationTarget}; | ||
13 | |||
14 | #[derive(Debug, Clone)] | ||
15 | pub struct Runnable { | ||
16 | pub nav: NavigationTarget, | ||
17 | pub kind: RunnableKind, | ||
18 | pub cfg_exprs: Vec<CfgExpr>, | ||
19 | } | ||
20 | |||
21 | #[derive(Debug, Clone)] | ||
22 | pub enum TestId { | ||
23 | Name(String), | ||
24 | Path(String), | ||
25 | } | ||
26 | |||
27 | impl fmt::Display for TestId { | ||
28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
29 | match self { | ||
30 | TestId::Name(name) => write!(f, "{}", name), | ||
31 | TestId::Path(path) => write!(f, "{}", path), | ||
32 | } | ||
33 | } | ||
34 | } | ||
35 | |||
36 | #[derive(Debug, Clone)] | ||
37 | pub enum RunnableKind { | ||
38 | Test { test_id: TestId, attr: TestAttr }, | ||
39 | TestMod { path: String }, | ||
40 | Bench { test_id: TestId }, | ||
41 | DocTest { test_id: TestId }, | ||
42 | Bin, | ||
43 | } | ||
44 | |||
45 | #[derive(Debug, Eq, PartialEq)] | ||
46 | pub struct RunnableAction { | ||
47 | pub run_title: &'static str, | ||
48 | pub debugee: bool, | ||
49 | } | ||
50 | |||
51 | const TEST: RunnableAction = RunnableAction { run_title: "â–¶\u{fe0e} Run Test", debugee: true }; | ||
52 | const DOCTEST: RunnableAction = | ||
53 | RunnableAction { run_title: "â–¶\u{fe0e} Run Doctest", debugee: false }; | ||
54 | const BENCH: RunnableAction = RunnableAction { run_title: "â–¶\u{fe0e} Run Bench", debugee: true }; | ||
55 | const BIN: RunnableAction = RunnableAction { run_title: "â–¶\u{fe0e} Run", debugee: true }; | ||
56 | |||
57 | impl Runnable { | ||
58 | // test package::module::testname | ||
59 | pub fn label(&self, target: Option<String>) -> String { | ||
60 | match &self.kind { | ||
61 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), | ||
62 | RunnableKind::TestMod { path } => format!("test-mod {}", path), | ||
63 | RunnableKind::Bench { test_id } => format!("bench {}", test_id), | ||
64 | RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), | ||
65 | RunnableKind::Bin => { | ||
66 | target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | pub fn action(&self) -> &'static RunnableAction { | ||
72 | match &self.kind { | ||
73 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => &TEST, | ||
74 | RunnableKind::DocTest { .. } => &DOCTEST, | ||
75 | RunnableKind::Bench { .. } => &BENCH, | ||
76 | RunnableKind::Bin => &BIN, | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | |||
81 | // Feature: Run | ||
82 | // | ||
83 | // Shows a popup suggesting to run a test/benchmark/binary **at the current cursor | ||
84 | // location**. Super useful for repeatedly running just a single test. Do bind this | ||
85 | // to a shortcut! | ||
86 | // | ||
87 | // |=== | ||
88 | // | Editor | Action Name | ||
89 | // | ||
90 | // | VS Code | **Rust Analyzer: Run** | ||
91 | // |=== | ||
92 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | ||
93 | let sema = Semantics::new(db); | ||
94 | let source_file = sema.parse(file_id); | ||
95 | source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() | ||
96 | } | ||
97 | |||
98 | pub(crate) fn runnable( | ||
99 | sema: &Semantics<RootDatabase>, | ||
100 | item: SyntaxNode, | ||
101 | file_id: FileId, | ||
102 | ) -> Option<Runnable> { | ||
103 | match_ast! { | ||
104 | match item { | ||
105 | ast::Fn(it) => runnable_fn(sema, it, file_id), | ||
106 | ast::Module(it) => runnable_mod(sema, it, file_id), | ||
107 | _ => None, | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | |||
112 | fn runnable_fn( | ||
113 | sema: &Semantics<RootDatabase>, | ||
114 | fn_def: ast::Fn, | ||
115 | file_id: FileId, | ||
116 | ) -> Option<Runnable> { | ||
117 | let name_string = fn_def.name()?.text().to_string(); | ||
118 | |||
119 | let kind = if name_string == "main" { | ||
120 | RunnableKind::Bin | ||
121 | } else { | ||
122 | let test_id = match sema.to_def(&fn_def).map(|def| def.module(sema.db)) { | ||
123 | Some(module) => { | ||
124 | let def = sema.to_def(&fn_def)?; | ||
125 | let impl_trait_name = def.as_assoc_item(sema.db).and_then(|assoc_item| { | ||
126 | match assoc_item.container(sema.db) { | ||
127 | hir::AssocItemContainer::Trait(trait_item) => { | ||
128 | Some(trait_item.name(sema.db).to_string()) | ||
129 | } | ||
130 | hir::AssocItemContainer::ImplDef(impl_def) => impl_def | ||
131 | .target_ty(sema.db) | ||
132 | .as_adt() | ||
133 | .map(|adt| adt.name(sema.db).to_string()), | ||
134 | } | ||
135 | }); | ||
136 | |||
137 | let path_iter = module | ||
138 | .path_to_root(sema.db) | ||
139 | .into_iter() | ||
140 | .rev() | ||
141 | .filter_map(|it| it.name(sema.db)) | ||
142 | .map(|name| name.to_string()); | ||
143 | |||
144 | let path = if let Some(impl_trait_name) = impl_trait_name { | ||
145 | path_iter | ||
146 | .chain(std::iter::once(impl_trait_name)) | ||
147 | .chain(std::iter::once(name_string)) | ||
148 | .join("::") | ||
149 | } else { | ||
150 | path_iter.chain(std::iter::once(name_string)).join("::") | ||
151 | }; | ||
152 | |||
153 | TestId::Path(path) | ||
154 | } | ||
155 | None => TestId::Name(name_string), | ||
156 | }; | ||
157 | |||
158 | if has_test_related_attribute(&fn_def) { | ||
159 | let attr = TestAttr::from_fn(&fn_def); | ||
160 | RunnableKind::Test { test_id, attr } | ||
161 | } else if fn_def.has_atom_attr("bench") { | ||
162 | RunnableKind::Bench { test_id } | ||
163 | } else if has_doc_test(&fn_def) { | ||
164 | RunnableKind::DocTest { test_id } | ||
165 | } else { | ||
166 | return None; | ||
167 | } | ||
168 | }; | ||
169 | |||
170 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def)); | ||
171 | let cfg_exprs = attrs.cfg().collect(); | ||
172 | |||
173 | let nav = if let RunnableKind::DocTest { .. } = kind { | ||
174 | NavigationTarget::from_doc_commented( | ||
175 | sema.db, | ||
176 | InFile::new(file_id.into(), &fn_def), | ||
177 | InFile::new(file_id.into(), &fn_def), | ||
178 | ) | ||
179 | } else { | ||
180 | NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def)) | ||
181 | }; | ||
182 | Some(Runnable { nav, kind, cfg_exprs }) | ||
183 | } | ||
184 | |||
185 | #[derive(Debug, Copy, Clone)] | ||
186 | pub struct TestAttr { | ||
187 | pub ignore: bool, | ||
188 | } | ||
189 | |||
190 | impl TestAttr { | ||
191 | fn from_fn(fn_def: &ast::Fn) -> TestAttr { | ||
192 | let ignore = fn_def | ||
193 | .attrs() | ||
194 | .filter_map(|attr| attr.simple_name()) | ||
195 | .any(|attribute_text| attribute_text == "ignore"); | ||
196 | TestAttr { ignore } | ||
197 | } | ||
198 | } | ||
199 | |||
200 | /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as | ||
201 | /// `#[test_case(...)]`, `#[tokio::test]` and similar. | ||
202 | /// Also a regular `#[test]` annotation is supported. | ||
203 | /// | ||
204 | /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, | ||
205 | /// but it's better than not to have the runnables for the tests at all. | ||
206 | fn has_test_related_attribute(fn_def: &ast::Fn) -> bool { | ||
207 | fn_def | ||
208 | .attrs() | ||
209 | .filter_map(|attr| attr.path()) | ||
210 | .map(|path| path.syntax().to_string().to_lowercase()) | ||
211 | .any(|attribute_text| attribute_text.contains("test")) | ||
212 | } | ||
213 | |||
214 | fn has_doc_test(fn_def: &ast::Fn) -> bool { | ||
215 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) | ||
216 | } | ||
217 | |||
218 | fn runnable_mod( | ||
219 | sema: &Semantics<RootDatabase>, | ||
220 | module: ast::Module, | ||
221 | file_id: FileId, | ||
222 | ) -> Option<Runnable> { | ||
223 | if !has_test_function_or_multiple_test_submodules(&module) { | ||
224 | return None; | ||
225 | } | ||
226 | let module_def = sema.to_def(&module)?; | ||
227 | |||
228 | let path = module_def | ||
229 | .path_to_root(sema.db) | ||
230 | .into_iter() | ||
231 | .rev() | ||
232 | .filter_map(|it| it.name(sema.db)) | ||
233 | .join("::"); | ||
234 | |||
235 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module)); | ||
236 | let cfg_exprs = attrs.cfg().collect(); | ||
237 | let nav = module_def.to_nav(sema.db); | ||
238 | Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs }) | ||
239 | } | ||
240 | |||
241 | // We could create runnables for modules with number_of_test_submodules > 0, | ||
242 | // but that bloats the runnables for no real benefit, since all tests can be run by the submodule already | ||
243 | fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { | ||
244 | if let Some(item_list) = module.item_list() { | ||
245 | let mut number_of_test_submodules = 0; | ||
246 | |||
247 | for item in item_list.items() { | ||
248 | match item { | ||
249 | ast::Item::Fn(f) => { | ||
250 | if has_test_related_attribute(&f) { | ||
251 | return true; | ||
252 | } | ||
253 | } | ||
254 | ast::Item::Module(submodule) => { | ||
255 | if has_test_function_or_multiple_test_submodules(&submodule) { | ||
256 | number_of_test_submodules += 1; | ||
257 | } | ||
258 | } | ||
259 | _ => (), | ||
260 | } | ||
261 | } | ||
262 | |||
263 | number_of_test_submodules > 1 | ||
264 | } else { | ||
265 | false | ||
266 | } | ||
267 | } | ||
268 | |||
269 | #[cfg(test)] | ||
270 | mod tests { | ||
271 | use expect::{expect, Expect}; | ||
272 | |||
273 | use crate::mock_analysis::analysis_and_position; | ||
274 | |||
275 | use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST}; | ||
276 | |||
277 | fn check( | ||
278 | ra_fixture: &str, | ||
279 | // FIXME: fold this into `expect` as well | ||
280 | actions: &[&RunnableAction], | ||
281 | expect: Expect, | ||
282 | ) { | ||
283 | let (analysis, position) = analysis_and_position(ra_fixture); | ||
284 | let runnables = analysis.runnables(position.file_id).unwrap(); | ||
285 | expect.assert_debug_eq(&runnables); | ||
286 | assert_eq!( | ||
287 | actions, | ||
288 | runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice() | ||
289 | ); | ||
290 | } | ||
291 | |||
292 | #[test] | ||
293 | fn test_runnables() { | ||
294 | check( | ||
295 | r#" | ||
296 | //- /lib.rs | ||
297 | <|> | ||
298 | fn main() {} | ||
299 | |||
300 | #[test] | ||
301 | fn test_foo() {} | ||
302 | |||
303 | #[test] | ||
304 | #[ignore] | ||
305 | fn test_foo() {} | ||
306 | |||
307 | #[bench] | ||
308 | fn bench() {} | ||
309 | "#, | ||
310 | &[&BIN, &TEST, &TEST, &BENCH], | ||
311 | expect![[r#" | ||
312 | [ | ||
313 | Runnable { | ||
314 | nav: NavigationTarget { | ||
315 | file_id: FileId( | ||
316 | 1, | ||
317 | ), | ||
318 | full_range: 1..13, | ||
319 | focus_range: Some( | ||
320 | 4..8, | ||
321 | ), | ||
322 | name: "main", | ||
323 | kind: FN, | ||
324 | container_name: None, | ||
325 | description: None, | ||
326 | docs: None, | ||
327 | }, | ||
328 | kind: Bin, | ||
329 | cfg_exprs: [], | ||
330 | }, | ||
331 | Runnable { | ||
332 | nav: NavigationTarget { | ||
333 | file_id: FileId( | ||
334 | 1, | ||
335 | ), | ||
336 | full_range: 15..39, | ||
337 | focus_range: Some( | ||
338 | 26..34, | ||
339 | ), | ||
340 | name: "test_foo", | ||
341 | kind: FN, | ||
342 | container_name: None, | ||
343 | description: None, | ||
344 | docs: None, | ||
345 | }, | ||
346 | kind: Test { | ||
347 | test_id: Path( | ||
348 | "test_foo", | ||
349 | ), | ||
350 | attr: TestAttr { | ||
351 | ignore: false, | ||
352 | }, | ||
353 | }, | ||
354 | cfg_exprs: [], | ||
355 | }, | ||
356 | Runnable { | ||
357 | nav: NavigationTarget { | ||
358 | file_id: FileId( | ||
359 | 1, | ||
360 | ), | ||
361 | full_range: 41..75, | ||
362 | focus_range: Some( | ||
363 | 62..70, | ||
364 | ), | ||
365 | name: "test_foo", | ||
366 | kind: FN, | ||
367 | container_name: None, | ||
368 | description: None, | ||
369 | docs: None, | ||
370 | }, | ||
371 | kind: Test { | ||
372 | test_id: Path( | ||
373 | "test_foo", | ||
374 | ), | ||
375 | attr: TestAttr { | ||
376 | ignore: true, | ||
377 | }, | ||
378 | }, | ||
379 | cfg_exprs: [], | ||
380 | }, | ||
381 | Runnable { | ||
382 | nav: NavigationTarget { | ||
383 | file_id: FileId( | ||
384 | 1, | ||
385 | ), | ||
386 | full_range: 77..99, | ||
387 | focus_range: Some( | ||
388 | 89..94, | ||
389 | ), | ||
390 | name: "bench", | ||
391 | kind: FN, | ||
392 | container_name: None, | ||
393 | description: None, | ||
394 | docs: None, | ||
395 | }, | ||
396 | kind: Bench { | ||
397 | test_id: Path( | ||
398 | "bench", | ||
399 | ), | ||
400 | }, | ||
401 | cfg_exprs: [], | ||
402 | }, | ||
403 | ] | ||
404 | "#]], | ||
405 | ); | ||
406 | } | ||
407 | |||
408 | #[test] | ||
409 | fn test_runnables_doc_test() { | ||
410 | check( | ||
411 | r#" | ||
412 | //- /lib.rs | ||
413 | <|> | ||
414 | fn main() {} | ||
415 | |||
416 | /// ``` | ||
417 | /// let x = 5; | ||
418 | /// ``` | ||
419 | fn foo() {} | ||
420 | "#, | ||
421 | &[&BIN, &DOCTEST], | ||
422 | expect![[r#" | ||
423 | [ | ||
424 | Runnable { | ||
425 | nav: NavigationTarget { | ||
426 | file_id: FileId( | ||
427 | 1, | ||
428 | ), | ||
429 | full_range: 1..13, | ||
430 | focus_range: Some( | ||
431 | 4..8, | ||
432 | ), | ||
433 | name: "main", | ||
434 | kind: FN, | ||
435 | container_name: None, | ||
436 | description: None, | ||
437 | docs: None, | ||
438 | }, | ||
439 | kind: Bin, | ||
440 | cfg_exprs: [], | ||
441 | }, | ||
442 | Runnable { | ||
443 | nav: NavigationTarget { | ||
444 | file_id: FileId( | ||
445 | 1, | ||
446 | ), | ||
447 | full_range: 15..57, | ||
448 | focus_range: None, | ||
449 | name: "foo", | ||
450 | kind: FN, | ||
451 | container_name: None, | ||
452 | description: None, | ||
453 | docs: None, | ||
454 | }, | ||
455 | kind: DocTest { | ||
456 | test_id: Path( | ||
457 | "foo", | ||
458 | ), | ||
459 | }, | ||
460 | cfg_exprs: [], | ||
461 | }, | ||
462 | ] | ||
463 | "#]], | ||
464 | ); | ||
465 | } | ||
466 | |||
467 | #[test] | ||
468 | fn test_runnables_doc_test_in_impl() { | ||
469 | check( | ||
470 | r#" | ||
471 | //- /lib.rs | ||
472 | <|> | ||
473 | fn main() {} | ||
474 | |||
475 | struct Data; | ||
476 | impl Data { | ||
477 | /// ``` | ||
478 | /// let x = 5; | ||
479 | /// ``` | ||
480 | fn foo() {} | ||
481 | } | ||
482 | "#, | ||
483 | &[&BIN, &DOCTEST], | ||
484 | expect![[r#" | ||
485 | [ | ||
486 | Runnable { | ||
487 | nav: NavigationTarget { | ||
488 | file_id: FileId( | ||
489 | 1, | ||
490 | ), | ||
491 | full_range: 1..13, | ||
492 | focus_range: Some( | ||
493 | 4..8, | ||
494 | ), | ||
495 | name: "main", | ||
496 | kind: FN, | ||
497 | container_name: None, | ||
498 | description: None, | ||
499 | docs: None, | ||
500 | }, | ||
501 | kind: Bin, | ||
502 | cfg_exprs: [], | ||
503 | }, | ||
504 | Runnable { | ||
505 | nav: NavigationTarget { | ||
506 | file_id: FileId( | ||
507 | 1, | ||
508 | ), | ||
509 | full_range: 44..98, | ||
510 | focus_range: None, | ||
511 | name: "foo", | ||
512 | kind: FN, | ||
513 | container_name: None, | ||
514 | description: None, | ||
515 | docs: None, | ||
516 | }, | ||
517 | kind: DocTest { | ||
518 | test_id: Path( | ||
519 | "Data::foo", | ||
520 | ), | ||
521 | }, | ||
522 | cfg_exprs: [], | ||
523 | }, | ||
524 | ] | ||
525 | "#]], | ||
526 | ); | ||
527 | } | ||
528 | |||
529 | #[test] | ||
530 | fn test_runnables_module() { | ||
531 | check( | ||
532 | r#" | ||
533 | //- /lib.rs | ||
534 | <|> | ||
535 | mod test_mod { | ||
536 | #[test] | ||
537 | fn test_foo1() {} | ||
538 | } | ||
539 | "#, | ||
540 | &[&TEST, &TEST], | ||
541 | expect![[r#" | ||
542 | [ | ||
543 | Runnable { | ||
544 | nav: NavigationTarget { | ||
545 | file_id: FileId( | ||
546 | 1, | ||
547 | ), | ||
548 | full_range: 1..51, | ||
549 | focus_range: Some( | ||
550 | 5..13, | ||
551 | ), | ||
552 | name: "test_mod", | ||
553 | kind: MODULE, | ||
554 | container_name: None, | ||
555 | description: None, | ||
556 | docs: None, | ||
557 | }, | ||
558 | kind: TestMod { | ||
559 | path: "test_mod", | ||
560 | }, | ||
561 | cfg_exprs: [], | ||
562 | }, | ||
563 | Runnable { | ||
564 | nav: NavigationTarget { | ||
565 | file_id: FileId( | ||
566 | 1, | ||
567 | ), | ||
568 | full_range: 20..49, | ||
569 | focus_range: Some( | ||
570 | 35..44, | ||
571 | ), | ||
572 | name: "test_foo1", | ||
573 | kind: FN, | ||
574 | container_name: None, | ||
575 | description: None, | ||
576 | docs: None, | ||
577 | }, | ||
578 | kind: Test { | ||
579 | test_id: Path( | ||
580 | "test_mod::test_foo1", | ||
581 | ), | ||
582 | attr: TestAttr { | ||
583 | ignore: false, | ||
584 | }, | ||
585 | }, | ||
586 | cfg_exprs: [], | ||
587 | }, | ||
588 | ] | ||
589 | "#]], | ||
590 | ); | ||
591 | } | ||
592 | |||
593 | #[test] | ||
594 | fn only_modules_with_test_functions_or_more_than_one_test_submodule_have_runners() { | ||
595 | check( | ||
596 | r#" | ||
597 | //- /lib.rs | ||
598 | <|> | ||
599 | mod root_tests { | ||
600 | mod nested_tests_0 { | ||
601 | mod nested_tests_1 { | ||
602 | #[test] | ||
603 | fn nested_test_11() {} | ||
604 | |||
605 | #[test] | ||
606 | fn nested_test_12() {} | ||
607 | } | ||
608 | |||
609 | mod nested_tests_2 { | ||
610 | #[test] | ||
611 | fn nested_test_2() {} | ||
612 | } | ||
613 | |||
614 | mod nested_tests_3 {} | ||
615 | } | ||
616 | |||
617 | mod nested_tests_4 {} | ||
618 | } | ||
619 | "#, | ||
620 | &[&TEST, &TEST, &TEST, &TEST, &TEST, &TEST], | ||
621 | expect![[r#" | ||
622 | [ | ||
623 | Runnable { | ||
624 | nav: NavigationTarget { | ||
625 | file_id: FileId( | ||
626 | 1, | ||
627 | ), | ||
628 | full_range: 22..323, | ||
629 | focus_range: Some( | ||
630 | 26..40, | ||
631 | ), | ||
632 | name: "nested_tests_0", | ||
633 | kind: MODULE, | ||
634 | container_name: None, | ||
635 | description: None, | ||
636 | docs: None, | ||
637 | }, | ||
638 | kind: TestMod { | ||
639 | path: "root_tests::nested_tests_0", | ||
640 | }, | ||
641 | cfg_exprs: [], | ||
642 | }, | ||
643 | Runnable { | ||
644 | nav: NavigationTarget { | ||
645 | file_id: FileId( | ||
646 | 1, | ||
647 | ), | ||
648 | full_range: 51..192, | ||
649 | focus_range: Some( | ||
650 | 55..69, | ||
651 | ), | ||
652 | name: "nested_tests_1", | ||
653 | kind: MODULE, | ||
654 | container_name: None, | ||
655 | description: None, | ||
656 | docs: None, | ||
657 | }, | ||
658 | kind: TestMod { | ||
659 | path: "root_tests::nested_tests_0::nested_tests_1", | ||
660 | }, | ||
661 | cfg_exprs: [], | ||
662 | }, | ||
663 | Runnable { | ||
664 | nav: NavigationTarget { | ||
665 | file_id: FileId( | ||
666 | 1, | ||
667 | ), | ||
668 | full_range: 84..126, | ||
669 | focus_range: Some( | ||
670 | 107..121, | ||
671 | ), | ||
672 | name: "nested_test_11", | ||
673 | kind: FN, | ||
674 | container_name: None, | ||
675 | description: None, | ||
676 | docs: None, | ||
677 | }, | ||
678 | kind: Test { | ||
679 | test_id: Path( | ||
680 | "root_tests::nested_tests_0::nested_tests_1::nested_test_11", | ||
681 | ), | ||
682 | attr: TestAttr { | ||
683 | ignore: false, | ||
684 | }, | ||
685 | }, | ||
686 | cfg_exprs: [], | ||
687 | }, | ||
688 | Runnable { | ||
689 | nav: NavigationTarget { | ||
690 | file_id: FileId( | ||
691 | 1, | ||
692 | ), | ||
693 | full_range: 140..182, | ||
694 | focus_range: Some( | ||
695 | 163..177, | ||
696 | ), | ||
697 | name: "nested_test_12", | ||
698 | kind: FN, | ||
699 | container_name: None, | ||
700 | description: None, | ||
701 | docs: None, | ||
702 | }, | ||
703 | kind: Test { | ||
704 | test_id: Path( | ||
705 | "root_tests::nested_tests_0::nested_tests_1::nested_test_12", | ||
706 | ), | ||
707 | attr: TestAttr { | ||
708 | ignore: false, | ||
709 | }, | ||
710 | }, | ||
711 | cfg_exprs: [], | ||
712 | }, | ||
713 | Runnable { | ||
714 | nav: NavigationTarget { | ||
715 | file_id: FileId( | ||
716 | 1, | ||
717 | ), | ||
718 | full_range: 202..286, | ||
719 | focus_range: Some( | ||
720 | 206..220, | ||
721 | ), | ||
722 | name: "nested_tests_2", | ||
723 | kind: MODULE, | ||
724 | container_name: None, | ||
725 | description: None, | ||
726 | docs: None, | ||
727 | }, | ||
728 | kind: TestMod { | ||
729 | path: "root_tests::nested_tests_0::nested_tests_2", | ||
730 | }, | ||
731 | cfg_exprs: [], | ||
732 | }, | ||
733 | Runnable { | ||
734 | nav: NavigationTarget { | ||
735 | file_id: FileId( | ||
736 | 1, | ||
737 | ), | ||
738 | full_range: 235..276, | ||
739 | focus_range: Some( | ||
740 | 258..271, | ||
741 | ), | ||
742 | name: "nested_test_2", | ||
743 | kind: FN, | ||
744 | container_name: None, | ||
745 | description: None, | ||
746 | docs: None, | ||
747 | }, | ||
748 | kind: Test { | ||
749 | test_id: Path( | ||
750 | "root_tests::nested_tests_0::nested_tests_2::nested_test_2", | ||
751 | ), | ||
752 | attr: TestAttr { | ||
753 | ignore: false, | ||
754 | }, | ||
755 | }, | ||
756 | cfg_exprs: [], | ||
757 | }, | ||
758 | ] | ||
759 | "#]], | ||
760 | ); | ||
761 | } | ||
762 | |||
763 | #[test] | ||
764 | fn test_runnables_with_feature() { | ||
765 | check( | ||
766 | r#" | ||
767 | //- /lib.rs crate:foo cfg:feature=foo | ||
768 | <|> | ||
769 | #[test] | ||
770 | #[cfg(feature = "foo")] | ||
771 | fn test_foo1() {} | ||
772 | "#, | ||
773 | &[&TEST], | ||
774 | expect![[r#" | ||
775 | [ | ||
776 | Runnable { | ||
777 | nav: NavigationTarget { | ||
778 | file_id: FileId( | ||
779 | 1, | ||
780 | ), | ||
781 | full_range: 1..50, | ||
782 | focus_range: Some( | ||
783 | 36..45, | ||
784 | ), | ||
785 | name: "test_foo1", | ||
786 | kind: FN, | ||
787 | container_name: None, | ||
788 | description: None, | ||
789 | docs: None, | ||
790 | }, | ||
791 | kind: Test { | ||
792 | test_id: Path( | ||
793 | "test_foo1", | ||
794 | ), | ||
795 | attr: TestAttr { | ||
796 | ignore: false, | ||
797 | }, | ||
798 | }, | ||
799 | cfg_exprs: [ | ||
800 | KeyValue { | ||
801 | key: "feature", | ||
802 | value: "foo", | ||
803 | }, | ||
804 | ], | ||
805 | }, | ||
806 | ] | ||
807 | "#]], | ||
808 | ); | ||
809 | } | ||
810 | |||
811 | #[test] | ||
812 | fn test_runnables_with_features() { | ||
813 | check( | ||
814 | r#" | ||
815 | //- /lib.rs crate:foo cfg:feature=foo,feature=bar | ||
816 | <|> | ||
817 | #[test] | ||
818 | #[cfg(all(feature = "foo", feature = "bar"))] | ||
819 | fn test_foo1() {} | ||
820 | "#, | ||
821 | &[&TEST], | ||
822 | expect![[r#" | ||
823 | [ | ||
824 | Runnable { | ||
825 | nav: NavigationTarget { | ||
826 | file_id: FileId( | ||
827 | 1, | ||
828 | ), | ||
829 | full_range: 1..72, | ||
830 | focus_range: Some( | ||
831 | 58..67, | ||
832 | ), | ||
833 | name: "test_foo1", | ||
834 | kind: FN, | ||
835 | container_name: None, | ||
836 | description: None, | ||
837 | docs: None, | ||
838 | }, | ||
839 | kind: Test { | ||
840 | test_id: Path( | ||
841 | "test_foo1", | ||
842 | ), | ||
843 | attr: TestAttr { | ||
844 | ignore: false, | ||
845 | }, | ||
846 | }, | ||
847 | cfg_exprs: [ | ||
848 | All( | ||
849 | [ | ||
850 | KeyValue { | ||
851 | key: "feature", | ||
852 | value: "foo", | ||
853 | }, | ||
854 | KeyValue { | ||
855 | key: "feature", | ||
856 | value: "bar", | ||
857 | }, | ||
858 | ], | ||
859 | ), | ||
860 | ], | ||
861 | }, | ||
862 | ] | ||
863 | "#]], | ||
864 | ); | ||
865 | } | ||
866 | |||
867 | #[test] | ||
868 | fn test_runnables_no_test_function_in_module() { | ||
869 | check( | ||
870 | r#" | ||
871 | //- /lib.rs | ||
872 | <|> | ||
873 | mod test_mod { | ||
874 | fn foo1() {} | ||
875 | } | ||
876 | "#, | ||
877 | &[], | ||
878 | expect![[r#" | ||
879 | [] | ||
880 | "#]], | ||
881 | ); | ||
882 | } | ||
883 | } | ||