1 //! This crate hosts a selection of "unit tests" for components of the `InstrumentCoverage` MIR
4 //! The tests construct a few "mock" objects, as needed, to support the `InstrumentCoverage`
5 //! functions and algorithms. Mocked objects include instances of `mir::Body`; including
6 //! `Terminator`s of various `kind`s, and `Span` objects. Some functions used by or used on
7 //! real, runtime versions of these mocked-up objects have constraints (such as cross-thread
8 //! limitations) and deep dependencies on other elements of the full Rust compiler (which is
9 //! *not* constructed or mocked for these tests).
11 //! Of particular note, attempting to simply print elements of the `mir::Body` with default
12 //! `Debug` formatting can fail because some `Debug` format implementations require the
13 //! `TyCtxt`, obtained via a static global variable that is *not* set for these tests.
14 //! Initializing the global type context is prohibitively complex for the scope and scale of these
15 //! tests (essentially requiring initializing the entire compiler).
17 //! Also note, some basic features of `Span` also rely on the `Span`s own "session globals", which
18 //! are unrelated to the `TyCtxt` global. Without initializing the `Span` session globals, some
19 //! basic, coverage-specific features would be impossible to test, but thankfully initializing these
20 //! globals is comparitively simpler. The easiest way is to wrap the test in a closure argument
21 //! to: `rustc_span::with_default_session_globals(|| { test_here(); })`.
28 use coverage_test_macros
::let_bcb
;
30 use rustc_data_structures
::graph
::WithNumNodes
;
31 use rustc_data_structures
::graph
::WithSuccessors
;
32 use rustc_index
::vec
::{Idx, IndexVec}
;
33 use rustc_middle
::mir
::coverage
::CoverageKind
;
34 use rustc_middle
::mir
::*;
35 use rustc_middle
::ty
::{self, DebruijnIndex, TyS, TypeFlags}
;
36 use rustc_span
::{self, BytePos, Pos, Span, DUMMY_SP}
;
38 // All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`.
39 const TEMP_BLOCK
: BasicBlock
= BasicBlock
::MAX
;
41 fn dummy_ty() -> &'
static TyS
<'
static> {
43 static DUMMY_TYS
: &'
static TyS
<'
static> = Box
::leak(box TyS
::make_for_test(
46 DebruijnIndex
::from_usize(0),
50 &DUMMY_TYS
.with(|tys
| *tys
)
53 struct MockBlocks
<'tcx
> {
54 blocks
: IndexVec
<BasicBlock
, BasicBlockData
<'tcx
>>,
55 dummy_place
: Place
<'tcx
>,
59 impl<'tcx
> MockBlocks
<'tcx
> {
62 blocks
: IndexVec
::new(),
63 dummy_place
: Place { local: RETURN_PLACE, projection: ty::List::empty() }
,
68 fn new_temp(&mut self) -> Local
{
69 let index
= self.next_local
;
74 fn push(&mut self, kind
: TerminatorKind
<'tcx
>) -> BasicBlock
{
75 let next_lo
= if let Some(last
) = self.blocks
.last() {
76 self.blocks
[last
].terminator().source_info
.span
.hi()
80 let next_hi
= next_lo
+ BytePos(1);
81 self.blocks
.push(BasicBlockData
{
83 terminator
: Some(Terminator
{
84 source_info
: SourceInfo
::outermost(Span
::with_root_ctxt(next_lo
, next_hi
)),
91 fn link(&mut self, from_block
: BasicBlock
, to_block
: BasicBlock
) {
92 match self.blocks
[from_block
].terminator_mut().kind
{
93 TerminatorKind
::Assert { ref mut target, .. }
94 | TerminatorKind
::Call { destination: Some((_, ref mut target)), .. }
95 | TerminatorKind
::Drop { ref mut target, .. }
96 | TerminatorKind
::DropAndReplace { ref mut target, .. }
97 | TerminatorKind
::FalseEdge { real_target: ref mut target, .. }
98 | TerminatorKind
::FalseUnwind { real_target: ref mut target, .. }
99 | TerminatorKind
::Goto { ref mut target }
100 | TerminatorKind
::InlineAsm { destination: Some(ref mut target), .. }
101 | TerminatorKind
::Yield { resume: ref mut target, .. }
=> *target
= to_block
,
102 ref invalid
=> bug
!("Invalid from_block: {:?}", invalid
),
108 some_from_block
: Option
<BasicBlock
>,
109 to_kind
: TerminatorKind
<'tcx
>,
111 let new_block
= self.push(to_kind
);
112 if let Some(from_block
) = some_from_block
{
113 self.link(from_block
, new_block
);
118 fn set_branch(&mut self, switchint
: BasicBlock
, branch_index
: usize, to_block
: BasicBlock
) {
119 match self.blocks
[switchint
].terminator_mut().kind
{
120 TerminatorKind
::SwitchInt { ref mut targets, .. }
=> {
121 let mut branches
= targets
.iter().collect
::<Vec
<_
>>();
122 let otherwise
= if branch_index
== branches
.len() {
125 let old_otherwise
= targets
.otherwise();
126 if branch_index
> branches
.len() {
127 branches
.push((branches
.len() as u128
, old_otherwise
));
128 while branches
.len() < branch_index
{
129 branches
.push((branches
.len() as u128
, TEMP_BLOCK
));
133 branches
[branch_index
] = (branch_index
as u128
, to_block
);
137 *targets
= SwitchTargets
::new(branches
.into_iter(), otherwise
);
139 ref invalid
=> bug
!("Invalid BasicBlock kind or no to_block: {:?}", invalid
),
143 fn call(&mut self, some_from_block
: Option
<BasicBlock
>) -> BasicBlock
{
146 TerminatorKind
::Call
{
147 func
: Operand
::Copy(self.dummy_place
.clone()),
149 destination
: Some((self.dummy_place
.clone(), TEMP_BLOCK
)),
151 from_hir_call
: false,
157 fn goto(&mut self, some_from_block
: Option
<BasicBlock
>) -> BasicBlock
{
158 self.add_block_from(some_from_block
, TerminatorKind
::Goto { target: TEMP_BLOCK }
)
161 fn switchint(&mut self, some_from_block
: Option
<BasicBlock
>) -> BasicBlock
{
162 let switchint_kind
= TerminatorKind
::SwitchInt
{
163 discr
: Operand
::Move(Place
::from(self.new_temp())),
164 switch_ty
: dummy_ty(),
165 targets
: SwitchTargets
::static_if(0, TEMP_BLOCK
, TEMP_BLOCK
),
167 self.add_block_from(some_from_block
, switchint_kind
)
170 fn return_(&mut self, some_from_block
: Option
<BasicBlock
>) -> BasicBlock
{
171 self.add_block_from(some_from_block
, TerminatorKind
::Return
)
174 fn to_body(self) -> Body
<'tcx
> {
175 Body
::new_cfg_only(self.blocks
)
179 fn debug_basic_blocks(mir_body
: &Body
<'tcx
>) -> String
{
186 let term
= &data
.terminator();
187 let kind
= &term
.kind
;
188 let span
= term
.source_info
.span
;
189 let sp
= format
!("(span:{},{})", span
.lo().to_u32(), span
.hi().to_u32());
191 TerminatorKind
::Assert { target, .. }
192 | TerminatorKind
::Call { destination: Some((_, target)), .. }
193 | TerminatorKind
::Drop { target, .. }
194 | TerminatorKind
::DropAndReplace { target, .. }
195 | TerminatorKind
::FalseEdge { real_target: target, .. }
196 | TerminatorKind
::FalseUnwind { real_target: target, .. }
197 | TerminatorKind
::Goto { target }
198 | TerminatorKind
::InlineAsm { destination: Some(target), .. }
199 | TerminatorKind
::Yield { resume: target, .. }
=> {
200 format
!("{}{:?}:{} -> {:?}", sp
, bb
, debug
::term_type(kind
), target
)
202 TerminatorKind
::SwitchInt { targets, .. }
=> {
203 format
!("{}{:?}:{} -> {:?}", sp
, bb
, debug
::term_type(kind
), targets
)
205 _
=> format
!("{}{:?}:{}", sp
, bb
, debug
::term_type(kind
)),
212 static PRINT_GRAPHS
: bool
= false;
214 fn print_mir_graphviz(name
: &str, mir_body
: &Body
<'_
>) {
217 "digraph {} {{\n{}\n}}",
224 " {:?} [label=\"{:?}: {}\"];\n{}",
227 debug
::term_type(&data
.terminator().kind
),
230 .map(|successor
| { format!(" {:?}
-> {:?}
;", bb, successor) })
241 fn print_coverage_graphviz(
244 basic_coverage_blocks: &graph::CoverageGraph,
248 "digraph {} {{\n{}
\n}}",
250 basic_coverage_blocks
252 .map(|(bcb, bcb_data)| {
254 " {:?}
[label
=\"{:?}
: {}
\"];\n{}
",
257 debug::term_type(&bcb_data.terminator(mir_body).kind),
258 basic_coverage_blocks
260 .map(|successor| { format!(" {:?} -> {:?};", bcb
, successor
) })
271 /// Create a mock `Body` with a simple flow.
272 fn goto_switchint() -> Body
<'a
> {
273 let mut blocks
= MockBlocks
::new();
274 let start
= blocks
.call(None
);
275 let goto
= blocks
.goto(Some(start
));
276 let switchint
= blocks
.switchint(Some(goto
));
277 let then_call
= blocks
.call(None
);
278 let else_call
= blocks
.call(None
);
279 blocks
.set_branch(switchint
, 0, then_call
);
280 blocks
.set_branch(switchint
, 1, else_call
);
281 blocks
.return_(Some(then_call
));
282 blocks
.return_(Some(else_call
));
284 let mir_body
= blocks
.to_body();
285 print_mir_graphviz("mir_goto_switchint", &mir_body
);
286 /* Graphviz character plots created using: `graph-easy --as=boxart`:
299 ┌─────────────┐ ┌────────────────┐
300 │ bb4: Call │ ◀── │ bb2: SwitchInt │
301 └─────────────┘ └────────────────┘
305 ┌─────────────┐ ┌────────────────┐
306 │ bb6: Return │ │ bb3: Call │
307 └─────────────┘ └────────────────┘
318 macro_rules
! assert_successors
{
319 ($basic_coverage_blocks
:ident
, $i
:ident
, [$
($successor
:ident
),*]) => {
320 let mut successors
= $basic_coverage_blocks
.successors
[$i
].clone();
321 successors
.sort_unstable();
322 assert_eq
!(successors
, vec
![$
($successor
),*]);
327 fn test_covgraph_goto_switchint() {
328 let mir_body
= goto_switchint();
330 eprintln
!("basic_blocks = {}", debug_basic_blocks(&mir_body
));
332 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
333 print_coverage_graphviz("covgraph_goto_switchint ", &mir_body
, &basic_coverage_blocks
);
335 ┌──────────────┐ ┌─────────────────┐
336 │ bcb2: Return │ ◀── │ bcb0: SwitchInt │
337 └──────────────┘ └─────────────────┘
346 basic_coverage_blocks
.num_nodes(),
348 "basic_coverage_blocks: {:?}",
349 basic_coverage_blocks
.iter_enumerated().collect
::<Vec
<_
>>()
356 assert_successors
!(basic_coverage_blocks
, bcb0
, [bcb1
, bcb2
]);
357 assert_successors
!(basic_coverage_blocks
, bcb1
, []);
358 assert_successors
!(basic_coverage_blocks
, bcb2
, []);
361 /// Create a mock `Body` with a loop.
362 fn switchint_then_loop_else_return() -> Body
<'a
> {
363 let mut blocks
= MockBlocks
::new();
364 let start
= blocks
.call(None
);
365 let switchint
= blocks
.switchint(Some(start
));
366 let then_call
= blocks
.call(None
);
367 blocks
.set_branch(switchint
, 0, then_call
);
368 let backedge_goto
= blocks
.goto(Some(then_call
));
369 blocks
.link(backedge_goto
, switchint
);
370 let else_return
= blocks
.return_(None
);
371 blocks
.set_branch(switchint
, 1, else_return
);
373 let mir_body
= blocks
.to_body();
374 print_mir_graphviz("mir_switchint_then_loop_else_return", &mir_body
);
382 ┌─────────────┐ ┌────────────────┐
383 │ bb4: Return │ ◀── │ bb1: SwitchInt │ ◀┐
384 └─────────────┘ └────────────────┘ │
402 fn test_covgraph_switchint_then_loop_else_return() {
403 let mir_body
= switchint_then_loop_else_return();
404 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
405 print_coverage_graphviz(
406 "covgraph_switchint_then_loop_else_return",
408 &basic_coverage_blocks
,
417 ┌────────────┐ ┌─────────────────┐
418 │ bcb3: Goto │ ◀── │ bcb1: SwitchInt │ ◀┐
419 └────────────┘ └─────────────────┘ │
423 │ ┌─────────────────┐ │
425 │ └─────────────────┘ │
427 └─────────────────────────────────────┘
430 basic_coverage_blocks
.num_nodes(),
432 "basic_coverage_blocks: {:?}",
433 basic_coverage_blocks
.iter_enumerated().collect
::<Vec
<_
>>()
441 assert_successors
!(basic_coverage_blocks
, bcb0
, [bcb1
]);
442 assert_successors
!(basic_coverage_blocks
, bcb1
, [bcb2
, bcb3
]);
443 assert_successors
!(basic_coverage_blocks
, bcb2
, []);
444 assert_successors
!(basic_coverage_blocks
, bcb3
, [bcb1
]);
447 /// Create a mock `Body` with nested loops.
448 fn switchint_loop_then_inner_loop_else_break() -> Body
<'a
> {
449 let mut blocks
= MockBlocks
::new();
450 let start
= blocks
.call(None
);
451 let switchint
= blocks
.switchint(Some(start
));
452 let then_call
= blocks
.call(None
);
453 blocks
.set_branch(switchint
, 0, then_call
);
454 let else_return
= blocks
.return_(None
);
455 blocks
.set_branch(switchint
, 1, else_return
);
457 let inner_start
= blocks
.call(Some(then_call
));
458 let inner_switchint
= blocks
.switchint(Some(inner_start
));
459 let inner_then_call
= blocks
.call(None
);
460 blocks
.set_branch(inner_switchint
, 0, inner_then_call
);
461 let inner_backedge_goto
= blocks
.goto(Some(inner_then_call
));
462 blocks
.link(inner_backedge_goto
, inner_switchint
);
463 let inner_else_break_goto
= blocks
.goto(None
);
464 blocks
.set_branch(inner_switchint
, 1, inner_else_break_goto
);
466 let backedge_goto
= blocks
.goto(Some(inner_else_break_goto
));
467 blocks
.link(backedge_goto
, switchint
);
469 let mir_body
= blocks
.to_body();
470 print_mir_graphviz("mir_switchint_loop_then_inner_loop_else_break", &mir_body
);
478 ┌─────────────┐ ┌────────────────┐
479 │ bb3: Return │ ◀── │ bb1: SwitchInt │ ◀─────┐
480 └─────────────┘ └────────────────┘ │
496 ┌─────────────┐ ┌────────────────┐ │
497 │ bb8: Goto │ ◀── │ bb5: SwitchInt │ ◀┐ │
498 └─────────────┘ └────────────────┘ │ │
502 ┌─────────────┐ ┌────────────────┐ │ │
503 │ bb9: Goto │ ─┐ │ bb6: Call │ │ │
504 └─────────────┘ │ └────────────────┘ │ │
508 │ ┌────────────────┐ │ │
510 │ └────────────────┘ │
512 └───────────────────────────┘
518 fn test_covgraph_switchint_loop_then_inner_loop_else_break() {
519 let mir_body
= switchint_loop_then_inner_loop_else_break();
520 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
521 print_coverage_graphviz(
522 "covgraph_switchint_loop_then_inner_loop_else_break",
524 &basic_coverage_blocks
,
533 ┌──────────────┐ ┌─────────────────┐
534 │ bcb2: Return │ ◀── │ bcb1: SwitchInt │ ◀┐
535 └──────────────┘ └─────────────────┘ │
539 ┌─────────────────┐ │
541 └─────────────────┘ │
545 ┌──────────────┐ ┌─────────────────┐ │
546 │ bcb6: Goto │ ◀── │ bcb4: SwitchInt │ ◀┼────┐
547 └──────────────┘ └─────────────────┘ │ │
551 │ ┌─────────────────┐ │ │
552 │ │ bcb5: Goto │ ─┘ │
553 │ └─────────────────┘ │
555 └────────────────────────────────────────────┘
558 basic_coverage_blocks
.num_nodes(),
560 "basic_coverage_blocks: {:?}",
561 basic_coverage_blocks
.iter_enumerated().collect
::<Vec
<_
>>()
572 assert_successors
!(basic_coverage_blocks
, bcb0
, [bcb1
]);
573 assert_successors
!(basic_coverage_blocks
, bcb1
, [bcb2
, bcb3
]);
574 assert_successors
!(basic_coverage_blocks
, bcb2
, []);
575 assert_successors
!(basic_coverage_blocks
, bcb3
, [bcb4
]);
576 assert_successors
!(basic_coverage_blocks
, bcb4
, [bcb5
, bcb6
]);
577 assert_successors
!(basic_coverage_blocks
, bcb5
, [bcb1
]);
578 assert_successors
!(basic_coverage_blocks
, bcb6
, [bcb4
]);
582 fn test_find_loop_backedges_none() {
583 let mir_body
= goto_switchint();
584 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
587 "basic_coverage_blocks = {:?}",
588 basic_coverage_blocks
.iter_enumerated().collect
::<Vec
<_
>>()
590 eprintln
!("successors = {:?}", basic_coverage_blocks
.successors
);
592 let backedges
= graph
::find_loop_backedges(&basic_coverage_blocks
);
594 backedges
.iter_enumerated().map(|(_bcb
, backedges
)| backedges
.len()).sum
::<usize>(),
602 fn test_find_loop_backedges_one() {
603 let mir_body
= switchint_then_loop_else_return();
604 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
605 let backedges
= graph
::find_loop_backedges(&basic_coverage_blocks
);
607 backedges
.iter_enumerated().map(|(_bcb
, backedges
)| backedges
.len()).sum
::<usize>(),
616 assert_eq
!(backedges
[bcb1
], vec
![bcb3
]);
620 fn test_find_loop_backedges_two() {
621 let mir_body
= switchint_loop_then_inner_loop_else_break();
622 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
623 let backedges
= graph
::find_loop_backedges(&basic_coverage_blocks
);
625 backedges
.iter_enumerated().map(|(_bcb
, backedges
)| backedges
.len()).sum
::<usize>(),
636 assert_eq
!(backedges
[bcb1
], vec
![bcb5
]);
637 assert_eq
!(backedges
[bcb4
], vec
![bcb6
]);
641 fn test_traverse_coverage_with_loops() {
642 let mir_body
= switchint_loop_then_inner_loop_else_break();
643 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
644 let mut traversed_in_order
= Vec
::new();
645 let mut traversal
= graph
::TraverseCoverageGraphWithLoops
::new(&basic_coverage_blocks
);
646 while let Some(bcb
) = traversal
.next(&basic_coverage_blocks
) {
647 traversed_in_order
.push(bcb
);
652 // bcb0 is visited first. Then bcb1 starts the first loop, and all remaining nodes, *except*
653 // bcb6 are inside the first loop.
655 *traversed_in_order
.last().expect("should have elements"),
657 "bcb6 should not be visited until all nodes inside the first loop have been visited"
661 fn synthesize_body_span_from_terminators(mir_body
: &Body
<'_
>) -> Span
{
662 let mut some_span
: Option
<Span
> = None
;
663 for (_
, data
) in mir_body
.basic_blocks().iter_enumerated() {
664 let term_span
= data
.terminator().source_info
.span
;
665 if let Some(span
) = some_span
.as_mut() {
666 *span
= span
.to(term_span
);
668 some_span
= Some(term_span
)
671 some_span
.expect("body must have at least one BasicBlock")
675 fn test_make_bcb_counters() {
676 rustc_span
::with_default_session_globals(|| {
677 let mir_body
= goto_switchint();
678 let body_span
= synthesize_body_span_from_terminators(&mir_body
);
679 let mut basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
680 let mut coverage_spans
= Vec
::new();
681 for (bcb
, data
) in basic_coverage_blocks
.iter_enumerated() {
683 spans
::filtered_terminator_span(data
.terminator(&mir_body
), body_span
)
685 coverage_spans
.push(spans
::CoverageSpan
::for_terminator(span
, bcb
, data
.last_bb()));
688 let mut coverage_counters
= counters
::CoverageCounters
::new(0);
689 let intermediate_expressions
= coverage_counters
690 .make_bcb_counters(&mut basic_coverage_blocks
, &coverage_spans
)
691 .expect("should be Ok");
692 assert_eq
!(intermediate_expressions
.len(), 0);
696 1, // coincidentally, bcb1 has a `Counter` with id = 1
697 match basic_coverage_blocks
[bcb1
].counter().expect("should have a counter") {
698 CoverageKind
::Counter { id, .. }
=> id
,
699 _
=> panic
!("expected a Counter"),
706 2, // coincidentally, bcb2 has a `Counter` with id = 2
707 match basic_coverage_blocks
[bcb2
].counter().expect("should have a counter") {
708 CoverageKind
::Counter { id, .. }
=> id
,
709 _
=> panic
!("expected a Counter"),