From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by mx.groups.io with SMTP id smtpd.web11.10789.1671594133014547255 for ; Tue, 20 Dec 2022 19:42:13 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@linux.microsoft.com header.s=default header.b=AYF3WMDo; spf=pass (domain: linux.microsoft.com, ip: 13.77.154.182, mailfrom: mikuback@linux.microsoft.com) Received: from [192.168.4.22] (unknown [47.201.8.94]) by linux.microsoft.com (Postfix) with ESMTPSA id 8D54A20C3360; Tue, 20 Dec 2022 19:42:11 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 8D54A20C3360 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1671594132; bh=yuGv4wUZfhbpJPTlBIEZLarxbYVru48al4tbmDsVOV8=; h=Date:Subject:To:Cc:References:From:In-Reply-To:From; b=AYF3WMDoyKRjXOuCtSYQK1uDsZdFxrLANipQHUSmiA2vYYPyDjZj8+yZW6XgcuJ4q csk21PhgqpDTpEfz5UCE6BRlXPZ8XAa6c7WdoWW+QX7L6eWj3a55oNKsYclGNfl6II D6hSaxiCVYSUHg9Rqye+P3NsK6iOz0dHquoG/p20= Message-ID: <53ea1aea-07f2-0e73-f5c2-36b22213f90f@linux.microsoft.com> Date: Tue, 20 Dec 2022 22:42:10 -0500 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.13.1 Subject: Re: [edk2-devel] [PATCH 2/3] BaseTools/Plugin: Add coverage support for Unit Test To: devel@edk2.groups.io, gua.guo@intel.com Cc: Bob Feng , Bret Barkelew , Liming Gao , Michael D Kinney , Sean Brogan References: <487d272749228714039d4da8b4f48d66533383ec.1664502701.git.gua.guo@intel.com> From: "Michael Kubacki" In-Reply-To: <487d272749228714039d4da8b4f48d66533383ec.1664502701.git.gua.guo@intel.com> Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit 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 On 9/29/2022 9:53 PM, Guo, Gua wrote: > From: Gua Guo > > For GCC, use lcov to generate Unit Test code coverage > report > > For VS2019, use OpenCppCoverage to generate code > coverage report > > Cc: Bob Feng > Cc: Bret Barkelew > Cc: Liming Gao > Cc: Michael D Kinney > Cc: Sean Brogan > Signed-off-by: Gua Guo > --- > .../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 >