From 1135cd3e783d51ff5910cbff37b443d0acbd08e6 Mon Sep 17 00:00:00 2001
From: zhuyunxing <zhuyunxing.zyx@alibaba-inc.com>
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 bb8509fe41377..03580c0f14c3e 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -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()
     }
 
diff --git a/src/tools/coverage-dump/src/covfun.rs b/src/tools/coverage-dump/src/covfun.rs
index 49e3a6ed58383..b308c8de14fe3 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 @@ 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 {

From 4b3538352dae7fed4097d92fd373f8de9d22229f Mon Sep 17 00:00:00 2001
From: Zalathar <Zalathar@users.noreply.github.com>
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 ee42243e83d8a..817b56109a504 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<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);