Skip to content

Commit 60eb535

Browse files
nrnhinesGrok
andauthored
Add cover-diff target for changed-line coverage reports (#3820)
Integrate optional diff-cover behind a cover_diff ninja/make target and document the pull-request iteration workflow. Add ci/coverage_files_from_diff.sh to build NRN_COVERAGE_FILES from a branch diff. Document NRN_COVERAGE_DIFF_BRANCH Can invoke diff-cover from PROJECT_SOURCE_DIR with an absolute path to coverage-combined.info. Add cover-begin, cover-html, cover-diff ninja/make target aliases Hyphenated names are thin DEPENDS aliases for the existing underscore targets so both forms work at the build tool level. --------- Co-authored-by: Grok <grok@x.ai>
1 parent db8b6a8 commit 60eb535

5 files changed

Lines changed: 103 additions & 12 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ set(NRN_RX3D_OPT_LEVEL
216216
"${NRN_RX3D_OPT_LEVEL_DEFAULT}"
217217
CACHE STRING "Optimization level for Cython generated files (non-zero may compile slowly)")
218218

219-
option(NRN_ENABLE_COVERAGE "EnableCode Coverage (make cover_begin, make cover_html)" OFF)
219+
option(NRN_ENABLE_COVERAGE "EnableCode Coverage (cover-begin, cover-html, cover-diff)" OFF)
220220
set(NRN_COVERAGE_FILES
221221
""
222222
CACHE STRING "semicolon (;) separated list of files to collect code coverage")

ci/coverage_files_from_diff.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
# Emit a semicolon-separated source file list for -DNRN_COVERAGE_FILES=...
3+
# Usage: coverage_files_from_diff.sh [base-ref]
4+
# Default base-ref is master (three-dot diff: base...HEAD).
5+
# Exits 1 with a stderr message when no compiled sources (.cpp/.c) changed.
6+
set -euo pipefail
7+
8+
base="${1:-master}"
9+
mapfile -t files < <(git diff --name-only "${base}...HEAD" | grep -E '\.(cpp|c)$' || true)
10+
if ((${#files[@]} == 0)); then
11+
echo "No compiled sources (.cpp/.c) changed vs ${base}; omit -DNRN_COVERAGE_FILES" >&2
12+
exit 1
13+
fi
14+
(IFS=';'; echo "${files[*]}")

cmake/Coverage.cmake

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
# NRN_COVERAGE_FILES speeds the workflow tremendously, when iteratively
1313
# working on a single or a few files.
1414
#
15-
# Two targets are created: cover_begin and cover_html.
15+
# Targets: cover_begin (alias cover-begin), cover_html (cover-html),
16+
# and cover_diff (cover-diff, requires diff-cover).
1617
#
1718
# cover_begin erases all the *.gcda coverage files and
1819
# creates a baseline report (coverage-base.info)
@@ -124,6 +125,19 @@ if(NRN_ENABLE_COVERAGE)
124125
set(cover_combine_command "${LCOV}" "--add-tracefile" "coverage-base.info" "--add-tracefile"
125126
"coverage-run.info" "--output-file" "coverage-combined.info")
126127
set(cover_html_command genhtml "coverage-combined.info" "--output-directory" html)
128+
129+
set(NRN_COVERAGE_DIFF_BRANCH
130+
"master"
131+
CACHE STRING "Git branch for cover_diff (diff-cover --compare-branch)")
132+
133+
find_program(DIFF_COVER diff-cover)
134+
if(DIFF_COVER)
135+
# diff-cover matches git diff paths to lcov SF: paths via GitPathTool, which is relative to cwd.
136+
# Run from PROJECT_SOURCE_DIR (not the build dir) so repo-root paths like src/foo.cpp align with
137+
# the lcov report.
138+
set(cover_diff_report "${PROJECT_BINARY_DIR}/html-diff/index.html")
139+
endif()
140+
127141
add_custom_target(
128142
cover_clean
129143
COMMAND ${cover_clean_command}
@@ -137,6 +151,7 @@ if(NRN_ENABLE_COVERAGE)
137151
COMMAND ${cover_clean_command}
138152
COMMAND ${cover_baseline_command}
139153
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}")
154+
add_custom_target(cover-begin DEPENDS cover_begin)
140155
add_custom_target(
141156
cover_collect
142157
COMMAND ${cover_collect_command}
@@ -152,4 +167,22 @@ if(NRN_ENABLE_COVERAGE)
152167
COMMAND ${cover_html_command}
153168
COMMAND echo "View in browser at file://${PROJECT_BINARY_DIR}/html/index.html"
154169
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}")
170+
add_custom_target(cover-html DEPENDS cover_html)
171+
172+
if(DIFF_COVER)
173+
add_custom_target(
174+
cover_diff
175+
COMMAND ${cover_collect_command}
176+
COMMAND ${cover_combine_command}
177+
COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_BINARY_DIR}/html-diff"
178+
COMMAND
179+
${CMAKE_COMMAND} -E chdir "${PROJECT_SOURCE_DIR}" "${DIFF_COVER}"
180+
"${PROJECT_BINARY_DIR}/coverage-combined.info" "--compare-branch"
181+
"${NRN_COVERAGE_DIFF_BRANCH}" "--show-uncovered" "--format" "html:${cover_diff_report}"
182+
COMMAND echo "View changed-line coverage at file://${cover_diff_report}"
183+
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}")
184+
add_custom_target(cover-diff DEPENDS cover_diff)
185+
else()
186+
message(STATUS "diff-cover not found; cover_diff target unavailable (pip install diff-cover)")
187+
endif()
155188
endif()

docs/cmake_doc/options.rst

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -572,16 +572,20 @@ NRN_ENABLE_COVERAGE:BOOL=OFF
572572

573573
Requires ``lcov`` (e.g. ``sudo apt install lcov``).
574574

575-
Provides two make targets to simplify the repeated "run tests, examine coverage"
575+
Provides make targets to simplify the repeated "run tests, examine coverage"
576576
workflow.
577-
-- ``make cover_begin`` erases all previous coverage data
577+
-- ``make cover-begin`` (``cover_begin``) erases all previous coverage data
578578
(``*.gcda`` files), and creates a baseline report. (Note all files and
579579
folders are created in the ``CMAKE_BINARY_DIR`` where you ran cmake.)
580580

581-
-- ``make cover_html`` creates a coverage report for the sum of all the
582-
software runs since the last ``cover_begin`` and prints a file url
581+
-- ``make cover-html`` (``cover_html``) creates a coverage report for the sum of all the
582+
software runs since the last ``cover-begin`` and prints a file url
583583
that you can paste into your browser to review the coverage.
584584

585+
-- ``make cover-diff`` (``cover_diff``; requires ``pip install diff-cover``) reports
586+
coverage on changed lines vs. ``NRN_COVERAGE_DIFF_BRANCH`` (default
587+
``master``). See `Developer Builds: Code Coverage <../install/code_coverage.html>`_.
588+
585589
When using an iterative workflow to examine test coverage of a single
586590
or a few files, the above targets run much faster when this option is
587591
combined with `NRN_COVERAGE_FILES:STRING=`_
@@ -596,14 +600,19 @@ NRN_COVERAGE_FILES:STRING=
596600

597601
``-DNRN_COVERAGE_FILES="src/nrniv/partrans.cpp;src/nmodl/parsact.cpp;src/nrnpython/nrnpy_hoc.cpp"``
598602

599-
For a list of all the cpp files changed in a pull request, consider
600-
copy/pasting the ``;`` separated list obtained with
603+
For a list of compiled sources changed on a branch, use
601604

602605
.. code-block:: shell
603606
604-
a=`git diff --name-only master | grep '\.cpp'`
605-
echo $a | sed 's/ /;/g'
607+
ci/coverage_files_from_diff.sh master
608+
609+
The script lists ``.cpp`` and ``.c`` files only. It exits with an error
610+
when none changed (omit this option in that case).
606611

612+
NRN_COVERAGE_DIFF_BRANCH:STRING=master
613+
--------------------------------------
614+
Git branch passed to ``diff-cover --compare-branch`` for the
615+
``cover_diff`` target. Defaults to ``master``.
607616

608617
NRN_SANITIZERS:STRING=
609618
----------------------

docs/install/code_coverage.md

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
## Dependencies (Linux)
55
```
66
sudo apt install lcov
7+
pip install diff-cover
78
```
89

910
## Instructions
@@ -88,9 +89,43 @@ cmake .. -DCMAKE_INSTALL_PREFIX=install \
8889
8990
make -j 6 install
9091
91-
make cover_begin
92+
make cover-begin
9293
9394
make test
9495
95-
make cover_html
96+
make cover-html
9697
```
98+
99+
## Changed-line coverage (pull requests)
100+
101+
Whole-file ``cover_html`` reports do not distinguish lines you changed from
102+
legacy uncovered code. For pull-request iteration, use ``cover_diff`` after
103+
running tests. It wraps `diff-cover <https://github.com/Bachmann1234/diff_cover>`_
104+
and reports coverage on the diff against a base branch (Codecov "patch coverage"
105+
uses the same idea).
106+
107+
```
108+
ninja cover-begin
109+
ctest -j8 -R 'some_test_pattern'
110+
ninja cover-diff
111+
```
112+
113+
``cover_diff`` collects coverage, then prints uncovered **changed** lines on the
114+
console and writes ``html-diff/index.html``.
115+
116+
The comparison branch defaults to ``master``. Override at configure time:
117+
118+
```
119+
cmake .. -DNRN_COVERAGE_DIFF_BRANCH=master ...
120+
```
121+
122+
Optional: limit instrumentation to compiled sources changed on your branch:
123+
124+
```
125+
files=$(../ci/coverage_files_from_diff.sh master)
126+
cmake .. -DNRN_ENABLE_COVERAGE=ON -DNRN_COVERAGE_FILES="${files}" ...
127+
```
128+
129+
The script lists only ``.cpp`` and ``.c`` files (headers are not compiled).
130+
It exits with an error when no such files changed; in that case omit
131+
``-DNRN_COVERAGE_FILES`` and use full or ``cover_diff`` reporting only.

0 commit comments

Comments
 (0)