Skip to content

Commit fb0aab1

Browse files
Rollup merge of #123626 - Zalathar:test-tools-mcdc, r=oli-obk
Add MC/DC support to coverage test tools Extracted and squashed from #123409 by `@ZhuUx.` These updates to the coverage test tools can land ahead of the main changes, slightly reducing the size and complexity of that PR. --- The `coverage-dump` changes aren't directly tested in this PR, but the tests in #123409 demonstrate that they do work on real MC/DC coverage output. `@rustbot` label +A-code-coverage
2 parents 1c5db28 + 4b35383 commit fb0aab1

File tree

3 files changed

+105
-54
lines changed

3 files changed

+105
-54
lines changed

src/tools/compiletest/src/runtest.rs

+13
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,19 @@ impl<'test> TestCx<'test> {
752752
Lazy::new(|| Regex::new(r"(?m:^)(?<prefix>(?: \|)+ Branch \()[0-9]+:").unwrap());
753753
let coverage = BRANCH_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:");
754754

755+
// ` |---> MC/DC Decision Region (1:30) to (2:` => ` |---> MC/DC Decision Region (LL:30) to (LL:`
756+
static MCDC_DECISION_LINE_NUMBER_RE: Lazy<Regex> = Lazy::new(|| {
757+
Regex::new(r"(?m:^)(?<prefix>(?: \|)+---> MC/DC Decision Region \()[0-9]+:(?<middle>[0-9]+\) to \()[0-9]+:").unwrap()
758+
});
759+
let coverage =
760+
MCDC_DECISION_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:${middle}LL:");
761+
762+
// ` | Condition C1 --> (1:` => ` | Condition C1 --> (LL:`
763+
static MCDC_CONDITION_LINE_NUMBER_RE: Lazy<Regex> = Lazy::new(|| {
764+
Regex::new(r"(?m:^)(?<prefix>(?: \|)+ Condition C[0-9]+ --> \()[0-9]+:").unwrap()
765+
});
766+
let coverage = MCDC_CONDITION_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:");
767+
755768
coverage.into_owned()
756769
}
757770

src/tools/compiletest/src/runtest/tests.rs

+48-52
Original file line numberDiff line numberDiff line change
@@ -50,72 +50,68 @@ fn normalize_platform_differences() {
5050
}
5151

5252
/// Test for anonymizing line numbers in coverage reports, especially for
53-
/// branch regions.
53+
/// MC/DC regions.
5454
///
55-
/// FIXME(#119681): This test can be removed when we have examples of branch
55+
/// FIXME(#123409): This test can be removed when we have examples of MC/DC
5656
/// coverage in the actual coverage test suite.
5757
#[test]
5858
fn anonymize_coverage_line_numbers() {
5959
let anon = |coverage| TestCx::anonymize_coverage_line_numbers(coverage);
6060

6161
let input = r#"
62-
6| 3|fn print_size<T>() {
63-
7| 3| if std::mem::size_of::<T>() > 4 {
62+
7| 2|fn mcdc_check_neither(a: bool, b: bool) {
63+
8| 2| if a && b {
64+
^0
6465
------------------
65-
| Branch (7:8): [True: 0, False: 1]
66-
| Branch (7:8): [True: 0, False: 1]
67-
| Branch (7:8): [True: 1, False: 0]
66+
|---> MC/DC Decision Region (8:8) to (8:14)
67+
|
68+
| Number of Conditions: 2
69+
| Condition C1 --> (8:8)
70+
| Condition C2 --> (8:13)
71+
|
72+
| Executed MC/DC Test Vectors:
73+
|
74+
| C1, C2 Result
75+
| 1 { F, - = F }
76+
|
77+
| C1-Pair: not covered
78+
| C2-Pair: not covered
79+
| MC/DC Coverage for Decision: 0.00%
80+
|
6881
------------------
69-
8| 1| println!("size > 4");
82+
9| 0| say("a and b");
83+
10| 2| } else {
84+
11| 2| say("not both");
85+
12| 2| }
86+
13| 2|}
7087
"#;
7188

7289
let expected = r#"
73-
LL| 3|fn print_size<T>() {
74-
LL| 3| if std::mem::size_of::<T>() > 4 {
90+
LL| 2|fn mcdc_check_neither(a: bool, b: bool) {
91+
LL| 2| if a && b {
92+
^0
7593
------------------
76-
| Branch (LL:8): [True: 0, False: 1]
77-
| Branch (LL:8): [True: 0, False: 1]
78-
| Branch (LL:8): [True: 1, False: 0]
79-
------------------
80-
LL| 1| println!("size > 4");
81-
"#;
82-
83-
assert_eq!(anon(input), expected);
84-
85-
//////////
86-
87-
let input = r#"
88-
12| 3|}
89-
------------------
90-
| branch_generics::print_size::<()>:
91-
| 6| 1|fn print_size<T>() {
92-
| 7| 1| if std::mem::size_of::<T>() > 4 {
93-
| ------------------
94-
| | Branch (7:8): [True: 0, False: 1]
95-
| ------------------
96-
| 8| 0| println!("size > 4");
97-
| 9| 1| } else {
98-
| 10| 1| println!("size <= 4");
99-
| 11| 1| }
100-
| 12| 1|}
101-
------------------
102-
"#;
103-
104-
let expected = r#"
105-
LL| 3|}
106-
------------------
107-
| branch_generics::print_size::<()>:
108-
| LL| 1|fn print_size<T>() {
109-
| LL| 1| if std::mem::size_of::<T>() > 4 {
110-
| ------------------
111-
| | Branch (LL:8): [True: 0, False: 1]
112-
| ------------------
113-
| LL| 0| println!("size > 4");
114-
| LL| 1| } else {
115-
| LL| 1| println!("size <= 4");
116-
| LL| 1| }
117-
| LL| 1|}
94+
|---> MC/DC Decision Region (LL:8) to (LL:14)
95+
|
96+
| Number of Conditions: 2
97+
| Condition C1 --> (LL:8)
98+
| Condition C2 --> (LL:13)
99+
|
100+
| Executed MC/DC Test Vectors:
101+
|
102+
| C1, C2 Result
103+
| 1 { F, - = F }
104+
|
105+
| C1-Pair: not covered
106+
| C2-Pair: not covered
107+
| MC/DC Coverage for Decision: 0.00%
108+
|
118109
------------------
110+
LL| 0| say("a and b");
111+
LL| 2| } else {
112+
LL| 2| say("not both");
113+
LL| 2| }
114+
LL| 2|}
119115
"#;
120116

121117
assert_eq!(anon(input), expected);

src/tools/coverage-dump/src/covfun.rs

+44-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ pub(crate) fn dump_covfun_mappings(
7070
}
7171
// If the mapping is a branch region, print both of its arms
7272
// in resolved form (even if they aren't expressions).
73-
MappingKind::Branch { r#true, r#false } => {
73+
MappingKind::Branch { r#true, r#false }
74+
| MappingKind::MCDCBranch { r#true, r#false, .. } => {
7475
println!(" true = {}", expression_resolver.format_term(r#true));
7576
println!(" false = {}", expression_resolver.format_term(r#false));
7677
}
@@ -164,6 +165,26 @@ impl<'a> Parser<'a> {
164165
let r#false = self.read_simple_term()?;
165166
Ok(MappingKind::Branch { r#true, r#false })
166167
}
168+
5 => {
169+
let bitmap_idx = self.read_uleb128_u32()?;
170+
let conditions_num = self.read_uleb128_u32()?;
171+
Ok(MappingKind::MCDCDecision { bitmap_idx, conditions_num })
172+
}
173+
6 => {
174+
let r#true = self.read_simple_term()?;
175+
let r#false = self.read_simple_term()?;
176+
let condition_id = self.read_uleb128_u32()?;
177+
let true_next_id = self.read_uleb128_u32()?;
178+
let false_next_id = self.read_uleb128_u32()?;
179+
Ok(MappingKind::MCDCBranch {
180+
r#true,
181+
r#false,
182+
condition_id,
183+
true_next_id,
184+
false_next_id,
185+
})
186+
}
187+
167188
_ => Err(anyhow!("unknown mapping kind: {raw_mapping_kind:#x}")),
168189
}
169190
}
@@ -224,7 +245,28 @@ enum MappingKind {
224245
// Using raw identifiers here makes the dump output a little bit nicer
225246
// (via the derived Debug), at the expense of making this tool's source
226247
// code a little bit uglier.
227-
Branch { r#true: CovTerm, r#false: CovTerm },
248+
Branch {
249+
r#true: CovTerm,
250+
r#false: CovTerm,
251+
},
252+
MCDCBranch {
253+
r#true: CovTerm,
254+
r#false: CovTerm,
255+
// These attributes are printed in Debug but not used directly.
256+
#[allow(dead_code)]
257+
condition_id: u32,
258+
#[allow(dead_code)]
259+
true_next_id: u32,
260+
#[allow(dead_code)]
261+
false_next_id: u32,
262+
},
263+
MCDCDecision {
264+
// These attributes are printed in Debug but not used directly.
265+
#[allow(dead_code)]
266+
bitmap_idx: u32,
267+
#[allow(dead_code)]
268+
conditions_num: u32,
269+
},
228270
}
229271

230272
struct MappingRegion {

0 commit comments

Comments
 (0)