diff --git a/.circleci/config.yml b/.circleci/config.yml index aa5f80d6..ddcb89c2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -310,6 +310,17 @@ jobs: build_kind: 'cmake' generate: 'false' + - run: + name: "Execute the C++ tests" + command: | + # compile the tests (it would probably would be better to do this when + # compiling the rest of Grackle) + cmake -DGRACKLE_BUILD_TESTS=ON -Bbuild + cmake --build build + # execute the tests + cd build + ctest + # TODO: once we merge in GH-#208, we need to issue a new gold standard, and then we should # issue a followup PR where we uncomment the remainder of this test test-standalone-pygrackle: diff --git a/CMakeLists.txt b/CMakeLists.txt index 6925b127..5ca3ea8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,12 @@ endif() cmake_minimum_required(VERSION 3.16) cmake_policy(SET CMP0077 NEW) cmake_policy(SET CMP0082 NEW) + +if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) + cmake_policy(SET CMP0135 NEW) # makes automatic dependency downloads more + # robust (& addresses noisy warnings) +endif() + project(Grackle VERSION ${_GRACKLE_VERSION_NO_DEVTAG} LANGUAGES C CXX Fortran) # Specify that `./cmake` contains cmake modules defining useful functionality @@ -184,6 +190,21 @@ endif() # ----- # (this is where we will add targets for integration tests) +# Add BUILD_TESTS option available +option(GRACKLE_BUILD_TESTS "build Grackle tests using GoogleTest" OFF) + +# if desired, setup Grackle's tests that use GoogleTest (this has no impact on +# the tests run via pytest, those are handled outside of cmake) +if(GRACKLE_BUILD_TESTS) + # setup GoogleTest framework (search for an installed copy on the machine + # OR download it & add its compilation to the rest of the build procedure) + include(setup_GTest) + # load CMake's built-in module that defines functions to registering unit + # tests implemented with GoogleTest into CMake's test-runner, CTest + include(GoogleTest) + enable_testing() # enable the test-runner, CTest + add_subdirectory(tests) # declare the recipes for each test +endif() # Packaging # --------- diff --git a/cmake/setup_GTest.cmake b/cmake/setup_GTest.cmake new file mode 100644 index 00000000..d08f9a2c --- /dev/null +++ b/cmake/setup_GTest.cmake @@ -0,0 +1,35 @@ +# This file includes logic for setting up GoogleTest +find_package(GTest) + +if (NOT GTest_FOUND) + message(STATUS + "Preparing to download GTest from GitHub & configure its build. Don't " + "worry about any mentions of Python in the following text -- that is " + "only used internally by the Google test build" + ) + + # NOTE: it is idiomatic to use FetchContent with a git hash rather than the + # name of a tag or branch (since the latter 2 can freely change). If we use + # the name of a tag/branch, then CMake will query GitHub on every + # subsequent build to check whether the tag/branch is still the same + + include(FetchContent) + FetchContent_Declare( + googletest # the url contains the hash for v1.13.0 + URL https://github.com/google/googletest/archive/b796f7d44681514f58a683a3a71ff17c94edb0c1.zip + ) + + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + +elseif("${CMAKE_VERSION}" VERSION_LESS "3.20") + # CMake's built-in `FindGTest` module imported targets have different names + # in earlier CMake versions + add_library(GTest::gtest ALIAS GTest::GTest) + add_library(GTest::gtest_main ALIAS GTest::Main) +endif() + +if (NOT TARGET GTest::gtest_main) + message("Target GTest:: stuff MISSING") +endif() diff --git a/src/clib/c_wrappers/wrap_interpolators_g.c b/src/clib/c_wrappers/wrap_interpolators_g.c new file mode 100644 index 00000000..9509fd05 --- /dev/null +++ b/src/clib/c_wrappers/wrap_interpolators_g.c @@ -0,0 +1,60 @@ +#include "grackle_macros.h" + +// TODO: FORTRAN_NAME not needed for unit tests +extern void FORTRAN_NAME(interpolate_1d_g)(double *input1, + long long *gridDim, + double *gridPar1, double *dgridPar1, + long long *dataSize, double *dataField, + double *value); + +extern void FORTRAN_NAME(interpolate_2d_g)(double *input1, double *input2, + long long *gridDim, + double *gridPar1, double *dgridPar1, + double *gridPar2, double *dgridPar2, + long long *dataSize, double *dataField, + double *value); + +extern void FORTRAN_NAME(interpolate_3d_g)(double *input1, double *input2, double *input3, + long long *gridDim, + double *gridPar1, double *dgridPar1, + double *gridPar2, double *dgridPar2, + double *gridPar3, double *dgridPar3, + long long *dataSize, double *dataField, + double *value); + +extern void FORTRAN_NAME(interpolate_3dz_g)(double *input1, double *input2, double *input3, + long long *gridDim, + double *gridPar1, double *dgridPar1, + double *gridPar2, long long *index2, + double *gridPar3, double *dgridPar3, + long long *dataSize, double *dataField, + int *end_int, + double *value); + +extern void FORTRAN_NAME(interpolate_2df3d_g)(double *input1, double *input3, + long long *gridDim, + double *gridPar1, double *dgridPar1, + long long *index2, + double *gridPar3, double *dgridPar3, + long long *dataSize, double *dataField, + double *value); + +extern void FORTRAN_NAME(interpolate_4d_g)(double *input1, double *input2, double *input3, double *input4, + long long *gridDim, + double *gridPar1, double *dgridPar1, + double *gridPar2, double *dgridPar2, + double *gridPar3, double *dgridPar3, + double *gridPar4, double *dgridPar4, + long long *dataSize, double *dataField, + double *value); + +extern void FORTRAN_NAME(interpolate_5d_g)(double *input1, double *input2, double *input3, double *input4, double *input5, + long long *gridDim, + double *gridPar1, double *dgridPar1, + double *gridPar2, double *dgridPar2, + double *gridPar3, double *dgridPar3, + double *gridPar4, double *dgridPar4, + double *gridPar5, double *dgridPar5, + long long *dataSize, double *dataField, + double *value); + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..269aea0c --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(unit) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt new file mode 100644 index 00000000..6f39c5d6 --- /dev/null +++ b/tests/unit/CMakeLists.txt @@ -0,0 +1,15 @@ +# declare testdeps to bundle together dependencies used by all tests +# ------------------------------------------------------------------ +add_library(testdeps INTERFACE) +target_link_libraries(testdeps INTERFACE Grackle::Grackle GTest::gtest_main) + +# short-term hack to let tests invoke Fortran functions from C +target_compile_definitions(testdeps INTERFACE "$<$:LINUX>") +target_include_directories(testdeps INTERFACE ${PROJECT_SOURCE_DIR}/src/clib) + +# start declaring targets for tests +# --------------------------------- +add_executable(runInterpolationTests test_unit_interpolators_g.cpp) +target_link_libraries(runInterpolationTests testdeps) + +gtest_discover_tests(runInterpolationTests) diff --git a/tests/unit/test_unit_interpolators_g.cpp b/tests/unit/test_unit_interpolators_g.cpp new file mode 100644 index 00000000..a38f360a --- /dev/null +++ b/tests/unit/test_unit_interpolators_g.cpp @@ -0,0 +1,412 @@ +#include "gtest/gtest.h" + +extern "C" { + #include "grackle_macros.h" +} + + +// TODO: FORTRAN_NAME for unit tests not needed +extern "C" void FORTRAN_NAME(interpolate_1d_g)(double *input1, + long long *gridDim, + double *gridPar1, double *dgridPar1, + long long *dataSize, double *dataField, + double *value); + +extern "C" void FORTRAN_NAME(interpolate_2d_g)(double *input1, double *input2, + long long *gridDim, + double *gridPar1, double *dgridPar1, + double *gridPar2, double *dgridPar2, + long long *dataSize, double *dataField, + double *value); + +extern "C" void FORTRAN_NAME(interpolate_3d_g)(double *input1, double *input2, double *input3, + long long *gridDim, + double *gridPar1, double *dgridPar1, + double *gridPar2, double *dgridPar2, + double *gridPar3, double *dgridPar3, + long long *dataSize, double *dataField, + double *value); + +extern "C" void FORTRAN_NAME(interpolate_3dz_g)(double *input1, double *input2, double *input3, + long long *gridDim, + double *gridPar1, double *dgridPar1, + double *gridPar2, long long *index2, + double *gridPar3, double *dgridPar3, + long long *dataSize, double *dataField, + int *end_int, + double *value); + +extern "C" void FORTRAN_NAME(interpolate_2df3d_g)(double *input1, double *input3, + long long *gridDim, + double *gridPar1, double *dgridPar1, + long long *index2, + double *gridPar3, double *dgridPar3, + long long *dataSize, double *dataField, + double *value); + +extern "C" void FORTRAN_NAME(interpolate_4d_g)(double *input1, double *input2, double *input3, double *input4, + long long *gridDim, + double *gridPar1, double *dgridPar1, + double *gridPar2, double *dgridPar2, + double *gridPar3, double *dgridPar3, + double *gridPar4, double *dgridPar4, + long long *dataSize, double *dataField, + double *value); + +extern "C" void FORTRAN_NAME(interpolate_5d_g)(double *input1, double *input2, double *input3, double *input4, double *input5, + long long *gridDim, + double *gridPar1, double *dgridPar1, + double *gridPar2, double *dgridPar2, + double *gridPar3, double *dgridPar3, + double *gridPar4, double *dgridPar4, + double *gridPar5, double *dgridPar5, + long long *dataSize, double *dataField, + double *value); + +TEST(InterpolationTest, Interpolate1D) { + + long long dataSize = 10; + double input1 = 5.0; + double dgridPar1 = 1.3; + std::vector gridDim(1,dataSize); + std::vector gridPar1(dataSize); + std::vector dataField(dataSize); + double value; + + for(auto i=0;i gridDim = {dataSize1, dataSize2}; + std::vector gridPar1(dataSize1); + std::vector gridPar2(dataSize2); + std::vector dataField(dataSize); + double value; + + for(auto i=0;i gridDim = {dataSize1, dataSize2, dataSize3}; + std::vector gridPar1(dataSize1); + std::vector gridPar2(dataSize2); + std::vector gridPar3(dataSize3); + std::vector dataField(dataSize); + double value; + + for(auto i=0;i gridDim = {dataSize1, dataSize2, dataSize3}; + std::vector gridPar1(dataSize1); + std::vector gridPar2(dataSize2); + std::vector gridPar3(dataSize3); + std::vector dataField(dataSize); + double value_end_int_0; + double value_end_int_1; + + for(auto i=0;i gridDim = {dataSize1, dataSize2, dataSize3}; + std::vector gridPar1(dataSize1); + std::vector gridPar2(dataSize2); + std::vector gridPar3(dataSize3); + std::vector dataField(dataSize); + double value; + + for(auto i=0;i gridDim = {dataSize1, dataSize2, dataSize3, dataSize4}; + std::vector gridPar1(dataSize1); + std::vector gridPar2(dataSize2); + std::vector gridPar3(dataSize3); + std::vector gridPar4(dataSize4); + std::vector dataField(dataSize); + double value; + + for(auto i=0;i gridDim = {dataSize1, dataSize2, dataSize3, dataSize4, dataSize5}; + std::vector gridPar1(dataSize1); + std::vector gridPar2(dataSize2); + std::vector gridPar3(dataSize3); + std::vector gridPar4(dataSize4); + std::vector gridPar5(dataSize5); + std::vector dataField(dataSize); + double value; + + for(auto i=0;i