file(GLOB EWALD_CUDA_SOURCES ../*.cu)
endif()
-gmx_add_unit_test(EwaldUnitTests ewald-test
+gmx_add_unit_test(EwaldUnitTests ewald-test HARDWARE_DETECTION
${EWALD_TEST_SOURCES} ${EWALD_CUDA_SOURCES})
continue;
}
- const auto contextsToTest = pmeEnv->getHardwareContexts(mode.first);
+ const auto contextsToTest = getPmeTestEnv()->getHardwareContexts(mode.first);
for (const auto &context : contextsToTest)
{
/* Describing the test uniquely */
{
gridOrderingsToTest[GridOrdering::XYZ] = "XYZ";
}
- const auto contextsToTest = pmeEnv->getHardwareContexts(mode.first);
+ const auto contextsToTest = getPmeTestEnv()->getHardwareContexts(mode.first);
for (const auto &gridOrdering : gridOrderingsToTest)
{
for (const auto &context : contextsToTest)
continue;
}
- const auto contextsToTest = pmeEnv->getHardwareContexts(mode.first);
+ const auto contextsToTest = getPmeTestEnv()->getHardwareContexts(mode.first);
for (const auto &context : contextsToTest)
{
for (const auto &option : optionsToTest)
#include "gromacs/taskassignment/hardwareassign.h"
#include "gromacs/utility/exceptions.h"
#include "gromacs/utility/loggerbuilder.h"
+#include "gromacs/utility/unique_cptr.h"
namespace gmx
{
namespace test
{
-//! This constructs the test environment
-PmeTestEnvironment * const pmeEnv = (PmeTestEnvironment *)::testing::AddGlobalTestEnvironment(new PmeTestEnvironment);
+/* Implements the "construct on first use" idiom to avoid any static
+ * initialization order fiasco.
+ *
+ * Note that thread-safety of the initialization is guaranteed by the
+ * C++11 language standard.
+ *
+ * The pointer itself (not the memory it points to) has no destructor,
+ * so there is no deinitialization issue. See
+ * https://isocpp.org/wiki/faq/ctors for discussion of alternatives
+ * and trade-offs. */
+const PmeTestEnvironment *getPmeTestEnv()
+{
+ static PmeTestEnvironment *pmeTestEnvironment = nullptr;
+ if (pmeTestEnvironment == nullptr)
+ {
+ // Ownership of the TestEnvironment is taken by GoogleTest, so nothing can leak
+ pmeTestEnvironment = static_cast<PmeTestEnvironment *>(::testing::AddGlobalTestEnvironment(new PmeTestEnvironment));
+ }
+ return pmeTestEnvironment;
+}
+
+void callAddGlobalTestEnvironment()
+{
+ getPmeTestEnv();
+}
//! Simple hardware initialization
-void PmeTestEnvironment::hardwareInit()
+static gmx_hw_info_t *hardwareInit()
{
unique_cptr<t_commrec, done_commrec> commrec(init_commrec());
gmx_init_intranode_counters(commrec.get());
LoggerBuilder builder;
LoggerOwner logOwner(builder.build());
MDLogger log(logOwner.logger());
- hardwareInfo_.reset(gmx_detect_hardware(log, commrec.get()));
+ return gmx_detect_hardware(log, commrec.get());
}
void PmeTestEnvironment::SetUp()
TestHardwareContext emptyContext("", nullptr);
hardwareContextsByMode_[CodePath::CPU].push_back(emptyContext);
- hardwareInit();
+ hardwareInfo_ = hardwareInit();
// Constructing contexts for all compatible GPUs - will be empty on non-GPU builds
TestHardwareContexts gpuContexts;
char stmp[200] = {};
get_gpu_device_info_string(stmp, hardwareInfo_->gpu_info, gpuIndex);
std::string description = "(GPU " + std::string(stmp) + ") ";
- auto *gpuInfo = reinterpret_cast<gmx_device_info_t *>(reinterpret_cast<char *>(hardwareInfo_->gpu_info.gpu_dev) + gpuIndex * sizeof_gpu_dev_info());
- //TODO move previous line to gpu_utils and reuse in hardwareassign.cpp
- gpuContexts.emplace_back(TestHardwareContext(description.c_str(), gpuInfo));
+ gpuContexts.emplace_back(TestHardwareContext(description.c_str(), getDeviceInfo(hardwareInfo_->gpu_info, gpuIndex)));
}
#if GMX_GPU == GMX_GPU_CUDA
hardwareContextsByMode_[CodePath::CUDA] = gpuContexts;
#endif
}
+void PmeTestEnvironment::TearDown()
+{
+ gmx_hardware_info_free(hardwareInfo_);
+}
+
}
}
#include "gromacs/hardware/detecthardware.h"
#include "gromacs/hardware/gpu_hw_info.h"
-#include "gromacs/utility/unique_cptr.h"
namespace gmx
{
/*! \internal \brief
* This class performs one-time test initialization (enumerating the hardware)
*/
+// cppcheck-suppress noConstructor
class PmeTestEnvironment : public ::testing::Environment
{
private:
//! General hardware info
- unique_cptr<gmx_hw_info_t, gmx_hardware_info_free> hardwareInfo_;
+ gmx_hw_info_t *hardwareInfo_;
//! Storage of hardware contexts
- std::map<CodePath, TestHardwareContexts> hardwareContextsByMode_;
- //! Simple GPU initialization, allowing for PME to work on GPU
- void hardwareInit();
+ std::map<CodePath, TestHardwareContexts> hardwareContextsByMode_;
public:
- //! Default
- ~PmeTestEnvironment() = default;
//! This is called by GTest framework once to query the hardware
- void SetUp();
+ virtual void SetUp();
+ //! This is called by GTest framework once to clean up
+ virtual void TearDown();
//! Get available hardware contexts for given code path
- const TestHardwareContexts &getHardwareContexts(CodePath mode){return hardwareContextsByMode_.at(mode); }
+ const TestHardwareContexts &getHardwareContexts(CodePath mode) const {return hardwareContextsByMode_.at(mode); }
};
-//! The test environment
-extern PmeTestEnvironment * const pmeEnv;
+//! Get the test environment
+const PmeTestEnvironment *getPmeTestEnv();
+
+/*! \brief This constructs the test environment during setup of the
+ * unit test so that they can use the hardware context. */
+void callAddGlobalTestEnvironment();
+
}
}
#endif
#
# This file is part of the GROMACS molecular simulation package.
#
-# Copyright (c) 2011,2012,2013,2014,2015,2016, by the GROMACS development team, led by
+# Copyright (c) 2011,2012,2013,2014,2015,2016,2017, by the GROMACS development team, led by
# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
# and including many others, as listed in the AUTHORS file in the
# top-level source directory and at http://www.gromacs.org.
function (gmx_add_gtest_executable EXENAME)
if (GMX_BUILD_UNITTESTS AND BUILD_TESTING)
- set(_options MPI)
+ set(_options MPI HARDWARE_DETECTION)
cmake_parse_arguments(ARG "${_options}" "" "" ${ARGN})
set(_source_files ${ARG_UNPARSED_ARGUMENTS})
list(APPEND EXTRA_COMPILE_DEFINITIONS
TEST_USES_MPI=true)
endif()
+ if (ARG_HARDWARE_DETECTION)
+ list(APPEND EXTRA_COMPILE_DEFINITIONS
+ TEST_USES_HARDWARE_DETECTION=true)
+ endif()
include_directories(BEFORE SYSTEM ${GMOCK_INCLUDE_DIRS})
add_executable(${EXENAME} ${UNITTEST_TARGET_OPTIONS}
//! \cond internal
void initTestUtils(const char *dataPath, const char *tempPath, bool usesMpi,
- int *argc, char ***argv)
+ bool usesHardwareDetection, int *argc, char ***argv)
{
#ifndef NDEBUG
gmx_feenableexcept();
finalizeForCommandLine();
std::exit(1);
}
+ if (usesHardwareDetection)
+ {
+ callAddGlobalTestEnvironment();
+ }
g_testContext.reset(new TestProgramContext(context));
setProgramContext(g_testContext.get());
// Use the default finder that does not respect GMXLIB, since the tests
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2016,2017, by the GROMACS development team, led by
* Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
* and including many others, as listed in the AUTHORS file in the
* top-level source directory and at http://www.gromacs.org.
* \ingroup module_testutils
*/
void initTestUtils(const char *dataPath, const char *tempPath, bool usesMpi,
- int *argc, char ***argv);
+ bool usesHardwareDetection, int *argc, char ***argv);
/*! \internal
* \brief
void finalizeTestUtils();
//! \endcond
+/*! \brief Declare a function that all unit test implementations can use
+ * to set up any environment that they need.
+ *
+ * When registering the unit test in CMake, the HARDWARE_DETECTION
+ * flag requires that the code for that unit test implements this
+ * function. Otherwise, a default stub implementation is provided.
+ *
+ * This approach conforms to the recommendation by GoogleTest to
+ * arrange for the code that sets up the global test environment to be
+ * called from main, rather than potentially rely on brittle static
+ * initialization order. */
+void callAddGlobalTestEnvironment();
+
} // namespace test
} // namespace gmx
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2010,2011,2012,2013,2014,2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2017, by the GROMACS development team, led by
* Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
* and including many others, as listed in the AUTHORS file in the
* top-level source directory and at http://www.gromacs.org.
#define TEST_USES_MPI false
#endif
+#ifndef TEST_USES_HARDWARE_DETECTION
+//! Whether the test expects/supports running with knowledge of the hardware.
+#define TEST_USES_HARDWARE_DETECTION false
+namespace gmx
+{
+namespace test
+{
+//! Implement a stub definition for tests that don't ask for a real one.
+void callAddGlobalTestEnvironment() {};
+}
+}
+#endif
+
/*! \brief
* Initializes unit testing for \ref module_testutils.
*/
int main(int argc, char *argv[])
{
// Calls ::testing::InitGoogleMock()
- ::gmx::test::initTestUtils(TEST_DATA_PATH, TEST_TEMP_PATH, TEST_USES_MPI,
+ ::gmx::test::initTestUtils(TEST_DATA_PATH, TEST_TEMP_PATH,
+ TEST_USES_MPI, TEST_USES_HARDWARE_DETECTION,
&argc, &argv);
int errcode = RUN_ALL_TESTS();
::gmx::test::finalizeTestUtils();