diff --git a/.gitlab-ci/clang10-mpi-debug.yml b/.gitlab-ci/clang10-mpi-debug.yml index 116a3f1034cd679352569680a09d878b31b83c73..c9152e7ed8d39b4ed782771966e5a3f472472e66 100644 --- a/.gitlab-ci/clang10-mpi-debug.yml +++ b/.gitlab-ci/clang10-mpi-debug.yml @@ -21,7 +21,7 @@ test:clang10-mpi-debug: - mkdir -p build/clang10-debug-mpi - cd build/clang10-debug-mpi - CXX=clang++-10 CC=clang-10 cmake ../.. -DCMAKE_BUILD_TYPE=Debug - - make run_unit_tests + - make test cache: key: "${CI_COMMIT_REF_SLUG}-clang10-debug-mpi" paths: diff --git a/.gitlab-ci/clang10-mpi-release.yml b/.gitlab-ci/clang10-mpi-release.yml index cb1ffbda2ddc3bbed30f03d2caa4f0c8e45629c6..ebe76ce67fad1eb0325c15dea41108bdb088e5ca 100644 --- a/.gitlab-ci/clang10-mpi-release.yml +++ b/.gitlab-ci/clang10-mpi-release.yml @@ -21,7 +21,7 @@ test:clang10-mpi-release: - mkdir -p build/clang10-release-mpi - cd build/clang10-release-mpi - CXX=clang++-10 CC=clang-10 cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCLANG_FORMAT=/usr/bin/clang-format-10 - - make run_unit_tests + - make test cache: key: "${CI_COMMIT_REF_SLUG}-clang10-release-mpi" paths: diff --git a/.gitlab-ci/gcc10-mpi-debug.yml b/.gitlab-ci/gcc10-mpi-debug.yml index 6dd2806678651be82e4e2defdbb0c57ea2597f79..c29467b2aa5478d67ddbb49794205bcdf08da07b 100644 --- a/.gitlab-ci/gcc10-mpi-debug.yml +++ b/.gitlab-ci/gcc10-mpi-debug.yml @@ -21,7 +21,7 @@ test:gcc10-mpi-debug: - mkdir -p build/gcc10-debug-mpi - cd build/gcc10-debug-mpi - CXX=g++-10 CC=gcc-10 cmake ../.. -DCMAKE_BUILD_TYPE=Debug - - make run_unit_tests + - make test cache: key: "${CI_COMMIT_REF_SLUG}-gcc10-debug-mpi" paths: diff --git a/.gitlab-ci/gcc10-mpi-release.yml b/.gitlab-ci/gcc10-mpi-release.yml index 3f7d2a6a9aae7368216047a5fb691dc71df2aff9..027d5e115f2419f858d7fe5cfbceee4bd1c09cc8 100644 --- a/.gitlab-ci/gcc10-mpi-release.yml +++ b/.gitlab-ci/gcc10-mpi-release.yml @@ -21,7 +21,7 @@ test:gcc10-mpi-release: - mkdir -p build/gcc10-release-mpi - cd build/gcc10-release-mpi - CXX=g++-10 CC=gcc-10 cmake ../.. -DCMAKE_BUILD_TYPE=Release - - make run_unit_tests + - make test cache: key: "${CI_COMMIT_REF_SLUG}-gcc10-release-mpi" paths: diff --git a/.gitlab-ci/gcc10-seq-release.yml b/.gitlab-ci/gcc10-seq-release.yml index 525a08a80f295df5a8e766304279b79cd3fef4b0..8370c4684d79b9fac3b5389e62cecac8d15d00df 100644 --- a/.gitlab-ci/gcc10-seq-release.yml +++ b/.gitlab-ci/gcc10-seq-release.yml @@ -21,7 +21,7 @@ test:gcc10-seq-release: - mkdir -p build/gcc10-release-seq - cd build/gcc10-release-seq - CXX=g++-10 CC=gcc-10 cmake ../.. -DCMAKE_BUILD_TYPE=Release - - make run_unit_tests + - make test cache: key: "${CI_COMMIT_REF_SLUG}-gcc10-release-seq" paths: diff --git a/CMakeLists.txt b/CMakeLists.txt index 621b77e2c51593d1e7bf9fc2240af8acb41c051b..6878c9a0de25c77d51ce974a5615b6e2d821c3bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -313,12 +313,50 @@ include("${CATCH_MODULE_PATH}/contrib/ParseAndAddCatchTests.cmake") add_subdirectory("${CATCH_MODULE_PATH}") add_subdirectory(tests) -enable_testing() -add_custom_target(run_unit_tests - COMMAND ${CMAKE_CTEST_COMMAND} +if(${PUGS_HAS_MPI}) + set(MPIEXEC_OPTION_FLAGS --oversubscribe) + if (NOT "$ENV{GITLAB_CI}" STREQUAL "") + set(MPIEXEC_OPTION_FLAGS ${MPIEXEC_OPTION_FLAGS} --allow-run-as-root) + endif() + set(MPIEXEC_NUMPROC 3) + set(MPIEXEC_PATH_FLAG --path) + set(MPI_LAUNCHER ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROC} ${MPIEXEC_PATH_FLAG} ${PUGS_BINARY_DIR} ${MPIEXEC_OPTION_FLAGS}) +endif() + +add_custom_target(all_unit_tests DEPENDS unit_tests mpi_unit_tests - COMMENT "Executing unit tests." +) + +add_custom_target(check + DEPENDS test + ) + +add_custom_target(test + DEPENDS run_all_unit_tests + ) + +add_custom_target(run_all_unit_tests + DEPENDS run_mpi_unit_tests + ) + +if(PUGS_HAS_MPI) + set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [using ${MPIEXEC_NUMPROC} procs]") +else() + set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [sequentially]") +endif() + +add_custom_target(run_mpi_unit_tests + COMMAND ${MPI_LAUNCHER} "${PUGS_BINARY_DIR}/mpi_unit_tests" + DEPENDS run_unit_tests + COMMENT ${RUN_MPI_UNIT_TESTS_COMMENT} + ) + + +add_custom_target(run_unit_tests + COMMAND "${PUGS_BINARY_DIR}/unit_tests" + DEPENDS all_unit_tests + COMMENT "Running unit_tests" ) # unit tests coverage @@ -364,16 +402,42 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") find_program(FASTCOV fastcov fastcov.py) + add_custom_target(coverage_unit_tests + ALL # in coverage mode we do coverage! + + COMMAND "${PUGS_BINARY_DIR}/unit_tests" + + COMMENT "Running unit_tests" + DEPENDS coverage_zero_counters + ) + + add_custom_target(coverage_mpi_unit_tests + ALL # in coverage mode we do coverage! + + COMMAND ${MPI_LAUNCHER} "mpi_unit_tests" + + COMMENT "Running mpi_unit_tests" + DEPENDS coverage_unit_tests + ) + + add_custom_target(coverage_run_all_unit_tests + ALL # in coverage mode we do coverage! + + DEPENDS coverage_mpi_unit_tests + ) + if (FASTCOV AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "9")) - add_custom_target(coverage + add_custom_target(coverage_zero_counters ALL # in coverage mode we do coverage! # zero all counters COMMAND ${FASTCOV} -q -z --gcov "${GCOV_BIN}" + DEPENDS all_unit_tests + ) - # Run tests - COMMAND ${CMAKE_CTEST_COMMAND} + add_custom_target(coverage + ALL # in coverage mode we do coverage! COMMAND ${FASTCOV} -q --gcov "${GCOV_BIN}" --include "${PUGS_SOURCE_DIR}/src" @@ -382,22 +446,25 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") COMMAND ${LCOV} --gcov "${GCOV_BIN}" --list coverage.info - DEPENDS unit_tests mpi_unit_tests + DEPENDS coverage_run_all_unit_tests COMMENT "Running test coverage." WORKING_DIRECTORY "${PUGS_BINARY_DIR}" ) else() - add_custom_target(coverage + add_custom_target(coverage_zero_counters ALL # in coverage mode we do coverage! # Cleanup previously generated profiling data COMMAND ${LCOV} -q --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --zerocounters # Initialize profiling data with zero coverage for every instrumented line of the project # This way the percentage of total lines covered will always be correct, even when not all source code files were loaded during the test(s) COMMAND ${LCOV} -q --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --capture --initial --output-file coverage_base.info - # Run tests - COMMAND ${CMAKE_CTEST_COMMAND} + DEPENDS all_unit_tests + ) + + + add_custom_target(coverage # Collect data from executions COMMAND ${LCOV} --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --capture --output-file coverage_ctest.info # Combine base and ctest results @@ -412,7 +479,7 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") COMMAND ${LCOV} --gcov "${GCOV_BIN}" --list coverage.info - DEPENDS unit_tests mpi_unit_tests + DEPENDS coverage_run_all_unit_tests COMMENT "Running test coverage." WORKING_DIRECTORY "${PUGS_BINARY_DIR}" ) diff --git a/src/utils/pugs_config.hpp.in b/src/utils/pugs_config.hpp.in index 326884f97f401f0d70688fa4ad7259bdb64ab967..de267d26e10fb41994bbc72438b57d577e4b447a 100644 --- a/src/utils/pugs_config.hpp.in +++ b/src/utils/pugs_config.hpp.in @@ -10,5 +10,6 @@ #cmakedefine SYSTEM_IS_WINDOWS #define PUGS_BUILD_TYPE "@CMAKE_BUILD_TYPE@" +#define PUGS_BINARY_DIR "@PUGS_BINARY_DIR@" #endif // PUGS_CONFIG_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0eae10348642cd64948d9bc1ff2bfd45578100d7..11680e00f08cc7aa5ed80d9ed022fd3705bc8078 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -89,7 +89,11 @@ add_executable (mpi_unit_tests mpi_test_Messenger.cpp ) +add_library(test_Pugs_MeshDataBase + MeshDataBaseForTests.cpp) + target_link_libraries (unit_tests + test_Pugs_MeshDataBase PugsLanguage PugsLanguageAST PugsLanguageModules @@ -108,6 +112,7 @@ target_link_libraries (unit_tests ) target_link_libraries (mpi_unit_tests + test_Pugs_MeshDataBase PugsUtils PugsAlgebra PugsMesh diff --git a/tests/MeshDataBaseForTests.cpp b/tests/MeshDataBaseForTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8debe252a86fac67463ffc74ab17e1f9ccf141ea --- /dev/null +++ b/tests/MeshDataBaseForTests.cpp @@ -0,0 +1,55 @@ +#include <MeshDataBaseForTests.hpp> +#include <mesh/CartesianMeshBuilder.hpp> +#include <mesh/Connectivity.hpp> +#include <utils/PugsAssert.hpp> + +const MeshDataBaseForTests* MeshDataBaseForTests::m_instance = nullptr; + +MeshDataBaseForTests::MeshDataBaseForTests() +{ + m_cartesian_1d_mesh = CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{23}}.mesh(); + + m_cartesian_2d_mesh = + CartesianMeshBuilder{TinyVector<2>{0, -1}, TinyVector<2>{3, 2}, TinyVector<2, size_t>{6, 7}}.mesh(); + + m_cartesian_3d_mesh = + CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh(); +} + +const MeshDataBaseForTests& +MeshDataBaseForTests::get() +{ + return *m_instance; +} + +void +MeshDataBaseForTests::create() +{ + Assert(m_instance == nullptr); + m_instance = new MeshDataBaseForTests(); +} + +void +MeshDataBaseForTests::destroy() +{ + Assert(m_instance != nullptr); + delete m_instance; + m_instance = nullptr; +} + +template <size_t Dimension> +const Mesh<Connectivity<Dimension>>& +MeshDataBaseForTests::cartesianMesh() const +{ + if constexpr (Dimension == 1) { + return dynamic_cast<const Mesh<Connectivity<Dimension>>&>(*m_cartesian_1d_mesh); + } else if constexpr (Dimension == 2) { + return dynamic_cast<const Mesh<Connectivity<Dimension>>&>(*m_cartesian_2d_mesh); + } else if constexpr (Dimension == 3) { + return dynamic_cast<const Mesh<Connectivity<Dimension>>&>(*m_cartesian_3d_mesh); + } +} + +template const Mesh<Connectivity<1>>& MeshDataBaseForTests::cartesianMesh<1>() const; +template const Mesh<Connectivity<2>>& MeshDataBaseForTests::cartesianMesh<2>() const; +template const Mesh<Connectivity<3>>& MeshDataBaseForTests::cartesianMesh<3>() const; diff --git a/tests/MeshDataBaseForTests.hpp b/tests/MeshDataBaseForTests.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2a1b1c06c864f37981fd275067e0994a589afaac --- /dev/null +++ b/tests/MeshDataBaseForTests.hpp @@ -0,0 +1,36 @@ +#ifndef MESH_DATA_BASE_FOR_TESTS_HPP +#define MESH_DATA_BASE_FOR_TESTS_HPP + +#include <mesh/IMesh.hpp> + +template <size_t Dimension> +class Connectivity; + +template <typename ConnectivityT> +class Mesh; + +#include <memory> + +class MeshDataBaseForTests +{ + private: + explicit MeshDataBaseForTests(); + + static const MeshDataBaseForTests* m_instance; + + std::shared_ptr<const IMesh> m_cartesian_1d_mesh; + std::shared_ptr<const IMesh> m_cartesian_2d_mesh; + std::shared_ptr<const IMesh> m_cartesian_3d_mesh; + + public: + template <size_t Dimension> + const Mesh<Connectivity<Dimension>>& cartesianMesh() const; + + static const MeshDataBaseForTests& get(); + static void create(); + static void destroy(); + + ~MeshDataBaseForTests() = default; +}; + +#endif // MESH_DATA_BASE_FOR_TESTS_HPP diff --git a/tests/mpi_test_main.cpp b/tests/mpi_test_main.cpp index 565e46690910c05d6800ca1931bd811db790cc12..45b733f94d6877a5c6792ed7439965a00e2aa428 100644 --- a/tests/mpi_test_main.cpp +++ b/tests/mpi_test_main.cpp @@ -3,9 +3,17 @@ #include <Kokkos_Core.hpp> +#include <mesh/DiamondDualConnectivityManager.hpp> +#include <mesh/DiamondDualMeshManager.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/SynchronizerManager.hpp> #include <utils/Messenger.hpp> +#include <utils/pugs_config.hpp> + +#include <MeshDataBaseForTests.hpp> #include <cstdlib> +#include <filesystem> int main(int argc, char* argv[]) @@ -13,19 +21,69 @@ main(int argc, char* argv[]) parallel::Messenger::create(argc, argv); Kokkos::initialize({4, -1, -1, true}); - // Disable outputs from tested classes to the standard output - std::cout.setstate(std::ios::badbit); + const std::string output_base_name{"mpi_test_rank_"}; + + std::filesystem::path parallel_output(std::string{PUGS_BINARY_DIR}); + + std::filesystem::path gcov_prefix = [&]() -> std::filesystem::path { + std::string template_temp_dir = std::filesystem::temp_directory_path() / "pugs_gcov_XXXXXX"; + return std::filesystem::path{mkdtemp(&template_temp_dir[0])}; + }(); - if (parallel::rank() != 0) { - setenv("GCOV_PREFIX", "/dev/null", 1); - } Catch::Session session; int result = session.applyCommandLine(argc, argv); if (result == 0) { - // Disable outputs from tested classes to the standard output - std::cout.setstate(std::ios::badbit); - result = session.run(); + const auto& config = session.config(); + if (config.listReporters() or config.listTags() or config.listTestNamesOnly() or config.listTests()) { + if (parallel::rank() == 0) { + session.run(); + } + } else { + if (parallel::rank() != 0) { + // Disable outputs for ranks != 0 + setenv("GCOV_PREFIX", gcov_prefix.string().c_str(), 1); + parallel_output /= output_base_name + std::to_string(parallel::rank()); + + Catch::ConfigData data{session.configData()}; + data.outputFilename = parallel_output.string(); + session.useConfigData(data); + } + + // Disable outputs from tested classes to the standard output + std::cout.setstate(std::ios::badbit); + + SynchronizerManager::create(); + MeshDataManager::create(); + DiamondDualConnectivityManager::create(); + DiamondDualMeshManager::create(); + + MeshDataBaseForTests::create(); + + if (parallel::rank() == 0) { + if (parallel::size() > 1) { + session.config().stream() << rang::fgB::green << "Other rank outputs are stored in corresponding files" + << rang::style::reset << '\n'; + + for (size_t i_rank = 1; i_rank < parallel::size(); ++i_rank) { + std::filesystem::path parallel_output(std::string{PUGS_BINARY_DIR}); + parallel_output /= output_base_name + std::to_string(i_rank); + session.config().stream() << " - " << rang::fg::green << parallel_output.parent_path().string() + << parallel_output.preferred_separator << rang::style::reset << rang::fgB::green + << parallel_output.filename().string() << rang::style::reset << '\n'; + } + } + } + + result = session.run(); + + MeshDataBaseForTests::destroy(); + + DiamondDualMeshManager::destroy(); + DiamondDualConnectivityManager::destroy(); + MeshDataManager::destroy(); + SynchronizerManager::destroy(); + } } Kokkos::finalize(); diff --git a/tests/test_main.cpp b/tests/test_main.cpp index eba6d05d0ea0cc29b222ec92f4815a3820118c20..7ab5a66fb05301c3e9e41e4140a898bda96cac76 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -3,21 +3,51 @@ #include <Kokkos_Core.hpp> +#include <mesh/DiamondDualConnectivityManager.hpp> +#include <mesh/DiamondDualMeshManager.hpp> +#include <mesh/MeshDataManager.hpp> +#include <mesh/SynchronizerManager.hpp> +#include <utils/Messenger.hpp> + +#include <MeshDataBaseForTests.hpp> + int main(int argc, char* argv[]) { + parallel::Messenger::create(argc, argv); Kokkos::initialize({4, -1, -1, true}); Catch::Session session; int result = session.applyCommandLine(argc, argv); if (result == 0) { - // Disable outputs from tested classes to the standard output - std::cout.setstate(std::ios::badbit); - result = session.run(); + const auto& config = session.config(); + if (config.listReporters() or config.listTags() or config.listTestNamesOnly() or config.listTests()) { + result = session.run(); + } else { + // Disable outputs from tested classes to the standard output + std::cout.setstate(std::ios::badbit); + + SynchronizerManager::create(); + MeshDataManager::create(); + DiamondDualConnectivityManager::create(); + DiamondDualMeshManager::create(); + + MeshDataBaseForTests::create(); + + result = session.run(); + + MeshDataBaseForTests::destroy(); + + DiamondDualMeshManager::destroy(); + DiamondDualConnectivityManager::destroy(); + MeshDataManager::destroy(); + SynchronizerManager::destroy(); + } } Kokkos::finalize(); + parallel::Messenger::destroy(); return result; }