From 731af592fec65cc5166fef1753dc5b71ad8cb0f5 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Mon, 5 Oct 2026 16:30:20 +0200 Subject: [PATCH 01/20] unittests: start introducing unit tests in Idefix (currently for the lookupTable) --- CMakeLists.txt | 41 ++++-- cmake/AddIdefixSource.cmake | 2 +- cmake/ReplaceIdefixSource.cmake | 2 +- src/CMakeLists.txt | 55 ++++---- src/dataBlock/CMakeLists.txt | 28 ++-- src/dataBlock/planetarySystem/CMakeLists.txt | 12 +- src/fluid/CMakeLists.txt | 40 +++--- src/fluid/RiemannSolver/CMakeLists.txt | 14 +- .../RiemannSolver/Dustsolvers/CMakeLists.txt | 4 +- .../RiemannSolver/HDsolvers/CMakeLists.txt | 10 +- .../RiemannSolver/MHDsolvers/CMakeLists.txt | 10 +- src/fluid/boundary/CMakeLists.txt | 8 +- src/fluid/braginskii/CMakeLists.txt | 10 +- src/fluid/constrainedTransport/CMakeLists.txt | 20 +-- src/fluid/eos/CMakeLists.txt | 8 +- src/fluid/tracer/CMakeLists.txt | 6 +- src/gravity/CMakeLists.txt | 14 +- src/mpi/CMakeLists.txt | 12 +- src/output/CMakeLists.txt | 20 +-- src/rkl/CMakeLists.txt | 4 +- src/utils/CMakeLists.txt | 18 ++- src/utils/iterativesolver/CMakeLists.txt | 10 +- src/utils/tests/data/data.npy | Bin 0 -> 1208 bytes src/utils/tests/data/toto.csv | 6 + src/utils/tests/data/toto1D.csv | 5 + src/utils/tests/data/toto1Dcolumn.csv | 5 + src/utils/tests/data/toto1Dlog.csv | 6 + src/utils/tests/data/x.npy | Bin 0 -> 200 bytes src/utils/tests/data/y.npy | Bin 0 -> 168 bytes src/utils/tests/data/z.npy | Bin 0 -> 152 bytes src/utils/tests/gtest-idfx.hpp | 130 ++++++++++++++++++ src/utils/tests/testLookupTable.cpp | 86 ++++++++++++ test/utils/lookupTable/main.cpp | 10 ++ 33 files changed, 433 insertions(+), 163 deletions(-) create mode 100644 src/utils/tests/data/data.npy create mode 100644 src/utils/tests/data/toto.csv create mode 100644 src/utils/tests/data/toto1D.csv create mode 100644 src/utils/tests/data/toto1Dcolumn.csv create mode 100644 src/utils/tests/data/toto1Dlog.csv create mode 100644 src/utils/tests/data/x.npy create mode 100644 src/utils/tests/data/y.npy create mode 100644 src/utils/tests/data/z.npy create mode 100644 src/utils/tests/gtest-idfx.hpp create mode 100644 src/utils/tests/testLookupTable.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e0206f4d7..4aeb55376 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,10 @@ include(SetRequiredBuildSettingsForGCC8) include(CheckHdf5ParallelSupport) include(SuppressFMA) +# enable tests +enable_testing() +find_package(GTest REQUIRED) + #Idefix requires Cuda Lambdas (experimental) if(Kokkos_ENABLE_CUDA) set(Kokkos_ENABLE_CUDA_LAMBDA ON CACHE BOOL "Idefix requires lambdas on Cuda" FORCE) @@ -98,18 +102,27 @@ if(Idefix_WERROR) endif() endif() +# add tests +add_executable(unittests) +target_link_libraries(unittests GTest::gtest) + # sub directories should be called after add_executable # because the binary target "idefix" should be defined # before adding source files via "target_sources" # create target binary "idefix" add_executable(idefix) +add_library(idefix-core STATIC) add_subdirectory(src build) +# link idefix-core where it should +target_link_libraries(idefix idefix-core) +target_link_libraries(unittests idefix-core) + # make absolute get_filename_component(Idefix_PROBLEM_DIR_ABS ${Idefix_PROBLEM_DIR} ABSOLUTE BASE_DIR ${PROJECT_BINARY_DIR}) if(EXISTS ${Idefix_PROBLEM_DIR_ABS}/setup.cpp) - target_sources(idefix PUBLIC ${Idefix_PROBLEM_DIR_ABS}/setup.cpp) + target_sources(idefix-core PRIVATE ${Idefix_PROBLEM_DIR_ABS}/setup.cpp) else() message(WARNING "No specific setup.cpp found in the problem directory (this message can be ignored if using python to define your problem)") endif() @@ -130,7 +143,7 @@ endif() if(Idefix_MPI) add_compile_definitions("WITH_MPI") find_package(MPI REQUIRED) - target_link_libraries(idefix MPI::MPI_CXX) + target_link_libraries(idefix-core PUBLIC MPI::MPI_CXX) add_subdirectory(src/mpi) endif() @@ -143,9 +156,9 @@ if(Idefix_HDF5) if(Idefix_MPI) set(HDF5_PREFER_PARALLEL TRUE) endif() - target_sources(idefix - PUBLIC src/output/xdmf.cpp - PUBLIC src/output/xdmf.hpp + target_sources(idefix-core + PRIVATE src/output/xdmf.cpp + PRIVATE src/output/xdmf.hpp ) # Prefer imported targets (matches older working behavior), then fall back. find_package(HDF5 QUIET COMPONENTS C) @@ -160,9 +173,9 @@ if(Idefix_HDF5) set(_idefix_hdf5_link_items HDF5::HDF5) endif() - target_link_libraries(idefix ${_idefix_hdf5_link_items}) + target_link_libraries(idefix-core PUBLIC ${_idefix_hdf5_link_items}) message(STATUS "Found HDF5 include directories: ${HDF5_INCLUDE_DIRS}") - target_include_directories(idefix PUBLIC "${HDF5_INCLUDE_DIRS}") + target_include_directories(idefix-core PUBLIC "${HDF5_INCLUDE_DIRS}") if(Idefix_MPI) CheckHdf5ParallelSupport("${_idefix_hdf5_link_items}") if(NOT IDEFIX_HDF5_IS_PARALLEL) @@ -191,8 +204,8 @@ if(Idefix_PYTHON) endif () set(PYBIND11_FINDPYTHON ON CACHE BOOL "Idefix requires python" FORCE) find_package(pybind11 REQUIRED) - target_link_libraries(idefix pybind11::embed) - target_sources(idefix + target_link_libraries(idefix-core PUBLIC pybind11::embed) + target_sources(idefix-core PUBLIC src/pydefix.cpp PUBLIC src/pydefix.hpp ) @@ -288,10 +301,10 @@ if (${Idefix_MPI_GPU_FORCE_COPY}) add_compile_definitions("WITH_GPU_FORCE_COPY") endif() -target_include_directories(idefix PUBLIC +target_include_directories(idefix-core PUBLIC "${Idefix_PROBLEM_DIR_ABS}" ) -target_include_directories(idefix PUBLIC +target_include_directories(idefix-core PUBLIC src/kokkos/core/src src/dataBlock src/dataBlock/planetarySystem @@ -314,7 +327,7 @@ target_include_directories(idefix PUBLIC src ) -target_link_libraries(idefix Kokkos::kokkos) +target_link_libraries(idefix-core PUBLIC Kokkos::kokkos) # Generate header with compiler information (name, flags, path) configure_file( @@ -323,12 +336,14 @@ configure_file( @ONLY ) # Make sure the generated header is on the include path -target_include_directories(idefix PRIVATE ${CMAKE_BINARY_DIR}/build/generated) +target_include_directories(idefix-core PRIVATE ${CMAKE_BINARY_DIR}/build/generated) +target_include_directories(unittests PRIVATE ${CMAKE_BINARY_DIR}/build/generated) # disable FMA if needed if(Idefix_SUPPRESS_FMA) message(STATUS "FMA (fused multiply-add) contraction/codegen is disabled") target_suppress_fma(idefix) + target_suppress_fma(unitests) endif() message(STATUS "Idefix final configuration") diff --git a/cmake/AddIdefixSource.cmake b/cmake/AddIdefixSource.cmake index 328ddcb0c..0b4d04946 100644 --- a/cmake/AddIdefixSource.cmake +++ b/cmake/AddIdefixSource.cmake @@ -14,5 +14,5 @@ function(add_idefix_source _new_source) message(STATUS " Adding problem-specific source file ${_new_source}") - target_sources(idefix PUBLIC ${_new_source}) + target_sources(idefix-core PRIVATE ${_new_source}) endfunction() diff --git a/cmake/ReplaceIdefixSource.cmake b/cmake/ReplaceIdefixSource.cmake index d48f4cbab..1032d035a 100644 --- a/cmake/ReplaceIdefixSource.cmake +++ b/cmake/ReplaceIdefixSource.cmake @@ -32,5 +32,5 @@ function(replace_idefix_source _old_source _new_source) # replace the source with our new source list set_property(TARGET idefix PROPERTY SOURCES ${mylist}) # add the new source file - target_sources(idefix PUBLIC ${_new_source}) + target_sources(idefix-core PRIVATE ${_new_source}) endfunction() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d0b714368..9a4d8d8f2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,32 +21,35 @@ add_subdirectory(gravity) add_subdirectory(utils) target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/arrays.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/error.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/error.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/global.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/global.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/grid.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/grid.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/gridHost.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/gridHost.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/idefix.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/input.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/input.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/loop.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/macros.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/main.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/profiler.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/profiler.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/real_types.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/reduce.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/setup.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/setup.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/units.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/units.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/timeIntegrator.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/timeIntegrator.cpp - ) + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/main.cpp +) + +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/arrays.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/error.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/error.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/global.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/global.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/grid.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/grid.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/gridHost.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/gridHost.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/idefix.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/input.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/input.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/loop.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/macros.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/profiler.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/profiler.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/real_types.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/reduce.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/setup.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/setup.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/units.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/units.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/timeIntegrator.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/timeIntegrator.cpp +) # NOTE: mpi file are added by the root CMakeLists along with dependencies to the MPI library diff --git a/src/dataBlock/CMakeLists.txt b/src/dataBlock/CMakeLists.txt index 3ddf4016d..72dcedebf 100644 --- a/src/dataBlock/CMakeLists.txt +++ b/src/dataBlock/CMakeLists.txt @@ -16,19 +16,19 @@ add_subdirectory(planetarySystem) -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/coarsen.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dataBlock.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dataBlock.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dataBlockHost.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dataBlockHost.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dumpToFile.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/evolveStage.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/fargo.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/fargo.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/makeGeometry.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/stateContainer.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/stateContainer.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/validation.cpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/coarsen.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/dataBlock.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/dataBlock.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/dataBlockHost.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/dataBlockHost.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/dumpToFile.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/evolveStage.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/fargo.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/fargo.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/makeGeometry.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/stateContainer.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/stateContainer.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/validation.cpp ) diff --git a/src/dataBlock/planetarySystem/CMakeLists.txt b/src/dataBlock/planetarySystem/CMakeLists.txt index 8be62f30a..f89a7615e 100644 --- a/src/dataBlock/planetarySystem/CMakeLists.txt +++ b/src/dataBlock/planetarySystem/CMakeLists.txt @@ -12,11 +12,11 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/planet.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/planet.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/planetarySystem.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/planetarySystem.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/planetStructs.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/planet.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/planet.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/planetarySystem.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/planetarySystem.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/planetStructs.hpp ) diff --git a/src/fluid/CMakeLists.txt b/src/fluid/CMakeLists.txt index b5d70be2c..084c0beab 100644 --- a/src/fluid/CMakeLists.txt +++ b/src/fluid/CMakeLists.txt @@ -20,24 +20,24 @@ add_subdirectory(RiemannSolver) add_subdirectory(tracer) -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/addNonIdealMHDFlux.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/addSourceTerms.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/calcCurrent.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/calcParabolicFlux.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/calcRightHandSide.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/checkNan.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/checkDivB.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/coarsenFlow.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/convertConsToPrim.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/drag.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/drag.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/evolveStage.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/fluid_defs.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/enroll.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/fluid.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/viscosity.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/viscosity.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/thermalDiffusion.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/thermalDiffusion.cpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/addNonIdealMHDFlux.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/addSourceTerms.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/calcCurrent.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/calcParabolicFlux.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/calcRightHandSide.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/checkNan.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/checkDivB.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/coarsenFlow.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/convertConsToPrim.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/drag.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/drag.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/evolveStage.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/fluid_defs.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/enroll.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/fluid.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/viscosity.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/viscosity.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/thermalDiffusion.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/thermalDiffusion.cpp ) diff --git a/src/fluid/RiemannSolver/CMakeLists.txt b/src/fluid/RiemannSolver/CMakeLists.txt index 9099fea25..d4863fda0 100644 --- a/src/fluid/RiemannSolver/CMakeLists.txt +++ b/src/fluid/RiemannSolver/CMakeLists.txt @@ -17,11 +17,11 @@ add_subdirectory(HDsolvers) add_subdirectory(Dustsolvers) -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/calcFlux.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/extrapolateToFaces.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/flux.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/riemannSolver.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/shockFlattening.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/slopeLimiter.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/calcFlux.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/extrapolateToFaces.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/flux.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/riemannSolver.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/shockFlattening.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/slopeLimiter.hpp ) diff --git a/src/fluid/RiemannSolver/Dustsolvers/CMakeLists.txt b/src/fluid/RiemannSolver/Dustsolvers/CMakeLists.txt index 6f5a7bb38..0dea6346e 100644 --- a/src/fluid/RiemannSolver/Dustsolvers/CMakeLists.txt +++ b/src/fluid/RiemannSolver/Dustsolvers/CMakeLists.txt @@ -12,6 +12,6 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/hllDust.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/hllDust.hpp ) diff --git a/src/fluid/RiemannSolver/HDsolvers/CMakeLists.txt b/src/fluid/RiemannSolver/HDsolvers/CMakeLists.txt index 5d684e731..9fccb4bec 100644 --- a/src/fluid/RiemannSolver/HDsolvers/CMakeLists.txt +++ b/src/fluid/RiemannSolver/HDsolvers/CMakeLists.txt @@ -12,9 +12,9 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/hllcHD.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/hllHD.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/roeHD.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/tvdlfHD.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/hllcHD.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/hllHD.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/roeHD.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/tvdlfHD.hpp ) diff --git a/src/fluid/RiemannSolver/MHDsolvers/CMakeLists.txt b/src/fluid/RiemannSolver/MHDsolvers/CMakeLists.txt index 268318f84..6a32c7b9c 100644 --- a/src/fluid/RiemannSolver/MHDsolvers/CMakeLists.txt +++ b/src/fluid/RiemannSolver/MHDsolvers/CMakeLists.txt @@ -12,9 +12,9 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/hlldMHD.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/hllMHD.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/roeMHD.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/tvdlfMHD.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/hlldMHD.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/hllMHD.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/roeMHD.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/tvdlfMHD.hpp ) diff --git a/src/fluid/boundary/CMakeLists.txt b/src/fluid/boundary/CMakeLists.txt index 8c04f2b3c..280e53232 100644 --- a/src/fluid/boundary/CMakeLists.txt +++ b/src/fluid/boundary/CMakeLists.txt @@ -12,8 +12,8 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/axis.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/axis.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/boundary.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/axis.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/axis.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/boundary.hpp ) diff --git a/src/fluid/braginskii/CMakeLists.txt b/src/fluid/braginskii/CMakeLists.txt index 8b6414014..eabd0ec1d 100644 --- a/src/fluid/braginskii/CMakeLists.txt +++ b/src/fluid/braginskii/CMakeLists.txt @@ -12,9 +12,9 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/bragThermalDiffusion.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/bragThermalDiffusion.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/bragViscosity.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/bragViscosity.cpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/bragThermalDiffusion.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/bragThermalDiffusion.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/bragViscosity.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/bragViscosity.cpp ) diff --git a/src/fluid/constrainedTransport/CMakeLists.txt b/src/fluid/constrainedTransport/CMakeLists.txt index d9ce0c658..20cac360b 100644 --- a/src/fluid/constrainedTransport/CMakeLists.txt +++ b/src/fluid/constrainedTransport/CMakeLists.txt @@ -12,14 +12,14 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/calcCornerEmf.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/calcNonidealEMF.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/calcRiemannEmf.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/constrainedTransport.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/EMFexchange.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/enforceEMFBoundary.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/enforceVectorPotentialBoundary.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/evolveMagField.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/evolveVectorPotential.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/calcCornerEmf.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/calcNonidealEMF.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/calcRiemannEmf.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/constrainedTransport.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/EMFexchange.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/enforceEMFBoundary.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/enforceVectorPotentialBoundary.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/evolveMagField.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/evolveVectorPotential.hpp ) diff --git a/src/fluid/eos/CMakeLists.txt b/src/fluid/eos/CMakeLists.txt index 14a2c484d..04f6be9a9 100644 --- a/src/fluid/eos/CMakeLists.txt +++ b/src/fluid/eos/CMakeLists.txt @@ -12,8 +12,8 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/eos_adiabatic.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/eos_isothermal.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/eos.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/eos_adiabatic.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/eos_isothermal.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/eos.hpp ) diff --git a/src/fluid/tracer/CMakeLists.txt b/src/fluid/tracer/CMakeLists.txt index 282ec7ddb..1f8a3d55a 100644 --- a/src/fluid/tracer/CMakeLists.txt +++ b/src/fluid/tracer/CMakeLists.txt @@ -12,7 +12,7 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/tracer.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/tracer.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/tracer.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/tracer.hpp ) diff --git a/src/gravity/CMakeLists.txt b/src/gravity/CMakeLists.txt index 1a643fa23..9721992b0 100644 --- a/src/gravity/CMakeLists.txt +++ b/src/gravity/CMakeLists.txt @@ -12,11 +12,11 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/gravity.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/gravity.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/laplacian.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/laplacian.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/selfGravity.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/selfGravity.cpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/gravity.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/gravity.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/laplacian.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/laplacian.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/selfGravity.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/selfGravity.cpp ) diff --git a/src/mpi/CMakeLists.txt b/src/mpi/CMakeLists.txt index 8a9eef4d9..bc57f0313 100644 --- a/src/mpi/CMakeLists.txt +++ b/src/mpi/CMakeLists.txt @@ -12,10 +12,10 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/buffer.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/exchanger.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/exchanger.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/mpi.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/mpi.cpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/buffer.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/exchanger.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/exchanger.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/mpi.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/mpi.cpp ) diff --git a/src/output/CMakeLists.txt b/src/output/CMakeLists.txt index 17d19b549..263076579 100644 --- a/src/output/CMakeLists.txt +++ b/src/output/CMakeLists.txt @@ -13,14 +13,14 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/slice.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/slice.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dump.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dump.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/output.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/output.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/scalarField.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/vtk.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/vtk.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/slice.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/slice.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/dump.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/dump.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/output.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/output.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/scalarField.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/vtk.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/vtk.hpp ) diff --git a/src/rkl/CMakeLists.txt b/src/rkl/CMakeLists.txt index 865410265..74d979e72 100644 --- a/src/rkl/CMakeLists.txt +++ b/src/rkl/CMakeLists.txt @@ -12,6 +12,6 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/rkl.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/rkl.hpp ) diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index ef71c20c7..ea31252ec 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -14,11 +14,15 @@ add_subdirectory(iterativesolver) -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/bigEndian.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dumpImage.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dumpImage.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/lookupTable.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/column.cpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/column.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/bigEndian.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/dumpImage.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/dumpImage.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/lookupTable.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/column.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/column.hpp ) + +target_sources(unittests + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/tests/testLookupTable.cpp +) diff --git a/src/utils/iterativesolver/CMakeLists.txt b/src/utils/iterativesolver/CMakeLists.txt index f9cd84c6e..fa745a069 100644 --- a/src/utils/iterativesolver/CMakeLists.txt +++ b/src/utils/iterativesolver/CMakeLists.txt @@ -12,9 +12,9 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -target_sources(idefix - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/cg.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/minres.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/bicgstab.hpp - PUBLIC ${CMAKE_CURRENT_LIST_DIR}/jacobi.hpp +target_sources(idefix-core + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/cg.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/minres.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/bicgstab.hpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/jacobi.hpp ) diff --git a/src/utils/tests/data/data.npy b/src/utils/tests/data/data.npy new file mode 100644 index 0000000000000000000000000000000000000000..8287fc9e325317c7e6f8d0dea5a1481f0c1f9782 GIT binary patch literal 1208 zcmcJNy9&ZU6hvd|H&`q|+$f9qeqmu{r`T9nNzjOe7)iuN{0e{As51!DMRc3tF2kHV zo80Hi*C;^DHJ3ikrB8F|Yk{YH?#o;s zzF+lfBTxC (IPAG/UGA/CNRS - 2026) +// and other code contributors +// +// Licensed under CeCILL 2.1 License, see COPYING for more information +// *********************************************************************************** + +#ifndef UTILS_TESTS_GTEST_IDFX_HPP_ +#define UTILS_TESTS_GTEST_IDFX_HPP_ + +// includes +#include +#if __has_include() + #include // NOLINT [build/c++17] + namespace fs = std::filesystem; +#elif __has_include() + #include + namespace fs = std::experimental::filesystem; +#else + error "Missing the header." +#endif +#include +#ifdef WITH_MPI + #include +#endif //WITH_MPI +#include "idefix.hpp" + +#define DATA_FILE(fname) (get_data_file_path(__FILE__, fname)) + +std::string get_data_file_path(const std::string & sourceFile, const std::string & fname) { + fs::path p(sourceFile); + return p.parent_path() / "data" / fname; +} + +// configure MPI environnement to setup/tear down +class MPIEnvironment : public ::testing::Environment { + public: + ~MPIEnvironment() override {} + + // Override this to define how to set up the environment. + void SetUp() override { + char** argv; + int argc = 0; + #ifdef WITH_MPI + int mpiError = MPI_Init(&argc, &argv); + ASSERT_EQ(mpiError, 0); + #endif + } + + // Override this to define how to tear down the environment. + void TearDown() override { + #ifdef WITH_MPI + int mpiError = MPI_Finalize(); + ASSERT_EQ(mpiError, 0); + #endif + } +}; + +// configure MPI environnement to setup/tear down +class KokkosEnvironment : public ::testing::Environment { + public: + ~KokkosEnvironment() override {} + + // Override this to define how to set up the environment. + void SetUp() override { + char** argv; + int argc = 0; + Kokkos::initialize(argc, argv); + } + + // Override this to define how to tear down the environment. + void TearDown() override { + Kokkos::finalize(); + } +}; + +// configure MPI environnement to setup/tear down +class IdefixEnvironment : public ::testing::Environment { + public: + ~IdefixEnvironment() override {} + + // Override this to define how to set up the environment. + void SetUp() override { + int status = idfx::initialize(); + ASSERT_EQ(status, 0); + } + + // Override this to define how to tear down the environment. + void TearDown() override { + } +}; + +// setup advanded main for tests using MPI / Kokkos +int main(int argc, char* argv[]) { + bool initKokkosBeforeMPI = false; + // When running on GPUS with Omnipath network, + // Kokkos needs to be initialised *before* the MPI layer + #ifdef KOKKOS_ENABLE_CUDA + if(std::getenv("PSM2_CUDA") != NULL) { + initKokkosBeforeMPI = true; + } + #endif + + //ini gtest + ::testing::InitGoogleTest(&argc, argv); + + //init MPI & Kokkos with right order + if (initKokkosBeforeMPI) { + ::testing::AddGlobalTestEnvironment(new MPIEnvironment); + ::testing::AddGlobalTestEnvironment(new KokkosEnvironment); + } else { + ::testing::AddGlobalTestEnvironment(new KokkosEnvironment); + ::testing::AddGlobalTestEnvironment(new MPIEnvironment); + } + + //init idefix + ::testing::AddGlobalTestEnvironment(new IdefixEnvironment); + + //run tests + return RUN_ALL_TESTS(); +} + +#endif //UTILS_TESTS_GTEST_IDFX_HPP_ diff --git a/src/utils/tests/testLookupTable.cpp b/src/utils/tests/testLookupTable.cpp new file mode 100644 index 000000000..19bf94a72 --- /dev/null +++ b/src/utils/tests/testLookupTable.cpp @@ -0,0 +1,86 @@ +// *********************************************************************************** +// Idefix MHD astrophysical code +// +// Source file src/utils/testLookupTable.hpp +// +// Last modified : 10/2026 +// +// Copyright(C) by : +// - Sébastien Valat (IPAG/UGA/CNRS - 2026) +// and other code contributors +// +// Licensed under CeCILL 2.1 License, see COPYING for more information +// *********************************************************************************** + +// headers +#include "gtest-idfx.hpp" +#include "lookupTable.hpp" + +// Expected bracketing neighbours of the 2D CSV table (toto.csv) around (x=2.1, y=3.5): x brackets +// [2,3], y brackets [3,4], and the data on the four surrounding vertices is 5,6,6,7. This is +// checked from several angles across the file below (Get vs GetNeighbours vs GetNeighboursIndx, +// host vs device, cached vs uncached search), so it is defined once here instead of being +// retyped as magic numbers in every block. +const real kXN2D[4] = {2.0, 3.0, 3.0, 4.0}; +const real kDataN2D[4] = {5.0, 6.0, 6.0, 7.0}; +const int kIdx2D[2] = {0, 1}; +const int kDataIdx2D[4] = {1, 4, 2, 5}; +const real kValue2D = 5.6; // csv.Get({2.1, 3.5}) + +//Testing 2D CSV file on device +TEST(lookupTable, 2d_csv_file_on_device) { + IdefixArray1D arr = IdefixArray1D("Test",1); + IdefixArray1D::host_mirror_type arrHost = Kokkos::create_mirror_view(arr); + + LookupTable<2> csv(DATA_FILE("toto.csv"),','); + + idefix_for("loop",0, 1, KOKKOS_LAMBDA (int i) { + real x[2]; + x[0] = 2.1; + x[1] = 3.5; + arr(i) = csv.Get(x); + }); + + Kokkos::deep_copy(arrHost , arr); + + ASSERT_NEAR(arrHost(0), kValue2D, 1e-13) << "2D CSV, device"; +} + +//Testing 2D CSV file on Host +TEST(lookupTable, 2d_csv_file_on_host) { + LookupTable<2> csv(DATA_FILE("toto.csv"),','); + real x[2]; + x[0] = 2.1; + x[1] = 3.5; + real result = csv.GetHost(x); + ASSERT_NEAR(result, kValue2D, 1e-13) << "2D CSV, host"; +} + +//Testing 1D CSV file on device. +TEST(lookupTable, 1d_csv_file_on_device) { + IdefixArray1D arr = IdefixArray1D("Test",1); + IdefixArray1D::host_mirror_type arrHost = Kokkos::create_mirror_view(arr); + + // Read 1D CSV File + LookupTable<1> csv1D(DATA_FILE("toto1D.csv"),','); + + idefix_for("loop",0, 1, KOKKOS_LAMBDA (int i) { + real x[2]; + x[0] = 2.1; + x[1] = 3.5; + arr(i) = csv1D.Get(x); + }); + + Kokkos::deep_copy(arrHost , arr); + ASSERT_EQ(arrHost(0), real(4.2)) << "1D CSV, device"; +} + +//Testing 1D CSV file on host. +TEST(lookupTable, 1d_csv_file_on_host) { + LookupTable<1> csv1D(DATA_FILE("toto1D.csv"),','); + real x[2]; + x[0] = 2.1; + x[1] = 3.5; + real result = csv1D.GetHost(x); + ASSERT_EQ(result, real(4.2)) << "1D CSV, host"; +} diff --git a/test/utils/lookupTable/main.cpp b/test/utils/lookupTable/main.cpp index 263178a0b..6a91e7e25 100644 --- a/test/utils/lookupTable/main.cpp +++ b/test/utils/lookupTable/main.cpp @@ -132,6 +132,8 @@ int main( int argc, char* argv[] ) { idfx::initialize(); + ////////////////////////////////////////////// + Banner("Testing 2D CSV file on device."); IdefixArray1D arr = IdefixArray1D("Test",1); IdefixArray1D::host_mirror_type arrHost = Kokkos::create_mirror_view(arr); @@ -150,6 +152,8 @@ int main( int argc, char* argv[] ) CheckClose(arrHost(0), kValue2D, 1e-13, "2D CSV, device"); Success(); + ////////////////////////////////////////////// + Banner("Testing 2D CSV file on Host."); real x[2]; x[0] = 2.1; @@ -159,6 +163,8 @@ int main( int argc, char* argv[] ) CheckClose(result, kValue2D, 1e-13, "2D CSV, host"); Success(); + ////////////////////////////////////////////// + Banner("Testing 1D CSV file on device."); // Read 1D CSV File LookupTable<1> csv1D("toto1D.csv",','); @@ -174,6 +180,8 @@ int main( int argc, char* argv[] ) CheckEqual(arrHost(0), real(4.2), "1D CSV, device"); Success(); + ////////////////////////////////////////////// + Banner("Testing 1D CSV file on Host."); x[0] = 2.1; result = csv1D.GetHost(x); @@ -181,6 +189,8 @@ int main( int argc, char* argv[] ) CheckEqual(result, real(4.2), "1D CSV, host"); Success(); + ////////////////////////////////////////////// + Banner("Testing 1D CSV file read as columns on device."); // Read the same 1D table, but stored as columns of the CSV file LookupTable<1> csv1Dcolumn("toto1Dcolumn.csv",',', true, true); From 44847c8ec652b9183c9a4a865ba746d2d87fb1c8 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Mon, 5 Oct 2026 17:02:39 +0200 Subject: [PATCH 02/20] unittest: register ./unitests in "make test" --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4aeb55376..813be3c1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,7 @@ endif() # add tests add_executable(unittests) target_link_libraries(unittests GTest::gtest) +add_test(unittests unittests) # sub directories should be called after add_executable # because the binary target "idefix" should be defined From 831f174148232a49e9706e8964ce7b416e547dac Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Wed, 7 Oct 2026 17:33:46 +0200 Subject: [PATCH 03/20] unittest: Port all lookuptable tests in googletest framework --- src/{utils/tests => }/gtest-idfx.hpp | 47 ++- src/utils/tests/data/gendata.py | 42 +++ src/utils/tests/testLookupTable.cpp | 495 ++++++++++++++++++++++++++- test/utils/lookupTable/main.cpp | 10 - 4 files changed, 570 insertions(+), 24 deletions(-) rename src/{utils/tests => }/gtest-idfx.hpp (71%) create mode 100644 src/utils/tests/data/gendata.py diff --git a/src/utils/tests/gtest-idfx.hpp b/src/gtest-idfx.hpp similarity index 71% rename from src/utils/tests/gtest-idfx.hpp rename to src/gtest-idfx.hpp index bc8aeff98..3f3a3f7a3 100644 --- a/src/utils/tests/gtest-idfx.hpp +++ b/src/gtest-idfx.hpp @@ -12,8 +12,8 @@ // Licensed under CeCILL 2.1 License, see COPYING for more information // *********************************************************************************** -#ifndef UTILS_TESTS_GTEST_IDFX_HPP_ -#define UTILS_TESTS_GTEST_IDFX_HPP_ +#ifndef GTEST_IDFX_HPP_ +#define GTEST_IDFX_HPP_ // includes #include @@ -32,14 +32,39 @@ #endif //WITH_MPI #include "idefix.hpp" -#define DATA_FILE(fname) (get_data_file_path(__FILE__, fname)) - -std::string get_data_file_path(const std::string & sourceFile, const std::string & fname) { +/** + * A macro to load a datafile present at data/{fname}, data/ being in the same dir than the sources. + * @param fname The file name to append to the SRC_DIR/data path. +*/ +#define DATA_FILE(fname) (idfx_get_data_file_path(__FILE__, fname)) + +/** + * Compare two C arrays of given size. + * @param arr1 First C pointer. + * @param arr2 Second C pointer. + * @param cnt Number of elements to compare. + * @param msg Debugging message to print on error. +**/ +#define ASSERT_C_ARRAY_EQ(arr1, arr2, cnt, msg) \ + do { \ + for (size_t __i__ = 0 ; __i__ < cnt ; __i__++) \ + ASSERT_EQ(arr1[__i__], arr2[__i__]) << "index=" << __i__ << "\n" << msg; \ + } while(0) + +/** + * Get the full path to get access to the data/ directory on same location than the sourceFile. + * @param sourceFile Absolute path to the source file to consider. + * @param fname Filename to append to SRC_DIR/data/. +*/ +static std::string idfx_get_data_file_path( + const std::string & sourceFile, const std::string & fname) { fs::path p(sourceFile); return p.parent_path() / "data" / fname; } -// configure MPI environnement to setup/tear down +/** + * Configure MPI environnement to setup/tear down +**/ class MPIEnvironment : public ::testing::Environment { public: ~MPIEnvironment() override {} @@ -63,7 +88,9 @@ class MPIEnvironment : public ::testing::Environment { } }; -// configure MPI environnement to setup/tear down +/** + * configure MPI environnement to setup/tear down +*/ class KokkosEnvironment : public ::testing::Environment { public: ~KokkosEnvironment() override {} @@ -81,7 +108,9 @@ class KokkosEnvironment : public ::testing::Environment { } }; -// configure MPI environnement to setup/tear down +/** + * configure MPI environnement to setup/tear down +*/ class IdefixEnvironment : public ::testing::Environment { public: ~IdefixEnvironment() override {} @@ -127,4 +156,4 @@ int main(int argc, char* argv[]) { return RUN_ALL_TESTS(); } -#endif //UTILS_TESTS_GTEST_IDFX_HPP_ +#endif //GTEST_IDFX_HPP_ diff --git a/src/utils/tests/data/gendata.py b/src/utils/tests/data/gendata.py new file mode 100644 index 000000000..52fb36434 --- /dev/null +++ b/src/utils/tests/data/gendata.py @@ -0,0 +1,42 @@ +###################################################################################### +# Idefix MHD astrophysical code +# +# Source file test/utils/lookupTable/testmelib.py +# +# Last modified : 08/2026 +# +# Copyright(C) by : +# - Sébastien Valat (IPAG/UGA/CNRS - 2026) +# - Alankar Dutta (MPA Garching - 2026) +# and other code contributors +# +# Licensed under CeCILL 2.1 License, see COPYING for more information +###################################################################################### + +import numpy as np +from scipy.interpolate import RegularGridInterpolator + + +def MakeNumpyFile(): + x = 2 ** np.arange(np.log2(1), np.log2(10), 0.2) + y = 2 ** np.arange(np.log2(5), np.log2(10), 0.2) + z = 2 ** np.arange(np.log2(2), np.log2(5), 0.2) + + print(x) + print(y) + print(z) + xp, yp, zp = np.meshgrid(x, y, z, indexing="ij") + + data = xp + 2 * yp - zp + + np.save("x.npy", x) + np.save("y.npy", y) + np.save("z.npy", z) + np.save("data.npy", data) + f = RegularGridInterpolator((x, y, z), data) + return f([2.7, 7.4, 3.9]) + + +if __name__ == "__main__": + f = MakeNumpyFile() + print("expected result:%f", f) diff --git a/src/utils/tests/testLookupTable.cpp b/src/utils/tests/testLookupTable.cpp index 19bf94a72..aa1be55b4 100644 --- a/src/utils/tests/testLookupTable.cpp +++ b/src/utils/tests/testLookupTable.cpp @@ -13,9 +13,25 @@ // *********************************************************************************** // headers +#include +#include #include "gtest-idfx.hpp" #include "lookupTable.hpp" +// Custom transformation (and its inverse) to check that the functions used for the interpolation +// in function space can be changed when the lookup table is created +struct MyLog10 { + KOKKOS_INLINE_FUNCTION real operator() (const real x) const { + return(log10(x)); + } +}; + +struct MyPow10 { + KOKKOS_INLINE_FUNCTION real operator() (const real x) const { + return(pow(10.0,x)); + } +}; + // Expected bracketing neighbours of the 2D CSV table (toto.csv) around (x=2.1, y=3.5): x brackets // [2,3], y brackets [3,4], and the data on the four surrounding vertices is 5,6,6,7. This is // checked from several angles across the file below (Get vs GetNeighbours vs GetNeighboursIndx, @@ -77,10 +93,479 @@ TEST(lookupTable, 1d_csv_file_on_device) { //Testing 1D CSV file on host. TEST(lookupTable, 1d_csv_file_on_host) { - LookupTable<1> csv1D(DATA_FILE("toto1D.csv"),','); - real x[2]; + LookupTable<1> csv1D(DATA_FILE("toto1D.csv"),','); + real x[2]; + x[0] = 2.1; + x[1] = 3.5; + real result = csv1D.GetHost(x); + ASSERT_EQ(result, real(4.2)) << "1D CSV, host"; +} + +//Testing 1D CSV file read as columns on device. +TEST(LookupTable, 1d_csv_read_as_col_on_device) { + // Read the same 1D table, but stored as columns of the CSV file + LookupTable<1> csv1Dcolumn(DATA_FILE("toto1Dcolumn.csv"),',', true, true); + IdefixArray1D arr = IdefixArray1D("Test",1); + IdefixArray1D::host_mirror_type arrHost = Kokkos::create_mirror_view(arr); + + idefix_for("loop",0, 1, KOKKOS_LAMBDA (int i) { + real x[1]; x[0] = 2.1; - x[1] = 3.5; - real result = csv1D.GetHost(x); - ASSERT_EQ(result, real(4.2)) << "1D CSV, host"; + arr(i) = csv1Dcolumn.Get(x); + }); + + Kokkos::deep_copy(arrHost , arr); + ASSERT_EQ(arrHost(0), real(4.2)) << "1D CSV read as columns, device"; +} + +//Testing 1D CSV file read as columns on Host. +TEST(LookupTable, 1d_csv_read_as_col_on_host) { + // Read the same 1D table, but stored as columns of the CSV file + LookupTable<1> csv1Dcolumn(DATA_FILE("toto1Dcolumn.csv"),',', true, true); + + real x[1]; + x[0] = 2.1; + real result = csv1Dcolumn.GetHost(x); + ASSERT_EQ(result, real(4.2)) << "1D CSV read as columns, host"; +} + +//Testing interpolation in function space on device. +TEST(LookupTable, interpol_func_space_on_device) { + IdefixArray1D arr = IdefixArray1D("Test",1); + IdefixArray1D::host_mirror_type arrHost = Kokkos::create_mirror_view(arr); + + // data = x^2 sampled in x=1,2,4. A linear interpolation in x=3 gives 10, while an + // interpolation performed on log(x) and log(data) gives the exact result 9 + LookupTable<1> csv1Dlin(DATA_FILE("toto1Dlog.csv"),',', true, true); + LookupTable<1> csv1Dlog(DATA_FILE("toto1Dlog.csv"),',', true, true, true); + // the transformation and its inverse can also be chosen when the table is created + LookupTable<1, MyLog10, MyPow10> csv1Dlog10(DATA_FILE("toto1Dlog.csv"),',', true, true, true); + + idefix_for("loop",0, 1, KOKKOS_LAMBDA (int i) { + real x[1]; + x[0] = 3.0; + arr(i) = csv1Dlog.Get(x); + }); + Kokkos::deep_copy(arrHost , arr); + ASSERT_NEAR(arrHost(0), 9.0, 1e-13) << "function-space interpolation (default log), device"; + + idefix_for("loop",0, 1, KOKKOS_LAMBDA (int i) { + real x[1]; + x[0] = 3.0; + arr(i) = csv1Dlog10.Get(x); + }); + Kokkos::deep_copy(arrHost , arr); + ASSERT_NEAR(arrHost(0), 9.0, 1e-13) << "function-space interpolation (custom log10), device"; +} + +//Testing interpolation in function space on Host. +TEST(LookupTable, interpol_func_space_on_host) { + // data = x^2 sampled in x=1,2,4. A linear interpolation in x=3 gives 10, while an + // interpolation performed on log(x) and log(data) gives the exact result 9 + LookupTable<1> csv1Dlin(DATA_FILE("toto1Dlog.csv"),',', true, true); + LookupTable<1> csv1Dlog(DATA_FILE("toto1Dlog.csv"),',', true, true, true); + // the transformation and its inverse can also be chosen when the table is created + LookupTable<1, MyLog10, MyPow10> csv1Dlog10(DATA_FILE("toto1Dlog.csv"),',', true, true, true); + + real x[1]; + x[0] = 3.0; + real result = csv1Dlog.GetHost(x); + ASSERT_NEAR(result, 9.0, 1e-13) << "function-space interpolation (default log), host"; + + result = csv1Dlog10.GetHost(x); + ASSERT_NEAR(result, 9.0, 1e-13) << "function-space interpolation (custom log10), host"; + + // the same table, interpolated linearly (default behaviour) + result = csv1Dlin.GetHost(x); + ASSERT_NEAR(result, 10.0, 1e-13) << "plain linear interpolation, host"; +} + +//Testing the neighbours used for the interpolation on Host. +TEST(LookupTable, neighbours_used_for_interpol_on_host) { + // data = x^2 sampled in x=1,2,4. A linear interpolation in x=3 gives 10, while an + // interpolation performed on log(x) and log(data) gives the exact result 9 + LookupTable<1> csv1Dlog(DATA_FILE("toto1Dlog.csv"),',', true, true, true); + LookupTable<1> csv1D(DATA_FILE("toto1D.csv"),','); + LookupTable<2> csv(DATA_FILE("toto.csv"),','); + + // 1D table (x=1,2,3 and data=2,4,6) interpolated in x=2.1 + real xq[1]; + xq[0] = 2.1; + real xN[2]; + real dataN[2]; + int idx[1]; + int dataIdx[2]; + csv1D.GetNeighboursHost(xq, xN, dataN); + csv1D.GetNeighboursIndxHost(xq, idx, dataIdx); + //idfx::cout << "1D: x=" << xN[0] << "," << xN[1] << " data=" << dataN[0] << "," << dataN[1] + // << " idx=" << idx[0] << " dataIdx=" << dataIdx[0] << "," << dataIdx[1] + // << std::endl; + const real expectedXN[2] = {2.0, 3.0}; + const real expectedDataN[2] = {4.0, 6.0}; + const int expectedDataIdx[2] = {1, 2}; + ASSERT_C_ARRAY_EQ(xN, expectedXN, 2, "1D neighbours, coordinates"); + ASSERT_C_ARRAY_EQ(dataN, expectedDataN, 2, "1D neighbours, data"); + ASSERT_EQ(idx[0], 1) << "1D neighbours, idx"; + ASSERT_C_ARRAY_EQ(dataIdx, expectedDataIdx, 2, "1D neighbours, dataIdx"); + + // 2D table interpolated in (2.1,3.5). The vertices are ordered so that the bit n of the + // vertex index tells whether we are on the right (1) or on the left (0) of the dimension n + real xq2[2]; + xq2[0] = 2.1; + xq2[1] = 3.5; + real xN2[4]; + real dataN2[4]; + int idx2[2]; + int dataIdx2[4]; + csv.GetNeighboursHost(xq2, xN2, dataN2); + csv.GetNeighboursIndxHost(xq2, idx2, dataIdx2); + //idfx::cout << "2D: x=" << xN2[0] << "," << xN2[1] << " y=" << xN2[2] << "," << xN2[3] + // << " data=" << dataN2[0] << "," << dataN2[1] << "," << dataN2[2] << "," + // << dataN2[3] << " idx=" << idx2[0] << "," << idx2[1] << std::endl; + ASSERT_C_ARRAY_EQ(xN2, kXN2D, 4, "2D neighbours, coordinates"); + ASSERT_C_ARRAY_EQ(dataN2, kDataN2D, 4, "2D neighbours, data"); + ASSERT_C_ARRAY_EQ(idx2, kIdx2D, 2, "2D neighbours, idx"); + ASSERT_C_ARRAY_EQ(dataIdx2, kDataIdx2D, 4, "2D neighbours, dataIdx"); + + // The neighbours should reproduce the value returned by GetHost + real dx = (xq2[0]-xN2[0])/(xN2[1]-xN2[0]); + real dy = (xq2[1]-xN2[2])/(xN2[3]-xN2[2]); + real interpolated = (1-dx)*(1-dy)*dataN2[0] + dx*(1-dy)*dataN2[1] + + (1-dx)*dy*dataN2[2] + dx*dy*dataN2[3]; + //idfx::cout << "2D: interpolation from the neighbours=" << interpolated << std::endl; + ASSERT_NEAR(interpolated, csv.GetHost(xq2), 1e-13) + << "2D neighbours, manual interpolation vs GetHost"; + + // When the table is interpolated in function space, the neighbours are returned in the + // original space of the table (x=1,2,4 and data=1,4,16 here) + real xq3[1]; + xq3[0] = 3.0; + real xN3[2]; + real dataN3[2]; + int idx3[1]; + int dataIdx3[2]; + csv1Dlog.GetNeighboursHost(xq3, xN3, dataN3); + csv1Dlog.GetNeighboursIndxHost(xq3, idx3, dataIdx3); + //idfx::cout << "1D (function space): x=" << xN3[0] << "," << xN3[1] + // << " data=" << dataN3[0] << "," << dataN3[1] << " idx=" << idx3[0] << std::endl; + ASSERT_NEAR(xN3[0], 2.0, 1e-13) << "1D function-space neighbours, x lower bound"; + ASSERT_NEAR(xN3[1], 4.0, 1e-13) << "1D function-space neighbours, x upper bound"; + ASSERT_NEAR(dataN3[0], 4.0, 1e-13) << "1D function-space neighbours, data lower bound"; + ASSERT_NEAR(dataN3[1], 16.0, 1e-13) << "1D function-space neighbours, data upper bound"; + ASSERT_EQ(idx3[0], 1) << "1D function-space neighbours, idx"; + const int expectedDataIdx3[2] = {1, 2}; + ASSERT_C_ARRAY_EQ(dataIdx3, expectedDataIdx3, 2, "1D function-space neighbours, dataIdx"); +} + +//Testing the neighbours used for the interpolation on device."); +TEST(LookupTable, neighbours_used_for_interpol_on_device) { + LookupTable<2> csv(DATA_FILE("toto.csv"),','); + + // data = x^2 sampled in x=1,2,4. A linear interpolation in x=3 gives 10, while an + // interpolation performed on log(x) and log(data) gives the exact result 9 + LookupTable<1> csv1Dlin(DATA_FILE("toto1Dlog.csv"),',', true, true); + LookupTable<1> csv1Dlog(DATA_FILE("toto1Dlog.csv"),',', true, true, true); + + IdefixArray1D xNdev = IdefixArray1D("xN",4); + IdefixArray1D dataNdev = IdefixArray1D("dataN",4); + IdefixArray1D idxDev = IdefixArray1D("idx",2); + IdefixArray1D dataIdxDev = IdefixArray1D("dataIdx",4); + + idefix_for("neighbours",0, 1, KOKKOS_LAMBDA (int i) { + real xq[2]; + xq[0] = 2.1; + xq[1] = 3.5; + real xN[4]; + real dataN[4]; + int idx[2]; + int dataIdx[4]; + csv.GetNeighbours(xq, xN, dataN); + csv.GetNeighboursIndx(xq, idx, dataIdx); + for(int n = 0 ; n < 4 ; n++) { + xNdev(n) = xN[n]; + dataNdev(n) = dataN[n]; + dataIdxDev(n) = dataIdx[n]; + } + idxDev(0) = idx[0]; + idxDev(1) = idx[1]; + }); + + auto xNHost = Kokkos::create_mirror_view(xNdev); + auto dataNHost = Kokkos::create_mirror_view(dataNdev); + auto idxHost = Kokkos::create_mirror_view(idxDev); + auto dataIdxHost = Kokkos::create_mirror_view(dataIdxDev); + Kokkos::deep_copy(xNHost, xNdev); + Kokkos::deep_copy(dataNHost, dataNdev); + Kokkos::deep_copy(idxHost, idxDev); + Kokkos::deep_copy(dataIdxHost, dataIdxDev); + + //idfx::cout << "2D: x=" << xNHost(0) << "," << xNHost(1) << " y=" << xNHost(2) << "," + // << xNHost(3) << " data=" << dataNHost(0) << "," << dataNHost(1) << "," + // << dataNHost(2) << "," << dataNHost(3) << " idx=" << idxHost(0) << "," + // << idxHost(1) << std::endl; + real xN[4], dataN[4]; + int idx[2], dataIdx[4]; + for(int n = 0 ; n < 4 ; n++) { xN[n] = xNHost(n); dataN[n] = dataNHost(n); } + idx[0] = idxHost(0); idx[1] = idxHost(1); + for(int n = 0 ; n < 4 ; n++) dataIdx[n] = dataIdxHost(n); + ASSERT_C_ARRAY_EQ(xN, kXN2D, 4, "2D neighbours (device), coordinates"); + ASSERT_C_ARRAY_EQ(dataN, kDataN2D, 4, "2D neighbours (device), data"); + ASSERT_C_ARRAY_EQ(idx, kIdx2D, 2, "2D neighbours (device), idx"); + ASSERT_C_ARRAY_EQ(dataIdx, kDataIdx2D, 4, "2D neighbours (device), dataIdx"); +} + +//Testing the 1D neighbours on the edges of the table. +TEST(LookupTable, neighbours_on_edges_1d) { + // Read 1D CSV File + LookupTable<1> csv1D(DATA_FILE("toto1D.csv"),','); + + // toto1D.csv holds x=1,2,3 and data=2,4,6. Whatever the requested value, we expect the two + // neighbours bracketing it, i.e. the last two nodes when we sit on the upper edge + real xq[1]; + real xN[2]; + real dataN[2]; + real expected[3][2] = {{1.0,2.0}, {2.0,3.0}, {2.0,3.0}}; + real xRequest[3] = {1.0, 2.0, 3.0}; + for(int n = 0 ; n < 3 ; n++) { + xq[0] = xRequest[n]; + csv1D.GetNeighboursHost(xq, xN, dataN); + //idfx::cout << "x=" << xq[0] << " -> neighbours " << xN[0] << "," << xN[1] + // << " (data " << dataN[0] << "," << dataN[1] << ")" << std::endl; + ASSERT_C_ARRAY_EQ(xN, expected[n], 2, "1D edge neighbours, coordinates"); + EXPECT_LE(xN[0], xq[0]) << "1D edge neighbours do not bracket the requested value"; + EXPECT_GE(xN[1], xq[0]) << "1D edge neighbours do not bracket the requested value"; + } +} + +//Testing the reuse of the search between Get and GetNeighbours on Host. +TEST(LookupTable, reuse_search_between_get_and_get_neighbours_on_host) { + LookupTable<2> csv(DATA_FILE("toto.csv"),','); + + real xq[2]; + xq[0] = 2.1; + xq[1] = 3.5; + // The neighbours found by Get are stored in nb... + LookupTableSearchCache<2> nb; + real result = csv.GetHost(xq, nb); + ASSERT_NEAR(result, kValue2D, 1e-13) << "Get-then-GetNeighbours (host), value"; + ASSERT_TRUE(nb.valid) << "Get-then-GetNeighbours (host), search validity"; + ASSERT_C_ARRAY_EQ(nb.idx, kIdx2D, 2, "Get-then-GetNeighbours (host), cached idx"); + + // ... and are reused (not computed again) by the getters below + real xN[4]; + real dataN[4]; + int idx[2]; + int dataIdx[4]; + csv.GetNeighboursHost(xq, nb, xN, dataN); + csv.GetNeighboursIndxHost(xq, nb, idx, dataIdx); + ASSERT_C_ARRAY_EQ(xN, kXN2D, 4, "Get-then-GetNeighbours (host), coordinates"); + ASSERT_C_ARRAY_EQ(dataN, kDataN2D, 4, "Get-then-GetNeighbours (host), data"); + ASSERT_C_ARRAY_EQ(idx, kIdx2D, 2, "Get-then-GetNeighbours (host), idx"); + ASSERT_C_ARRAY_EQ(dataIdx, kDataIdx2D, 4, "Get-then-GetNeighbours (host), dataIdx"); + + // Check that the table is really not searched again for the same coordinates: we corrupt + // the stored search, and check that the corrupted result is the one which is used + nb.idx[0] = 1; + csv.GetNeighboursIndxHost(xq, nb, idx, dataIdx); + ASSERT_EQ(idx[0], 1) << "Get-then-GetNeighbours (host): the stored search was not reused"; + + // ... while different coordinates trigger a new search, as usual + real xq2[2]; + xq2[0] = 2.9; + xq2[1] = 2.5; + csv.GetNeighboursIndxHost(xq2, nb, idx, dataIdx); + ASSERT_EQ(idx[0], 0) + << "Get-then-GetNeighbours (host): different coordinates did not trigger a new search"; + ASSERT_EQ(idx[1], 0) + << "Get-then-GetNeighbours (host): different coordinates did not trigger a new search"; +} + +//Testing the search performed by GetNeighbours first, and reused by Get, on Host. +TEST(LookupTable, get_and_reuse_on_host) { + LookupTable<2> csv(DATA_FILE("toto.csv"),','); + LookupTable<1> csv1D(DATA_FILE("toto1D.csv"),','); + real xq[2]; + xq[0] = 2.1; + xq[1] = 3.5; + + // No call to Get yet: GetNeighbours searches the table as usual, and stores the result + LookupTableSearchCache<2> nb; + real xN[4]; + real dataN[4]; + csv.GetNeighboursHost(xq, nb, xN, dataN); + ASSERT_TRUE(nb.valid) << "GetNeighbours-then-Get (host), search validity"; + ASSERT_C_ARRAY_EQ(nb.idx, kIdx2D, 2, "GetNeighbours-then-Get (host), cached idx"); + ASSERT_C_ARRAY_EQ(xN, kXN2D, 4, "GetNeighbours-then-Get (host), coordinates"); + ASSERT_C_ARRAY_EQ(dataN, kDataN2D, 4, "GetNeighbours-then-Get (host), data"); + + // Get now reuses that search for the same coordinates + real result = csv.GetHost(xq, nb); + ASSERT_NEAR(result, kValue2D, 1e-13) << "GetNeighbours-then-Get (host), value"; + + // Same check as before, the other way around: we corrupt the ratio stored by + // GetNeighbours, and check that Get uses it instead of computing it again. + // With delta[0]=0.5 instead of 0.1, the interpolation gives (5+6+6+7)/4=6 + nb.delta[0] = 0.5; + result = csv.GetHost(xq, nb); + ASSERT_NEAR(result, 6.0, 1e-13) + << "GetNeighbours-then-Get (host): the stored search was not reused by Get"; + + // ... while different coordinates make Get search the table again + real xq2[2]; + xq2[0] = 2.9; + xq2[1] = 2.5; + result = csv.GetHost(xq2, nb); + ASSERT_NEAR(result, csv.GetHost(xq2), 1e-13) + << "GetNeighbours-then-Get (host): different coordinates did not trigger a new search"; + ASSERT_EQ(nb.idx[1], 0) + << "GetNeighbours-then-Get (host): different coordinates did not trigger a new search"; + + // The same holds when the search comes from GetNeighboursIndx, here on the 1D table + LookupTableSearchCache<1> nb1D; + real xq1[1]; + xq1[0] = 2.1; + int idx1[1]; + int dataIdx1[2]; + csv1D.GetNeighboursIndxHost(xq1, nb1D, idx1, dataIdx1); + ASSERT_TRUE(nb1D.valid) << "GetNeighboursIndx-then-Get (host, 1D), search validity"; + ASSERT_EQ(idx1[0], 1) << "GetNeighboursIndx-then-Get (host, 1D), idx"; + nb1D.delta[0] = 0.0; // x is now on the left neighbour, so we expect its data + result = csv1D.GetHost(xq1, nb1D); + ASSERT_NEAR(result, 4.0, 1e-13) + << "GetNeighboursIndx-then-Get (host, 1D): the stored search was not reused by Get"; +} + +//Testing the reuse of the search between Get and GetNeighbours on device. +TEST(LookupTable, get_and_reuse_on_device) { + IdefixArray1D arr = IdefixArray1D("Test",1); + IdefixArray1D::host_mirror_type arrHost = Kokkos::create_mirror_view(arr); + + LookupTable<2> csv(DATA_FILE("toto.csv"),','); + LookupTable<1> csv1D(DATA_FILE("toto1D.csv"),','); + + IdefixArray1D xNdev = IdefixArray1D("xN",4); + IdefixArray1D dataNdev = IdefixArray1D("dataN",4); + IdefixArray1D idxDev = IdefixArray1D("idx",2); + IdefixArray1D dataIdxDev = IdefixArray1D("dataIdx",4); + + idefix_for("neighbours",0, 1, KOKKOS_LAMBDA (int i) { + real xq[2]; + xq[0] = 2.1; + xq[1] = 3.5; + // the structure is local to the loop, and is therefore private to each thread + LookupTableSearchCache<2> nb; + real xN[4]; + real dataN[4]; + int idx[2]; + int dataIdx[4]; + arr(i) = csv.Get(xq, nb); + csv.GetNeighbours(xq, nb, xN, dataN); + csv.GetNeighboursIndx(xq, nb, idx, dataIdx); + for(int n = 0 ; n < 4 ; n++) { + xNdev(n) = xN[n]; + dataNdev(n) = dataN[n]; + dataIdxDev(n) = dataIdx[n]; + } + idxDev(0) = idx[0]; + idxDev(1) = idx[1]; + }); + + Kokkos::deep_copy(arrHost , arr); + auto xNHost = Kokkos::create_mirror_view(xNdev); + auto dataNHost = Kokkos::create_mirror_view(dataNdev); + auto idxHost = Kokkos::create_mirror_view(idxDev); + auto dataIdxHost = Kokkos::create_mirror_view(dataIdxDev); + Kokkos::deep_copy(xNHost, xNdev); + Kokkos::deep_copy(dataNHost, dataNdev); + Kokkos::deep_copy(idxHost, idxDev); + Kokkos::deep_copy(dataIdxHost, dataIdxDev); + + // Same test on device, but with GetNeighbours called first and Get reusing its search. + // The ratio stored by GetNeighbours is corrupted in between, so that a value of 6 (instead + // of 5.6) proves that Get did not search the table again + IdefixArray1D valuesDev = IdefixArray1D("values",2); + idefix_for("neighboursFirst",0, 1, KOKKOS_LAMBDA (int i) { + real xq[2]; + xq[0] = 2.1; + xq[1] = 3.5; + LookupTableSearchCache<2> nb; + real xN[4]; + real dataN[4]; + // no call to Get yet: the search is performed here for the first time + csv.GetNeighbours(xq, nb, xN, dataN); + valuesDev(0) = csv.Get(xq, nb); + nb.delta[0] = 0.5; + valuesDev(1) = csv.Get(xq, nb); + }); + auto valuesHost = Kokkos::create_mirror_view(valuesDev); + Kokkos::deep_copy(valuesHost, valuesDev); + //idfx::cout << "neighbours first: value=" << valuesHost(0) + // << " (with a corrupted stored search)=" << valuesHost(1) << std::endl; + ASSERT_NEAR(valuesHost(0), kValue2D, 1e-13) + << "GetNeighbours-then-Get (device): the stored search was not reused by Get"; + ASSERT_NEAR(valuesHost(1), 6.0, 1e-13) + << "GetNeighbours-then-Get (device): the stored search was not reused by Get"; + + //idfx::cout << "value=" << arrHost(0) << " x=" << xNHost(0) << "," << xNHost(1) + // << " y=" << xNHost(2) << "," << xNHost(3) + // << " data=" << dataNHost(0) << "," << dataNHost(1) << "," << dataNHost(2) + // << "," << dataNHost(3) << " idx=" << idxHost(0) << "," << idxHost(1) << std::endl; + ASSERT_NEAR(arrHost(0), kValue2D, 1e-13) << "Get-then-GetNeighbours (device), value"; + real xN[4], dataN[4]; + int idx[2], dataIdx[4]; + for(int n = 0 ; n < 4 ; n++) { xN[n] = xNHost(n); dataN[n] = dataNHost(n); } + idx[0] = idxHost(0); idx[1] = idxHost(1); + for(int n = 0 ; n < 4 ; n++) dataIdx[n] = dataIdxHost(n); + ASSERT_C_ARRAY_EQ(xN, kXN2D, 4, "Get-then-GetNeighbours (device), coordinates"); + ASSERT_C_ARRAY_EQ(dataN, kDataN2D, 4, "Get-then-GetNeighbours (device), data"); + ASSERT_C_ARRAY_EQ(idx, kIdx2D, 2, "Get-then-GetNeighbours (device), idx"); + ASSERT_C_ARRAY_EQ(dataIdx, kDataIdx2D, 4, "Get-then-GetNeighbours (device), dataIdx"); +} + +//Testing 3D npy file on device. +TEST(LookupTable, numpy_file_3d_on_device) { + IdefixArray1D arr = IdefixArray1D("Test",1); + IdefixArray1D::host_mirror_type arrHost = Kokkos::create_mirror_view(arr); + + // Read npy File + std::vector coords({ + DATA_FILE("x.npy"), + DATA_FILE("y.npy"), + DATA_FILE("z.npy") + }); + LookupTable<3> csvnpy(coords, std::string(DATA_FILE("data.npy"))); + + idefix_for("loop",0, 1, KOKKOS_LAMBDA (int i) { + real x[3]; + x[0] = 2.7; + x[1] = 7.4; + x[2] = 3.9; + arr(i) = csvnpy.Get(x); + }); + + Kokkos::deep_copy(arrHost , arr); + //idfx::cout << "result="< coords({ + DATA_FILE("x.npy"), + DATA_FILE("y.npy"), + DATA_FILE("z.npy") + }); + LookupTable<3> csvnpy(coords, + std::string(DATA_FILE("data.npy")) + ); + + real y[3]; + y[0] = 2.7; + y[1] = 7.4; + y[2] = 3.9; + real result = csvnpy.GetHost(y); + + //idfx::cout << "result="<< result << std::endl; + ASSERT_NEAR(result, 13.6, 1e-13) << "3D npy, host"; } diff --git a/test/utils/lookupTable/main.cpp b/test/utils/lookupTable/main.cpp index 6a91e7e25..263178a0b 100644 --- a/test/utils/lookupTable/main.cpp +++ b/test/utils/lookupTable/main.cpp @@ -132,8 +132,6 @@ int main( int argc, char* argv[] ) { idfx::initialize(); - ////////////////////////////////////////////// - Banner("Testing 2D CSV file on device."); IdefixArray1D arr = IdefixArray1D("Test",1); IdefixArray1D::host_mirror_type arrHost = Kokkos::create_mirror_view(arr); @@ -152,8 +150,6 @@ int main( int argc, char* argv[] ) CheckClose(arrHost(0), kValue2D, 1e-13, "2D CSV, device"); Success(); - ////////////////////////////////////////////// - Banner("Testing 2D CSV file on Host."); real x[2]; x[0] = 2.1; @@ -163,8 +159,6 @@ int main( int argc, char* argv[] ) CheckClose(result, kValue2D, 1e-13, "2D CSV, host"); Success(); - ////////////////////////////////////////////// - Banner("Testing 1D CSV file on device."); // Read 1D CSV File LookupTable<1> csv1D("toto1D.csv",','); @@ -180,8 +174,6 @@ int main( int argc, char* argv[] ) CheckEqual(arrHost(0), real(4.2), "1D CSV, device"); Success(); - ////////////////////////////////////////////// - Banner("Testing 1D CSV file on Host."); x[0] = 2.1; result = csv1D.GetHost(x); @@ -189,8 +181,6 @@ int main( int argc, char* argv[] ) CheckEqual(result, real(4.2), "1D CSV, host"); Success(); - ////////////////////////////////////////////// - Banner("Testing 1D CSV file read as columns on device."); // Read the same 1D table, but stored as columns of the CSV file LookupTable<1> csv1Dcolumn("toto1Dcolumn.csv",',', true, true); From 51f2aeefa35c77ca8d2675579a99efee58b04505 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Wed, 7 Oct 2026 17:59:34 +0200 Subject: [PATCH 04/20] unittest: Make unit tests optional at build time via Idefix_GTEST and download GoogleTest if not found on the system --- CMakeLists.txt | 39 ++++++++++++++++++++++++++++++++------- src/utils/CMakeLists.txt | 8 +++++--- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 813be3c1e..1289bfb10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ option(Idefix_MHD "enable MHD" OFF) option(Idefix_MPI "enable Message Passing Interface parallelisation" OFF) option(Idefix_HIGH_ORDER_FARGO "Force Fargo to use a PPM reconstruction scheme" OFF) option(Idefix_DEBUG "Enable Idefix debug features (makes the code very slow)" OFF) +option(Idefix_GTEST "Enable building unittests with GoogleTest framework)" OFF) option(Idefix_RUNTIME_CHECKS "Enable runtime sanity checks" OFF) option(Idefix_WERROR "Treat compiler warnings as errors" OFF) option(Idefix_PYTHON "Enable python bindings (requires pybind11)" OFF) @@ -72,8 +73,26 @@ include(CheckHdf5ParallelSupport) include(SuppressFMA) # enable tests -enable_testing() -find_package(GTest REQUIRED) +if (Idefix_GTEST) + # enable supports of "make test" + enable_testing() + + # try to use the system one + find_package(GTest QUIET) + + # it not, download it internally to the project + if (NOT GTest_FOUND) + cmake_policy(SET CMP0135 NEW) + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/releases/download/v1.18.0/googletest-1.18.0.tar.gz + ) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + endif() +endif() #Idefix requires Cuda Lambdas (experimental) if(Kokkos_ENABLE_CUDA) @@ -103,9 +122,11 @@ if(Idefix_WERROR) endif() # add tests -add_executable(unittests) -target_link_libraries(unittests GTest::gtest) -add_test(unittests unittests) +if (Idefix_GTEST) + add_executable(unittests) + target_link_libraries(unittests GTest::gtest) + add_test(unittests unittests) +endif() # sub directories should be called after add_executable # because the binary target "idefix" should be defined @@ -117,7 +138,9 @@ add_subdirectory(src build) # link idefix-core where it should target_link_libraries(idefix idefix-core) -target_link_libraries(unittests idefix-core) +if (Idefix_GTEST) + target_link_libraries(unittests idefix-core) +endif() # make absolute get_filename_component(Idefix_PROBLEM_DIR_ABS ${Idefix_PROBLEM_DIR} ABSOLUTE BASE_DIR ${PROJECT_BINARY_DIR}) @@ -338,7 +361,9 @@ configure_file( ) # Make sure the generated header is on the include path target_include_directories(idefix-core PRIVATE ${CMAKE_BINARY_DIR}/build/generated) -target_include_directories(unittests PRIVATE ${CMAKE_BINARY_DIR}/build/generated) +if (Idefix_GTEST) + target_include_directories(unittests PRIVATE ${CMAKE_BINARY_DIR}/build/generated) +endif() # disable FMA if needed if(Idefix_SUPPRESS_FMA) diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index ea31252ec..f5276557d 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -23,6 +23,8 @@ target_sources(idefix-core PRIVATE ${CMAKE_CURRENT_LIST_DIR}/column.hpp ) -target_sources(unittests - PRIVATE ${CMAKE_CURRENT_LIST_DIR}/tests/testLookupTable.cpp -) +if (Idefix_GTEST) + target_sources(unittests + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/tests/testLookupTable.cpp + ) +endif() From feeff7744f06f5e6e46455fb5e47fe4ebb850ed4 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Tue, 13 Oct 2026 16:27:54 +0200 Subject: [PATCH 05/20] tests: Build some examples how to simply compare some Kokkos::view and IdefixXDArray 1D,2D,3D,4D --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 7 + src/gtest-idfx.cpp | 137 ++++++++++++++ src/gtest-idfx.hpp | 400 ++++++++++++++++++++++++++++----------- src/tests/testKokkos.cpp | 135 +++++++++++++ 5 files changed, 570 insertions(+), 111 deletions(-) create mode 100644 src/gtest-idfx.cpp create mode 100644 src/tests/testKokkos.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1289bfb10..13946cc54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,7 +124,7 @@ endif() # add tests if (Idefix_GTEST) add_executable(unittests) - target_link_libraries(unittests GTest::gtest) + target_link_libraries(unittests GTest::gtest GTest::gmock) add_test(unittests unittests) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9a4d8d8f2..c51fbe49f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,5 +52,12 @@ target_sources(idefix-core ) # NOTE: mpi file are added by the root CMakeLists along with dependencies to the MPI library +if (Idefix_GTEST) + target_sources(unittests + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/tests/testKokkos.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/tests/testArrays.cpp + PRIVATE ${CMAKE_CURRENT_LIST_DIR}/gtest-idfx.cpp + ) +endif() #add_subdirectory("${PROJECT_BINARY_DIR}" "${PROJECT_BINARY_DIR}/local") diff --git a/src/gtest-idfx.cpp b/src/gtest-idfx.cpp new file mode 100644 index 000000000..25e4541f9 --- /dev/null +++ b/src/gtest-idfx.cpp @@ -0,0 +1,137 @@ +// *********************************************************************************** +// Idefix MHD astrophysical code +// +// Source file src/utils/gtest-idfx.hpp +// +// Last modified : 10/2026 +// +// Copyright(C) by : +// - Sébastien Valat (IPAG/UGA/CNRS - 2026) +// and other code contributors +// +// Licensed under CeCILL 2.1 License, see COPYING for more information +// *********************************************************************************** + +// includes +#include +#if __has_include() + #include // NOLINT [build/c++17] + namespace fs = std::filesystem; +#elif __has_include() + #include + namespace fs = std::experimental::filesystem; +#else + error "Missing the header." +#endif +#include +#include +#ifdef WITH_MPI + #include +#endif //WITH_MPI +#include "idefix.hpp" +#include "gtest-idfx.hpp" + +/** + * Get the full path to get access to the data/ directory on same location than the sourceFile. + * @param sourceFile Absolute path to the source file to consider. + * @param fname Filename to append to SRC_DIR/data/. +*/ +std::string idfx_get_data_file_path( + const std::string & sourceFile, const std::string & fname) { + fs::path p(sourceFile); + return p.parent_path() / "data" / fname; +} + +/** + * Configure MPI environnement to setup/tear down +**/ +class MPIEnvironment : public ::testing::Environment { + public: + ~MPIEnvironment() override {} + + // Override this to define how to set up the environment. + void SetUp() override { + char** argv; + int argc = 0; + #ifdef WITH_MPI + int mpiError = MPI_Init(&argc, &argv); + ASSERT_EQ(mpiError, 0); + #endif + } + + // Override this to define how to tear down the environment. + void TearDown() override { + #ifdef WITH_MPI + int mpiError = MPI_Finalize(); + ASSERT_EQ(mpiError, 0); + #endif + } +}; + +/** + * configure MPI environnement to setup/tear down +*/ +class KokkosEnvironment : public ::testing::Environment { + public: + ~KokkosEnvironment() override {} + + // Override this to define how to set up the environment. + void SetUp() override { + char** argv; + int argc = 0; + Kokkos::initialize(argc, argv); + } + + // Override this to define how to tear down the environment. + void TearDown() override { + Kokkos::finalize(); + } +}; + +/** + * configure MPI environnement to setup/tear down +*/ +class IdefixEnvironment : public ::testing::Environment { + public: + ~IdefixEnvironment() override {} + + // Override this to define how to set up the environment. + void SetUp() override { + int status = idfx::initialize(); + ASSERT_EQ(status, 0); + } + + // Override this to define how to tear down the environment. + void TearDown() override { + } +}; + +// setup advanded main for tests using MPI / Kokkos +int main(int argc, char* argv[]) { + bool initKokkosBeforeMPI = false; + // When running on GPUS with Omnipath network, + // Kokkos needs to be initialised *before* the MPI layer + #ifdef KOKKOS_ENABLE_CUDA + if(std::getenv("PSM2_CUDA") != NULL) { + initKokkosBeforeMPI = true; + } + #endif + + //ini gtest + ::testing::InitGoogleTest(&argc, argv); + + //init MPI & Kokkos with right order + if (initKokkosBeforeMPI) { + ::testing::AddGlobalTestEnvironment(new MPIEnvironment); + ::testing::AddGlobalTestEnvironment(new KokkosEnvironment); + } else { + ::testing::AddGlobalTestEnvironment(new KokkosEnvironment); + ::testing::AddGlobalTestEnvironment(new MPIEnvironment); + } + + //init idefix + ::testing::AddGlobalTestEnvironment(new IdefixEnvironment); + + //run tests + return RUN_ALL_TESTS(); +} diff --git a/src/gtest-idfx.hpp b/src/gtest-idfx.hpp index 3f3a3f7a3..6f3e59e0d 100644 --- a/src/gtest-idfx.hpp +++ b/src/gtest-idfx.hpp @@ -16,22 +16,13 @@ #define GTEST_IDFX_HPP_ // includes -#include -#if __has_include() - #include // NOLINT [build/c++17] - namespace fs = std::filesystem; -#elif __has_include() - #include - namespace fs = std::experimental::filesystem; -#else - error "Missing the header." -#endif #include -#ifdef WITH_MPI - #include -#endif //WITH_MPI +#include +#include #include "idefix.hpp" +std::string idfx_get_data_file_path(const std::string & sourceFile, const std::string & fname); + /** * A macro to load a datafile present at data/{fname}, data/ being in the same dir than the sources. * @param fname The file name to append to the SRC_DIR/data path. @@ -52,108 +43,297 @@ } while(0) /** - * Get the full path to get access to the data/ directory on same location than the sourceFile. - * @param sourceFile Absolute path to the source file to consider. - * @param fname Filename to append to SRC_DIR/data/. -*/ -static std::string idfx_get_data_file_path( - const std::string & sourceFile, const std::string & fname) { - fs::path p(sourceFile); - return p.parent_path() / "data" / fname; -} + * Apply a google mock patcher matcher to each element of an array + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. + * @param ... Optional arguments to pass to the matcher constructor. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_1D_ARRAY(array, matcher, ...) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i), matcher(__VA_ARGS__)) << " at: (" << i << ")"; \ + } \ + } while(0) /** - * Configure MPI environnement to setup/tear down -**/ -class MPIEnvironment : public ::testing::Environment { - public: - ~MPIEnvironment() override {} - - // Override this to define how to set up the environment. - void SetUp() override { - char** argv; - int argc = 0; - #ifdef WITH_MPI - int mpiError = MPI_Init(&argc, &argv); - ASSERT_EQ(mpiError, 0); - #endif - } - - // Override this to define how to tear down the environment. - void TearDown() override { - #ifdef WITH_MPI - int mpiError = MPI_Finalize(); - ASSERT_EQ(mpiError, 0); - #endif - } -}; + * Apply a google mock patcher matcher to each element of two array applying + * the given matcher to each element. + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. It will recieve the array + * value as first parameter. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_1D_ARRAYS(array, matcher, array2) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + auto array2OnHost = Kokkos::create_mirror_view(array2); \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i), matcher(array2OnHost(i))) << " at: (" << i << ")"; \ + } \ + } while(0) /** - * configure MPI environnement to setup/tear down -*/ -class KokkosEnvironment : public ::testing::Environment { - public: - ~KokkosEnvironment() override {} - - // Override this to define how to set up the environment. - void SetUp() override { - char** argv; - int argc = 0; - Kokkos::initialize(argc, argv); - } - - // Override this to define how to tear down the environment. - void TearDown() override { - Kokkos::finalize(); - } -}; + * Apply a google mock patcher matcher to each element of two array applying + * the given matcher to each element. + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. It will recieve the array + * value as first parameter. + * @param ... Optional extra arguments to pass to the matcher constructor. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_1D_ARRAYS_ARGS(array, that, array2, ...) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + auto array2OnHost = Kokkos::create_mirror_view(array2); \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i), that(array2OnHost(i), __VA_ARGS__)) \ + << " at: (" << i << ")"; \ + } \ + } while(0) /** - * configure MPI environnement to setup/tear down -*/ -class IdefixEnvironment : public ::testing::Environment { - public: - ~IdefixEnvironment() override {} - - // Override this to define how to set up the environment. - void SetUp() override { - int status = idfx::initialize(); - ASSERT_EQ(status, 0); - } - - // Override this to define how to tear down the environment. - void TearDown() override { - } -}; - -// setup advanded main for tests using MPI / Kokkos -int main(int argc, char* argv[]) { - bool initKokkosBeforeMPI = false; - // When running on GPUS with Omnipath network, - // Kokkos needs to be initialised *before* the MPI layer - #ifdef KOKKOS_ENABLE_CUDA - if(std::getenv("PSM2_CUDA") != NULL) { - initKokkosBeforeMPI = true; - } - #endif - - //ini gtest - ::testing::InitGoogleTest(&argc, argv); - - //init MPI & Kokkos with right order - if (initKokkosBeforeMPI) { - ::testing::AddGlobalTestEnvironment(new MPIEnvironment); - ::testing::AddGlobalTestEnvironment(new KokkosEnvironment); - } else { - ::testing::AddGlobalTestEnvironment(new KokkosEnvironment); - ::testing::AddGlobalTestEnvironment(new MPIEnvironment); - } - - //init idefix - ::testing::AddGlobalTestEnvironment(new IdefixEnvironment); - - //run tests - return RUN_ALL_TESTS(); -} + * Apply a google mock patcher matcher to each element of an array + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. + * @param ... Optional arguments to pass to the matcher constructor. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_2D_ARRAY(array, matcher, ...) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i, j), matcher(__VA_ARGS__)) \ + << " at: (" << i << " , " << j << ")"; \ + } \ + } \ + } while(0) + +/** + * Apply a google mock patcher matcher to each element of two array applying + * the given matcher to each element. + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. It will recieve the array + * value as first parameter. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_2D_ARRAYS(array, matcher, array2) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + auto array2OnHost = Kokkos::create_mirror_view(array2); \ + for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i, j), matcher(array2OnHost(i, j))) \ + << " at: (" << i << " , " << j << ")"; \ + } \ + } \ + } while(0) + +/** + * Apply a google mock patcher matcher to each element of two array applying + * the given matcher to each element. + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. It will recieve the array + * value as first parameter. + * @param ... Optional extra arguments to pass to the matcher constructor. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_2D_ARRAYS_ARGS(array, matcher, array2, ...) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + auto array2OnHost = Kokkos::create_mirror_view(array2); \ + for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i, j), matcher(array2OnHost(i, j), __VA_ARGS__)) \ + << " at: (" << i << " , " << j << ")"; \ + } \ + } \ + } while(0) + +/** + * Apply a google mock patcher matcher to each element of an array + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. + * @param ... Optional arguments to pass to the matcher constructor. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_3D_ARRAY(array, matcher, ...) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i, j, k), matcher(__VA_ARGS__)) \ + << " at: (" << i << " , " << j << " , " << k << ")"; \ + } \ + } \ + } \ + } while(0) + +/** + * Apply a google mock patcher matcher to each element of two array applying + * the given matcher to each element. + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. It will recieve the array + * value as first parameter. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_3D_ARRAYS(array, matcher, array2) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + auto array2OnHost = Kokkos::create_mirror_view(array2); \ + for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i, j, k), matcher(array2OnHost(i, j, k))) \ + << " at: (" << i << " , " << j << " , " << k << ")"; \ + } \ + } \ + } \ + } while(0) + +/** + * Apply a google mock patcher matcher to each element of two array applying + * the given matcher to each element. + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. It will recieve the array + * value as first parameter. + * @param ... Optional extra arguments to pass to the matcher constructor. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_3D_ARRAYS_ARGS(array, matcher, array2, ...) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + auto array2OnHost = Kokkos::create_mirror_view(array2); \ + for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i, j, k), matcher(array2OnHost(i, j, k), __VA_ARGS__)) \ + << " at: (" << i << " , " << j << " , " << k << ")"; \ + } \ + } \ + } \ + } while(0) + +/** + * Apply a google mock patcher matcher to each element of an array + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. + * @param ... Optional arguments to pass to the matcher constructor. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_4D_ARRAY(array, matcher, ...) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + for (size_t l = 0 ; l < arrayOnHost.extent(0) ; l++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(__VA_ARGS__)) \ + << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ + } \ + } \ + } \ + } \ + } while(0) + +/** + * Apply a google mock patcher matcher to each element of two array applying + * the given matcher to each element. + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. It will recieve the array + * value as first parameter. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_4D_ARRAYS(array, matcher, array2) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + auto array2OnHost = Kokkos::create_mirror_view(array2); \ + for (size_t l = 0 ; l < arrayOnHost.extent(0) ; l++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(array2OnHost(i, j, k, l))) \ + << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ + } \ + } \ + } \ + } \ + } while(0) + +/** + * Apply a google mock patcher matcher to each element of two array applying + * the given matcher to each element. + * @param array Name of the Kokkos::view to validate. + * @param matcher Name of the google mock matcher to apply. It will recieve the array + * value as first parameter. + * @param ... Optional extra arguments to pass to the matcher constructor. + * + * For usage example, please see ./src/tests/testKokkos.cpp + * + * For what can be used as matcher name, see : + * https://google.github.io/googletest/reference/matchers.html#generic-comparison + */ +#define ASSERT_THAT_4D_ARRAYS_ARGS(array, matcher, array2, ...) \ + do { \ + auto arrayOnHost = Kokkos::create_mirror_view(array); \ + auto array2OnHost = Kokkos::create_mirror_view(array2); \ + for (size_t l = 0 ; l < arrayOnHost.extent(0) ; l++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(array2OnHost(i, j, k, l), __VA_ARGS__)) \ + << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ + } \ + } \ + } \ + } \ + } while(0) #endif //GTEST_IDFX_HPP_ diff --git a/src/tests/testKokkos.cpp b/src/tests/testKokkos.cpp new file mode 100644 index 000000000..a4204d787 --- /dev/null +++ b/src/tests/testKokkos.cpp @@ -0,0 +1,135 @@ +// *********************************************************************************** +// Idefix MHD astrophysical code +// +// Source file src/tests/testKokkos.hpp +// +// Last modified : 10/2026 +// +// Copyright(C) by : +// - Sébastien Valat (IPAG/UGA/CNRS - 2026) +// and other code contributors +// +// Licensed under CeCILL 2.1 License, see COPYING for more information +// *********************************************************************************** + +// headers +#include +#include +#include +#include "gtest-idfx.hpp" + +TEST(Kokkos, assert_view_1d) { + //data array + Kokkos::View a("a", 10); + + //ref array + Kokkos::View b("a", 10); + + //make both 5 + for (size_t i = 0 ; i < 10 ; i++) { + a(i) = b(i) = 5; + } + + //check all 5 static + ASSERT_THAT_1D_ARRAY(a, testing::Eq, 5); + + //compare both array + ASSERT_THAT_1D_ARRAYS(a, testing::Eq, b); + + //compare with near + ASSERT_THAT_1D_ARRAYS_ARGS(a, testing::DoubleNear, b, 0.001); +} + +TEST(Kokkos, assert_view_1d_is_nan) { + //data array + Kokkos::View a("a", 10); + + //ref array + Kokkos::View b("a", 10); + + //make both 5 + for (size_t i = 0 ; i < 10 ; i++) { + a(i) = b(i) = NAN; + } + + //check all 5 static + ASSERT_THAT_1D_ARRAY(a, testing::IsNan); +} + +TEST(Kokkos, assert_view_2d) { + //data array + Kokkos::View a("a", 10, 10); + + //ref array + Kokkos::View b("a", 10, 10); + + //make both 5 + for (size_t j = 0 ; j < 10 ; j++) { + for (size_t i = 0 ; i < 10 ; i++) { + a(i, j) = b(i, j) = 5; + } + } + + //check all 5 static + ASSERT_THAT_2D_ARRAY(a, testing::Eq, 5); + + //compare both array + ASSERT_THAT_2D_ARRAYS(a, testing::Eq, b); + + //compare with near + ASSERT_THAT_2D_ARRAYS_ARGS(a, testing::DoubleNear, b, 0.001); +} + +TEST(Kokkos, assert_view_3d) { + //data array + Kokkos::View a("a", 10, 10, 10); + + //ref array + Kokkos::View b("a", 10, 10, 10); + + //make both 5 + for (size_t k = 0 ; k < 10 ; k++) { + for (size_t j = 0 ; j < 10 ; j++) { + for (size_t i = 0 ; i < 10 ; i++) { + a(i, j, k) = b(i, j, k) = 5; + } + } + } + + //check all 5 static + ASSERT_THAT_3D_ARRAY(a, testing::Eq, 5); + + //compare both array + ASSERT_THAT_3D_ARRAYS(a, testing::Eq, b); + + //compare with near + ASSERT_THAT_3D_ARRAYS_ARGS(a, testing::DoubleNear, b, 0.001); +} + +TEST(Array, assert_view_4d) { + //data array + Kokkos::View a("a", 10, 10, 10, 10); + + //ref array + Kokkos::View b("b", 10, 10, 10, 10); + + //make both 5 + for (size_t l = 0 ; l < 10 ; l++) { + for (size_t k = 0 ; k < 10 ; k++) { + for (size_t j = 0 ; j < 10 ; j++) { + for (size_t i = 0 ; i < 10 ; i++) { + a(i, j, k, l) = b(i, j, k, l) = 5; + } + } + } + } + + //check all 5 static + ASSERT_THAT_4D_ARRAY(a, testing::Eq, 5); + + //compare both array + ASSERT_THAT_4D_ARRAYS(a, testing::Eq, b); + + //compare with near + ASSERT_THAT_4D_ARRAYS_ARGS(a, testing::DoubleNear, b, 0.001); +} From 96fa3f3d876cc1699a16b4bf815274ea99523137 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Tue, 13 Oct 2026 17:03:04 +0200 Subject: [PATCH 06/20] test: move search GTest in a function and dedicated cmake/HandleDeps.cmake + add a missing test file --- CMakeLists.txt | 16 +---- cmake/HandleDeps.cmake | 31 +++++++++ src/tests/testArrays.cpp | 136 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 14 deletions(-) create mode 100644 cmake/HandleDeps.cmake create mode 100644 src/tests/testArrays.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 13946cc54..a3bfeeb26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,27 +71,15 @@ include(SetIdefixProperty) include(SetRequiredBuildSettingsForGCC8) include(CheckHdf5ParallelSupport) include(SuppressFMA) +include(HandleDeps) # enable tests if (Idefix_GTEST) # enable supports of "make test" enable_testing() - # try to use the system one - find_package(GTest QUIET) - # it not, download it internally to the project - if (NOT GTest_FOUND) - cmake_policy(SET CMP0135 NEW) - include(FetchContent) - FetchContent_Declare( - googletest - URL https://github.com/google/googletest/releases/download/v1.18.0/googletest-1.18.0.tar.gz - ) - # For Windows: Prevent overriding the parent project's compiler/linker settings - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - FetchContent_MakeAvailable(googletest) - endif() + FindSystemGoogleTestOrDownload() endif() #Idefix requires Cuda Lambdas (experimental) diff --git a/cmake/HandleDeps.cmake b/cmake/HandleDeps.cmake new file mode 100644 index 000000000..da28a572b --- /dev/null +++ b/cmake/HandleDeps.cmake @@ -0,0 +1,31 @@ +###################################################################################### +# Idefix MHD astrophysical code +# +# Source file cmake/AddIdefixSource.cmake +# +# Last modified : 12/2021 +# +# Copyright(C) by : +# - Sébastien Valat (IPAG/UGA/CNRS - 2026) +# and other code contributors +# +# Licensed under CeCILL 2.1 License, see COPYING for more information +###################################################################################### + +function(FindSystemGoogleTestOrDownload) + # try to use the system one + find_package(GTest QUIET) + + # it not, download it internally to the project + if (NOT GTest_FOUND) + cmake_policy(SET CMP0135 NEW) + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/releases/download/v1.18.0/googletest-1.18.0.tar.gz + ) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + endif() +endfunction() diff --git a/src/tests/testArrays.cpp b/src/tests/testArrays.cpp new file mode 100644 index 000000000..088011009 --- /dev/null +++ b/src/tests/testArrays.cpp @@ -0,0 +1,136 @@ +// *********************************************************************************** +// Idefix MHD astrophysical code +// +// Source file src/tests/testArrays.hpp +// +// Last modified : 10/2026 +// +// Copyright(C) by : +// - Sébastien Valat (IPAG/UGA/CNRS - 2026) +// and other code contributors +// +// Licensed under CeCILL 2.1 License, see COPYING for more information +// *********************************************************************************** + +// headers +#include +#include +#include "idefix.hpp" +#include "arrays.hpp" +#include "gtest-idfx.hpp" + +TEST(Array, assert_1d) { + //data array + IdefixArray1D a("a", 10); + + //ref array + IdefixArray1D b("b", 10); + + //make both 5 + for (size_t i = 0 ; i < 10 ; i++) { + a(i) = b(i) = 5; + } + + //check all 5 static + ASSERT_THAT_1D_ARRAY(a, testing::Eq, 5); + + //compare both array + ASSERT_THAT_1D_ARRAYS(a, testing::Eq, b); + + //compare with near + ASSERT_THAT_1D_ARRAYS_ARGS(a, testing::DoubleNear, b, 0.001); +} + +TEST(Array, assert_1d_is_nan) { + //data array + IdefixArray1D a("a", 10); + + //ref array + IdefixArray1D b("b", 10); + + //make both 5 + for (size_t i = 0 ; i < 10 ; i++) { + a(i) = b(i) = NAN; + } + + //check all 5 static + ASSERT_THAT_1D_ARRAY(a, testing::IsNan); +} + +TEST(Array, assert_2d) { + //data array + IdefixArray2D a("a", 10, 10); + + //ref array + IdefixArray2D b("b", 10, 10); + + //make both 5 + for (size_t j = 0 ; j < 10 ; j++) { + for (size_t i = 0 ; i < 10 ; i++) { + a(i, j) = b(i, j) = 5; + } + } + + //check all 5 static + ASSERT_THAT_2D_ARRAY(a, testing::Eq, 5); + + //compare both array + ASSERT_THAT_2D_ARRAYS(a, testing::Eq, b); + + //compare with near + ASSERT_THAT_2D_ARRAYS_ARGS(a, testing::DoubleNear, b, 0.001); +} + +TEST(Array, assert_3d) { + //data array + IdefixArray3D a("a", 10, 10, 10); + + //ref array + IdefixArray3D b("b", 10, 10, 10); + + //make both 5 + for (size_t k = 0 ; k < 10 ; k++) { + for (size_t j = 0 ; j < 10 ; j++) { + for (size_t i = 0 ; i < 10 ; i++) { + a(i, j, k) = b(i, j, k) = 5; + } + } + } + + //check all 5 static + ASSERT_THAT_3D_ARRAY(a, testing::Eq, 5); + + //compare both array + ASSERT_THAT_3D_ARRAYS(a, testing::Eq, b); + + //compare with near + ASSERT_THAT_3D_ARRAYS_ARGS(a, testing::DoubleNear, b, 0.001); +} + +TEST(Array, assert_4d) { + //data array + IdefixArray4D a("a", 10, 10, 10, 10); + + //ref array + IdefixArray4D b("b", 10, 10, 10, 10); + + //make both 5 + for (size_t l = 0 ; l < 10 ; l++) { + for (size_t k = 0 ; k < 10 ; k++) { + for (size_t j = 0 ; j < 10 ; j++) { + for (size_t i = 0 ; i < 10 ; i++) { + a(i, j, k, l) = b(i, j, k, l) = 5; + } + } + } + } + + //check all 5 static + ASSERT_THAT_4D_ARRAY(a, testing::Eq, 5); + + //compare both array + ASSERT_THAT_4D_ARRAYS(a, testing::Eq, b); + + //compare with near + ASSERT_THAT_4D_ARRAYS_ARGS(a, testing::DoubleNear, b, 0.001); +} From d9e2db9d9af79fb3991278e408614cab80195f01 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Tue, 13 Oct 2026 17:58:45 +0200 Subject: [PATCH 07/20] tests: introduce runs of the unit test in github actions --- .github/workflows/idefix-ci-jobs.yml | 33 ++++++--- scripts/ci/run-unit-tests | 101 +++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 9 deletions(-) create mode 100755 scripts/ci/run-unit-tests diff --git a/.github/workflows/idefix-ci-jobs.yml b/.github/workflows/idefix-ci-jobs.yml index 4c3b54715..150c1f96f 100644 --- a/.github/workflows/idefix-ci-jobs.yml +++ b/.github/workflows/idefix-ci-jobs.yml @@ -24,7 +24,19 @@ env: permissions: {} jobs: + UnitTests: + runs-on: self-hosted + steps: + - name: Check out repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + submodules: recursive + persist-credentials: false + - name: Configure, Build Idefix and Run Unit Tests + run: scripts/ci/run-unit-tests $TESTME_OPTIONS + ShocksHydro: + needs: UnitTests runs-on: self-hosted steps: - name: Check out repo @@ -44,6 +56,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/HD/SedovBlastWave -all $TESTME_OPTIONS ParabolicHydro: + needs: UnitTests runs-on: self-hosted steps: - name: Check out repo @@ -61,6 +74,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/HD/thermalDiffusion -all $TESTME_OPTIONS ShocksMHD: + needs: UnitTests runs-on: self-hosted steps: - name: Check out repo @@ -84,6 +98,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/MHD/AxisFluxTube -all $TESTME_OPTIONS ParabolicMHD: + needs: UnitTests runs-on: self-hosted steps: - name: Check out repo @@ -105,7 +120,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/MHD/HallWhistler -all $TESTME_OPTIONS Fargo: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] runs-on: self-hosted steps: - name: Check out repo @@ -121,7 +136,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/MHD/FargoMHDSpherical -all $TESTME_OPTIONS ShearingBox: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] runs-on: self-hosted steps: - name: Check out repo @@ -137,7 +152,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/MHD/ShearingBox -all $TESTME_OPTIONS SelfGravity: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] runs-on: self-hosted steps: - name: Check out repo @@ -159,7 +174,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/SelfGravity/DustyCollapse -all $TESTME_OPTIONS Planet: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] runs-on: self-hosted steps: - name: Check out repo @@ -183,7 +198,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/Planet/PlanetsIsActiveRK52D -all $TESTME_OPTIONS Dust: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] runs-on: self-hosted steps: - name: Check out repo @@ -199,7 +214,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/Dust/DustyWave -all $TESTME_OPTIONS Braginskii: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] runs-on: self-hosted steps: - name: Check out repo @@ -219,7 +234,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/MHD/clessTDiffusion -all $TESTME_OPTIONS Examples: - needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity] + needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity, UnitTests] runs-on: self-hosted steps: - name: Check out repo @@ -233,7 +248,7 @@ jobs: run: cd test && ./checks_examples.sh $TEST_OPTIONS Utils: - needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity] + needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity, UnitTests] runs-on: self-hosted steps: - name: Check out repo @@ -251,7 +266,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/utils/columnDensity -all $TESTME_OPTIONS IOs: - needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity] + needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity, UnitTests] runs-on: self-hosted steps: - name: Check out repo diff --git a/scripts/ci/run-unit-tests b/scripts/ci/run-unit-tests new file mode 100755 index 000000000..91f8387ed --- /dev/null +++ b/scripts/ci/run-unit-tests @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +###################################################################################### +# Idefix MHD astrophysical code +# +# Source file scripts/ci/run-unit-tests +# +# Last modified : 10/2026 +# +# Copyright(C) by : +# - Sébastien Valat (IPAG/UGA/CNRS - 2026) +# and other code contributors +# +# Licensed under CeCILL 2.1 License, see COPYING for more information +###################################################################################### + +function gen_cmake_options() +{ + # scan args + while [ $# != 0 ]; do + case "${1}" in + '-Werror') + echo "-DIdefix_WERROR=ON" + shift 1 + ;; + '-ccache') + echo "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache" + shift 1 + ;; + '-intel') + echo "-DCMAKE_CXX_COMPILER=icpx" + echo "-DCMAKE_C_COMPILER=icx" + shift 1 + ;; + '-cuda') + echo "-DKokkos_ENABLE_CUDA=ON" + # disable Async cuda malloc for tests performed on old UCX implementations + echo "-DKokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC=OFF" + shift 1 + ;; + '-hip') + echo "-DKokkos_ENABLE_HIP=ON" + shift 1 + ;; + *) + echo "Invalid option : ${1}" 1>&2 + return 1 + esac + done +} + +function display_and_run() +{ + echo "" + echo "======================================================================================" + echo ">> $@" + echo "" + "$@" || return 1 +} + +# load ICC compiler env +if [ "$IDEFIX_COMPILER" == icc ]; then + source /opt/intel/oneapi/setvars.sh +fi + +# enable bash features +set -ue + +# load python venv if present +if [ -d .venv ]; then + source .venv/bin/activate +fi + +# to get access to idefix ./pytools/ module in python code +if [[ -v PYTHONPATH ]]; then + export PYTHONPATH="${PWD}:${PYTHONPATH}" +else + export PYTHONPATH="${PWD}" +fi + +# check gen options +display_and_run gen_cmake_options "$@" + +# create build dir +display_and_run mkdir -p build-unittests + +# configure via cmake +display_and_run cmake . -B ./build-unittests -DIdefix_GTEST=ON -DIdefix_PROBLEM_DIR=$PWD/test/MHD/AxisFluxTube/ $(gen_cmake_options "$@") --fresh + +# compile +display_and_run make -C build-unittests -j8 + +# jump in dir to run in +display_and_run cd build-unittests + +# run the unit tests +display_and_run ./unittests + +# END +echo "" +echo "======================================================================================" +echo "Finished with SUCCESS" From 94b23fb1415d503d3defa48e375f85254e01d6ff Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Wed, 14 Oct 2026 11:46:09 +0200 Subject: [PATCH 08/20] tests: fix header of cmake/HandleDeps.cmake --- cmake/HandleDeps.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/HandleDeps.cmake b/cmake/HandleDeps.cmake index da28a572b..fc8a12382 100644 --- a/cmake/HandleDeps.cmake +++ b/cmake/HandleDeps.cmake @@ -1,9 +1,9 @@ ###################################################################################### # Idefix MHD astrophysical code # -# Source file cmake/AddIdefixSource.cmake +# Source file cmake/HandleDeps.cmake # -# Last modified : 12/2021 +# Last modified : 10/2026 # # Copyright(C) by : # - Sébastien Valat (IPAG/UGA/CNRS - 2026) From 6cea3f8801178e2361714bb85347e15157d3bd99 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Wed, 14 Oct 2026 13:58:42 +0200 Subject: [PATCH 09/20] tests: remove CPU loop to replace them by idefix_for() & parallel_for() + remove some useless redundant tests --- src/gtest-idfx.hpp | 36 +++++++-------- src/tests/testArrays.cpp | 51 +++++++++------------- src/tests/testKokkos.cpp | 94 ++++------------------------------------ 3 files changed, 46 insertions(+), 135 deletions(-) diff --git a/src/gtest-idfx.hpp b/src/gtest-idfx.hpp index 6f3e59e0d..bebe2ec1b 100644 --- a/src/gtest-idfx.hpp +++ b/src/gtest-idfx.hpp @@ -119,7 +119,7 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_2D_ARRAY(array, matcher, ...) \ do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ - for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i, j), matcher(__VA_ARGS__)) \ << " at: (" << i << " , " << j << ")"; \ @@ -143,7 +143,7 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i, j), matcher(array2OnHost(i, j))) \ << " at: (" << i << " , " << j << ")"; \ @@ -168,7 +168,7 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i, j), matcher(array2OnHost(i, j), __VA_ARGS__)) \ << " at: (" << i << " , " << j << ")"; \ @@ -190,8 +190,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_3D_ARRAY(array, matcher, ...) \ do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ - for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i, j, k), matcher(__VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << ")"; \ @@ -216,8 +216,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i, j, k), matcher(array2OnHost(i, j, k))) \ << " at: (" << i << " , " << j << " , " << k << ")"; \ @@ -243,8 +243,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i, j, k), matcher(array2OnHost(i, j, k), __VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << ")"; \ @@ -267,9 +267,9 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_4D_ARRAY(array, matcher, ...) \ do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ - for (size_t l = 0 ; l < arrayOnHost.extent(0) ; l++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(__VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ @@ -295,9 +295,9 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t l = 0 ; l < arrayOnHost.extent(0) ; l++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(array2OnHost(i, j, k, l))) \ << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ @@ -324,9 +324,9 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t l = 0 ; l < arrayOnHost.extent(0) ; l++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(0) ; k++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(0) ; j++) { \ + for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(array2OnHost(i, j, k, l), __VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ diff --git a/src/tests/testArrays.cpp b/src/tests/testArrays.cpp index 088011009..177553555 100644 --- a/src/tests/testArrays.cpp +++ b/src/tests/testArrays.cpp @@ -27,9 +27,9 @@ TEST(Array, assert_1d) { IdefixArray1D b("b", 10); //make both 5 - for (size_t i = 0 ; i < 10 ; i++) { + idefix_for("Loop1", 0, 10, KOKKOS_LAMBDA (const int i) { a(i) = b(i) = 5; - } + }); //check all 5 static ASSERT_THAT_1D_ARRAY(a, testing::Eq, 5); @@ -49,9 +49,9 @@ TEST(Array, assert_1d_is_nan) { IdefixArray1D b("b", 10); //make both 5 - for (size_t i = 0 ; i < 10 ; i++) { + idefix_for("Loop1", 0, 10, KOKKOS_LAMBDA (const int i) { a(i) = b(i) = NAN; - } + }); //check all 5 static ASSERT_THAT_1D_ARRAY(a, testing::IsNan); @@ -59,17 +59,15 @@ TEST(Array, assert_1d_is_nan) { TEST(Array, assert_2d) { //data array - IdefixArray2D a("a", 10, 10); + IdefixArray2D a("a", 10, 20); //ref array - IdefixArray2D b("b", 10, 10); + IdefixArray2D b("b", 10, 20); //make both 5 - for (size_t j = 0 ; j < 10 ; j++) { - for (size_t i = 0 ; i < 10 ; i++) { - a(i, j) = b(i, j) = 5; - } - } + idefix_for("Loop1", 0, 10, 0, 20, KOKKOS_LAMBDA (const int j, const int i) { + a(j, i) = b(j, i) = 5; + }); //check all 5 static ASSERT_THAT_2D_ARRAY(a, testing::Eq, 5); @@ -83,19 +81,15 @@ TEST(Array, assert_2d) { TEST(Array, assert_3d) { //data array - IdefixArray3D a("a", 10, 10, 10); + IdefixArray3D a("a", 10, 20, 30); //ref array - IdefixArray3D b("b", 10, 10, 10); + IdefixArray3D b("b", 10, 20, 30); //make both 5 - for (size_t k = 0 ; k < 10 ; k++) { - for (size_t j = 0 ; j < 10 ; j++) { - for (size_t i = 0 ; i < 10 ; i++) { - a(i, j, k) = b(i, j, k) = 5; - } - } - } + idefix_for("Loop1", 0, 10, 0, 20, 0, 30, KOKKOS_LAMBDA (const int k, const int j, const int i) { + a(k, j, i) = b(k, j, i) = 5; + }); //check all 5 static ASSERT_THAT_3D_ARRAY(a, testing::Eq, 5); @@ -109,21 +103,16 @@ TEST(Array, assert_3d) { TEST(Array, assert_4d) { //data array - IdefixArray4D a("a", 10, 10, 10, 10); + IdefixArray4D a("a", 10, 20, 30, 40); //ref array - IdefixArray4D b("b", 10, 10, 10, 10); + IdefixArray4D b("b", 10, 20, 30, 40); //make both 5 - for (size_t l = 0 ; l < 10 ; l++) { - for (size_t k = 0 ; k < 10 ; k++) { - for (size_t j = 0 ; j < 10 ; j++) { - for (size_t i = 0 ; i < 10 ; i++) { - a(i, j, k, l) = b(i, j, k, l) = 5; - } - } - } - } + idefix_for("Loop1", 0, 10, 0, 20, 0, 30, 0, 40, + KOKKOS_LAMBDA (const int l, const int k, const int j, const int i) { + a(l, k, j, i) = b(l, k, j, i) = 5; + }); //check all 5 static ASSERT_THAT_4D_ARRAY(a, testing::Eq, 5); diff --git a/src/tests/testKokkos.cpp b/src/tests/testKokkos.cpp index a4204d787..fed7ff25f 100644 --- a/src/tests/testKokkos.cpp +++ b/src/tests/testKokkos.cpp @@ -20,15 +20,15 @@ TEST(Kokkos, assert_view_1d) { //data array - Kokkos::View a("a", 10); + Kokkos::View a("a", 10); //ref array - Kokkos::View b("a", 10); + Kokkos::View b("a", 10); //make both 5 - for (size_t i = 0 ; i < 10 ; i++) { + Kokkos::parallel_for("Loop1", 10, KOKKOS_LAMBDA (const int i) { a(i) = b(i) = 5; - } + }); //check all 5 static ASSERT_THAT_1D_ARRAY(a, testing::Eq, 5); @@ -42,94 +42,16 @@ TEST(Kokkos, assert_view_1d) { TEST(Kokkos, assert_view_1d_is_nan) { //data array - Kokkos::View a("a", 10); + Kokkos::View a("a", 10); //ref array - Kokkos::View b("a", 10); + Kokkos::View b("a", 10); //make both 5 - for (size_t i = 0 ; i < 10 ; i++) { + Kokkos::parallel_for("Loop1", 10, KOKKOS_LAMBDA (const int i) { a(i) = b(i) = NAN; - } + }); //check all 5 static ASSERT_THAT_1D_ARRAY(a, testing::IsNan); } - -TEST(Kokkos, assert_view_2d) { - //data array - Kokkos::View a("a", 10, 10); - - //ref array - Kokkos::View b("a", 10, 10); - - //make both 5 - for (size_t j = 0 ; j < 10 ; j++) { - for (size_t i = 0 ; i < 10 ; i++) { - a(i, j) = b(i, j) = 5; - } - } - - //check all 5 static - ASSERT_THAT_2D_ARRAY(a, testing::Eq, 5); - - //compare both array - ASSERT_THAT_2D_ARRAYS(a, testing::Eq, b); - - //compare with near - ASSERT_THAT_2D_ARRAYS_ARGS(a, testing::DoubleNear, b, 0.001); -} - -TEST(Kokkos, assert_view_3d) { - //data array - Kokkos::View a("a", 10, 10, 10); - - //ref array - Kokkos::View b("a", 10, 10, 10); - - //make both 5 - for (size_t k = 0 ; k < 10 ; k++) { - for (size_t j = 0 ; j < 10 ; j++) { - for (size_t i = 0 ; i < 10 ; i++) { - a(i, j, k) = b(i, j, k) = 5; - } - } - } - - //check all 5 static - ASSERT_THAT_3D_ARRAY(a, testing::Eq, 5); - - //compare both array - ASSERT_THAT_3D_ARRAYS(a, testing::Eq, b); - - //compare with near - ASSERT_THAT_3D_ARRAYS_ARGS(a, testing::DoubleNear, b, 0.001); -} - -TEST(Array, assert_view_4d) { - //data array - Kokkos::View a("a", 10, 10, 10, 10); - - //ref array - Kokkos::View b("b", 10, 10, 10, 10); - - //make both 5 - for (size_t l = 0 ; l < 10 ; l++) { - for (size_t k = 0 ; k < 10 ; k++) { - for (size_t j = 0 ; j < 10 ; j++) { - for (size_t i = 0 ; i < 10 ; i++) { - a(i, j, k, l) = b(i, j, k, l) = 5; - } - } - } - } - - //check all 5 static - ASSERT_THAT_4D_ARRAY(a, testing::Eq, 5); - - //compare both array - ASSERT_THAT_4D_ARRAYS(a, testing::Eq, b); - - //compare with near - ASSERT_THAT_4D_ARRAYS_ARGS(a, testing::DoubleNear, b, 0.001); -} From 9ef1ea1a072e265cafeacc600245baeb56d4c356 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Wed, 14 Oct 2026 14:18:31 +0200 Subject: [PATCH 10/20] test: oops fix loop order to have the right inner loop on the idefix mem representation --- src/gtest-idfx.hpp | 48 +++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/gtest-idfx.hpp b/src/gtest-idfx.hpp index bebe2ec1b..3fada34b4 100644 --- a/src/gtest-idfx.hpp +++ b/src/gtest-idfx.hpp @@ -119,8 +119,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_2D_ARRAY(array, matcher, ...) \ do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ ASSERT_THAT(arrayOnHost(i, j), matcher(__VA_ARGS__)) \ << " at: (" << i << " , " << j << ")"; \ } \ @@ -143,8 +143,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ ASSERT_THAT(arrayOnHost(i, j), matcher(array2OnHost(i, j))) \ << " at: (" << i << " , " << j << ")"; \ } \ @@ -168,8 +168,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ ASSERT_THAT(arrayOnHost(i, j), matcher(array2OnHost(i, j), __VA_ARGS__)) \ << " at: (" << i << " , " << j << ")"; \ } \ @@ -190,9 +190,9 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_3D_ARRAY(array, matcher, ...) \ do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ ASSERT_THAT(arrayOnHost(i, j, k), matcher(__VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << ")"; \ } \ @@ -216,9 +216,9 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ ASSERT_THAT(arrayOnHost(i, j, k), matcher(array2OnHost(i, j, k))) \ << " at: (" << i << " , " << j << " , " << k << ")"; \ } \ @@ -243,9 +243,9 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ ASSERT_THAT(arrayOnHost(i, j, k), matcher(array2OnHost(i, j, k), __VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << ")"; \ } \ @@ -267,10 +267,10 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_4D_ARRAY(array, matcher, ...) \ do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ - for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(__VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ } \ @@ -295,10 +295,10 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(array2OnHost(i, j, k, l))) \ << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ } \ @@ -324,10 +324,10 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ - for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ + for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ + for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ + for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(array2OnHost(i, j, k, l), __VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ } \ From b4d79a47b26e95f7fbca270c42309b59cad7b348 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Wed, 14 Oct 2026 14:39:42 +0200 Subject: [PATCH 11/20] test: fix GoogleTest usage with KOKKOS_LAMBDA --- src/gtest-idfx.hpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/gtest-idfx.hpp b/src/gtest-idfx.hpp index 3fada34b4..5ddf67ff0 100644 --- a/src/gtest-idfx.hpp +++ b/src/gtest-idfx.hpp @@ -336,4 +336,52 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s } \ } while(0) +//Note(Sébastien): I don't like too much that approach but that looks the only easy one. +//Patch GTEST_TEST_ so it is supported by NVCC. We fix here the usage of "private" +//keyword for TestBody function which prevent using KOKKOS_LAMBDA. +//Fix is reported here : https://github.com/google/googletest/issues/4104 +#define GTEST_TESTBODY_VISIBILITY public + +//We re-write the GTEST_TEST_ with that feature. +#undef GTEST_TEST_ +#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \ + static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \ + "test_suite_name must not be empty"); \ + static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \ + "test_name must not be empty"); \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public parent_class { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \ + ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + (const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ + const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name) &) = delete; /* NOLINT */ \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + (GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) noexcept = delete; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ + GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name) &&) noexcept = delete; /* NOLINT */ \ + \ + GTEST_TESTBODY_VISIBILITY: \ + void TestBody() override; \ + private: \ + [[maybe_unused]] static ::testing::TestInfo* const test_info_; \ + }; \ + \ + ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::test_info_ = \ + ::testing::internal::MakeAndRegisterTestInfo( \ + #test_suite_name, #test_name, nullptr, nullptr, \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \ + new ::testing::internal::TestFactoryImpl); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + #endif //GTEST_IDFX_HPP_ From eaac4e47c89da2a5d5d9c17f9437eb89a0185730 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Wed, 14 Oct 2026 14:52:37 +0200 Subject: [PATCH 12/20] test: add missing fences when running on GPU --- src/gtest-idfx.hpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/gtest-idfx.hpp b/src/gtest-idfx.hpp index 5ddf67ff0..1e8902c62 100644 --- a/src/gtest-idfx.hpp +++ b/src/gtest-idfx.hpp @@ -55,7 +55,9 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_1D_ARRAY(array, matcher, ...) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i), matcher(__VA_ARGS__)) << " at: (" << i << ")"; \ } \ @@ -75,8 +77,10 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_1D_ARRAYS(array, matcher, array2) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i), matcher(array2OnHost(i))) << " at: (" << i << ")"; \ } \ @@ -97,8 +101,10 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_1D_ARRAYS_ARGS(array, that, array2, ...) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i), that(array2OnHost(i), __VA_ARGS__)) \ << " at: (" << i << ")"; \ @@ -118,7 +124,9 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_2D_ARRAY(array, matcher, ...) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ ASSERT_THAT(arrayOnHost(i, j), matcher(__VA_ARGS__)) \ @@ -141,8 +149,10 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_2D_ARRAYS(array, matcher, array2) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ ASSERT_THAT(arrayOnHost(i, j), matcher(array2OnHost(i, j))) \ @@ -166,8 +176,10 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_2D_ARRAYS_ARGS(array, matcher, array2, ...) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ ASSERT_THAT(arrayOnHost(i, j), matcher(array2OnHost(i, j), __VA_ARGS__)) \ @@ -189,7 +201,9 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_3D_ARRAY(array, matcher, ...) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ @@ -214,8 +228,10 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_3D_ARRAYS(array, matcher, array2) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ @@ -241,8 +257,10 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_3D_ARRAYS_ARGS(array, matcher, array2, ...) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ @@ -266,7 +284,9 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_4D_ARRAY(array, matcher, ...) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ @@ -293,8 +313,10 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_4D_ARRAYS(array, matcher, array2) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ @@ -322,8 +344,10 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s */ #define ASSERT_THAT_4D_ARRAYS_ARGS(array, matcher, array2, ...) \ do { \ + Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ From fe0b95457e6125cc055fc2a59b64d37ca25d1b80 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Wed, 14 Oct 2026 15:09:03 +0200 Subject: [PATCH 13/20] test: force using downloaded v1.18.0 of googletest due to the Kokkos fix required --- CMakeLists.txt | 2 +- cmake/HandleDeps.cmake | 27 +++++++++++++-------------- src/gtest-idfx.hpp | 11 ++++++----- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3bfeeb26..26a42d677 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,7 @@ if (Idefix_GTEST) enable_testing() # it not, download it internally to the project - FindSystemGoogleTestOrDownload() + GoogleTestDownloadAndLink() endif() #Idefix requires Cuda Lambdas (experimental) diff --git a/cmake/HandleDeps.cmake b/cmake/HandleDeps.cmake index fc8a12382..cebcd7b73 100644 --- a/cmake/HandleDeps.cmake +++ b/cmake/HandleDeps.cmake @@ -12,20 +12,19 @@ # Licensed under CeCILL 2.1 License, see COPYING for more information ###################################################################################### -function(FindSystemGoogleTestOrDownload) - # try to use the system one - find_package(GTest QUIET) +function(GoogleTestDownloadAndLink) + # CAUTION: if you change this verison, you should also check if you need to adapt + # the fix in gtest-idfx and re-import the definition of GTEST_TEST_(). + set(gtest_version "1.18.0") # it not, download it internally to the project - if (NOT GTest_FOUND) - cmake_policy(SET CMP0135 NEW) - include(FetchContent) - FetchContent_Declare( - googletest - URL https://github.com/google/googletest/releases/download/v1.18.0/googletest-1.18.0.tar.gz - ) - # For Windows: Prevent overriding the parent project's compiler/linker settings - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - FetchContent_MakeAvailable(googletest) - endif() + cmake_policy(SET CMP0135 NEW) + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/releases/download/v${gtest_version}/googletest-${gtest_version}.tar.gz + ) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) endfunction() diff --git a/src/gtest-idfx.hpp b/src/gtest-idfx.hpp index 1e8902c62..7f8646f22 100644 --- a/src/gtest-idfx.hpp +++ b/src/gtest-idfx.hpp @@ -360,13 +360,14 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s } \ } while(0) -//Note(Sébastien): I don't like too much that approach but that looks the only easy one. -//Patch GTEST_TEST_ so it is supported by NVCC. We fix here the usage of "private" +//Note from Sébastien: I don't like too much that approach but it looks the only easy one. +//Patch GTEST_TEST_() so it is supported by NVCC. We fix here the usage of "private" //keyword for TestBody function which prevent using KOKKOS_LAMBDA. -//Fix is reported here : https://github.com/google/googletest/issues/4104 -#define GTEST_TESTBODY_VISIBILITY public - +//Fix is reported coming from : https://github.com/google/googletest/issues/4104 //We re-write the GTEST_TEST_ with that feature. +//Here it if the GEST_TEST_() imported from googletest/include/gtest/internal/gtest-internal.h +//version 1.18.0 +#define GTEST_TESTBODY_VISIBILITY public #undef GTEST_TEST_ #define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \ static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \ From 6f57aa309196b4e57e6ac973660585d0a9e897a8 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Wed, 14 Oct 2026 15:19:48 +0200 Subject: [PATCH 14/20] test: oops add missing Kokkos::deep_copy() when running on GPU --- src/gtest-idfx.hpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/gtest-idfx.hpp b/src/gtest-idfx.hpp index 7f8646f22..dd7ffb2a2 100644 --- a/src/gtest-idfx.hpp +++ b/src/gtest-idfx.hpp @@ -57,6 +57,7 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ + Kokkos::deep_copy(arrayOnHost, array); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i), matcher(__VA_ARGS__)) << " at: (" << i << ")"; \ @@ -80,6 +81,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(arrayOnHost, array); \ + Kokkos::deep_copy(array2OnHost, array2); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i), matcher(array2OnHost(i))) << " at: (" << i << ")"; \ @@ -104,6 +107,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(arrayOnHost, array); \ + Kokkos::deep_copy(array2OnHost, array2); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ ASSERT_THAT(arrayOnHost(i), that(array2OnHost(i), __VA_ARGS__)) \ @@ -126,6 +131,7 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ + Kokkos::deep_copy(arrayOnHost, array); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ @@ -152,6 +158,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(arrayOnHost, array); \ + Kokkos::deep_copy(array2OnHost, array2); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ @@ -179,6 +187,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(arrayOnHost, array); \ + Kokkos::deep_copy(array2OnHost, array2); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ @@ -203,6 +213,7 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ + Kokkos::deep_copy(arrayOnHost, array); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ @@ -231,6 +242,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(arrayOnHost, array); \ + Kokkos::deep_copy(array2OnHost, array2); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ @@ -260,6 +273,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(arrayOnHost, array); \ + Kokkos::deep_copy(array2OnHost, array2); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ @@ -286,6 +301,7 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s do { \ Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ + Kokkos::deep_copy(arrayOnHost, array); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ @@ -316,6 +332,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(arrayOnHost, array); \ + Kokkos::deep_copy(array2OnHost, array2); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ @@ -347,6 +365,8 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s Kokkos::fence(); \ auto arrayOnHost = Kokkos::create_mirror_view(array); \ auto array2OnHost = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(arrayOnHost, array); \ + Kokkos::deep_copy(array2OnHost, array2); \ Kokkos::fence(); \ for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ From 87786b35f55d2cb1169415f06119a9f6db88a2ad Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Wed, 14 Oct 2026 16:30:49 +0200 Subject: [PATCH 15/20] test: rename the host variable to reuse the original name + _host to ease debugging --- src/gtest-idfx.hpp | 164 ++++++++++++++++++++++----------------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/src/gtest-idfx.hpp b/src/gtest-idfx.hpp index dd7ffb2a2..cd8c012dd 100644 --- a/src/gtest-idfx.hpp +++ b/src/gtest-idfx.hpp @@ -56,11 +56,11 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_1D_ARRAY(array, matcher, ...) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - Kokkos::deep_copy(arrayOnHost, array); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + Kokkos::deep_copy(array##_host, array); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - ASSERT_THAT(arrayOnHost(i), matcher(__VA_ARGS__)) << " at: (" << i << ")"; \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + ASSERT_THAT(array##_host(i), matcher(__VA_ARGS__)) << " at: (" << i << ")"; \ } \ } while(0) @@ -79,13 +79,13 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_1D_ARRAYS(array, matcher, array2) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - auto array2OnHost = Kokkos::create_mirror_view(array2); \ - Kokkos::deep_copy(arrayOnHost, array); \ - Kokkos::deep_copy(array2OnHost, array2); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + auto array2##_host = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(array##_host, array); \ + Kokkos::deep_copy(array2##_host, array2); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - ASSERT_THAT(arrayOnHost(i), matcher(array2OnHost(i))) << " at: (" << i << ")"; \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + ASSERT_THAT(array##_host(i), matcher(array2##_host(i))) << " at: (" << i << ")"; \ } \ } while(0) @@ -105,13 +105,13 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_1D_ARRAYS_ARGS(array, that, array2, ...) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - auto array2OnHost = Kokkos::create_mirror_view(array2); \ - Kokkos::deep_copy(arrayOnHost, array); \ - Kokkos::deep_copy(array2OnHost, array2); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + auto array2##_host = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(array##_host, array); \ + Kokkos::deep_copy(array2##_host, array2); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - ASSERT_THAT(arrayOnHost(i), that(array2OnHost(i), __VA_ARGS__)) \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + ASSERT_THAT(array##_host(i), that(array2##_host(i), __VA_ARGS__)) \ << " at: (" << i << ")"; \ } \ } while(0) @@ -130,12 +130,12 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_2D_ARRAY(array, matcher, ...) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - Kokkos::deep_copy(arrayOnHost, array); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + Kokkos::deep_copy(array##_host, array); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - ASSERT_THAT(arrayOnHost(i, j), matcher(__VA_ARGS__)) \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + for (size_t j = 0 ; j < array##_host.extent(1) ; j++) { \ + ASSERT_THAT(array##_host(i, j), matcher(__VA_ARGS__)) \ << " at: (" << i << " , " << j << ")"; \ } \ } \ @@ -156,14 +156,14 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_2D_ARRAYS(array, matcher, array2) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - auto array2OnHost = Kokkos::create_mirror_view(array2); \ - Kokkos::deep_copy(arrayOnHost, array); \ - Kokkos::deep_copy(array2OnHost, array2); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + auto array2##_host = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(array##_host, array); \ + Kokkos::deep_copy(array2##_host, array2); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - ASSERT_THAT(arrayOnHost(i, j), matcher(array2OnHost(i, j))) \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + for (size_t j = 0 ; j < array##_host.extent(1) ; j++) { \ + ASSERT_THAT(array##_host(i, j), matcher(array2##_host(i, j))) \ << " at: (" << i << " , " << j << ")"; \ } \ } \ @@ -185,14 +185,14 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_2D_ARRAYS_ARGS(array, matcher, array2, ...) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - auto array2OnHost = Kokkos::create_mirror_view(array2); \ - Kokkos::deep_copy(arrayOnHost, array); \ - Kokkos::deep_copy(array2OnHost, array2); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + auto array2##_host = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(array##_host, array); \ + Kokkos::deep_copy(array2##_host, array2); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - ASSERT_THAT(arrayOnHost(i, j), matcher(array2OnHost(i, j), __VA_ARGS__)) \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + for (size_t j = 0 ; j < array##_host.extent(1) ; j++) { \ + ASSERT_THAT(array##_host(i, j), matcher(array2##_host(i, j), __VA_ARGS__)) \ << " at: (" << i << " , " << j << ")"; \ } \ } \ @@ -212,13 +212,13 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_3D_ARRAY(array, matcher, ...) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - Kokkos::deep_copy(arrayOnHost, array); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + Kokkos::deep_copy(array##_host, array); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ - ASSERT_THAT(arrayOnHost(i, j, k), matcher(__VA_ARGS__)) \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + for (size_t j = 0 ; j < array##_host.extent(1) ; j++) { \ + for (size_t k = 0 ; k < array##_host.extent(2) ; k++) { \ + ASSERT_THAT(array##_host(i, j, k), matcher(__VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << ")"; \ } \ } \ @@ -240,15 +240,15 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_3D_ARRAYS(array, matcher, array2) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - auto array2OnHost = Kokkos::create_mirror_view(array2); \ - Kokkos::deep_copy(arrayOnHost, array); \ - Kokkos::deep_copy(array2OnHost, array2); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + auto array2##_host = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(array##_host, array); \ + Kokkos::deep_copy(array2##_host, array2); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ - ASSERT_THAT(arrayOnHost(i, j, k), matcher(array2OnHost(i, j, k))) \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + for (size_t j = 0 ; j < array##_host.extent(1) ; j++) { \ + for (size_t k = 0 ; k < array##_host.extent(2) ; k++) { \ + ASSERT_THAT(array##_host(i, j, k), matcher(array2##_host(i, j, k))) \ << " at: (" << i << " , " << j << " , " << k << ")"; \ } \ } \ @@ -271,15 +271,15 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_3D_ARRAYS_ARGS(array, matcher, array2, ...) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - auto array2OnHost = Kokkos::create_mirror_view(array2); \ - Kokkos::deep_copy(arrayOnHost, array); \ - Kokkos::deep_copy(array2OnHost, array2); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + auto array2##_host = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(array##_host, array); \ + Kokkos::deep_copy(array2##_host, array2); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ - ASSERT_THAT(arrayOnHost(i, j, k), matcher(array2OnHost(i, j, k), __VA_ARGS__)) \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + for (size_t j = 0 ; j < array##_host.extent(1) ; j++) { \ + for (size_t k = 0 ; k < array##_host.extent(2) ; k++) { \ + ASSERT_THAT(array##_host(i, j, k), matcher(array2##_host(i, j, k), __VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << ")"; \ } \ } \ @@ -300,14 +300,14 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_4D_ARRAY(array, matcher, ...) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - Kokkos::deep_copy(arrayOnHost, array); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + Kokkos::deep_copy(array##_host, array); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ - for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ - ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(__VA_ARGS__)) \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + for (size_t j = 0 ; j < array##_host.extent(1) ; j++) { \ + for (size_t k = 0 ; k < array##_host.extent(2) ; k++) { \ + for (size_t l = 0 ; l < array##_host.extent(3) ; l++) { \ + ASSERT_THAT(array##_host(i, j, k, l), matcher(__VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ } \ } \ @@ -330,16 +330,16 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_4D_ARRAYS(array, matcher, array2) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - auto array2OnHost = Kokkos::create_mirror_view(array2); \ - Kokkos::deep_copy(arrayOnHost, array); \ - Kokkos::deep_copy(array2OnHost, array2); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + auto array2##_host = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(array##_host, array); \ + Kokkos::deep_copy(array2##_host, array2); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ - for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ - ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(array2OnHost(i, j, k, l))) \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + for (size_t j = 0 ; j < array##_host.extent(1) ; j++) { \ + for (size_t k = 0 ; k < array##_host.extent(2) ; k++) { \ + for (size_t l = 0 ; l < array##_host.extent(3) ; l++) { \ + ASSERT_THAT(array##_host(i, j, k, l), matcher(array2##_host(i, j, k, l))) \ << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ } \ } \ @@ -363,16 +363,16 @@ std::string idfx_get_data_file_path(const std::string & sourceFile, const std::s #define ASSERT_THAT_4D_ARRAYS_ARGS(array, matcher, array2, ...) \ do { \ Kokkos::fence(); \ - auto arrayOnHost = Kokkos::create_mirror_view(array); \ - auto array2OnHost = Kokkos::create_mirror_view(array2); \ - Kokkos::deep_copy(arrayOnHost, array); \ - Kokkos::deep_copy(array2OnHost, array2); \ + auto array##_host = Kokkos::create_mirror_view(array); \ + auto array2##_host = Kokkos::create_mirror_view(array2); \ + Kokkos::deep_copy(array##_host, array); \ + Kokkos::deep_copy(array2##_host, array2); \ Kokkos::fence(); \ - for (size_t i = 0 ; i < arrayOnHost.extent(0) ; i++) { \ - for (size_t j = 0 ; j < arrayOnHost.extent(1) ; j++) { \ - for (size_t k = 0 ; k < arrayOnHost.extent(2) ; k++) { \ - for (size_t l = 0 ; l < arrayOnHost.extent(3) ; l++) { \ - ASSERT_THAT(arrayOnHost(i, j, k, l), matcher(array2OnHost(i, j, k, l), __VA_ARGS__)) \ + for (size_t i = 0 ; i < array##_host.extent(0) ; i++) { \ + for (size_t j = 0 ; j < array##_host.extent(1) ; j++) { \ + for (size_t k = 0 ; k < array##_host.extent(2) ; k++) { \ + for (size_t l = 0 ; l < array##_host.extent(3) ; l++) { \ + ASSERT_THAT(array##_host(i, j, k, l), matcher(array2##_host(i, j, k, l), __VA_ARGS__)) \ << " at: (" << i << " , " << j << " , " << k << " , " << l << ")"; \ } \ } \ From 216d840334ad888a20c24e1ccfc440b0e20682ac Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Tue, 20 Oct 2026 09:39:47 +0200 Subject: [PATCH 16/20] unittests: fix argc, argv issues --- src/gtest-idfx.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/gtest-idfx.cpp b/src/gtest-idfx.cpp index 25e4541f9..2390b0276 100644 --- a/src/gtest-idfx.cpp +++ b/src/gtest-idfx.cpp @@ -47,14 +47,13 @@ std::string idfx_get_data_file_path( **/ class MPIEnvironment : public ::testing::Environment { public: + MPIEnvironment(int * argc, char *** argv): argc(argc), argv(argv) {} ~MPIEnvironment() override {} // Override this to define how to set up the environment. void SetUp() override { - char** argv; - int argc = 0; #ifdef WITH_MPI - int mpiError = MPI_Init(&argc, &argv); + int mpiError = MPI_Init(argc, argv); ASSERT_EQ(mpiError, 0); #endif } @@ -66,6 +65,10 @@ class MPIEnvironment : public ::testing::Environment { ASSERT_EQ(mpiError, 0); #endif } + + private: + int * argc{nullptr}; + char *** argv{nullptr}; }; /** @@ -73,19 +76,22 @@ class MPIEnvironment : public ::testing::Environment { */ class KokkosEnvironment : public ::testing::Environment { public: + KokkosEnvironment(int argc, char ** argv): argc(argc), argv(argv) {} ~KokkosEnvironment() override {} // Override this to define how to set up the environment. void SetUp() override { - char** argv; - int argc = 0; - Kokkos::initialize(argc, argv); + Kokkos::initialize(this->argc, this->argv); } // Override this to define how to tear down the environment. void TearDown() override { Kokkos::finalize(); } + + private: + int argc{0}; + char ** argv{nullptr}; }; /** @@ -122,11 +128,11 @@ int main(int argc, char* argv[]) { //init MPI & Kokkos with right order if (initKokkosBeforeMPI) { - ::testing::AddGlobalTestEnvironment(new MPIEnvironment); - ::testing::AddGlobalTestEnvironment(new KokkosEnvironment); + ::testing::AddGlobalTestEnvironment(new MPIEnvironment(&argc, &argv)); + ::testing::AddGlobalTestEnvironment(new KokkosEnvironment(argc, argv)); } else { - ::testing::AddGlobalTestEnvironment(new KokkosEnvironment); - ::testing::AddGlobalTestEnvironment(new MPIEnvironment); + ::testing::AddGlobalTestEnvironment(new KokkosEnvironment(argc, argv)); + ::testing::AddGlobalTestEnvironment(new MPIEnvironment(&argc, &argv)); } //init idefix From 44b3f5ac040b6313219109ed656fe0d76709fea9 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Tue, 20 Oct 2026 09:47:31 +0200 Subject: [PATCH 17/20] unittest: fix test dependencies --- .github/workflows/idefix-ci-jobs.yml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/idefix-ci-jobs.yml b/.github/workflows/idefix-ci-jobs.yml index 150c1f96f..73882f191 100644 --- a/.github/workflows/idefix-ci-jobs.yml +++ b/.github/workflows/idefix-ci-jobs.yml @@ -36,7 +36,6 @@ jobs: run: scripts/ci/run-unit-tests $TESTME_OPTIONS ShocksHydro: - needs: UnitTests runs-on: self-hosted steps: - name: Check out repo @@ -56,7 +55,6 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/HD/SedovBlastWave -all $TESTME_OPTIONS ParabolicHydro: - needs: UnitTests runs-on: self-hosted steps: - name: Check out repo @@ -74,7 +72,6 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/HD/thermalDiffusion -all $TESTME_OPTIONS ShocksMHD: - needs: UnitTests runs-on: self-hosted steps: - name: Check out repo @@ -98,7 +95,6 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/MHD/AxisFluxTube -all $TESTME_OPTIONS ParabolicMHD: - needs: UnitTests runs-on: self-hosted steps: - name: Check out repo @@ -120,7 +116,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/MHD/HallWhistler -all $TESTME_OPTIONS Fargo: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] runs-on: self-hosted steps: - name: Check out repo @@ -136,7 +132,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/MHD/FargoMHDSpherical -all $TESTME_OPTIONS ShearingBox: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] runs-on: self-hosted steps: - name: Check out repo @@ -152,7 +148,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/MHD/ShearingBox -all $TESTME_OPTIONS SelfGravity: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] runs-on: self-hosted steps: - name: Check out repo @@ -174,7 +170,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/SelfGravity/DustyCollapse -all $TESTME_OPTIONS Planet: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] runs-on: self-hosted steps: - name: Check out repo @@ -198,7 +194,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/Planet/PlanetsIsActiveRK52D -all $TESTME_OPTIONS Dust: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] runs-on: self-hosted steps: - name: Check out repo @@ -214,7 +210,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/Dust/DustyWave -all $TESTME_OPTIONS Braginskii: - needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD, UnitTests] + needs: [ShocksHydro, ParabolicHydro, ShocksMHD, ParabolicMHD] runs-on: self-hosted steps: - name: Check out repo @@ -234,7 +230,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/MHD/clessTDiffusion -all $TESTME_OPTIONS Examples: - needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity, UnitTests] + needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity] runs-on: self-hosted steps: - name: Check out repo @@ -248,7 +244,7 @@ jobs: run: cd test && ./checks_examples.sh $TEST_OPTIONS Utils: - needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity, UnitTests] + needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity] runs-on: self-hosted steps: - name: Check out repo @@ -266,7 +262,7 @@ jobs: run: scripts/ci/run-tests $IDEFIX_DIR/test/utils/columnDensity -all $TESTME_OPTIONS IOs: - needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity, UnitTests] + needs: [Fargo, Dust, Planet, ShearingBox, SelfGravity] runs-on: self-hosted steps: - name: Check out repo From 01d05680e739e074caec8ba2352ad22a759b56ea Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Tue, 20 Oct 2026 10:18:19 +0200 Subject: [PATCH 18/20] unittests: fix an issue in CMakeLists.txt --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 26a42d677..dd85bef43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -357,7 +357,9 @@ endif() if(Idefix_SUPPRESS_FMA) message(STATUS "FMA (fused multiply-add) contraction/codegen is disabled") target_suppress_fma(idefix) - target_suppress_fma(unitests) + if (Idefix_GTEST) + target_suppress_fma(unittests) + endif() endif() message(STATUS "Idefix final configuration") From af6d87e0f466928df46d2e25188f7eb7727bf07a Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Tue, 20 Oct 2026 10:34:52 +0200 Subject: [PATCH 19/20] unittest: due to weak symbol usage, setup.cpp should be build in idefix target not in libidefix-core --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd85bef43..53d8c4bdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,7 +134,7 @@ endif() get_filename_component(Idefix_PROBLEM_DIR_ABS ${Idefix_PROBLEM_DIR} ABSOLUTE BASE_DIR ${PROJECT_BINARY_DIR}) if(EXISTS ${Idefix_PROBLEM_DIR_ABS}/setup.cpp) - target_sources(idefix-core PRIVATE ${Idefix_PROBLEM_DIR_ABS}/setup.cpp) + target_sources(idefix PRIVATE ${Idefix_PROBLEM_DIR_ABS}/setup.cpp) else() message(WARNING "No specific setup.cpp found in the problem directory (this message can be ignored if using python to define your problem)") endif() From ba96dffb3aae522ca6052bff6c25bbc072cb4098 Mon Sep 17 00:00:00 2001 From: Sebastien Valat Date: Tue, 20 Oct 2026 11:02:16 +0200 Subject: [PATCH 20/20] unittest: should apply suppress_fma also on libidefix-core --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53d8c4bdf..3d75f891b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -357,6 +357,7 @@ endif() if(Idefix_SUPPRESS_FMA) message(STATUS "FMA (fused multiply-add) contraction/codegen is disabled") target_suppress_fma(idefix) + target_suppress_fma(idefix-core) if (Idefix_GTEST) target_suppress_fma(unittests) endif()