]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_transform/src/coverage/tests.rs
Rollup merge of #90132 - joshtriplett:stabilize-instrument-coverage, r=wesleywiser
[rust.git] / compiler / rustc_mir_transform / src / coverage / tests.rs
1 //! This crate hosts a selection of "unit tests" for components of the `InstrumentCoverage` MIR
2 //! pass.
3 //!
4 //! ```shell
5 //! ./x.py test --keep-stage 1 compiler/rustc_mir --test-args '--show-output coverage'
6 //! ```
7 //!
8 //! The tests construct a few "mock" objects, as needed, to support the `InstrumentCoverage`
9 //! functions and algorithms. Mocked objects include instances of `mir::Body`; including
10 //! `Terminator`s of various `kind`s, and `Span` objects. Some functions used by or used on
11 //! real, runtime versions of these mocked-up objects have constraints (such as cross-thread
12 //! limitations) and deep dependencies on other elements of the full Rust compiler (which is
13 //! *not* constructed or mocked for these tests).
14 //!
15 //! Of particular note, attempting to simply print elements of the `mir::Body` with default
16 //! `Debug` formatting can fail because some `Debug` format implementations require the
17 //! `TyCtxt`, obtained via a static global variable that is *not* set for these tests.
18 //! Initializing the global type context is prohibitively complex for the scope and scale of these
19 //! tests (essentially requiring initializing the entire compiler).
20 //!
21 //! Also note, some basic features of `Span` also rely on the `Span`s own "session globals", which
22 //! are unrelated to the `TyCtxt` global. Without initializing the `Span` session globals, some
23 //! basic, coverage-specific features would be impossible to test, but thankfully initializing these
24 //! globals is comparatively simpler. The easiest way is to wrap the test in a closure argument
25 //! to: `rustc_span::create_default_session_globals_then(|| { test_here(); })`.
26
27 use super::counters;
28 use super::debug;
29 use super::graph;
30 use super::spans;
31
32 use coverage_test_macros::let_bcb;
33
34 use itertools::Itertools;
35 use rustc_data_structures::graph::WithNumNodes;
36 use rustc_data_structures::graph::WithSuccessors;
37 use rustc_index::vec::{Idx, IndexVec};
38 use rustc_middle::mir::coverage::CoverageKind;
39 use rustc_middle::mir::*;
40 use rustc_middle::ty::{self, DebruijnIndex, TyS, TypeFlags};
41 use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP};
42
43 // All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`.
44 const TEMP_BLOCK: BasicBlock = BasicBlock::MAX;
45
46 fn dummy_ty() -> &'static TyS<'static> {
47     thread_local! {
48         static DUMMY_TYS: &'static TyS<'static> = Box::leak(Box::new(TyS::make_for_test(
49             ty::Bool,
50             TypeFlags::empty(),
51             DebruijnIndex::from_usize(0),
52         )));
53     }
54
55     &DUMMY_TYS.with(|tys| *tys)
56 }
57
58 struct MockBlocks<'tcx> {
59     blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
60     dummy_place: Place<'tcx>,
61     next_local: usize,
62 }
63
64 impl<'tcx> MockBlocks<'tcx> {
65     fn new() -> Self {
66         Self {
67             blocks: IndexVec::new(),
68             dummy_place: Place { local: RETURN_PLACE, projection: ty::List::empty() },
69             next_local: 0,
70         }
71     }
72
73     fn new_temp(&mut self) -> Local {
74         let index = self.next_local;
75         self.next_local += 1;
76         Local::new(index)
77     }
78
79     fn push(&mut self, kind: TerminatorKind<'tcx>) -> BasicBlock {
80         let next_lo = if let Some(last) = self.blocks.last() {
81             self.blocks[last].terminator().source_info.span.hi()
82         } else {
83             BytePos(1)
84         };
85         let next_hi = next_lo + BytePos(1);
86         self.blocks.push(BasicBlockData {
87             statements: vec![],
88             terminator: Some(Terminator {
89                 source_info: SourceInfo::outermost(Span::with_root_ctxt(next_lo, next_hi)),
90                 kind,
91             }),
92             is_cleanup: false,
93         })
94     }
95
96     fn link(&mut self, from_block: BasicBlock, to_block: BasicBlock) {
97         match self.blocks[from_block].terminator_mut().kind {
98             TerminatorKind::Assert { ref mut target, .. }
99             | TerminatorKind::Call { destination: Some((_, ref mut target)), .. }
100             | TerminatorKind::Drop { ref mut target, .. }
101             | TerminatorKind::DropAndReplace { ref mut target, .. }
102             | TerminatorKind::FalseEdge { real_target: ref mut target, .. }
103             | TerminatorKind::FalseUnwind { real_target: ref mut target, .. }
104             | TerminatorKind::Goto { ref mut target }
105             | TerminatorKind::InlineAsm { destination: Some(ref mut target), .. }
106             | TerminatorKind::Yield { resume: ref mut target, .. } => *target = to_block,
107             ref invalid => bug!("Invalid from_block: {:?}", invalid),
108         }
109     }
110
111     fn add_block_from(
112         &mut self,
113         some_from_block: Option<BasicBlock>,
114         to_kind: TerminatorKind<'tcx>,
115     ) -> BasicBlock {
116         let new_block = self.push(to_kind);
117         if let Some(from_block) = some_from_block {
118             self.link(from_block, new_block);
119         }
120         new_block
121     }
122
123     fn set_branch(&mut self, switchint: BasicBlock, branch_index: usize, to_block: BasicBlock) {
124         match self.blocks[switchint].terminator_mut().kind {
125             TerminatorKind::SwitchInt { ref mut targets, .. } => {
126                 let mut branches = targets.iter().collect::<Vec<_>>();
127                 let otherwise = if branch_index == branches.len() {
128                     to_block
129                 } else {
130                     let old_otherwise = targets.otherwise();
131                     if branch_index > branches.len() {
132                         branches.push((branches.len() as u128, old_otherwise));
133                         while branches.len() < branch_index {
134                             branches.push((branches.len() as u128, TEMP_BLOCK));
135                         }
136                         to_block
137                     } else {
138                         branches[branch_index] = (branch_index as u128, to_block);
139                         old_otherwise
140                     }
141                 };
142                 *targets = SwitchTargets::new(branches.into_iter(), otherwise);
143             }
144             ref invalid => bug!("Invalid BasicBlock kind or no to_block: {:?}", invalid),
145         }
146     }
147
148     fn call(&mut self, some_from_block: Option<BasicBlock>) -> BasicBlock {
149         self.add_block_from(
150             some_from_block,
151             TerminatorKind::Call {
152                 func: Operand::Copy(self.dummy_place.clone()),
153                 args: vec![],
154                 destination: Some((self.dummy_place.clone(), TEMP_BLOCK)),
155                 cleanup: None,
156                 from_hir_call: false,
157                 fn_span: DUMMY_SP,
158             },
159         )
160     }
161
162     fn goto(&mut self, some_from_block: Option<BasicBlock>) -> BasicBlock {
163         self.add_block_from(some_from_block, TerminatorKind::Goto { target: TEMP_BLOCK })
164     }
165
166     fn switchint(&mut self, some_from_block: Option<BasicBlock>) -> BasicBlock {
167         let switchint_kind = TerminatorKind::SwitchInt {
168             discr: Operand::Move(Place::from(self.new_temp())),
169             switch_ty: dummy_ty(),
170             targets: SwitchTargets::static_if(0, TEMP_BLOCK, TEMP_BLOCK),
171         };
172         self.add_block_from(some_from_block, switchint_kind)
173     }
174
175     fn return_(&mut self, some_from_block: Option<BasicBlock>) -> BasicBlock {
176         self.add_block_from(some_from_block, TerminatorKind::Return)
177     }
178
179     fn to_body(self) -> Body<'tcx> {
180         Body::new_cfg_only(self.blocks)
181     }
182 }
183
184 fn debug_basic_blocks<'tcx>(mir_body: &Body<'tcx>) -> String {
185     format!(
186         "{:?}",
187         mir_body
188             .basic_blocks()
189             .iter_enumerated()
190             .map(|(bb, data)| {
191                 let term = &data.terminator();
192                 let kind = &term.kind;
193                 let span = term.source_info.span;
194                 let sp = format!("(span:{},{})", span.lo().to_u32(), span.hi().to_u32());
195                 match kind {
196                     TerminatorKind::Assert { target, .. }
197                     | TerminatorKind::Call { destination: Some((_, target)), .. }
198                     | TerminatorKind::Drop { target, .. }
199                     | TerminatorKind::DropAndReplace { target, .. }
200                     | TerminatorKind::FalseEdge { real_target: target, .. }
201                     | TerminatorKind::FalseUnwind { real_target: target, .. }
202                     | TerminatorKind::Goto { target }
203                     | TerminatorKind::InlineAsm { destination: Some(target), .. }
204                     | TerminatorKind::Yield { resume: target, .. } => {
205                         format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), target)
206                     }
207                     TerminatorKind::SwitchInt { targets, .. } => {
208                         format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), targets)
209                     }
210                     _ => format!("{}{:?}:{}", sp, bb, debug::term_type(kind)),
211                 }
212             })
213             .collect::<Vec<_>>()
214     )
215 }
216
217 static PRINT_GRAPHS: bool = false;
218
219 fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) {
220     if PRINT_GRAPHS {
221         println!(
222             "digraph {} {{\n{}\n}}",
223             name,
224             mir_body
225                 .basic_blocks()
226                 .iter_enumerated()
227                 .map(|(bb, data)| {
228                     format!(
229                         "    {:?} [label=\"{:?}: {}\"];\n{}",
230                         bb,
231                         bb,
232                         debug::term_type(&data.terminator().kind),
233                         mir_body
234                             .successors(bb)
235                             .map(|successor| { format!("    {:?} -> {:?};", bb, successor) })
236                             .join("\n")
237                     )
238                 })
239                 .join("\n")
240         );
241     }
242 }
243
244 fn print_coverage_graphviz(
245     name: &str,
246     mir_body: &Body<'_>,
247     basic_coverage_blocks: &graph::CoverageGraph,
248 ) {
249     if PRINT_GRAPHS {
250         println!(
251             "digraph {} {{\n{}\n}}",
252             name,
253             basic_coverage_blocks
254                 .iter_enumerated()
255                 .map(|(bcb, bcb_data)| {
256                     format!(
257                         "    {:?} [label=\"{:?}: {}\"];\n{}",
258                         bcb,
259                         bcb,
260                         debug::term_type(&bcb_data.terminator(mir_body).kind),
261                         basic_coverage_blocks
262                             .successors(bcb)
263                             .map(|successor| { format!("    {:?} -> {:?};", bcb, successor) })
264                             .join("\n")
265                     )
266                 })
267                 .join("\n")
268         );
269     }
270 }
271
272 /// Create a mock `Body` with a simple flow.
273 fn goto_switchint<'a>() -> Body<'a> {
274     let mut blocks = MockBlocks::new();
275     let start = blocks.call(None);
276     let goto = blocks.goto(Some(start));
277     let switchint = blocks.switchint(Some(goto));
278     let then_call = blocks.call(None);
279     let else_call = blocks.call(None);
280     blocks.set_branch(switchint, 0, then_call);
281     blocks.set_branch(switchint, 1, else_call);
282     blocks.return_(Some(then_call));
283     blocks.return_(Some(else_call));
284
285     let mir_body = blocks.to_body();
286     print_mir_graphviz("mir_goto_switchint", &mir_body);
287     /* Graphviz character plots created using: `graph-easy --as=boxart`:
288                         ┌────────────────┐
289                         │   bb0: Call    │
290                         └────────────────┘
291                           │
292                           │
293                           ▼
294                         ┌────────────────┐
295                         │   bb1: Goto    │
296                         └────────────────┘
297                           │
298                           │
299                           ▼
300     ┌─────────────┐     ┌────────────────┐
301     │  bb4: Call  │ ◀── │ bb2: SwitchInt │
302     └─────────────┘     └────────────────┘
303       │                   │
304       │                   │
305       ▼                   ▼
306     ┌─────────────┐     ┌────────────────┐
307     │ bb6: Return │     │   bb3: Call    │
308     └─────────────┘     └────────────────┘
309                           │
310                           │
311                           ▼
312                         ┌────────────────┐
313                         │  bb5: Return   │
314                         └────────────────┘
315     */
316     mir_body
317 }
318
319 macro_rules! assert_successors {
320     ($basic_coverage_blocks:ident, $i:ident, [$($successor:ident),*]) => {
321         let mut successors = $basic_coverage_blocks.successors[$i].clone();
322         successors.sort_unstable();
323         assert_eq!(successors, vec![$($successor),*]);
324     }
325 }
326
327 #[test]
328 fn test_covgraph_goto_switchint() {
329     let mir_body = goto_switchint();
330     if false {
331         eprintln!("basic_blocks = {}", debug_basic_blocks(&mir_body));
332     }
333     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
334     print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &basic_coverage_blocks);
335     /*
336     ┌──────────────┐     ┌─────────────────┐
337     │ bcb2: Return │ ◀── │ bcb0: SwitchInt │
338     └──────────────┘     └─────────────────┘
339                            │
340                            │
341                            ▼
342                          ┌─────────────────┐
343                          │  bcb1: Return   │
344                          └─────────────────┘
345     */
346     assert_eq!(
347         basic_coverage_blocks.num_nodes(),
348         3,
349         "basic_coverage_blocks: {:?}",
350         basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
351     );
352
353     let_bcb!(0);
354     let_bcb!(1);
355     let_bcb!(2);
356
357     assert_successors!(basic_coverage_blocks, bcb0, [bcb1, bcb2]);
358     assert_successors!(basic_coverage_blocks, bcb1, []);
359     assert_successors!(basic_coverage_blocks, bcb2, []);
360 }
361
362 /// Create a mock `Body` with a loop.
363 fn switchint_then_loop_else_return<'a>() -> Body<'a> {
364     let mut blocks = MockBlocks::new();
365     let start = blocks.call(None);
366     let switchint = blocks.switchint(Some(start));
367     let then_call = blocks.call(None);
368     blocks.set_branch(switchint, 0, then_call);
369     let backedge_goto = blocks.goto(Some(then_call));
370     blocks.link(backedge_goto, switchint);
371     let else_return = blocks.return_(None);
372     blocks.set_branch(switchint, 1, else_return);
373
374     let mir_body = blocks.to_body();
375     print_mir_graphviz("mir_switchint_then_loop_else_return", &mir_body);
376     /*
377                         ┌────────────────┐
378                         │   bb0: Call    │
379                         └────────────────┘
380                           │
381                           │
382                           ▼
383     ┌─────────────┐     ┌────────────────┐
384     │ bb4: Return │ ◀── │ bb1: SwitchInt │ ◀┐
385     └─────────────┘     └────────────────┘  │
386                           │                 │
387                           │                 │
388                           ▼                 │
389                         ┌────────────────┐  │
390                         │   bb2: Call    │  │
391                         └────────────────┘  │
392                           │                 │
393                           │                 │
394                           ▼                 │
395                         ┌────────────────┐  │
396                         │   bb3: Goto    │ ─┘
397                         └────────────────┘
398     */
399     mir_body
400 }
401
402 #[test]
403 fn test_covgraph_switchint_then_loop_else_return() {
404     let mir_body = switchint_then_loop_else_return();
405     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
406     print_coverage_graphviz(
407         "covgraph_switchint_then_loop_else_return",
408         &mir_body,
409         &basic_coverage_blocks,
410     );
411     /*
412                        ┌─────────────────┐
413                        │   bcb0: Call    │
414                        └─────────────────┘
415                          │
416                          │
417                          ▼
418     ┌────────────┐     ┌─────────────────┐
419     │ bcb3: Goto │ ◀── │ bcb1: SwitchInt │ ◀┐
420     └────────────┘     └─────────────────┘  │
421       │                  │                  │
422       │                  │                  │
423       │                  ▼                  │
424       │                ┌─────────────────┐  │
425       │                │  bcb2: Return   │  │
426       │                └─────────────────┘  │
427       │                                     │
428       └─────────────────────────────────────┘
429     */
430     assert_eq!(
431         basic_coverage_blocks.num_nodes(),
432         4,
433         "basic_coverage_blocks: {:?}",
434         basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
435     );
436
437     let_bcb!(0);
438     let_bcb!(1);
439     let_bcb!(2);
440     let_bcb!(3);
441
442     assert_successors!(basic_coverage_blocks, bcb0, [bcb1]);
443     assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]);
444     assert_successors!(basic_coverage_blocks, bcb2, []);
445     assert_successors!(basic_coverage_blocks, bcb3, [bcb1]);
446 }
447
448 /// Create a mock `Body` with nested loops.
449 fn switchint_loop_then_inner_loop_else_break<'a>() -> Body<'a> {
450     let mut blocks = MockBlocks::new();
451     let start = blocks.call(None);
452     let switchint = blocks.switchint(Some(start));
453     let then_call = blocks.call(None);
454     blocks.set_branch(switchint, 0, then_call);
455     let else_return = blocks.return_(None);
456     blocks.set_branch(switchint, 1, else_return);
457
458     let inner_start = blocks.call(Some(then_call));
459     let inner_switchint = blocks.switchint(Some(inner_start));
460     let inner_then_call = blocks.call(None);
461     blocks.set_branch(inner_switchint, 0, inner_then_call);
462     let inner_backedge_goto = blocks.goto(Some(inner_then_call));
463     blocks.link(inner_backedge_goto, inner_switchint);
464     let inner_else_break_goto = blocks.goto(None);
465     blocks.set_branch(inner_switchint, 1, inner_else_break_goto);
466
467     let backedge_goto = blocks.goto(Some(inner_else_break_goto));
468     blocks.link(backedge_goto, switchint);
469
470     let mir_body = blocks.to_body();
471     print_mir_graphviz("mir_switchint_loop_then_inner_loop_else_break", &mir_body);
472     /*
473                         ┌────────────────┐
474                         │   bb0: Call    │
475                         └────────────────┘
476                           │
477                           │
478                           ▼
479     ┌─────────────┐     ┌────────────────┐
480     │ bb3: Return │ ◀── │ bb1: SwitchInt │ ◀─────┐
481     └─────────────┘     └────────────────┘       │
482                           │                      │
483                           │                      │
484                           ▼                      │
485                         ┌────────────────┐       │
486                         │   bb2: Call    │       │
487                         └────────────────┘       │
488                           │                      │
489                           │                      │
490                           ▼                      │
491                         ┌────────────────┐       │
492                         │   bb4: Call    │       │
493                         └────────────────┘       │
494                           │                      │
495                           │                      │
496                           ▼                      │
497     ┌─────────────┐     ┌────────────────┐       │
498     │  bb8: Goto  │ ◀── │ bb5: SwitchInt │ ◀┐    │
499     └─────────────┘     └────────────────┘  │    │
500       │                   │                 │    │
501       │                   │                 │    │
502       ▼                   ▼                 │    │
503     ┌─────────────┐     ┌────────────────┐  │    │
504     │  bb9: Goto  │ ─┐  │   bb6: Call    │  │    │
505     └─────────────┘  │  └────────────────┘  │    │
506                      │    │                 │    │
507                      │    │                 │    │
508                      │    ▼                 │    │
509                      │  ┌────────────────┐  │    │
510                      │  │   bb7: Goto    │ ─┘    │
511                      │  └────────────────┘       │
512                      │                           │
513                      └───────────────────────────┘
514     */
515     mir_body
516 }
517
518 #[test]
519 fn test_covgraph_switchint_loop_then_inner_loop_else_break() {
520     let mir_body = switchint_loop_then_inner_loop_else_break();
521     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
522     print_coverage_graphviz(
523         "covgraph_switchint_loop_then_inner_loop_else_break",
524         &mir_body,
525         &basic_coverage_blocks,
526     );
527     /*
528                          ┌─────────────────┐
529                          │   bcb0: Call    │
530                          └─────────────────┘
531                            │
532                            │
533                            ▼
534     ┌──────────────┐     ┌─────────────────┐
535     │ bcb2: Return │ ◀── │ bcb1: SwitchInt │ ◀┐
536     └──────────────┘     └─────────────────┘  │
537                            │                  │
538                            │                  │
539                            ▼                  │
540                          ┌─────────────────┐  │
541                          │   bcb3: Call    │  │
542                          └─────────────────┘  │
543                            │                  │
544                            │                  │
545                            ▼                  │
546     ┌──────────────┐     ┌─────────────────┐  │
547     │  bcb6: Goto  │ ◀── │ bcb4: SwitchInt │ ◀┼────┐
548     └──────────────┘     └─────────────────┘  │    │
549       │                    │                  │    │
550       │                    │                  │    │
551       │                    ▼                  │    │
552       │                  ┌─────────────────┐  │    │
553       │                  │   bcb5: Goto    │ ─┘    │
554       │                  └─────────────────┘       │
555       │                                            │
556       └────────────────────────────────────────────┘
557     */
558     assert_eq!(
559         basic_coverage_blocks.num_nodes(),
560         7,
561         "basic_coverage_blocks: {:?}",
562         basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
563     );
564
565     let_bcb!(0);
566     let_bcb!(1);
567     let_bcb!(2);
568     let_bcb!(3);
569     let_bcb!(4);
570     let_bcb!(5);
571     let_bcb!(6);
572
573     assert_successors!(basic_coverage_blocks, bcb0, [bcb1]);
574     assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]);
575     assert_successors!(basic_coverage_blocks, bcb2, []);
576     assert_successors!(basic_coverage_blocks, bcb3, [bcb4]);
577     assert_successors!(basic_coverage_blocks, bcb4, [bcb5, bcb6]);
578     assert_successors!(basic_coverage_blocks, bcb5, [bcb1]);
579     assert_successors!(basic_coverage_blocks, bcb6, [bcb4]);
580 }
581
582 #[test]
583 fn test_find_loop_backedges_none() {
584     let mir_body = goto_switchint();
585     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
586     if false {
587         eprintln!(
588             "basic_coverage_blocks = {:?}",
589             basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
590         );
591         eprintln!("successors = {:?}", basic_coverage_blocks.successors);
592     }
593     let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
594     assert_eq!(
595         backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
596         0,
597         "backedges: {:?}",
598         backedges
599     );
600 }
601
602 #[test]
603 fn test_find_loop_backedges_one() {
604     let mir_body = switchint_then_loop_else_return();
605     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
606     let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
607     assert_eq!(
608         backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
609         1,
610         "backedges: {:?}",
611         backedges
612     );
613
614     let_bcb!(1);
615     let_bcb!(3);
616
617     assert_eq!(backedges[bcb1], vec![bcb3]);
618 }
619
620 #[test]
621 fn test_find_loop_backedges_two() {
622     let mir_body = switchint_loop_then_inner_loop_else_break();
623     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
624     let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
625     assert_eq!(
626         backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
627         2,
628         "backedges: {:?}",
629         backedges
630     );
631
632     let_bcb!(1);
633     let_bcb!(4);
634     let_bcb!(5);
635     let_bcb!(6);
636
637     assert_eq!(backedges[bcb1], vec![bcb5]);
638     assert_eq!(backedges[bcb4], vec![bcb6]);
639 }
640
641 #[test]
642 fn test_traverse_coverage_with_loops() {
643     let mir_body = switchint_loop_then_inner_loop_else_break();
644     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
645     let mut traversed_in_order = Vec::new();
646     let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks);
647     while let Some(bcb) = traversal.next(&basic_coverage_blocks) {
648         traversed_in_order.push(bcb);
649     }
650
651     let_bcb!(6);
652
653     // bcb0 is visited first. Then bcb1 starts the first loop, and all remaining nodes, *except*
654     // bcb6 are inside the first loop.
655     assert_eq!(
656         *traversed_in_order.last().expect("should have elements"),
657         bcb6,
658         "bcb6 should not be visited until all nodes inside the first loop have been visited"
659     );
660 }
661
662 fn synthesize_body_span_from_terminators(mir_body: &Body<'_>) -> Span {
663     let mut some_span: Option<Span> = None;
664     for (_, data) in mir_body.basic_blocks().iter_enumerated() {
665         let term_span = data.terminator().source_info.span;
666         if let Some(span) = some_span.as_mut() {
667             *span = span.to(term_span);
668         } else {
669             some_span = Some(term_span)
670         }
671     }
672     some_span.expect("body must have at least one BasicBlock")
673 }
674
675 #[test]
676 fn test_make_bcb_counters() {
677     rustc_span::create_default_session_globals_then(|| {
678         let mir_body = goto_switchint();
679         let body_span = synthesize_body_span_from_terminators(&mir_body);
680         let mut basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
681         let mut coverage_spans = Vec::new();
682         for (bcb, data) in basic_coverage_blocks.iter_enumerated() {
683             if let Some(span) = spans::filtered_terminator_span(data.terminator(&mir_body)) {
684                 coverage_spans.push(spans::CoverageSpan::for_terminator(
685                     spans::function_source_span(span, body_span),
686                     span,
687                     bcb,
688                     data.last_bb(),
689                 ));
690             }
691         }
692         let mut coverage_counters = counters::CoverageCounters::new(0);
693         let intermediate_expressions = coverage_counters
694             .make_bcb_counters(&mut basic_coverage_blocks, &coverage_spans)
695             .expect("should be Ok");
696         assert_eq!(intermediate_expressions.len(), 0);
697
698         let_bcb!(1);
699         assert_eq!(
700             1, // coincidentally, bcb1 has a `Counter` with id = 1
701             match basic_coverage_blocks[bcb1].counter().expect("should have a counter") {
702                 CoverageKind::Counter { id, .. } => id,
703                 _ => panic!("expected a Counter"),
704             }
705             .as_u32()
706         );
707
708         let_bcb!(2);
709         assert_eq!(
710             2, // coincidentally, bcb2 has a `Counter` with id = 2
711             match basic_coverage_blocks[bcb2].counter().expect("should have a counter") {
712                 CoverageKind::Counter { id, .. } => id,
713                 _ => panic!("expected a Counter"),
714             }
715             .as_u32()
716         );
717     });
718 }