From 1135cd3e783d51ff5910cbff37b443d0acbd08e6 Mon Sep 17 00:00:00 2001 From: zhuyunxing Date: Mon, 8 Apr 2024 21:01:16 +1000 Subject: [PATCH 1/2] Add MC/DC support to coverage test tools --- src/tools/compiletest/src/runtest.rs | 13 ++++++++ src/tools/coverage-dump/src/covfun.rs | 46 +++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index bb8509fe413..03580c0f14c 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -752,6 +752,19 @@ fn anonymize_coverage_line_numbers(coverage: &str) -> String { Lazy::new(|| Regex::new(r"(?m:^)(?(?: \|)+ 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 = Lazy::new(|| { + Regex::new(r"(?m:^)(?(?: \|)+---> MC/DC Decision Region \()[0-9]+:(?[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 = Lazy::new(|| { + Regex::new(r"(?m:^)(?(?: \|)+ Condition C[0-9]+ --> \()[0-9]+:").unwrap() + }); + let coverage = MCDC_CONDITION_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:"); + coverage.into_owned() } diff --git a/src/tools/coverage-dump/src/covfun.rs b/src/tools/coverage-dump/src/covfun.rs index 49e3a6ed583..b308c8de14f 100644 --- a/src/tools/coverage-dump/src/covfun.rs +++ b/src/tools/coverage-dump/src/covfun.rs @@ -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 @@ fn read_raw_mapping_kind(&mut self) -> anyhow::Result { 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 { From 4b3538352dae7fed4097d92fd373f8de9d22229f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 8 Apr 2024 21:27:18 +1000 Subject: [PATCH 2/2] Replace branch coverage line anonymization test with MC/DC We don't need the branch coverage version of this test, but we can recycle is to make sure that the MC/DC coverage support works as expected. --- src/tools/compiletest/src/runtest/tests.rs | 100 ++++++++++----------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/src/tools/compiletest/src/runtest/tests.rs b/src/tools/compiletest/src/runtest/tests.rs index ee42243e83d..817b56109a5 100644 --- a/src/tools/compiletest/src/runtest/tests.rs +++ b/src/tools/compiletest/src/runtest/tests.rs @@ -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() { - 7| 3| if std::mem::size_of::() > 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() { - LL| 3| if std::mem::size_of::() > 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() { - | 7| 1| if std::mem::size_of::() > 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() { - | LL| 1| if std::mem::size_of::() > 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);