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

2022-12-20 Thread Michael Kubacki
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 pack

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

2022-09-29 Thread Guo, Gua
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(