From bd399db73d2ee5aae1bc24d9edbe068c79180dbd Mon Sep 17 00:00:00 2001 From: cjkas Date: Sat, 31 Oct 2026 12:07:58 +0100 Subject: [PATCH] ci: coverage reports for the native and web UI tests A coverage env (the native env with gcov counters) runs the native tests in CI; gcovr writes the HTML report and a table on the run's summary page. The web UI tests report coverage with Node's built-in coverage, and genhtml turns its lcov into HTML. Both reports are run artifacts, and pushes to main publish them to GitHub Pages. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yaml | 98 ++++++++++++++++++++++++++++++++++-- .gitignore | 1 + AGENTS.md | 2 + gcovr.cfg | 9 ++++ platformio.ini | 11 ++++ 5 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 gcovr.cfg diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2c4c97c..c3fc962 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -24,11 +24,27 @@ jobs: # pioarduino's fork of PlatformIO Core, which the pioarduino platform needs. # Pinned: 6.2.0.post1 reports itself as 6.2.0-post.1, which the # platform's ">=6.2.0" reads as a pre-release and refuses. - - name: Install PlatformIO - run: pip install pioarduino==6.2.0 + - name: Install PlatformIO and gcovr + run: pip install pioarduino==6.2.0 gcovr==8.8 + # The coverage env is the native env with gcov counters: the same + # tests, measured. - name: Run native unit tests - run: pio test -e native + run: pio test -e coverage + + # gcovr.cfg picks the files; the table goes to the run's summary page. + - name: Coverage report + run: | + mkdir -p coverage + gcovr --html-details coverage/index.html \ + --markdown "$GITHUB_STEP_SUMMARY" --markdown-title "Native tests coverage" + + - name: Upload coverage report + uses: actions/upload-artifact@v4 + with: + name: coverage-cpp + path: coverage/ + retention-days: 14 webui: name: Web UI tests @@ -44,8 +60,82 @@ jobs: node-version: "22" # The protocol 2 client (data-src/js) against the recorded fixtures. + # Coverage: a table in the log, and lcov for the HTML report. - name: Run web UI tests - run: node --test "test/js/*.test.js" + run: | + mkdir -p coverage + node --test --experimental-test-coverage \ + --test-coverage-include="data-src/js/**" \ + --test-reporter=spec --test-reporter-destination=stdout \ + --test-reporter=lcov --test-reporter-destination=coverage/lcov.info \ + "test/js/*.test.js" + + - name: Coverage report + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq lcov + genhtml coverage/lcov.info --output-directory coverage/html \ + --title "ESPSomfy-RTS web UI tests" + + - name: Upload coverage report + uses: actions/upload-artifact@v4 + with: + name: coverage-js + path: coverage/html/ + retention-days: 14 + + # The coverage of main on the repo's GitHub Pages site + # (Settings > Pages > Source: GitHub Actions). + coverage-pages: + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: [test, webui] + name: Publish coverage + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deploy.outputs.page_url }} + concurrency: + group: pages + cancel-in-progress: false + + steps: + - name: Download native tests coverage + uses: actions/download-artifact@v4 + with: + name: coverage-cpp + path: site/cpp + + - name: Download web UI tests coverage + uses: actions/download-artifact@v4 + with: + name: coverage-js + path: site/js + + - name: Index page + run: | + cat > site/index.html < + + ESPSomfy-RTS coverage +

ESPSomfy-RTS test coverage

+

main at ${GITHUB_SHA::7}, $(date -u '+%Y-%m-%d %H:%M UTC')

+ + EOF + + - name: Upload site + uses: actions/upload-pages-artifact@v3 + with: + path: site + + - name: Deploy to GitHub Pages + id: deploy + uses: actions/deploy-pages@v4 build: # Don't build or publish firmware that fails its tests. diff --git a/.gitignore b/.gitignore index d9e701a..c91b492 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ esp32s3.svd # Build, Logs and Output Folders build/ +coverage/ data/ logs/ managed_components/ diff --git a/AGENTS.md b/AGENTS.md index 55a0dbf..c824928 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,6 +3,8 @@ - Add or update tests for the code you change, even if nobody asked. - to test run `pio test -e native` - the web UI's protocol 2 client (`data-src/js/`): `node --test "test/js/*.test.js"`. The minify step joins those files into `index.js`, so each one ends with a `module.exports` footer for Node and adds nothing global but its classes. +- coverage of the native tests: `pio test -e coverage`, then `gcovr` (`pip install gcovr`), which reads `gcovr.cfg` and writes `coverage/coverage_details.html`. Files the native env doesn't build aren't in it. CI publishes the coverage of `main` to the repo's GitHub Pages. +- coverage of the web UI tests: add `--experimental-test-coverage --test-coverage-include="data-src/js/**"` to the `node --test` command. ## Building - Firmware builds need pioarduino's Core (`pip install pioarduino`, or the pioarduino IDE extension). Stock PlatformIO Core refuses the pinned platform, see [ADR 0003](docs/adr/0003-pioarduino-idf55.md). diff --git a/gcovr.cfg b/gcovr.cfg new file mode 100644 index 0000000..04fd331 --- /dev/null +++ b/gcovr.cfg @@ -0,0 +1,9 @@ +# Coverage of the native tests: pio test -e coverage, then gcovr. +# Only the files the native env builds show up (build_src_filter); +# the rest of src/ needs the ESP32 and isn't measured. +filter = src/ +object-directory = .pio/build/coverage +sort = uncovered-percent +print-summary = yes +html-title = ESPSomfy-RTS native tests +html-details = coverage/ diff --git a/platformio.ini b/platformio.ini index 601d0b9..36b446e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -161,3 +161,14 @@ build_flags = -Isrc -Itest/stubs -D_POSIX_THREAD_SAFE_FUNCTIONS + +; The native tests with gcov counters, for the coverage report: +; pio test -e coverage && gcovr +; gcovr.cfg sets what the report covers and where it goes. +[env:coverage] +extends = env:native +build_flags = + ${env:native.build_flags} + --coverage + -O0 + -lgcov