]> git.proxmox.com Git - mirror_edk2.git/commitdiff
BaseTools/Plugin: Add coverage support for Unit Test
authorGua Guo <gua.guo@intel.com>
Tue, 27 Sep 2022 04:25:17 +0000 (12:25 +0800)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Wed, 4 Jan 2023 02:06:01 +0000 (02:06 +0000)
For GCC, use lcov to generate Unit Test code coverage
report

For VS2019, use OpenCppCoverage to generate code
coverage report

Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Liming Gao <gaoliming@bysoft.com.cn>
Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Sean Brogan <sean.brogan@microsoft.com>
Reviewed-by: Michael Kubacki <mikuback@linux.microsoft.com>
Signed-off-by: Gua Guo <gua.guo@intel.com>
BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner.py

index a8220aacd3966a181f0bd86c0fc525aff61d6396..0e013c5f1a7fb363b5f9733b1053c611794188b3 100644 (file)
@@ -94,7 +94,7 @@ class HostBasedUnitTestRunner(IUefiBuildPlugin):
 \r
                 # Run the test.\r
                 ret = RunCmd('"' + test + '"', "", workingdir=cp)\r
-                if(ret != 0):\r
+                if ret != 0:\r
                     logging.error("UnitTest Execution Error: " +\r
                                   os.path.basename(test))\r
                 else:\r
@@ -115,4 +115,103 @@ class HostBasedUnitTestRunner(IUefiBuildPlugin):
                                             "  %s - %s" % (case.attrib['name'], result.text))\r
                                         failure_count += 1\r
 \r
+            if thebuilder.env.GetValue("CODE_COVERAGE") != "FALSE":\r
+                if thebuilder.env.GetValue("TOOL_CHAIN_TAG") == "GCC5":\r
+                    self.gen_code_coverage_gcc(thebuilder)\r
+                elif thebuilder.env.GetValue("TOOL_CHAIN_TAG").startswith ("VS"):\r
+                    self.gen_code_coverage_msvc(thebuilder)\r
+                else:\r
+                    logging.info("Skipping code coverage. Currently, support GCC and MSVC compiler.")\r
+\r
         return failure_count\r
+\r
+    def gen_code_coverage_gcc(self, thebuilder):\r
+        logging.info("Generating UnitTest code coverage")\r
+\r
+        buildOutputBase = thebuilder.env.GetValue("BUILD_OUTPUT_BASE")\r
+        workspace = thebuilder.env.GetValue("WORKSPACE")\r
+\r
+        # Generate base code coverage for all source files\r
+        ret = RunCmd("lcov", f"--no-external --capture --initial --directory {buildOutputBase} --output-file {buildOutputBase}/cov-base.info --rc lcov_branch_coverage=1")\r
+        if ret != 0:\r
+            logging.error("UnitTest Coverage: Failed to build initial coverage data.")\r
+            return 1\r
+\r
+        # Coverage data for tested files only\r
+        ret = RunCmd("lcov", f"--capture --directory {buildOutputBase}/ --output-file {buildOutputBase}/coverage-test.info --rc lcov_branch_coverage=1")\r
+        if ret != 0:\r
+            logging.error("UnitTest Coverage: Failed to build coverage data for tested files.")\r
+            return 1\r
+\r
+        # Aggregate all coverage data\r
+        ret = RunCmd("lcov", f"--add-tracefile {buildOutputBase}/cov-base.info --add-tracefile {buildOutputBase}/coverage-test.info --output-file {buildOutputBase}/total-coverage.info --rc lcov_branch_coverage=1")\r
+        if ret != 0:\r
+            logging.error("UnitTest Coverage: Failed to aggregate coverage data.")\r
+            return 1\r
+\r
+        # Generate coverage XML\r
+        ret = RunCmd("lcov_cobertura",f"{buildOutputBase}/total-coverage.info -o {buildOutputBase}/compare.xml")\r
+        if ret != 0:\r
+            logging.error("UnitTest Coverage: Failed to generate coverage XML.")\r
+            return 1\r
+\r
+        # Filter out auto-generated and test code\r
+        ret = RunCmd("lcov_cobertura",f"{buildOutputBase}/total-coverage.info --excludes ^.*UnitTest\|^.*MU\|^.*Mock\|^.*DEBUG -o {buildOutputBase}/coverage.xml")\r
+        if ret != 0:\r
+            logging.error("UnitTest Coverage: Failed generate filtered coverage XML.")\r
+            return 1\r
+\r
+        # Generate all coverage file\r
+        testCoverageList = glob.glob (f"{workspace}/Build/**/total-coverage.info", recursive=True)\r
+\r
+        coverageFile = ""\r
+        for testCoverage in testCoverageList:\r
+            coverageFile += " --add-tracefile " + testCoverage\r
+        ret = RunCmd("lcov", f"{coverageFile} --output-file {workspace}/Build/all-coverage.info --rc lcov_branch_coverage=1")\r
+        if ret != 0:\r
+            logging.error("UnitTest Coverage: Failed generate all coverage file.")\r
+            return 1\r
+\r
+        # Generate and XML file if requested.for all package\r
+        if os.path.isfile(f"{workspace}/Build/coverage.xml"):\r
+            os.remove(f"{workspace}/Build/coverage.xml")\r
+        ret = RunCmd("lcov_cobertura",f"{workspace}/Build/all-coverage.info --excludes ^.*UnitTest\|^.*MU\|^.*Mock\|^.*DEBUG -o {workspace}/Build/coverage.xml")\r
+\r
+        return 0\r
+\r
+\r
+    def gen_code_coverage_msvc(self, thebuilder):\r
+        logging.info("Generating UnitTest code coverage")\r
+\r
+\r
+        buildOutputBase = thebuilder.env.GetValue("BUILD_OUTPUT_BASE")\r
+        testList = glob.glob(os.path.join(buildOutputBase, "**","*Test*.exe"), recursive=True)\r
+        workspace = thebuilder.env.GetValue("WORKSPACE")\r
+        workspace = (workspace + os.sep) if workspace[-1] != os.sep else workspace\r
+        # Generate coverage file\r
+        coverageFile = ""\r
+        for testFile in testList:\r
+            ret = RunCmd("OpenCppCoverage", f"--source {workspace} --export_type binary:{testFile}.cov -- {testFile}")\r
+            coverageFile += " --input_coverage=" + testFile + ".cov"\r
+            if ret != 0:\r
+                logging.error("UnitTest Coverage: Failed to collect coverage data.")\r
+                return 1\r
+\r
+        # Generate and XML file if requested.by each package\r
+        ret = RunCmd("OpenCppCoverage", f"--export_type cobertura:{os.path.join(buildOutputBase, 'coverage.xml')} --working_dir={workspace}Build {coverageFile}")\r
+        if ret != 0:\r
+            logging.error("UnitTest Coverage: Failed to generate cobertura format xml in single package.")\r
+            return 1\r
+\r
+        # Generate total report XML file for all package\r
+        testCoverageList = glob.glob(os.path.join(workspace, "Build", "**","*Test*.exe.cov"), recursive=True)\r
+        coverageFile = ""\r
+        for testCoverage in testCoverageList:\r
+            coverageFile += " --input_coverage=" + testCoverage\r
+\r
+        ret = RunCmd("OpenCppCoverage", f"--export_type cobertura:{workspace}Build/coverage.xml --working_dir={workspace}Build {coverageFile}")\r
+        if ret != 0:\r
+            logging.error("UnitTest Coverage: Failed to generate cobertura format xml.")\r
+            return 1\r
+\r
+        return 0\r