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