Skip to content

Commit 88dc8fa

Browse files
authored
Merge pull request #26 from jaredmales/feature/cmake-tests
Integrate CMake testing/coverage docs
2 parents 57b4035 + e00e77a commit 88dc8fa

30 files changed

Lines changed: 1038 additions & 426 deletions

CMakeLists.txt

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ option(MXLIB_USE_OPENMP "Whether or not to use OpenMP in mxlib and other librari
173173

174174
option(MXLIB_USE_CUDA "Whether or not to use CUDA library for mxlib" ON)
175175
option(MXLIB_USE_ISIO "Whether or not to use the ImageStreamIO library for mxlib" ON)
176+
option(MXLIB_BUILD_TESTS "Whether or not to build mxlib tests" OFF)
177+
option(MXLIB_BUILD_COVERAGE "Whether or not to enable coverage instrumentation for C/C++ builds" OFF)
178+
set(MXLIB_COVERAGE_TEST_TIMEOUT 300 CACHE STRING "CTest timeout (seconds) for coverage target")
179+
option(MXLIB_BUILD_DOCS "Whether or not to build Doxygen docs as part of the default build" OFF)
176180

177181
set(MXLIB_USE_FFT_FROM "fftw" CACHE STRING "Which library to use for the FFT interface in mxlib")
178182

@@ -360,6 +364,18 @@ set(CMAKE_CXX_FLAGS "${MXLIB_CXXVERSION} ${MXLIB_OPTIMIZE} ${MXLIB_CXXFLAGS}")
360364

361365
set(MXLIB_PC_CFLAGS "${MXLIB_PC_CFLAGS} ${CMAKE_CXX_FLAGS} ${MXLIB_DEFINES}")
362366

367+
if(MXLIB_BUILD_COVERAGE)
368+
# Match the legacy Make coverage behavior: --coverage and -O0 for instrumented builds.
369+
add_compile_definitions(MXLIB_BUILD_COVERAGE=1)
370+
add_compile_options(
371+
$<$<COMPILE_LANGUAGE:C>:--coverage>
372+
$<$<COMPILE_LANGUAGE:C>:-O0>
373+
$<$<COMPILE_LANGUAGE:CXX>:--coverage>
374+
$<$<COMPILE_LANGUAGE:CXX>:-O0>
375+
)
376+
add_link_options(--coverage)
377+
endif()
378+
363379
############################################
364380
# OpenMP setup
365381
############################################
@@ -785,4 +801,93 @@ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
785801

786802
install( PROGRAMS gengithead.sh DESTINATION bin)
787803

804+
############################################
805+
# Tests
806+
############################################
807+
808+
if(MXLIB_BUILD_TESTS)
809+
include(CTest)
810+
add_subdirectory(tests)
811+
else()
812+
add_subdirectory(tests EXCLUDE_FROM_ALL)
813+
endif()
814+
815+
add_custom_target(tests
816+
DEPENDS mxlibTests
817+
)
818+
819+
set(MXLIB_COVERAGE_BUILD_DIR ${CMAKE_BINARY_DIR}/coverage-build)
820+
set(MXLIB_COVERAGE_INFO ${CMAKE_BINARY_DIR}/coverage.info)
821+
set(MXLIB_COVERAGE_FILTERED_INFO ${CMAKE_BINARY_DIR}/coverage_filtered.info)
822+
set(MXLIB_DOC_OUTPUT_DIR ${CMAKE_BINARY_DIR}/doc)
823+
set(MXLIB_DOC_HTML_DIR ${MXLIB_DOC_OUTPUT_DIR}/html)
824+
set(MXLIB_COVERAGE_REPORT_DIR ${MXLIB_DOC_HTML_DIR}/coverage)
825+
826+
find_package(Doxygen QUIET)
827+
828+
if(Doxygen_FOUND)
829+
set(MXLIB_DOXYGEN_CONFIG_IN ${CMAKE_SOURCE_DIR}/doc/mxlib_doxygen.in)
830+
set(MXLIB_DOXYGEN_CONFIG ${CMAKE_BINARY_DIR}/mxlib_doxygen)
831+
set(MXLIB_DOXYGEN_OUTPUT_DIRECTORY ${MXLIB_DOC_OUTPUT_DIR})
832+
833+
configure_file(${MXLIB_DOXYGEN_CONFIG_IN} ${MXLIB_DOXYGEN_CONFIG} @ONLY)
834+
835+
if(MXLIB_BUILD_DOCS)
836+
set(_mxlib_docs_all ALL)
837+
else()
838+
set(_mxlib_docs_all)
839+
endif()
840+
841+
add_custom_target(docs ${_mxlib_docs_all}
842+
COMMAND ${CMAKE_COMMAND} -E make_directory ${MXLIB_DOC_OUTPUT_DIR}
843+
COMMAND ${DOXYGEN_EXECUTABLE} ${MXLIB_DOXYGEN_CONFIG}
844+
COMMAND ${CMAKE_COMMAND} -DCOVERAGE_REPORT_DIR=${MXLIB_COVERAGE_REPORT_DIR} -P ${CMAKE_SOURCE_DIR}/cmake/ensure_coverage_placeholder.cmake
845+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/doc
846+
DEPENDS ${MXLIB_DOXYGEN_CONFIG}
847+
${CMAKE_SOURCE_DIR}/doc/mxlib_header.html
848+
${CMAKE_SOURCE_DIR}/doc/mxlibDoxygenLayout.xml
849+
${CMAKE_SOURCE_DIR}/doc/main.dox
850+
${CMAKE_SOURCE_DIR}/doc/coverage_report.dox
851+
${CMAKE_SOURCE_DIR}/cmake/ensure_coverage_placeholder.cmake
852+
USES_TERMINAL
853+
)
854+
else()
855+
message(WARNING "doxygen not found. The docs target will not be available.")
856+
endif()
857+
858+
find_program(MXLIB_LCOV_EXECUTABLE lcov)
859+
find_program(MXLIB_GENHTML_EXECUTABLE genhtml)
860+
861+
if(MXLIB_LCOV_EXECUTABLE AND MXLIB_GENHTML_EXECUTABLE)
862+
add_custom_target(coverage
863+
COMMAND ${CMAKE_COMMAND} -S ${CMAKE_SOURCE_DIR} -B ${MXLIB_COVERAGE_BUILD_DIR} -DMXLIB_BUILD_TESTS=ON -DMXLIB_BUILD_COVERAGE=ON -DMXLIB_USE_CUDA=OFF -DMXLIB_USE_OPENMP=${MXLIB_USE_OPENMP} -DMXLIB_USE_ISIO=${MXLIB_USE_ISIO} -DMXLIB_USE_FFT_FROM=${MXLIB_USE_FFT_FROM} -DMXLIB_USE_BLAS_FROM=${MXLIB_USE_BLAS_FROM}
864+
COMMAND ${CMAKE_COMMAND} --build ${MXLIB_COVERAGE_BUILD_DIR} --target mxlibTests -j
865+
COMMAND ${MXLIB_LCOV_EXECUTABLE} --directory ${MXLIB_COVERAGE_BUILD_DIR} --zerocounters
866+
COMMAND ${CMAKE_CTEST_COMMAND} --test-dir ${MXLIB_COVERAGE_BUILD_DIR} --output-on-failure --timeout ${MXLIB_COVERAGE_TEST_TIMEOUT}
867+
COMMAND ${MXLIB_LCOV_EXECUTABLE} --directory ${MXLIB_COVERAGE_BUILD_DIR} --capture --output-file ${MXLIB_COVERAGE_INFO}
868+
COMMAND ${MXLIB_LCOV_EXECUTABLE} --remove ${MXLIB_COVERAGE_INFO} ${CMAKE_SOURCE_DIR}/tests/* /usr/* /sys/* /tty/* --ignore-errors unused,unused --ignore-errors inconsistent,inconsistent --output-file ${MXLIB_COVERAGE_FILTERED_INFO}
869+
COMMAND ${CMAKE_COMMAND} -E make_directory ${MXLIB_COVERAGE_REPORT_DIR}
870+
COMMAND ${MXLIB_GENHTML_EXECUTABLE} ${MXLIB_COVERAGE_FILTERED_INFO} --output-directory ${MXLIB_COVERAGE_REPORT_DIR} --title mxlib --hierarchical --merge-aliases --suppress-aliases --filter function --demangle-cpp --css-file ${CMAKE_SOURCE_DIR}/tests/coverage/gcov.css
871+
USES_TERMINAL
872+
)
873+
if(TARGET docs)
874+
add_dependencies(coverage docs)
875+
endif()
876+
else()
877+
message(WARNING "lcov/genhtml not found. The coverage target will not be available.")
878+
endif()
879+
880+
add_custom_target(coverage_clean
881+
COMMAND ${CMAKE_COMMAND} -E rm -f ${MXLIB_COVERAGE_INFO}
882+
COMMAND ${CMAKE_COMMAND} -E rm -f ${MXLIB_COVERAGE_FILTERED_INFO}
883+
COMMAND ${CMAKE_COMMAND} -E rm -rf ${MXLIB_COVERAGE_REPORT_DIR}
884+
COMMAND ${CMAKE_COMMAND} -E rm -rf ${MXLIB_COVERAGE_BUILD_DIR}
885+
)
886+
887+
if(TARGET docs)
888+
add_custom_target(docs_clean
889+
COMMAND ${CMAKE_COMMAND} -E rm -rf ${MXLIB_DOC_OUTPUT_DIR}
890+
)
891+
endif()
892+
788893
#dump_cmake_variables(".")

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ The documentation is located here: https://jaredmales.github.io/mxlib-doc/
88

99
See the [User's Guide](https://jaredmales.github.io/mxlib-doc/modules.html) for [installation instructions](https://jaredmales.github.io/mxlib-doc/group__installation.html)
1010

11+
See the [Testing](https://jaredmales.github.io/mxlib-doc/group__mxlib__testing.html)
12+
section for test build, test execution, and coverage instructions.

agent_context.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
Follow these code style and documentation rules exactly.
2+
3+
1) File-Level Documentation
4+
- Each header/source should have a top Doxygen file block:
5+
- \file
6+
- \brief
7+
- \author (if project uses it)
8+
9+
2) Include Guards and Includes
10+
- Match existing project include-guard naming convention.
11+
- Keep include ordering consistent with project style.
12+
- Do not introduce new include style unless project already uses it.
13+
14+
3) Function Declaration Documentation
15+
- Every function declaration must have a brief `///` summary.
16+
- Add a short `/** ... */` details block only when needed.
17+
- Document every parameter inline at declaration site using:
18+
- `type name /**< [in] description */`
19+
- Apply to normal methods, constructors, slots, and signals.
20+
- Keep return-value docs where project uses them.
21+
22+
4) Member Variable Documentation
23+
- Document non-trivial class members with `///`.
24+
- Describe role/ownership/state, not just type.
25+
26+
5) Naming and Structure
27+
- Use project member naming convention (e.g. `m_` prefix).
28+
- Keep declaration ordering/grouping stable:
29+
- public/protected/private
30+
- slots/signals grouped consistently.
31+
- Leave a blank line between declarations for readability.
32+
33+
6) Header vs Source Placement
34+
- Keep non-trivial definitions out of headers.
35+
- Move implementations to `.cpp` unless intentionally inline.
36+
37+
7) Editing Discipline
38+
- Preserve existing behavior unless explicitly requested.
39+
- When renaming members/APIs, update all dependent call sites.
40+
- Keep changes minimal and scoped.
41+
42+
8) Formatting and Verification
43+
- Run `clang-format` on touched files.
44+
- Ensure docs and naming are consistent after formatting.
45+
- Report any places where project style is ambiguous before making assumptions.
46+
47+
9) Doxygen Named Section Ordering
48+
- For classes that expose configuration via member data + accessors, keep named sections split into:
49+
- `... - Data` for protected/private member state
50+
- `...` (without `- Data`) for public access functions
51+
- Place the `... - Data` section before the corresponding public accessor section.
52+
53+
10) Header Declaration Parameter Docs
54+
- In headers, prefer inline parameter documentation on declarations (`type name /**< ... */`) rather than separate `\param` lists, unless there is a specific reason to deviate.
55+
56+
11) PR Prompt Attribution
57+
- At the top of PR descriptions, include an explicit attribution line when work was performed with Codex.
58+
- Preferred format:
59+
- `This work was performed by GPT-5.3-Codex in response to the prompt: "...".`
60+
- Include the primary user prompt verbatim (or a faithful condensed version if it is extremely long).
61+
62+
When you finish:
63+
- Summarize what changed.
64+
- List affected files.
65+
- Note any follow-up items or potential edge cases.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
if(NOT DEFINED COVERAGE_REPORT_DIR OR COVERAGE_REPORT_DIR STREQUAL "")
2+
message(FATAL_ERROR "COVERAGE_REPORT_DIR is required")
3+
endif()
4+
5+
set(_coverage_index "${COVERAGE_REPORT_DIR}/index.html")
6+
7+
# Never overwrite a real coverage report.
8+
if(EXISTS "${_coverage_index}")
9+
message(STATUS "Coverage index already exists at ${_coverage_index}; leaving it unchanged.")
10+
return()
11+
endif()
12+
13+
file(MAKE_DIRECTORY "${COVERAGE_REPORT_DIR}")
14+
15+
file(WRITE "${_coverage_index}" "<!doctype html>
16+
<html lang=\"en\">
17+
<head>
18+
<meta charset=\"utf-8\">
19+
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
20+
<title>mxlib coverage report</title>
21+
<link href=\"../doxygen.css\" rel=\"stylesheet\" type=\"text/css\">
22+
<link href=\"../doxygen-awesome.css\" rel=\"stylesheet\" type=\"text/css\">
23+
<link href=\"../doxygen-awesome-sidebar-only.css\" rel=\"stylesheet\" type=\"text/css\">
24+
<link href=\"../doxygen-awesome-sidebar-only-darkmode-toggle.css\" rel=\"stylesheet\" type=\"text/css\">
25+
<style>
26+
body {
27+
font-family: var(--font-family, sans-serif);
28+
color: var(--primary-foreground-color, #d0d7de);
29+
background: var(--page-background-color, #1f2428);
30+
margin: 2rem;
31+
}
32+
h1 { margin: 0 0 0.75rem 0; }
33+
p { margin: 0.5rem 0; }
34+
code {
35+
font-family: var(--font-family-monospace, monospace);
36+
color: var(--primary-foreground-color, #d0d7de);
37+
}
38+
</style>
39+
</head>
40+
<body>
41+
<h1>Coverage report has not been generated.</h1>
42+
<p>Run <code>cmake --build _build --target coverage</code> to generate it.</p>
43+
</body>
44+
</html>
45+
")
46+
47+
message(STATUS "Wrote coverage placeholder at ${_coverage_index}")

doc/building_tests.dox

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/** \addtogroup testing_building
2+
* @{
3+
*
4+
* Build tests on demand (always available, even if `MXLIB_BUILD_TESTS=OFF`):
5+
*
6+
* \code{.sh}
7+
* cmake -S . -B _build
8+
* cmake --build _build --target tests -j
9+
* \endcode
10+
*
11+
* Configure with tests enabled:
12+
*
13+
* \code{.sh}
14+
* cmake -S . -B _build -DMXLIB_BUILD_TESTS=ON
15+
* \endcode
16+
*
17+
* With `MXLIB_BUILD_TESTS=ON`, test executables are part of the default build.
18+
* With `MXLIB_BUILD_TESTS=OFF`, they are skipped by default and built only via
19+
* `tests`/`mxlibTests` targets.
20+
*
21+
* Build all test executables:
22+
*
23+
* \code{.sh}
24+
* cmake --build _build --target mxlibTests -j
25+
* \endcode
26+
*
27+
* Run tests:
28+
*
29+
* \code{.sh}
30+
* ctest --test-dir _build --output-on-failure
31+
* \endcode
32+
*
33+
* Run the CTest test suite directly:
34+
*
35+
* \code{.sh}
36+
* cmake --build _build --target mxlibTestRun
37+
* \endcode
38+
*
39+
* Build and run a single test source (Makefile.one equivalent):
40+
*
41+
* \code{.sh}
42+
* cmake -S . -B _build -DMXLIB_BUILD_TESTS=ON -DMXLIB_ONE_TEST=include/math/geo_test.cpp
43+
* cmake --build _build --target mxlibTestOne -j
44+
* cmake --build _build --target mxlibTestOneRun
45+
* \endcode
46+
*
47+
* Coverage generation is integrated into CMake and modeled after the MagAOX flow.
48+
*
49+
* Prerequisites:
50+
*
51+
* \code{.sh}
52+
* lcov --version
53+
* genhtml --version
54+
* \endcode
55+
*
56+
* Generate an HTML coverage report:
57+
*
58+
* \code{.sh}
59+
* cmake -S . -B _build
60+
* cmake --build _build --target coverage
61+
* \endcode
62+
*
63+
* Build Doxygen docs directly from CMake:
64+
*
65+
* \code{.sh}
66+
* cmake --build _build --target docs
67+
* \endcode
68+
*
69+
* Optionally include docs in the default build:
70+
*
71+
* \code{.sh}
72+
* cmake -S . -B _build -DMXLIB_BUILD_DOCS=ON
73+
* \endcode
74+
*
75+
* Optional: tune coverage test timeout (default `300` seconds):
76+
*
77+
* \code{.sh}
78+
* cmake -S . -B _build -DMXLIB_COVERAGE_TEST_TIMEOUT=600
79+
* \endcode
80+
*
81+
* Coverage artifacts are written under `_build/`:
82+
*
83+
* - `_build/coverage.info`
84+
* - `_build/coverage_filtered.info`
85+
* - `_build/doc/html/coverage/index.html`
86+
* - `_build/doc/html/index.html`
87+
*
88+
* Clean coverage artifacts:
89+
*
90+
* \code{.sh}
91+
* cmake --build _build --target coverage_clean
92+
* \endcode
93+
*
94+
* Convenience scripts are also available:
95+
*
96+
* - `tests/coverage/make_coverage`
97+
* - `tests/coverage/update_coverage`
98+
*
99+
* @}
100+
*/

doc/coverage_report.dox

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/** \addtogroup testing_coverage
2+
* @{
3+
*
4+
* \htmlonly
5+
* <style>
6+
* /* Coverage page: use full available content width (to the right of Doxygen sidebar) */
7+
* .contents,
8+
* .textblock {
9+
* max-width: none !important;
10+
* width: 100% !important;
11+
* }
12+
* .mxlib-coverage-embed {
13+
* display: block;
14+
* width: 100%;
15+
* height: calc(100vh - 220px);
16+
* min-height: 700px;
17+
* border: 1px solid var(--separator-color, #2b3035);
18+
* border-radius: 6px;
19+
* background: var(--page-background-color, #1f2428);
20+
* }
21+
* </style>
22+
* <iframe class="mxlib-coverage-embed" src="coverage/index.html" title="mxlib coverage report"></iframe>
23+
* \endhtmlonly
24+
*
25+
* @}
26+
*/

doc/groupdefs.dox

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
* Various miscellaneous utilities
3333
*/
3434

35-
3635
//------------------ error_handling -----------------------
3736

3837
/** \defgroup error_handling_codes Error Codes
@@ -564,6 +563,14 @@
564563
/** \defgroup mxlib_testing Testing
565564
*/
566565

566+
/** \defgroup testing_building Building Tests
567+
* \ingroup mxlib_testing
568+
*/
569+
570+
/** \defgroup testing_coverage Coverage Report
571+
* \ingroup mxlib_testing
572+
*/
573+
567574
/** \defgroup unit_tests Unit Tests
568575
* \ingroup mxlib_testing
569576
*/

0 commit comments

Comments
 (0)