diff --git a/CMakeLists.txt b/CMakeLists.txt index 6288dbb0..9bfef1ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ option(MSCCLPP_USE_ROCM "Use AMD/ROCm." OFF) option(MSCCLPP_USE_IB "Use InfiniBand." ON) option(MSCCLPP_BYPASS_GPU_CHECK "Bypass GPU check." OFF) option(MSCCLPP_NPKIT_FLAGS "Set NPKIT flags" OFF) +option(MSCCLPP_ENABLE_COVERAGE "Enable code coverage" OFF) set(MSCCLPP_GPU_ARCHS "" CACHE STRING "Specify GPU architectures with delimiters (comma, space, or semicolon).") if(MSCCLPP_BYPASS_GPU_CHECK) @@ -98,6 +99,69 @@ else() message(FATAL_ERROR "No compatible GPU found. Set MSCCLPP_USE_CUDA or MSCCLPP_USE_ROCM to ON.") endif() endif() + +# Code coverage setup +if(MSCCLPP_ENABLE_COVERAGE) + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "Code coverage results with an optimized (non-Debug) build may be misleading") + endif() + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + message(STATUS "Code coverage enabled") + + # Add coverage flags to all targets + add_compile_options(--coverage -O0 -g) + add_link_options(--coverage) + + # Find lcov + find_program(LCOV_PATH lcov) + find_program(GENHTML_PATH genhtml) + + if(NOT LCOV_PATH) + message(WARNING "lcov not found. Install lcov to generate coverage reports.") + endif() + + if(NOT GENHTML_PATH) + message(WARNING "genhtml not found. Install lcov to generate HTML coverage reports.") + endif() + + if(LCOV_PATH AND GENHTML_PATH) + # Add coverage target + add_custom_target(coverage + COMMAND ${CMAKE_COMMAND} -E echo "Removing old coverage data..." + COMMAND ${LCOV_PATH} --directory . --zerocounters + + COMMAND ${CMAKE_COMMAND} -E echo "Running tests..." + COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure + + COMMAND ${CMAKE_COMMAND} -E echo "Collecting coverage data..." + COMMAND ${LCOV_PATH} --directory . --capture --output-file coverage.info + + COMMAND ${CMAKE_COMMAND} -E echo "Filtering coverage data..." + COMMAND ${LCOV_PATH} --remove coverage.info '/usr/*' '*/test/*' '*/build/*' --output-file coverage.info + + COMMAND ${CMAKE_COMMAND} -E echo "Generating HTML report..." + COMMAND ${GENHTML_PATH} coverage.info --output-directory coverage_html + + COMMAND ${CMAKE_COMMAND} -E echo "Coverage report generated in coverage_html/index.html" + + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Generating code coverage report" + ) + + # Add coverage clean target + add_custom_target(coverage-clean + COMMAND ${CMAKE_COMMAND} -E remove_directory coverage_html + COMMAND ${CMAKE_COMMAND} -E remove coverage.info + COMMAND ${LCOV_PATH} --directory . --zerocounters + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Cleaning coverage data" + ) + endif() + else() + message(WARNING "Code coverage is only supported with GCC or Clang compilers") + endif() +endif() if(MSCCLPP_GPU_ARCHS) string(STRIP "${MSCCLPP_GPU_ARCHS}" MSCCLPP_GPU_ARCHS) string(REPLACE " " ";" MSCCLPP_GPU_ARCHS "${MSCCLPP_GPU_ARCHS}") diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6452ebf8..7c4e9684 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,7 +7,6 @@ set(TEST_LIBS_COMMON mscclpp ${GPU_LIBRARIES} ${NUMA_LIBRARIES} Threads::Threads if(MSCCLPP_USE_IB) list(APPEND TEST_LIBS_COMMON ${IBVERBS_LIBRARIES}) endif() -set(TEST_LIBS_GTEST GTest::gtest_main GTest::gmock_main) set(TEST_INC_COMMON PRIVATE ${PROJECT_SOURCE_DIR}/include SYSTEM PRIVATE ${GPU_INCLUDE_DIRS}) set(TEST_INC_INTERNAL PRIVATE ${PROJECT_SOURCE_DIR}/src/core/include) @@ -38,25 +37,25 @@ add_test_executable(executor_test executor_test.cc) configure_file(run_mpi_test.sh.in run_mpi_test.sh) include(CTest) -include(FetchContent) -FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip) -option(INSTALL_GTEST OFF) -FetchContent_MakeAvailable(googletest) -include(GoogleTest) + +# Build test framework library +add_library(test_framework STATIC framework.cc) +target_include_directories(test_framework PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(test_framework PUBLIC MPI::MPI_CXX) # Unit tests add_executable(unit_tests) -target_link_libraries(unit_tests ${TEST_LIBS_COMMON} ${TEST_LIBS_GTEST}) +target_link_libraries(unit_tests ${TEST_LIBS_COMMON} test_framework) target_include_directories(unit_tests ${TEST_INC_COMMON} ${TEST_INC_INTERNAL}) add_subdirectory(unit) -gtest_discover_tests(unit_tests DISCOVERY_MODE PRE_TEST) +add_test(NAME unit_tests COMMAND unit_tests) # Multi-process unit tests add_executable(mp_unit_tests) -target_link_libraries(mp_unit_tests ${TEST_LIBS_COMMON} ${TEST_LIBS_GTEST} MPI::MPI_CXX) +target_link_libraries(mp_unit_tests ${TEST_LIBS_COMMON} test_framework MPI::MPI_CXX) target_include_directories(mp_unit_tests ${TEST_INC_COMMON} ${TEST_INC_INTERNAL}) add_subdirectory(mp_unit) -gtest_discover_tests(mp_unit_tests DISCOVERY_MODE PRE_TEST) +add_test(NAME mp_unit_tests COMMAND ${CMAKE_CURRENT_BINARY_DIR}/run_mpi_test.sh mp_unit_tests 2) # mscclpp-test add_subdirectory(mscclpp-test) diff --git a/test/perf/CMakeLists.txt b/test/perf/CMakeLists.txt index 6a16c034..caee29f0 100644 --- a/test/perf/CMakeLists.txt +++ b/test/perf/CMakeLists.txt @@ -22,7 +22,7 @@ function(add_perf_test_executable name sources) set_source_files_properties(${sources} PROPERTIES LANGUAGE CXX) endif() add_executable(${name} ${sources}) - target_link_libraries(${name} ${PERF_TEST_LIBS_COMMON}) + target_link_libraries(${name} ${PERF_TEST_LIBS_COMMON} test_framework) # Link nlohmann_json - use the target from main project target_link_libraries(${name} nlohmann_json::nlohmann_json)