From 8f6318ae0535bca16a03b76438af0088d82ceb45 Mon Sep 17 00:00:00 2001 From: AydinIqbalNU Date: Wed, 24 Jun 2026 13:49:41 -0500 Subject: [PATCH 01/11] Update CMakeLists.txt --- CMakeLists.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c2efdf26..2debcaee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,3 +272,14 @@ if (APPLE) set(CMAKE_SKIP_RPATH "NO" CACHE INTERNAL "") endif(APPLE) + +# -------------------------------------------------- +# Unit Testing +# -------------------------------------------------- + +option(HARP_BUILD_TESTS "Build HARP unit tests" OFF) + +if(HARP_BUILD_TESTS) + enable_testing() + add_subdirectory(tests) +endif() From b95d381de3c9c06626737bcff5b665300a0ce048 Mon Sep 17 00:00:00 2001 From: AydinIqbalNU Date: Wed, 24 Jun 2026 13:50:58 -0500 Subject: [PATCH 02/11] Create CMakeLists.txt --- tests/CMakeLists.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/CMakeLists.txt diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..02eeb6ea --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,19 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip +) + +FetchContent_MakeAvailable(googletest) + +add_executable(harp_tests + test_main.cpp + test_smoke.cpp +) + +target_link_libraries(harp_tests PRIVATE + gtest_main +) + +add_test(NAME harp_tests COMMAND harp_tests) From f6b72c7fa905fd3b7da90f435115d830eabeef37 Mon Sep 17 00:00:00 2001 From: AydinIqbalNU Date: Wed, 24 Jun 2026 13:51:28 -0500 Subject: [PATCH 03/11] Create test_main.cpp --- tests/test_main.cpp | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/test_main.cpp diff --git a/tests/test_main.cpp b/tests/test_main.cpp new file mode 100644 index 00000000..9bb465e0 --- /dev/null +++ b/tests/test_main.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 751bbec96287cdbdb229589dc5e8585545bfbb46 Mon Sep 17 00:00:00 2001 From: AydinIqbalNU Date: Wed, 24 Jun 2026 13:51:55 -0500 Subject: [PATCH 04/11] Create test_smoke.cpp --- tests/test_smoke.cpp | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/test_smoke.cpp diff --git a/tests/test_smoke.cpp b/tests/test_smoke.cpp new file mode 100644 index 00000000..75dd069c --- /dev/null +++ b/tests/test_smoke.cpp @@ -0,0 +1,6 @@ +#include + +TEST(SmokeTest, BasicMath) +{ + EXPECT_EQ(2 + 2, 4); +} From 1b54ce3b3ef7b85688c159941dfd6b8da8c94ea2 Mon Sep 17 00:00:00 2001 From: AydinIqbalNU Date: Wed, 24 Jun 2026 13:56:22 -0500 Subject: [PATCH 05/11] Create pr-build-test.yml - Add CI workflow for unit tests and coverage --- .github/workflows/pr-build-test.yml | 120 ++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 .github/workflows/pr-build-test.yml diff --git a/.github/workflows/pr-build-test.yml b/.github/workflows/pr-build-test.yml new file mode 100644 index 00000000..cab2e63d --- /dev/null +++ b/.github/workflows/pr-build-test.yml @@ -0,0 +1,120 @@ +name: PR - Build / Test + +# triggers the workflow to run on pull request or manually. +# DESIGN CHOICES: could add daily auto trigger, +# trigger on github release, trigger on issue open, etc. +on: + pull_request: + branches: + - main + workflow_dispatch: + +# do not give actions write permissions +permissions: + actions: read + contents: read + +jobs: + build: + runs-on: ubuntu-latest # run job on ubuntu linux compiler + + steps: + + # pulls repo to be built by workflow + - name: Checkout code + uses: actions/checkout@v4 + + # install compilers, build system, coverage tool, and python + - name: Install dependencies + run: sudo apt-get update && sudo apt-get install -y build-essential cmake lcov python3 + + # configure project into a build directory, + # set to debug mode, and set coverage flags + - name: Configure CMake (with coverage) + run: cmake -S . -B build -DHARP_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="--coverage" -DCMAKE_C_FLAGS="--coverage" + + # compile library and tests + - name: Build + run: cmake --build build --config Debug + + # runs all tests registered by add_test() + - name: Run tests + run: ctest --test-dir build --output-on-failure + + # report which tests failed and what lines + - name: Extract failing tests + if: failure() # only run if tests failed + run: | + echo "## โŒ Failed Tests" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + ctest --test-dir build --output-on-failure | grep -E "FAILED|Failure|error:" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + + # uploads ctest result as an artifact to be downloaded. + # runs even on test failure. + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: ctest-results + path: build/Testing + + # write test results into summary UI + - name: Show test summary + if: always() + run: | + echo "## ๐Ÿงช Test Results" >> $GITHUB_STEP_SUMMARY + ctest --test-dir build --output-on-failure >> $GITHUB_STEP_SUMMARY + + # capture coverage, filter noise, generate html report + - name: Generate coverage report + if: always() + run: | + lcov --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --capture --directory build --output-file coverage.info + lcov --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --remove coverage.info '/usr/*' '*/tests/*' '*/_deps/*' --output-file coverage.info + genhtml coverage.info --output-directory coverage + + # update html coverage dashboard + - name: Upload coverage report + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage + + # AI-generated markdown test summary + + # parse coverage.info to compute coverage %. + # writes .md file containing percentage & progress bars + - name: Generate Markdown Coverage Dashboard + run: | + python3 << 'EOF' + import re + + covered = 0 + total = 0 + with open("coverage.info") as f: + for line in f: + if line.startswith("LF:"): + total += int(line.split(":")[1]) + if line.startswith("LH:"): + covered += int(line.split(":")[1]) + + pct = int(covered / total * 100) if total > 0 else 0 + bar_len = 40 + filled = int(bar_len * pct / 100) + bar = "โ–ˆ" * filled + "โ–‘" * (bar_len - filled) + + with open("coverage-dashboard.md", "w") as out: + out.write("# ๐Ÿ“ˆ Build Quality Dashboard\n\n") + out.write("## ๐Ÿงช Tests\n") + out.write("- Status: ๐ŸŸข All tests passed\n\n") + out.write("## ๐Ÿ“Š Coverage\n") + out.write(f"**Lines:** {pct}%\n") + out.write(bar + "\n\n") + out.write("_Dashboard generated automatically._\n") + EOF + + # put .md dashboard into actions summary + - name: Add Dashboard to Summary + run: cat coverage-dashboard.md >> $GITHUB_STEP_SUMMARY From 39db70b04500961dd36947e407e6eaca87c2d958 Mon Sep 17 00:00:00 2001 From: AydinIqbalNU Date: Wed, 24 Jun 2026 14:22:50 -0500 Subject: [PATCH 06/11] Update pr-build-test.yml --- .github/workflows/pr-build-test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/pr-build-test.yml b/.github/workflows/pr-build-test.yml index cab2e63d..cf07f2f6 100644 --- a/.github/workflows/pr-build-test.yml +++ b/.github/workflows/pr-build-test.yml @@ -23,6 +23,8 @@ jobs: # pulls repo to be built by workflow - name: Checkout code uses: actions/checkout@v4 + with: + submodules: recursive # install compilers, build system, coverage tool, and python - name: Install dependencies From ac79844b4890458d7aaadf507fd2f035db5adbca Mon Sep 17 00:00:00 2001 From: AydinIqbalNU Date: Wed, 24 Jun 2026 18:40:13 -0500 Subject: [PATCH 07/11] Update pr-build-test.yml --- .github/workflows/pr-build-test.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-build-test.yml b/.github/workflows/pr-build-test.yml index cf07f2f6..e917bce8 100644 --- a/.github/workflows/pr-build-test.yml +++ b/.github/workflows/pr-build-test.yml @@ -28,7 +28,23 @@ jobs: # install compilers, build system, coverage tool, and python - name: Install dependencies - run: sudo apt-get update && sudo apt-get install -y build-essential cmake lcov python3 + run: | + sudo apt-get update + + sudo apt-get install -y \ + build-essential \ + cmake \ + lcov \ + python3 \ + ninja-build \ + libasound2-dev \ + libx11-dev \ + libxinerama-dev \ + libxext-dev \ + libfreetype6-dev \ + libwebkit2gtk-4.0-dev \ + libglu1-mesa-dev \ + libcurl4-openssl-dev # configure project into a build directory, # set to debug mode, and set coverage flags From c78f0ea7fda680d6264f8aa69bf2e159e962a563 Mon Sep 17 00:00:00 2001 From: AydinIqbalNU Date: Wed, 24 Jun 2026 18:45:11 -0500 Subject: [PATCH 08/11] Update pr-build-test.yml --- .github/workflows/pr-build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-build-test.yml b/.github/workflows/pr-build-test.yml index e917bce8..1343f8e6 100644 --- a/.github/workflows/pr-build-test.yml +++ b/.github/workflows/pr-build-test.yml @@ -16,7 +16,7 @@ permissions: jobs: build: - runs-on: ubuntu-latest # run job on ubuntu linux compiler + runs-on: ubuntu-22.04 # run job on ubuntu linux compiler steps: From 2dc28a117e63e408701f977bd1fa1a21a7b2d902 Mon Sep 17 00:00:00 2001 From: AydinIqbalNU Date: Wed, 24 Jun 2026 18:54:15 -0500 Subject: [PATCH 09/11] Update pr-build-test.yml --- .github/workflows/pr-build-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-build-test.yml b/.github/workflows/pr-build-test.yml index 1343f8e6..33bfe5eb 100644 --- a/.github/workflows/pr-build-test.yml +++ b/.github/workflows/pr-build-test.yml @@ -88,8 +88,8 @@ jobs: - name: Generate coverage report if: always() run: | - lcov --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --capture --directory build --output-file coverage.info - lcov --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --remove coverage.info '/usr/*' '*/tests/*' '*/_deps/*' --output-file coverage.info + lcov --rc geninfo_unexecuted_blocks=1 --capture --directory build --output-file coverage.info + lcov --rc geninfo_unexecuted_blocks=1 --remove coverage.info '/usr/*' '*/tests/*' '*/_deps/*' --output-file coverage.info genhtml coverage.info --output-directory coverage # update html coverage dashboard From 0195c7cbd3ff79136671097717a9ee6019b81d54 Mon Sep 17 00:00:00 2001 From: AydinIqbalNU Date: Wed, 24 Jun 2026 19:06:48 -0500 Subject: [PATCH 10/11] Update pr-build-test.yml --- .github/workflows/pr-build-test.yml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-build-test.yml b/.github/workflows/pr-build-test.yml index 33bfe5eb..4d1cb0b0 100644 --- a/.github/workflows/pr-build-test.yml +++ b/.github/workflows/pr-build-test.yml @@ -89,8 +89,31 @@ jobs: if: always() run: | lcov --rc geninfo_unexecuted_blocks=1 --capture --directory build --output-file coverage.info - lcov --rc geninfo_unexecuted_blocks=1 --remove coverage.info '/usr/*' '*/tests/*' '*/_deps/*' --output-file coverage.info - genhtml coverage.info --output-directory coverage + + lcov --rc geninfo_unexecuted_blocks=1 \ + --remove coverage.info \ + '/usr/*' \ + '*/tests/*' \ + '*/_deps/*' \ + --output-file coverage.info + + if grep -q "^SF:" coverage.info; then + echo "HARP source files found in coverage data." + genhtml coverage.info --output-directory coverage + else + echo "No HARP source files were exercised." + mkdir -p coverage + + cat > coverage/index.html << 'EOF' + + Coverage Report + +

Coverage: 0%

+

No HARP source files were exercised by the tests.

+ + + EOF + fi # update html coverage dashboard - name: Upload coverage report From 8a0ef8de2898962278b56a7604643511ebcd7a7c Mon Sep 17 00:00:00 2001 From: AydinIqbalNU Date: Wed, 24 Jun 2026 19:16:41 -0500 Subject: [PATCH 11/11] Update pr-build-test.yml --- .github/workflows/pr-build-test.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pr-build-test.yml b/.github/workflows/pr-build-test.yml index 4d1cb0b0..60833300 100644 --- a/.github/workflows/pr-build-test.yml +++ b/.github/workflows/pr-build-test.yml @@ -102,17 +102,10 @@ jobs: genhtml coverage.info --output-directory coverage else echo "No HARP source files were exercised." + mkdir -p coverage - cat > coverage/index.html << 'EOF' - - Coverage Report - -

Coverage: 0%

-

No HARP source files were exercised by the tests.

- - - EOF + echo "

Coverage: 0%

No HARP source files were exercised by the tests.

" > coverage/index.html fi # update html coverage dashboard