Skip to content

Commit

Permalink
.pytool/Plugin/RustHostUnitTestPlugin: Ensure strings in ignore list (#…
Browse files Browse the repository at this point in the history
…541)

## Description

An ignore list is created in the plugin and passed as an argument to
`join()` in a couple places. For example:

- RustHostUnitTestPlugin.py:

`logging.debug(f"Paths to ignore when computing coverage: {'
'.join(ignore_list)}")`

- RustPackageHelper.py:

`params += f' -e COV_FLAGS="--out {report_type} --exclude-files
{",".join(ignore_list)}"'`

`join()` expects an iterable of strings. However, `Path` objects can
be added to the list. For example:

  `[pkg.path for pkg in rust_ws.members]`

(where `pkg.path` is a `Path` object)

This causes the following error:

```
   File ".pytool\Plugin\RustHostUnitTestPlugin\RustHostUnitTestPlugin.py", line 40, in RunBuildPlugin
    logging.debug(f"Paths to ignore when computing coverage: {' '.join(ignore_list)}")
                                                              ^^^^^^^^^^^^^^^^^^^^^
 TypeError: sequence item 1: expected str instance, WindowsPath found
 ```

This change converts all of the objects to their string representations
after `ignore_list` is finalized to simplify prior logic and ensure the
list consists of strings.

- [x] Impacts functionality?
  - **Functionality** - Does the change ultimately impact how firmware functions?
  - Examples: Add a new library, publish a new PPI, update an algorithm, ...
- [ ] Impacts security?
  - **Security** - Does the change have a direct security impact on an application,
    flow, or firmware?
  - Examples: Crypto algorithm change, buffer overflow fix, parameter
    validation improvement, ...
- [ ] Breaking change?
  - **Breaking change** - Will anyone consuming this change experience a break
    in build or boot behavior?
  - Examples: Add a new library class, move a module to a different repo, call
    a function in a new library class in a pre-existing module, ...
- [ ] Includes tests?
  - **Tests** - Does the change include any explicit test code?
  - Examples: Unit tests, integration tests, robot tests, ...
- [ ] Includes documentation?
  - **Documentation** - Does the change contain explicit documentation additions
    outside direct code modifications (and comments)?
  - Examples: Update readme file, add feature readme file, link to documentation
    on an a separate Web page, ...

## How This Was Tested

- Run RustHostUnitTestPlugin on a Cargo workspace
  with several members before and after the change.

## Integration Instructions

N/A

Signed-off-by: Michael Kubacki <[email protected]>
  • Loading branch information
makubacki authored Aug 29, 2023
1 parent 9a8606a commit 6c351bc
Showing 1 changed file with 14 additions and 13 deletions.
27 changes: 14 additions & 13 deletions .pytool/Plugin/RustHostUnitTestPlugin/RustHostUnitTestPlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
class RustHostUnitTestPlugin(ICiBuildPlugin):
def GetTestName(self, packagename: str, environment: object) -> tuple[str, str]:
return (f'Host Unit Tests in {packagename}', f'{packagename}.RustHostUnitTestPlugin')

def RunsOnTargetList(self) -> List[str]:
return ["NO-TARGET"]

def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM, PLMHelper, tc, output_stream):

ws = Edk2pathObj.WorkspacePath
rust_ws = PLMHelper.RustWorkspace(ws) # .pytool/Plugin/RustPackageHelper

Expand All @@ -34,28 +34,29 @@ def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM,
# 1. Any tests folder in a rust package
# 2. Everything in a submodule
# 3. Everything in an EDK2 package not being tested.
ignore_list = [str(Path("**", "tests", "*"))]
ignore_list.extend([str(Path(s, "**", "*")) for s in repo_details(ws)["Submodules"]])
ignore_list = [Path("**", "tests", "*")]
ignore_list.extend([Path(s, "**", "*") for s in repo_details(ws)["Submodules"]])
ignore_list.extend(list(set([pkg.path for pkg in rust_ws.members]) - set(package_path_list)))
ignore_list = [str(i) for i in ignore_list]
logging.debug(f"Paths to ignore when computing coverage: {' '.join(ignore_list)}")

# Run tests and evaluate results
results = rust_ws.coverage(package_name_list, ignore_list = ignore_list, report_type = "xml")

# Evaluate unit test results
failed = 0
for test in results["pass"]:
tc.LogStdOut(f'{test} ... PASS')

for test in results["fail"]:
tc.LogStdError(f'{test} ... FAIL')
failed += 1

# If we failed a unit test, we have no coverage data to evaluate
if failed > 0:
tc.SetFailed(f'Host unit tests failed. Failures {failed}', "CHECK_FAILED")
return failed

# Calculate coverage
coverage = {}
for file, cov in results["coverage"].items():
Expand All @@ -68,20 +69,20 @@ def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM,
coverage[package]["cov"] += int(covered)
coverage[package]["total"] += int(total)
else:
coverage[package] = {"cov": int(covered), "total": int(total)}
coverage[package] = {"cov": int(covered), "total": int(total)}

# Evaluate coverage results
default_cov = pkgconfig.get("coverage", 0.75)
for pkg, cov in coverage.items():
required_cov = pkgconfig.get("CoverageOverrides", {pkg: default_cov}).get(pkg, default_cov)

calc_cov = round(cov["cov"] / cov["total"], 2)
if calc_cov >= required_cov:
tc.LogStdOut(f'coverage::{pkg}: {calc_cov} greater than {required_cov} ... PASS')
else:
tc.LogStdError(f'coverage::{pkg}: {calc_cov} less than {required_cov} ... FAIL')
failed += 1

# Move coverage.xml to Build Directory
xml = Path(rust_ws.path) / "target" / "cobertura.xml"
out = Path(rust_ws.path) / "Build"
Expand All @@ -93,7 +94,7 @@ def RunBuildPlugin(self, packagename, Edk2pathObj, pkgconfig, environment, PLM,
with open(xml, 'r') as f:
contents = f.read()
contents = re.sub(r'<source>(.*?)</source>', r'<source>.</source>', contents)

with open (xml, "w") as f:
f.write(contents)

Expand Down

0 comments on commit 6c351bc

Please sign in to comment.