diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f23caf28e..a1026f232 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,7 +71,7 @@ jobs: - name: print-mpicxx-flags run: mpicxx -show - name: cmake - run: cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build-mode }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} -DCMAKE_C_COMPILER=${{ matrix.compiler.cc }} -DKAMPING_WARNINGS_ARE_ERRORS=ON -DKAMPING_EXCEPTION_MODE=${{ matrix.exception-mode }} -DKAMPING_ASSERTION_LEVEL=${{ matrix.assertion-level }} -DKAMPING_TESTS_DISCOVER=OFF + run: cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build-mode }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} -DCMAKE_C_COMPILER=${{ matrix.compiler.cc }} -DKAMPING_WARNINGS_ARE_ERRORS=ON -DKAMPING_EXCEPTION_MODE=${{ matrix.exception-mode }} -DKAMPING_ASSERTION_LEVEL=${{ matrix.assertion-level }} -DKAMPING_TESTS_DISCOVER=OFF -DKAMPING_TEST_ENABLE_SANITIZERS=ON - name: build run: cmake --build build/ --parallel - name: Allow-running-mpi diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5725a6bb8..c6c8820bd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,6 +17,8 @@ if (NOT N EQUAL 0) ) endif () +option(KAMPING_TEST_ENABLE_SANITIZERS "Enable undefined behavior sanitizer and address sanitizer." ON) + # Registering tests without MPI: kamping_register_test(test_checking_casts FILES checking_casts_test.cpp) kamping_register_test(test_mpi_function_wrapper_helpers FILES mpi_function_wrapper_helpers_test.cpp) diff --git a/tests/cmake/KaTestrophe.cmake b/tests/cmake/KaTestrophe.cmake index 3233d70b5..1dfb7f250 100644 --- a/tests/cmake/KaTestrophe.cmake +++ b/tests/cmake/KaTestrophe.cmake @@ -87,6 +87,9 @@ if (NOT DEFINED KATESTROPHE_INCLUDED) ${MPI} MPI_EXEC_COMMAND "${MPI_EXEC_COMMAND}" + PROPERTIES + ENVIRONMENT + "ASAN_OPTIONS=detect_leaks=0" # Prevent memory leaks in OpenMPI from making the test fail. ) else () add_test( @@ -94,6 +97,8 @@ if (NOT DEFINED KATESTROPHE_INCLUDED) COMMAND ${MPI_EXEC_COMMAND} $ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} ) + # Prevent memory leaks in OpenMPI from making the test fail. + set_property(TEST ${TEST_NAME} PROPERTY ENVIRONMENT "ASAN_OPTIONS=detect_leaks=0") endif () # TODO: Do not rely on the return value of mpiexec to check if a test succeeded, as this does not work for # ULFM. diff --git a/tests/cmake/KampingTestHelper.cmake b/tests/cmake/KampingTestHelper.cmake index 917d49037..8c7c7060d 100644 --- a/tests/cmake/KampingTestHelper.cmake +++ b/tests/cmake/KampingTestHelper.cmake @@ -28,6 +28,15 @@ function (kamping_register_test KAMPING_TARGET_NAME) kamping_set_kassert_flags(${KAMPING_TARGET_NAME} ${ARGN}) target_compile_definitions(${KAMPING_TARGET_NAME} PRIVATE -D_GLIBCXX_DEBUG) target_compile_definitions(${KAMPING_TARGET_NAME} PRIVATE -D_GLIBCXX_DEBUG_PEDANTIC) + + if (KAMPING_TEST_ENABLE_SANITIZERS) + target_compile_options(${KAMPING_TARGET_NAME} PRIVATE -fsanitize=address) + target_link_options(${KAMPING_TARGET_NAME} PRIVATE -fsanitize=address) + target_compile_options(${KAMPING_TARGET_NAME} PRIVATE -fsanitize=undefined) + target_link_options(${KAMPING_TARGET_NAME} PRIVATE -fsanitize=undefined) + target_compile_options(${KAMPING_TARGET_NAME} PRIVATE -fno-sanitize-recover=all) + target_link_options(${KAMPING_TARGET_NAME} PRIVATE -fno-sanitize-recover=all) + endif () endfunction () # Convenience wrapper for adding tests for KaMPIng which rely on MPI this creates the target, links googletest, kamping @@ -52,6 +61,14 @@ function (kamping_register_mpi_test KAMPING_TARGET_NAME) kamping_set_kassert_flags(${KAMPING_TARGET_NAME} ${ARGN}) target_compile_definitions(${KAMPING_TARGET_NAME} PRIVATE -D_GLIBCXX_DEBUG) target_compile_definitions(${KAMPING_TARGET_NAME} PRIVATE -D_GLIBCXX_DEBUG_PEDANTIC) + if (KAMPING_TEST_ENABLE_SANITIZERS) + target_compile_options(${KAMPING_TARGET_NAME} PRIVATE -fsanitize=undefined) + target_link_options(${KAMPING_TARGET_NAME} PRIVATE -fsanitize=undefined) + target_compile_options(${KAMPING_TARGET_NAME} PRIVATE -fsanitize=address) + target_link_options(${KAMPING_TARGET_NAME} PRIVATE -fsanitize=address) + target_compile_options(${KAMPING_TARGET_NAME} PRIVATE -fno-sanitize-recover=all) + target_link_options(${KAMPING_TARGET_NAME} PRIVATE -fno-sanitize-recover=all) + endif () endfunction () # Convenience wrapper for registering a set of tests that should fail to compile and require KaMPIng to be linked. diff --git a/tests/cmake/MPIGoogleTestAddTests.cmake b/tests/cmake/MPIGoogleTestAddTests.cmake index 34af312fa..acb1930d9 100644 --- a/tests/cmake/MPIGoogleTestAddTests.cmake +++ b/tests/cmake/MPIGoogleTestAddTests.cmake @@ -70,6 +70,8 @@ function (gtest_discover_tests_impl) if (NOT EXISTS "${_TEST_EXECUTABLE}") message(FATAL_ERROR "Specified test executable does not exist.\n" " Path: '${_TEST_EXECUTABLE}'") endif () + # Prevent OpenMPI memory leaks from making the next command fail. + set(ENV{ASAN_OPTIONS} detect_leaks=0) execute_process( COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests ${filter} WORKING_DIRECTORY "${_TEST_WORKING_DIR}" @@ -77,6 +79,8 @@ function (gtest_discover_tests_impl) OUTPUT_VARIABLE output RESULT_VARIABLE result ) + # Undo previous set. + unset(ENV{ASAN_OPTIONS}) if (NOT ${result} EQUAL 0) string(REPLACE "\n" "\n " output "${output}") message(FATAL_ERROR "Error running test executable.\n" " Path: '${_TEST_EXECUTABLE}'\n" diff --git a/tests/mpi_communicator_test.cpp b/tests/mpi_communicator_test.cpp index 523d5f680..8cd13a43b 100644 --- a/tests/mpi_communicator_test.cpp +++ b/tests/mpi_communicator_test.cpp @@ -11,6 +11,8 @@ // You should have received a copy of the GNU Lesser General Public License along with KaMPIng. If not, see // . +#include + #include #include #include @@ -130,7 +132,11 @@ TEST_F(CommunicatorTest, is_valid_tag) { ASSERT_TRUE(comm.is_valid_tag(0)); ASSERT_TRUE(comm.is_valid_tag(42)); ASSERT_TRUE(comm.is_valid_tag(mpi_tag_ub)); - ASSERT_FALSE(comm.is_valid_tag(mpi_tag_ub + 1)); + // Avoid signed integer overflow + if (mpi_tag_ub < std::numeric_limits::max()) { + ASSERT_FALSE(comm.is_valid_tag(mpi_tag_ub + 1)); + } + if (mpi_tag_ub == std::numeric_limits::max()) { ASSERT_TRUE(comm.is_valid_tag(std::numeric_limits::max())); } else { @@ -150,7 +156,10 @@ TEST_F(CommunicatorTest, set_default_tag) { ASSERT_EQ(comm.default_tag(), 23); comm.default_tag(mpi_tag_ub); ASSERT_EQ(comm.default_tag(), mpi_tag_ub); - EXPECT_THROW(comm.default_tag(mpi_tag_ub + 1), kassert::KassertException); + // Avoid signed integer overflow + if (mpi_tag_ub < std::numeric_limits::max()) { + EXPECT_THROW(comm.default_tag(mpi_tag_ub + 1), kassert::KassertException); + } EXPECT_THROW(comm.default_tag(-1), kassert::KassertException); }