Skip to content

Commit

Permalink
update test dependencies and add option to control downloading (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
AdhocMan authored Mar 19, 2024
1 parent 4652d85 commit 3cd5351
Show file tree
Hide file tree
Showing 11 changed files with 338 additions and 83 deletions.
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ set(CMAKE_HIP_STANDARD 17)
#add local module path
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake/modules)

include(CMakeDependentOption)

# Options
option(SPFFT_STATIC "Compile as static library" OFF)
option(SPFFT_OMP "Compile with OpenMP support" ON)
Expand All @@ -38,6 +40,11 @@ option(SPFFT_BUILD_TESTS "Build tests" OFF)
option(SPFFT_SINGLE_PRECISION "Enable single precision support" OFF)
option(SPFFT_INSTALL "Enable CMake install commands" ON)
option(SPFFT_FORTRAN "Compile fortran module" OFF)
option(SPFFT_BUNDLED_LIBS "Use bundled libraries for building tests" ON)

cmake_dependent_option(SPFFT_BUNDLED_GOOGLETEST "Use bundled googletest lib" ON "SPFFT_BUNDLED_LIBS" OFF)
cmake_dependent_option(SPFFT_BUNDLED_JSON "Use bundled json lib" ON "SPFFT_BUNDLED_LIBS" OFF)
cmake_dependent_option(SPFFT_BUNDLED_CLI11 "Use bundled CLI11 lib" ON "SPFFT_BUNDLED_LIBS" OFF)

set(SPFFT_GPU_BACKEND "OFF" CACHE STRING "GPU backend")
set_property(CACHE SPFFT_GPU_BACKEND PROPERTY STRINGS
Expand Down
93 changes: 41 additions & 52 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,72 +1,60 @@

if(SPFFT_BUILD_TESTS)
cmake_minimum_required(VERSION 3.11 FATAL_ERROR) # git fetch module requires at least 3.11
cmake_minimum_required(VERSION 3.14 FATAL_ERROR) # FetchContent_MakeAvailable requires at least 3.14

# update time stamps when using FetchContent
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
endif()

set(BUILD_GMOCK OFF CACHE BOOL "")
set(INSTALL_GTEST OFF CACHE BOOL "")
mark_as_advanced(BUILD_GMOCK INSTALL_GTEST)
include(FetchContent)

# add googletest
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.8.1
)
FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
message(STATUS "Downloading Google Test repository...")
FetchContent_Populate(googletest)
endif()
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})

# add gtest_mpi
FetchContent_Declare(
gtest_mpi
GIT_REPOSITORY https://github.com/AdhocMan/gtest_mpi.git
GIT_TAG v1.0.0
)
FetchContent_GetProperties(gtest_mpi)
if(NOT gtest_mpi_POPULATED)
message(STATUS "Downloading Google Test MPI extension repository...")
FetchContent_Populate(gtest_mpi)
if(SPFFT_BUNDLED_GOOGLETEST)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz
URL_MD5 c8340a482851ef6a3fe618a082304cfc
)
FetchContent_MakeAvailable(googletest)
else()
find_package(googletest CONFIG REQUIRED)
endif()
add_subdirectory(${gtest_mpi_SOURCE_DIR} ${gtest_mpi_BINARY_DIR})
list(APPEND SPFFT_TEST_LIBRARIES gtest_main)

# add command line parser
FetchContent_Declare(
cli11
GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
GIT_TAG v1.7.1
)
FetchContent_GetProperties(cli11)
if(NOT cli11_POPULATED)
message(STATUS "Downloading CLI11 command line parser repository...")
FetchContent_Populate(cli11)
if(SPFFT_BUNDLED_CLI11)
FetchContent_Declare(
cli11
URL https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.3.2.tar.gz
URL_MD5 b80cb645dee25982110b068b426363ff
)
FetchContent_MakeAvailable(cli11)
else()
find_package(CLI11 CONFIG REQUIRED)
endif()
list(APPEND SPFFT_EXTERNAL_INCLUDE_DIRS ${cli11_SOURCE_DIR}/include)
list(APPEND SPFFT_TEST_LIBRARIES CLI11::CLI11)

# add json parser
set(JSON_Install OFF CACHE BOOL "")
FetchContent_Declare(
json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.6.1
)
FetchContent_GetProperties(json)
if(NOT json_POPULATED)
message(STATUS "Downloading json repository...")
FetchContent_Populate(json)
# add json parser
if(SPFFT_BUNDLED_JSON)
FetchContent_Declare(
json
URL https://github.com/nlohmann/json/archive/refs/tags/v3.11.2.tar.gz
URL_MD5 e8d56bc54621037842ee9f0aeae27746
)
FetchContent_MakeAvailable(json)
else()
find_package(nlohmann_json CONFIG REQUIRED)
endif()
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR})
list(APPEND SPFFT_EXTERNAL_LIBS nlohmann_json::nlohmann_json)


list(APPEND SPFFT_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/tests)

# benchmark executable
add_executable(benchmark programs/benchmark.cpp)
target_link_libraries(benchmark PRIVATE spfft_test ${SPFFT_EXTERNAL_LIBS})
target_link_libraries(benchmark PRIVATE spfft_test ${SPFFT_EXTERNAL_LIBS} CLI11::CLI11 nlohmann_json::nlohmann_json)
target_include_directories(benchmark PRIVATE ${SPFFT_INCLUDE_DIRS} ${SPFFT_EXTERNAL_INCLUDE_DIRS})

# test executables
Expand All @@ -77,19 +65,20 @@ if(SPFFT_BUILD_TESTS)
local_tests/test_fftw_prop_hash.cpp
local_tests/test_local_transform.cpp
)
target_link_libraries(run_local_tests PRIVATE gtest_main gtest_mpi)
target_link_libraries(run_local_tests PRIVATE gtest_main)
target_link_libraries(run_local_tests PRIVATE spfft_test ${SPFFT_EXTERNAL_LIBS})
target_include_directories(run_local_tests PRIVATE ${SPFFT_INCLUDE_DIRS} ${SPFFT_EXTERNAL_INCLUDE_DIRS})

if(SPFFT_MPI)
add_executable(run_mpi_tests
run_mpi_tests.cpp
gtest_mpi.cpp
mpi_tests/test_transform.cpp
mpi_tests/test_multi_transform.cpp
mpi_tests/test_transpose.cpp
mpi_tests/test_transpose_gpu.cpp
)
target_link_libraries(run_mpi_tests PRIVATE gtest_main gtest_mpi)
target_link_libraries(run_mpi_tests PRIVATE gtest_main)
target_link_libraries(run_mpi_tests PRIVATE spfft_test ${SPFFT_EXTERNAL_LIBS})
target_include_directories(run_mpi_tests PRIVATE ${SPFFT_INCLUDE_DIRS} ${SPFFT_EXTERNAL_INCLUDE_DIRS})
endif()
Expand Down
216 changes: 216 additions & 0 deletions tests/gtest_mpi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
#include "gtest_mpi.hpp"
#include <gtest/gtest.h>
#include <iostream>
#include <memory>
#include <mpi.h>
#include <stdexcept>
#include <vector>

namespace gtest_mpi {

namespace {

class MPIListener : public testing::EmptyTestEventListener {
public:
using UnitTest = testing::UnitTest;
using TestCase = testing::TestCase;
using TestInfo = testing::TestInfo;
using TestPartResult = testing::TestPartResult;
using TestSuite = testing::TestSuite;

MPIListener(testing::TestEventListener *listener)
: listener_(listener), comm_(MPI_COMM_WORLD), gather_called_(false) {
MPI_Comm_dup(MPI_COMM_WORLD, &comm_);
int rank;
MPI_Comm_rank(comm_, &rank);
if (rank != 0)
listener_.reset();
}

void OnTestProgramStart(const UnitTest &u) override {
if (listener_)
listener_->OnTestProgramStart(u);
}

void OnTestProgramEnd(const UnitTest &u) override {
if (listener_)
listener_->OnTestProgramEnd(u);
}

void OnTestStart(const TestInfo &test_info) override {
gather_called_ = false;
if (listener_)
listener_->OnTestStart(test_info);
}

void OnTestPartResult(const TestPartResult &test_part_result) override {
if (listener_) {
listener_->OnTestPartResult(test_part_result);
} else if (test_part_result.type() == TestPartResult::Type::kFatalFailure ||
test_part_result.type() ==
TestPartResult::Type::kNonFatalFailure) {
std::size_t fileIndex = strings_.size();
strings_ += test_part_result.file_name();
strings_ += '\0';

std::size_t messageIndex = strings_.size();
strings_ += test_part_result.message();
strings_ += '\0';

infos_.emplace_back(ResultInfo{test_part_result.type(), fileIndex,
test_part_result.line_number(),
messageIndex});
}
}

void OnTestEnd(const TestInfo &test_info) override {
if(!gather_called_){
std::cerr << "Missing GTEST_MPI_GUARD in test case!" << std::endl;
throw std::runtime_error("Missing GTEST_MPI_GUARD in test case!");
}

if (listener_)
listener_->OnTestEnd(test_info);
}

void OnTestIterationStart(const UnitTest &u, int it) override {
if (listener_)
listener_->OnTestIterationStart(u, it);
}

void OnEnvironmentsSetUpStart(const UnitTest &u) override {
if (listener_)
listener_->OnEnvironmentsSetUpStart(u);
}

void OnEnvironmentsSetUpEnd(const UnitTest &u) override {
if (listener_)
listener_->OnEnvironmentsSetUpEnd(u);
}

void OnTestSuiteStart(const TestSuite &t) override {
if (listener_)
listener_->OnTestSuiteStart(t);
}

void OnTestDisabled(const TestInfo &t) override {
if (listener_)
listener_->OnTestDisabled(t);
}
void OnTestSuiteEnd(const TestSuite &t) override {
if (listener_)
listener_->OnTestSuiteEnd(t);
}

void OnEnvironmentsTearDownStart(const UnitTest &u) override {
if (listener_)
listener_->OnEnvironmentsTearDownStart(u);
}

void OnEnvironmentsTearDownEnd(const UnitTest &u) override {
if (listener_)
listener_->OnEnvironmentsTearDownEnd(u);
}

void OnTestIterationEnd(const UnitTest &u, int it) override {
if (listener_)
listener_->OnTestIterationEnd(u, it);
}

void GatherPartResults() {
gather_called_ = true;
int rank, n_proc;
MPI_Comm_rank(comm_, &rank);
MPI_Comm_size(comm_, &n_proc);

if (rank == 0) {
decltype(infos_) remoteInfos;
decltype(strings_) remoteStrings;
for (int r = 1; r < n_proc; ++r) {
MPI_Status status;
int count;

// Result infos
MPI_Probe(r, 0, comm_, &status);
MPI_Get_count(&status, MPI_CHAR, &count);
auto numResults = static_cast<std::size_t>(count) /
sizeof(decltype(remoteInfos)::value_type);
remoteInfos.resize(numResults);
MPI_Recv(remoteInfos.data(), count, MPI_BYTE, r, 0, comm_,
MPI_STATUS_IGNORE);

// Only continue if any results
if (numResults) {
// Get strings
MPI_Probe(r, 0, comm_, &status);
MPI_Get_count(&status, MPI_CHAR, &count);
auto stringSize = static_cast<std::size_t>(count) /
sizeof(decltype(remoteStrings)::value_type);
remoteStrings.resize(stringSize);
MPI_Recv(&remoteStrings[0], count, MPI_BYTE, r, 0, comm_,
MPI_STATUS_IGNORE);

// Create error for every remote fail
for (const auto &info : remoteInfos) {
if (info.type == TestPartResult::Type::kFatalFailure ||
info.type == TestPartResult::Type::kNonFatalFailure) {
ADD_FAILURE_AT(&remoteStrings[info.fileIndex], info.lineNumber)
<< "Rank " << r << ": " << &remoteStrings[info.messageIndex];
}
}
}
}
} else {
MPI_Send(infos_.data(),
infos_.size() * sizeof(decltype(infos_)::value_type), MPI_BYTE,
0, 0, comm_);

// Only send string if results exist
if (infos_.size()) {
MPI_Send(strings_.data(),
strings_.size() * sizeof(decltype(strings_)::value_type),
MPI_BYTE, 0, 0, comm_);
}
}

infos_.clear();
strings_.clear();
}

private:
struct ResultInfo {
TestPartResult::Type type;
std::size_t fileIndex;
int lineNumber;
std::size_t messageIndex;
};

std::unique_ptr<testing::TestEventListener> listener_;
MPI_Comm comm_;
bool gather_called_;

std::vector<ResultInfo> infos_;
std::string strings_;
};

MPIListener *globalMPIListener = nullptr;

} // namespace

void InitGoogleTestMPI(int *argc, char **argv) {

::testing::InitGoogleTest(argc, argv);

auto &test_listeners = ::testing::UnitTest::GetInstance()->listeners();

globalMPIListener = new MPIListener(
test_listeners.Release(test_listeners.default_result_printer()));

test_listeners.Append(globalMPIListener);
}

TestGuard CreateTestGuard() {
return TestGuard{[]() { globalMPIListener->GatherPartResults(); }};
}

} // namespace gtest_mpi
Loading

0 comments on commit 3cd5351

Please sign in to comment.