From c5eb27df333ce2d24b6d1721c77ce81520687d57 Mon Sep 17 00:00:00 2001 From: Shamitha Shashidhara Date: Thu, 16 Oct 2025 17:55:16 +0530 Subject: [PATCH] Add s32k1xx coverage and merge reports - Integrated tests-s32k1xx-debug into coverage workflow - Merged coverage reports for single badge and HTML output - Relative path to repo root - Excluded all third-party and mock files from coverage Change-Id: Ia1a8c46c4929494f64fa3613f811f38e380bfad2 --- .ci/code_coverage.py | 196 +++++++++++++--------------- .github/workflows/code-coverage.yml | 2 +- 2 files changed, 93 insertions(+), 105 deletions(-) diff --git a/.ci/code_coverage.py b/.ci/code_coverage.py index 3fb5e1d95b2..e18242a3e14 100644 --- a/.ci/code_coverage.py +++ b/.ci/code_coverage.py @@ -15,102 +15,83 @@ def get_full_path(command): sys.exit(1) return cmd - build_dir_name = "code_coverage" +targets = [ + ("s32k1xx", "tests-s32k1xx-debug"), + ("posix", "tests-posix-debug"), +] -def build(): - build_dir = Path(build_dir_name) - build_dir.parents - if build_dir.exists(): - shutil.rmtree(build_dir) +def build(env): + for name, preset in targets: + build_dir = Path(build_dir_name) / name - env = dict(os.environ) + if build_dir.exists(): + shutil.rmtree(build_dir) - threads = os.cpu_count() - 1 - if threads is None: - threads = 1 + subprocess.run([ + "cmake", "--preset", preset, + "-B", str(build_dir), + ], check=True, env=env) - env["CTEST_PARALLEL_LEVEL"] = str(threads) - env["CMAKE_BUILD_PARALLEL_LEVEL"] = str(threads) - env["CC"] = get_full_path(f"gcc-{GCC_VERSION}") - env["CXX"] = get_full_path(f"g++-{GCC_VERSION}") + subprocess.run([ + "cmake", "--build", str(build_dir), "--config", "Debug" + ], check=True, env=env) - subprocess.run( - [ - "cmake", - "--preset", - "tests-posix-debug", - "-B", - f"{build_dir_name}", - ], - check=True, - env=env, - ) + subprocess.run([ + "ctest", "--test-dir", str(build_dir), "--output-on-failure" + ], check=True, env=env) - subprocess.run( - ["cmake", "--build", f"{build_dir_name}", "--config", "Debug", "--verbose"], - check=True, - env=env, - ) +def generate_combined_coverage(): + tracefiles = [] - subprocess.run( - ["ctest", "--test-dir", f"{build_dir_name}", "--output-on-failure"], - check=True, - env=env, - ) + exclude_patterns = [ + "*/mock/*", + "*/gmock/*", + "*/gtest/*", + "*/test/*", + "*/3rdparty/*", + ] + for name, _ in targets: + unfiltered = f"code_coverage/coverage_{name}_unfiltered.info" + filtered = f"code_coverage/coverage_{name}.info" -def generate_coverage(): - # Capture coverage data - subprocess.run( - [ + subprocess.run([ "lcov", "--gcov-tool", f"gcov-{GCC_VERSION}", "--capture", - "--directory", - f"{build_dir_name}", + "--directory", f"code_coverage/{name}", "--no-external", - "--base-directory", - ".", - "--output-file", - f"{build_dir_name}/coverage_unfiltered.info", + "--base-directory", ".", + "--output-file", unfiltered, "--ignore-errors", "mismatch", - ], - check=True, - ) + "--rc", "geninfo_unexecuted_blocks=1", + ], check=True) + + subprocess.run([ + "lcov", "--remove", + unfiltered, + *exclude_patterns, + "--output-file", filtered, + "--ignore-errors", "mismatch", + ], check=True) - # Remove unwanted paths from coverage + tracefiles.append(filtered) - subprocess.run( - [ - "lcov", - "--gcov-tool", f"gcov-{GCC_VERSION}", - "--remove", - f"{build_dir_name}/coverage_unfiltered.info", - "*/3rdparty/*", - "*/mock/*", - "*/test/*", - "--output-file", - f"{build_dir_name}/coverage.info", - "--ignore-errors", "mismatch", - ], - check=True, - ) + merge_args = ["lcov", "--ignore-errors", "mismatch"] + for tf in tracefiles: + merge_args += ["--add-tracefile", tf] + merge_args += ["--output-file", f"{build_dir_name}/coverage.info"] - # Generate HTML report - subprocess.run( - [ - "genhtml", - f"{build_dir_name}/coverage.info", - "--output-directory", - f"{build_dir_name}/coverage", - "--prefix", - "/home/jenkins/", - ], - check=True, - ) + subprocess.run(merge_args, check=True) + repo_root = Path(__file__).resolve().parents[1] + subprocess.run([ + "genhtml", f"{build_dir_name}/coverage.info", + "--prefix", str(repo_root), + "--output-directory", f"{build_dir_name}/coverage" + ], check=True) def generate_badges(): # FIXME: It's questionable whether we want to have a dependency to an @@ -122,14 +103,14 @@ def generate_badges(): [ "lcov", "--gcov-tool", f"gcov-{GCC_VERSION}", - "--summary", - f"{build_dir_name}/coverage.info", + "--summary", f"{build_dir_name}/coverage.info", "--ignore-errors", "mismatch", ], capture_output=True, text=True, check=True, ) + summary = result.stdout line_percentage = re.search(r"lines\.*:\s+(\d+\.\d+)%", summary) @@ -139,37 +120,44 @@ def generate_badges(): coverage_badge_path.mkdir(parents=True, exist_ok=True) if line_percentage: - line_value = line_percentage.group(1) - print(f"Line Percentage: {line_value}%") - subprocess.run( - [ - "wget", - f"https://img.shields.io/badge/coverage-{line_value}%25-brightgreen.svg", - "-O", - coverage_badge_path.joinpath("line_coverage_badge.svg").as_posix(), - ], - check=True, - ) + line_value = line_percentage.group(1) + print(f"Line Percentage: {line_value}%") + subprocess.run( + [ + "wget", + f"https://img.shields.io/badge/coverage-{line_value}%25-brightgreen.svg", + "-O", + coverage_badge_path.joinpath("line_coverage_badge.svg").as_posix(), + ], + check=True, + ) if function_percentage: - function_value = function_percentage.group(1) - print(f"Function Percentage: {function_value}%") - subprocess.run( - [ - "wget", - f"https://img.shields.io/badge/coverage-{function_value}%25-brightgreen.svg", - "-O", - coverage_badge_path.joinpath("function_coverage_badge.svg").as_posix(), - ], - check=True, - ) - + function_value = function_percentage.group(1) + print(f"Function Percentage: {function_value}%") + subprocess.run( + [ + "wget", + f"https://img.shields.io/badge/coverage-{function_value}%25-brightgreen.svg", + "-O", + coverage_badge_path.joinpath("function_coverage_badge.svg").as_posix(), + ], + check=True, + ) if __name__ == "__main__": try: - build() - generate_coverage() + env = dict(os.environ) + threads = os.cpu_count() - 1 or 1 + env["CTEST_PARALLEL_LEVEL"] = str(threads) + env["CMAKE_BUILD_PARALLEL_LEVEL"] = str(threads) + + env["CC"] = get_full_path(f"gcc-{GCC_VERSION}") + env["CXX"] = get_full_path(f"g++-{GCC_VERSION}") + + build(env) + generate_combined_coverage() generate_badges() except subprocess.CalledProcessError as e: print(f"Command failed with exit code {e.returncode}") - sys.exit(e.returncode) + sys.exit(e.returncode) \ No newline at end of file diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 0f7de7b2b76..0b749adaf5a 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -50,4 +50,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: coverage_badges - path: code_coverage/coverage_badges.zip + path: code_coverage/coverage_badges.zip \ No newline at end of file