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
This commit is contained in:
Guillaume Gomez 2024-04-09 13:39:22 +02:00 committed by GitHub
commit fb0aab1976
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 105 additions and 54 deletions

View File

@ -752,6 +752,19 @@ impl<'test> TestCx<'test> {
Lazy::new(|| Regex::new(r"(?m:^)(?<prefix>(?: \|)+ Branch \()[0-9]+:").unwrap());
let coverage = BRANCH_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:");
// ` |---> MC/DC Decision Region (1:30) to (2:` => ` |---> MC/DC Decision Region (LL:30) to (LL:`
static MCDC_DECISION_LINE_NUMBER_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"(?m:^)(?<prefix>(?: \|)+---> MC/DC Decision Region \()[0-9]+:(?<middle>[0-9]+\) to \()[0-9]+:").unwrap()
});
let coverage =
MCDC_DECISION_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:${middle}LL:");
// ` | Condition C1 --> (1:` => ` | Condition C1 --> (LL:`
static MCDC_CONDITION_LINE_NUMBER_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"(?m:^)(?<prefix>(?: \|)+ Condition C[0-9]+ --> \()[0-9]+:").unwrap()
});
let coverage = MCDC_CONDITION_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:");
coverage.into_owned()
}

View File

@ -50,72 +50,68 @@ fn normalize_platform_differences() {
}
/// Test for anonymizing line numbers in coverage reports, especially for
/// branch regions.
/// MC/DC regions.
///
/// FIXME(#119681): This test can be removed when we have examples of branch
/// FIXME(#123409): This test can be removed when we have examples of MC/DC
/// coverage in the actual coverage test suite.
#[test]
fn anonymize_coverage_line_numbers() {
let anon = |coverage| TestCx::anonymize_coverage_line_numbers(coverage);
let input = r#"
6| 3|fn print_size<T>() {
7| 3| if std::mem::size_of::<T>() > 4 {
7| 2|fn mcdc_check_neither(a: bool, b: bool) {
8| 2| if a && b {
^0
------------------
| Branch (7:8): [True: 0, False: 1]
| Branch (7:8): [True: 0, False: 1]
| Branch (7:8): [True: 1, False: 0]
|---> MC/DC Decision Region (8:8) to (8:14)
|
| Number of Conditions: 2
| Condition C1 --> (8:8)
| Condition C2 --> (8:13)
|
| Executed MC/DC Test Vectors:
|
| C1, C2 Result
| 1 { F, - = F }
|
| C1-Pair: not covered
| C2-Pair: not covered
| MC/DC Coverage for Decision: 0.00%
|
------------------
8| 1| println!("size > 4");
9| 0| say("a and b");
10| 2| } else {
11| 2| say("not both");
12| 2| }
13| 2|}
"#;
let expected = r#"
LL| 3|fn print_size<T>() {
LL| 3| if std::mem::size_of::<T>() > 4 {
LL| 2|fn mcdc_check_neither(a: bool, b: bool) {
LL| 2| if a && b {
^0
------------------
| Branch (LL:8): [True: 0, False: 1]
| Branch (LL:8): [True: 0, False: 1]
| Branch (LL:8): [True: 1, False: 0]
------------------
LL| 1| println!("size > 4");
"#;
assert_eq!(anon(input), expected);
//////////
let input = r#"
12| 3|}
------------------
| branch_generics::print_size::<()>:
| 6| 1|fn print_size<T>() {
| 7| 1| if std::mem::size_of::<T>() > 4 {
| ------------------
| | Branch (7:8): [True: 0, False: 1]
| ------------------
| 8| 0| println!("size > 4");
| 9| 1| } else {
| 10| 1| println!("size <= 4");
| 11| 1| }
| 12| 1|}
------------------
"#;
let expected = r#"
LL| 3|}
------------------
| branch_generics::print_size::<()>:
| LL| 1|fn print_size<T>() {
| LL| 1| if std::mem::size_of::<T>() > 4 {
| ------------------
| | Branch (LL:8): [True: 0, False: 1]
| ------------------
| LL| 0| println!("size > 4");
| LL| 1| } else {
| LL| 1| println!("size <= 4");
| LL| 1| }
| LL| 1|}
|---> MC/DC Decision Region (LL:8) to (LL:14)
|
| Number of Conditions: 2
| Condition C1 --> (LL:8)
| Condition C2 --> (LL:13)
|
| Executed MC/DC Test Vectors:
|
| C1, C2 Result
| 1 { F, - = F }
|
| C1-Pair: not covered
| C2-Pair: not covered
| MC/DC Coverage for Decision: 0.00%
|
------------------
LL| 0| say("a and b");
LL| 2| } else {
LL| 2| say("not both");
LL| 2| }
LL| 2|}
"#;
assert_eq!(anon(input), expected);

View File

@ -70,7 +70,8 @@ pub(crate) fn dump_covfun_mappings(
}
// If the mapping is a branch region, print both of its arms
// in resolved form (even if they aren't expressions).
MappingKind::Branch { r#true, r#false } => {
MappingKind::Branch { r#true, r#false }
| MappingKind::MCDCBranch { r#true, r#false, .. } => {
println!(" true = {}", expression_resolver.format_term(r#true));
println!(" false = {}", expression_resolver.format_term(r#false));
}
@ -164,6 +165,26 @@ impl<'a> Parser<'a> {
let r#false = self.read_simple_term()?;
Ok(MappingKind::Branch { r#true, r#false })
}
5 => {
let bitmap_idx = self.read_uleb128_u32()?;
let conditions_num = self.read_uleb128_u32()?;
Ok(MappingKind::MCDCDecision { bitmap_idx, conditions_num })
}
6 => {
let r#true = self.read_simple_term()?;
let r#false = self.read_simple_term()?;
let condition_id = self.read_uleb128_u32()?;
let true_next_id = self.read_uleb128_u32()?;
let false_next_id = self.read_uleb128_u32()?;
Ok(MappingKind::MCDCBranch {
r#true,
r#false,
condition_id,
true_next_id,
false_next_id,
})
}
_ => Err(anyhow!("unknown mapping kind: {raw_mapping_kind:#x}")),
}
}
@ -224,7 +245,28 @@ enum MappingKind {
// Using raw identifiers here makes the dump output a little bit nicer
// (via the derived Debug), at the expense of making this tool's source
// code a little bit uglier.
Branch { r#true: CovTerm, r#false: CovTerm },
Branch {
r#true: CovTerm,
r#false: CovTerm,
},
MCDCBranch {
r#true: CovTerm,
r#false: CovTerm,
// These attributes are printed in Debug but not used directly.
#[allow(dead_code)]
condition_id: u32,
#[allow(dead_code)]
true_next_id: u32,
#[allow(dead_code)]
false_next_id: u32,
},
MCDCDecision {
// These attributes are printed in Debug but not used directly.
#[allow(dead_code)]
bitmap_idx: u32,
#[allow(dead_code)]
conditions_num: u32,
},
}
struct MappingRegion {