[edk2-devel] [PATCH 2/3] BaseTools/Plugin: Add coverage support for Unit Test

Michael Kubacki mikuback at linux.microsoft.com
Wed Dec 21 03:42:10 UTC 2022


Since you have direct access to the UEFI builder object, I think you can 
use "thebuilder.ws" to get the workspace path instead of looking it up 
in the build vars.

---

I know many of these pre-existing files place parentheses around 
conditions. This is not really Pythonic and I suggest new code avoid it.

The code additions in this patch have mixed usage.

This has no parentheses:

   if thebuilder.env.GetValue("TOOL_CHAIN_TAG") == "GCC5":

This does:

   if(ret != 0):

So, it would be:

   if ret != 0:

This is probably not worth sending a new patch over alone but something 
to consider if making other updates.

Reviewed-by: Michael Kubacki <michael.kubacki at microsoft.com>

On 9/29/2022 9:53 PM, Guo, Gua wrote:
> From: Gua Guo <gua.guo at intel.com>
> 
> 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 at intel.com>
> Cc: Bret Barkelew <Bret.Barkelew at microsoft.com>
> Cc: Liming Gao <gaoliming at bysoft.com.cn>
> Cc: Michael D Kinney <michael.d.kinney at intel.com>
> Cc: Sean Brogan <sean.brogan at microsoft.com>
> Signed-off-by: Gua Guo <gua.guo at intel.com>
> ---
>   .../HostBasedUnitTestRunner.py                | 119 ++++++++++++++++++
>   1 file changed, 119 insertions(+)
> 
> diff --git a/BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner.py b/BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner.py
> index c1eeaf2625..d92de236dc 100644
> --- a/BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner.py
> +++ b/BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner.py
> @@ -112,4 +112,123 @@ class HostBasedUnitTestRunner(IUefiBuildPlugin):
>                                               "  %s - %s" % (case.attrib['name'], result.text))
> 
>                                           failure_count += 1
> 
>   
> 
> +            if thebuilder.env.GetValue("TOOL_CHAIN_TAG") == "GCC5":
> 
> +                self.gen_code_coverage_gcc(thebuilder)
> 
> +            elif thebuilder.env.GetValue("TOOL_CHAIN_TAG") == "VS2019":
> 
> +                self.gen_code_coverage_msvc(thebuilder)
> 
> +            else:
> 
> +                logging.info("Skipping code coverage. Only supported on GCC.")
> 
> +
> 
>           return failure_count
> 
> +
> 
> +    def gen_code_coverage_gcc(self, thebuilder):
> 
> +        logging.info("Generating UnitTest code coverage")
> 
> +
> 
> +        buildOutputBase = thebuilder.env.GetValue("BUILD_OUTPUT_BASE")
> 
> +        workspace = thebuilder.env.GetValue("WORKSPACE")
> 
> +
> 
> +        # Generate base code coverage for all source files
> 
> +        ret = RunCmd("lcov", f"--no-external --capture --initial --directory {buildOutputBase} --output-file {buildOutputBase}/cov-base.info --rc lcov_branch_coverage=1")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed to build initial coverage data.")
> 
> +            return 1
> 
> +
> 
> +        # Coverage data for tested files only
> 
> +        ret = RunCmd("lcov", f"--capture --directory {buildOutputBase}/ --output-file {buildOutputBase}/coverage-test.info --rc lcov_branch_coverage=1")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed to build coverage data for tested files.")
> 
> +            return 1
> 
> +
> 
> +        # Aggregate all coverage data
> 
> +        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")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed to aggregate coverage data.")
> 
> +            return 1
> 
> +
> 
> +        # Generate coverage XML
> 
> +        ret = RunCmd("lcov_cobertura",f"{buildOutputBase}/total-coverage.info -o {buildOutputBase}/compare.xml")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed to generate coverage XML.")
> 
> +            return 1
> 
> +
> 
> +        # Filter out auto-generated and test code
> 
> +        ret = RunCmd("lcov_cobertura",f"{buildOutputBase}/total-coverage.info --excludes ^.*UnitTest\|^.*MU\|^.*Mock\|^.*DEBUG -o {buildOutputBase}/coverage.xml")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed generate filtered coverage XML.")
> 
> +            return 1
> 
> +
> 
> +        # Generate all coverage file
> 
> +        testCoverageList = glob.glob (f"{workspace}/Build/**/total-coverage.info", recursive=True)
> 
> +
> 
> +        coverageFile = ""
> 
> +        for testCoverage in testCoverageList:
> 
> +            coverageFile += " --add-tracefile " + testCoverage
> 
> +        ret = RunCmd("lcov", f"{coverageFile} --output-file {workspace}/Build/all-coverage.info --rc lcov_branch_coverage=1")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed generate all coverage file.")
> 
> +            return 1
> 
> +
> 
> +        # Generate and HTML file if requested.by each package
> 
> +        ret = RunCmd("pycobertura", f"show --format html --output {buildOutputBase}/coverage.html {buildOutputBase}/coverage.xml --source {workspace}")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed to generate HTML in single package..")
> 
> +
> 
> +        # Generate and HTML file if requested.for all package
> 
> +        if os.path.isfile(f"{workspace}/Build/coverage.xml"):
> 
> +            os.remove(f"{workspace}/Build/coverage.xml")
> 
> +        ret = RunCmd("lcov_cobertura",f"{workspace}/Build/all-coverage.info --excludes ^.*UnitTest\|^.*MU\|^.*Mock\|^.*DEBUG -o {workspace}/Build/coverage.xml")
> 
> +
> 
> +        if os.path.isfile(f"{workspace}/Build/coverage.html"):
> 
> +            os.remove(f"{workspace}/Build/coverage.html")
> 
> +        ret = RunCmd("pycobertura", f"show --format html --output {workspace}/Build/coverage.html {workspace}/Build/coverage.xml --source {workspace}")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed to generate HTML.")
> 
> +
> 
> +        return 0
> 
> +
> 
> +
> 
> +    def gen_code_coverage_msvc(self, thebuilder):
> 
> +        logging.info("Generating UnitTest code coverage")
> 
> +
> 
> +
> 
> +        buildOutputBase = thebuilder.env.GetValue("BUILD_OUTPUT_BASE")
> 
> +        testList = glob.glob(os.path.join(buildOutputBase, "**","*Test*.exe"), recursive=True)
> 
> +        workspace = thebuilder.env.GetValue("WORKSPACE")
> 
> +
> 
> +        # Generate coverage file
> 
> +        coverageFile = ""
> 
> +        for testFile in testList:
> 
> +            ret = RunCmd("OpenCppCoverage", f"--source {workspace} --export_type binary:{testFile}.cov -- {testFile}")
> 
> +            coverageFile += " --input_coverage=" + testFile + ".cov"
> 
> +            if(ret != 0):
> 
> +                logging.error("UnitTest Coverage: Failed to collect coverage data.")
> 
> +                return 1
> 
> +
> 
> +        # Generate and HTML file if requested.by each package
> 
> +        ret = RunCmd("OpenCppCoverage", f"--export_type cobertura:{buildOutputBase}/coverage.xml --working_dir={workspace}/Build {coverageFile}")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed to generate cobertura format xml in single package.")
> 
> +            return 1
> 
> +
> 
> +        ret = RunCmd("pycobertura", f"show --format html --output {buildOutputBase}/cverage.html {buildOutputBase}/coverage.xml --source {workspace}")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed to generate HTML in single package.")
> 
> +            return 1
> 
> +
> 
> +        # Generate total report HTML file for all package
> 
> +        testCoverageList = glob.glob(os.path.join(workspace, "Build", "**","*Test*.exe.cov"), recursive=True)
> 
> +        coverageFile = ""
> 
> +        for testCoverage in testCoverageList:
> 
> +            coverageFile += " --input_coverage=" + testCoverage
> 
> +
> 
> +        ret = RunCmd("OpenCppCoverage", f"--export_type cobertura:{workspace}/Build/coverage.xml --working_dir={workspace}/Build {coverageFile}")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed to generate cobertura format xml.")
> 
> +            return 1
> 
> +
> 
> +        ret = RunCmd("pycobertura", f"show --format html --output {workspace}/Build/coverage.html {workspace}/Build/coverage.xml --source {workspace}")
> 
> +        if(ret != 0):
> 
> +            logging.error("UnitTest Coverage: Failed to generate HTML.")
> 
> +            return 1
> 
> +
> 
> +        return 0
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#97658): https://edk2.groups.io/g/devel/message/97658
Mute This Topic: https://groups.io/mt/94008686/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-




More information about the edk2-devel-archive mailing list