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