Merge pull request #2420 from rbberger/mpi_tests
Add MPI-enabled unit tests (C/C++ only)
This commit is contained in:
@ -62,15 +62,18 @@ used.
|
||||
|
||||
**-in file**
|
||||
|
||||
Specify a file to use as an input script. This is an optional switch
|
||||
when running LAMMPS in one-partition mode. If it is not specified,
|
||||
LAMMPS reads its script from standard input, typically from a script
|
||||
via I/O redirection; e.g. lmp_linux < in.run. I/O redirection should
|
||||
also work in parallel, but if it does not (in the unlikely case that
|
||||
an MPI implementation does not support it), then use the -in flag.
|
||||
Specify a file to use as an input script. This is an optional but
|
||||
recommended switch when running LAMMPS in one-partition mode. If it
|
||||
is not specified, LAMMPS reads its script from standard input, typically
|
||||
from a script via I/O redirection; e.g. lmp_linux < in.run.
|
||||
With many MPI implementations I/O redirection also works in parallel,
|
||||
but using the -in flag will always work.
|
||||
|
||||
Note that this is a required switch when running LAMMPS in
|
||||
multi-partition mode, since multiple processors cannot all read from
|
||||
stdin.
|
||||
stdin concurrently. The file name may be "none" for starting
|
||||
multi-partition calculations without reading an initial input file
|
||||
from the library interface.
|
||||
|
||||
----------
|
||||
|
||||
@ -296,7 +299,7 @@ command-line option.
|
||||
**-pscreen file**
|
||||
|
||||
Specify the base name for the partition screen file, so partition N
|
||||
writes screen information to file.N. If file is none, then no
|
||||
writes screen information to file.N. If file is "none", then no
|
||||
partition screen files are created. This overrides the filename
|
||||
specified in the -screen command-line option. This option is useful
|
||||
when working with large numbers of partitions, allowing the partition
|
||||
|
||||
@ -2979,6 +2979,8 @@ Subclassed
|
||||
subcutoff
|
||||
subcycle
|
||||
subcycling
|
||||
subhi
|
||||
sublo
|
||||
Subramaniyan
|
||||
subscripted
|
||||
subscripting
|
||||
|
||||
@ -189,8 +189,15 @@ void Input::file()
|
||||
// if line ends in continuation char '&', concatenate next line
|
||||
|
||||
if (me == 0) {
|
||||
|
||||
m = 0;
|
||||
while (1) {
|
||||
|
||||
if (infile == nullptr) {
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (maxline-m < 2) reallocate(line,maxline,0);
|
||||
|
||||
// end of file reached, so break
|
||||
|
||||
@ -456,6 +456,7 @@ LAMMPS::LAMMPS(int narg, char **arg, MPI_Comm communicator) :
|
||||
|
||||
if (universe->me == 0) {
|
||||
if (inflag == 0) infile = stdin;
|
||||
else if (strcmp(arg[inflag], "none") == 0) infile = stdin;
|
||||
else infile = fopen(arg[inflag],"r");
|
||||
if (infile == nullptr)
|
||||
error->one(FLERR,fmt::format("Cannot open input script {}: {}",
|
||||
@ -530,10 +531,12 @@ LAMMPS::LAMMPS(int narg, char **arg, MPI_Comm communicator) :
|
||||
str, utils::getsyserror()));
|
||||
}
|
||||
|
||||
infile = fopen(arg[inflag],"r");
|
||||
if (infile == nullptr)
|
||||
error->one(FLERR,fmt::format("Cannot open input script {}: {}",
|
||||
arg[inflag], utils::getsyserror()));
|
||||
if (strcmp(arg[inflag], "none") != 0) {
|
||||
infile = fopen(arg[inflag],"r");
|
||||
if (infile == nullptr)
|
||||
error->one(FLERR,fmt::format("Cannot open input script {}: {}",
|
||||
arg[inflag], utils::getsyserror()));
|
||||
}
|
||||
}
|
||||
|
||||
// screen and logfile messages for universe and world
|
||||
@ -1103,7 +1106,7 @@ void _noopt LAMMPS::help()
|
||||
"List of command line options supported by this LAMMPS executable:\n\n"
|
||||
"-echo none/screen/log/both : echoing of input script (-e)\n"
|
||||
"-help : print this help message (-h)\n"
|
||||
"-in filename : read input from file, not stdin (-i)\n"
|
||||
"-in none/filename : read input from file or stdin (default) (-i)\n"
|
||||
"-kokkos on/off ... : turn KOKKOS mode on or off (-k)\n"
|
||||
"-log none/filename : where to send log output (-l)\n"
|
||||
"-mpicolor color : which exe in a multi-exe mpirun cmd (-m)\n"
|
||||
|
||||
@ -839,9 +839,19 @@ not recognized, the function returns -1.
|
||||
* - box_exist
|
||||
- 1 if the simulation box is defined, 0 if not.
|
||||
See :doc:`create_box`.
|
||||
* - nthreads
|
||||
- Number of requested OpenMP threads for LAMMPS' execution
|
||||
* - triclinic
|
||||
- 1 if the the simulation box is triclinic, 0 if orthogonal.
|
||||
See :doc:`change_box`.
|
||||
* - universe_rank
|
||||
- MPI rank on LAMMPS' universe communicator (0 <= universe_rank < universe_size)
|
||||
* - universe_size
|
||||
- Number of ranks on LAMMPS' universe communicator (world_size <= universe_size)
|
||||
* - world_rank
|
||||
- MPI rank on LAMMPS' world communicator (0 <= world_rank < world_size)
|
||||
* - world_size
|
||||
- Number of ranks on LAMMPS' world communicator
|
||||
|
||||
.. _extract_system_sizes:
|
||||
|
||||
@ -925,6 +935,12 @@ int lammps_extract_setting(void *handle, const char *keyword)
|
||||
if (strcmp(keyword,"box_exist") == 0) return lmp->domain->box_exist;
|
||||
if (strcmp(keyword,"triclinic") == 0) return lmp->domain->triclinic;
|
||||
|
||||
if (strcmp(keyword,"universe_rank") == 0) return lmp->universe->me;
|
||||
if (strcmp(keyword,"universe_size") == 0) return lmp->universe->nprocs;
|
||||
if (strcmp(keyword,"world_rank") == 0) return lmp->comm->me;
|
||||
if (strcmp(keyword,"world_size") == 0) return lmp->comm->nprocs;
|
||||
if (strcmp(keyword,"nthreads") == 0) return lmp->comm->nthreads;
|
||||
|
||||
if (strcmp(keyword,"nlocal") == 0) return lmp->atom->nlocal;
|
||||
if (strcmp(keyword,"nghost") == 0) return lmp->atom->nghost;
|
||||
if (strcmp(keyword,"nall") == 0) return lmp->atom->nlocal+lmp->atom->nghost;
|
||||
@ -979,6 +995,10 @@ int lammps_extract_global_datatype(void *handle, const char *name)
|
||||
|
||||
if (strcmp(name,"boxlo") == 0) return LAMMPS_DOUBLE;
|
||||
if (strcmp(name,"boxhi") == 0) return LAMMPS_DOUBLE;
|
||||
if (strcmp(name,"sublo") == 0) return LAMMPS_DOUBLE;
|
||||
if (strcmp(name,"subhi") == 0) return LAMMPS_DOUBLE;
|
||||
if (strcmp(name,"sublo_lambda") == 0) return LAMMPS_DOUBLE;
|
||||
if (strcmp(name,"subhi_lambda") == 0) return LAMMPS_DOUBLE;
|
||||
if (strcmp(name,"boxxlo") == 0) return LAMMPS_DOUBLE;
|
||||
if (strcmp(name,"boxxhi") == 0) return LAMMPS_DOUBLE;
|
||||
if (strcmp(name,"boxylo") == 0) return LAMMPS_DOUBLE;
|
||||
@ -1143,6 +1163,22 @@ report the "native" data type. The following tables are provided:
|
||||
- double
|
||||
- 1
|
||||
- upper box boundary in z-direction. See :doc:`create_box`.
|
||||
* - sublo
|
||||
- double
|
||||
- 3
|
||||
- subbox lower boundaries
|
||||
* - subhi
|
||||
- double
|
||||
- 3
|
||||
- subbox upper boundaries
|
||||
* - sublo_lambda
|
||||
- double
|
||||
- 3
|
||||
- subbox lower boundaries in fractional coordinates (for triclinic cells)
|
||||
* - subhi_lambda
|
||||
- double
|
||||
- 3
|
||||
- subbox upper boundaries in fractional coordinates (for triclinic cells)
|
||||
* - periodicity
|
||||
- int
|
||||
- 3
|
||||
@ -1334,6 +1370,10 @@ void *lammps_extract_global(void *handle, const char *name)
|
||||
|
||||
if (strcmp(name,"boxlo") == 0) return (void *) lmp->domain->boxlo;
|
||||
if (strcmp(name,"boxhi") == 0) return (void *) lmp->domain->boxhi;
|
||||
if (strcmp(name,"sublo") == 0) return (void *) lmp->domain->sublo;
|
||||
if (strcmp(name,"subhi") == 0) return (void *) lmp->domain->subhi;
|
||||
if (strcmp(name,"sublo_lambda") == 0) return (void *) lmp->domain->sublo_lamda;
|
||||
if (strcmp(name,"subhi_lambda") == 0) return (void *) lmp->domain->subhi_lamda;
|
||||
if (strcmp(name,"boxxlo") == 0) return (void *) &lmp->domain->boxlo[0];
|
||||
if (strcmp(name,"boxxhi") == 0) return (void *) &lmp->domain->boxhi[0];
|
||||
if (strcmp(name,"boxylo") == 0) return (void *) &lmp->domain->boxlo[1];
|
||||
@ -1809,36 +1849,44 @@ void *lammps_extract_fix(void *handle, char *id, int style, int type,
|
||||
\verbatim embed:rst
|
||||
|
||||
This function returns a pointer to data from a LAMMPS :doc:`variable`
|
||||
identified by its name. The variable must be either an *equal*\ -style
|
||||
compatible or an *atom*\ -style variable. Variables of style *internal*
|
||||
identified by its name. When the variable is either an *equal*\ -style
|
||||
compatible or an *atom*\ -style variable the variable is evaluated and
|
||||
the corresponding value(s) returned. Variables of style *internal*
|
||||
are compatible with *equal*\ -style variables and so are *python*\
|
||||
-style variables, if they return a numeric value. The function returns
|
||||
-style variables, if they return a numeric value. For other
|
||||
variable styles their string value is returned. The function returns
|
||||
``NULL`` when a variable of the provided *name* is not found or of an
|
||||
incompatible style. The *group* argument is only used for *atom*\
|
||||
-style variables and ignored otherwise. If set to ``NULL`` when
|
||||
extracting data from and *atom*\ -style variable, the group is assumed
|
||||
to be "all".
|
||||
|
||||
.. note::
|
||||
When requesting data from an *equal*\ -style or compatible variable
|
||||
this function allocates storage for a single double value, copies the
|
||||
returned value to it, and returns a pointer to the location of the
|
||||
copy. Therefore the allocated storage needs to be freed after its
|
||||
use to avoid a memory leak. Example:
|
||||
|
||||
When requesting data from an *equal*\ -style or compatible variable
|
||||
this function allocates storage for a single double value, copies the
|
||||
returned value to it, and returns a pointer to the location of the
|
||||
copy. Therefore the allocated storage needs to be freed after its
|
||||
use to avoid a memory leak. Example:
|
||||
.. code-block:: c
|
||||
|
||||
.. code-block:: c
|
||||
double *dptr = (double *) lammps_extract_variable(handle,name,NULL);
|
||||
double value = *dptr;
|
||||
lammps_free((void *)dptr);
|
||||
|
||||
double *dptr = (double *) lammps_extract_variable(handle,name,NULL);
|
||||
double value = *dptr;
|
||||
lammps_free((void *)dptr);
|
||||
For *atom*\ -style variables the data returned is a pointer to an
|
||||
allocated block of storage of double of the length ``atom->nlocal``.
|
||||
Since the data is returned a copy, the location will persist, but its
|
||||
content will not be updated, in case the variable is re-evaluated.
|
||||
To avoid a memory leak this pointer needs to be freed after use in
|
||||
the calling program.
|
||||
|
||||
For *atom*\ -style variables the data returned is a pointer to an
|
||||
allocated block of storage of double of the length ``atom->nlocal``.
|
||||
To avoid a memory leak, also this pointer needs to be freed after use.
|
||||
For other variable styles the returned pointer needs to be cast to
|
||||
a char pointer.
|
||||
|
||||
Since the data is returned as copies, the location will persist, but its
|
||||
values will not be updated, in case the variable is re-evaluated.
|
||||
.. code-block:: c
|
||||
|
||||
const char *cptr = (const char *) lammps_extract_variable(handle,name,NULL);
|
||||
printf("The value of variable %s is %s\n", name, cptr);
|
||||
|
||||
.. note::
|
||||
|
||||
@ -1856,7 +1904,7 @@ values will not be updated, in case the variable is re-evaluated.
|
||||
* \return pointer (cast to ``void *``) to the location of the
|
||||
* requested data or ``NULL`` if not found. */
|
||||
|
||||
void *lammps_extract_variable(void *handle, char *name, char *group)
|
||||
void *lammps_extract_variable(void *handle, const char *name, const char *group)
|
||||
{
|
||||
LAMMPS *lmp = (LAMMPS *) handle;
|
||||
|
||||
@ -1869,9 +1917,7 @@ void *lammps_extract_variable(void *handle, char *name, char *group)
|
||||
double *dptr = (double *) malloc(sizeof(double));
|
||||
*dptr = lmp->input->variable->compute_equal(ivar);
|
||||
return (void *) dptr;
|
||||
}
|
||||
|
||||
if (lmp->input->variable->atomstyle(ivar)) {
|
||||
} else if (lmp->input->variable->atomstyle(ivar)) {
|
||||
if (group == nullptr) group = (char *)"all";
|
||||
int igroup = lmp->group->find(group);
|
||||
if (igroup < 0) return nullptr;
|
||||
@ -1879,6 +1925,8 @@ void *lammps_extract_variable(void *handle, char *name, char *group)
|
||||
double *vector = (double *) malloc(nlocal*sizeof(double));
|
||||
lmp->input->variable->compute_atom(ivar,igroup,vector,1,0);
|
||||
return (void *) vector;
|
||||
} else {
|
||||
return lmp->input->variable->retrieve(name);
|
||||
}
|
||||
}
|
||||
END_CAPTURE
|
||||
|
||||
@ -140,7 +140,7 @@ void *lammps_extract_atom(void *handle, const char *name);
|
||||
|
||||
void *lammps_extract_compute(void *handle, char *id, int, int);
|
||||
void *lammps_extract_fix(void *handle, char *, int, int, int, int);
|
||||
void *lammps_extract_variable(void *handle, char *, char *);
|
||||
void *lammps_extract_variable(void *handle, const char *, const char *);
|
||||
int lammps_set_variable(void *, char *, char *);
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
|
||||
@ -158,7 +158,6 @@ void Universe::reorder(char *style, char *arg)
|
||||
void Universe::add_world(char *str)
|
||||
{
|
||||
int n,nper;
|
||||
char *ptr;
|
||||
|
||||
n = 1;
|
||||
nper = 0;
|
||||
@ -171,28 +170,23 @@ void Universe::add_world(char *str)
|
||||
|
||||
// str may not be empty and may only consist of digits or 'x'
|
||||
|
||||
size_t len = strlen(str);
|
||||
if (len < 1) valid = false;
|
||||
for (size_t i=0; i < len; ++i)
|
||||
if (isdigit(str[i]) || str[i] == 'x') continue;
|
||||
else valid = false;
|
||||
std::string part(str);
|
||||
if (part.size() == 0) valid = false;
|
||||
if (part.find_first_not_of("0123456789x") != std::string::npos) valid = false;
|
||||
|
||||
if (valid) {
|
||||
if ((ptr = strchr(str,'x')) != nullptr) {
|
||||
std::size_t found = part.find_first_of("x");
|
||||
|
||||
// 'x' may not be the first or last character
|
||||
// 'x' may not be the first or last character
|
||||
|
||||
if (ptr == str) {
|
||||
valid = false;
|
||||
} else if (strlen(str) == len-1) {
|
||||
valid = false;
|
||||
} else {
|
||||
*ptr = '\0';
|
||||
n = atoi(str);
|
||||
nper = atoi(ptr+1);
|
||||
*ptr = 'x';
|
||||
}
|
||||
} else nper = atoi(str);
|
||||
if ((found == 0) || (found == (part.size() - 1))) {
|
||||
valid = false;
|
||||
} else if (found == std::string::npos) {
|
||||
nper = atoi(part.c_str());
|
||||
} else {
|
||||
n = atoi(part.substr(0,found).c_str());
|
||||
nper = atoi(part.substr(found-1).c_str());\
|
||||
}
|
||||
}
|
||||
|
||||
// require minimum of 1 partition with 1 processor
|
||||
|
||||
@ -1,5 +1,21 @@
|
||||
include(GTest)
|
||||
|
||||
if(BUILD_MPI)
|
||||
function(add_mpi_test)
|
||||
set(MPI_TEST_NUM_PROCS 1)
|
||||
set(MPI_TEST_WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
cmake_parse_arguments(MPI_TEST "" "NAME;NUM_PROCS;WORKING_DIRECTORY" "COMMAND" ${ARGN})
|
||||
list(GET MPI_TEST_COMMAND 0 EXECUTABLE)
|
||||
list(REMOVE_AT MPI_TEST_COMMAND 0)
|
||||
set(ARGS ${MPI_TEST_COMMAND})
|
||||
add_test(NAME ${MPI_TEST_NAME}
|
||||
WORKING_DIRECTORY ${MPI_TEST_WORKING_DIRECTORY}
|
||||
COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${MPI_TEST_NUM_PROCS} ${MPIEXEC_PREFLAGS}
|
||||
${EXECUTABLE} ${MPIEXEC_POSTFLAGS} ${ARGS}
|
||||
)
|
||||
endfunction()
|
||||
endif()
|
||||
|
||||
add_subdirectory(utils)
|
||||
add_subdirectory(formats)
|
||||
add_subdirectory(commands)
|
||||
|
||||
@ -55,3 +55,10 @@ add_executable(test_library_config test_library_config.cpp test_main.cpp)
|
||||
target_link_libraries(test_library_config PRIVATE lammps GTest::GTest GTest::GMock)
|
||||
target_compile_definitions(test_library_config PRIVATE ${TEST_CONFIG_DEFS})
|
||||
add_test(LibraryConfig test_library_config)
|
||||
|
||||
if (BUILD_MPI)
|
||||
add_executable(test_library_mpi test_library_mpi.cpp)
|
||||
target_link_libraries(test_library_mpi PRIVATE lammps GTest::GTest GTest::GMock)
|
||||
target_compile_definitions(test_library_mpi PRIVATE ${TEST_CONFIG_DEFS})
|
||||
add_mpi_test(NAME LibraryMPI NUM_PROCS 4 COMMAND $<TARGET_FILE:test_library_mpi>)
|
||||
endif()
|
||||
|
||||
341
unittest/c-library/test_library_mpi.cpp
Normal file
341
unittest/c-library/test_library_mpi.cpp
Normal file
@ -0,0 +1,341 @@
|
||||
// unit tests for checking LAMMPS configuration settings through the library interface
|
||||
|
||||
#define LAMMPS_LIB_MPI 1
|
||||
#include "lammps.h"
|
||||
#include "library.h"
|
||||
#include "timer.h"
|
||||
#include <string>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "../testing/test_mpi_main.h"
|
||||
|
||||
using ::testing::ExitedWithCode;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::StartsWith;
|
||||
using ::testing::StrEq;
|
||||
|
||||
TEST(MPI, global_box)
|
||||
{
|
||||
int nprocs, me;
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &me);
|
||||
EXPECT_EQ(nprocs, 4);
|
||||
EXPECT_GT(me, -1);
|
||||
EXPECT_LT(me, 5);
|
||||
|
||||
double boxlo[3];
|
||||
double boxhi[3];
|
||||
double xy = 0.0;
|
||||
double yz = 0.0;
|
||||
double xz = 0.0;
|
||||
int pflags[3];
|
||||
int boxflag;
|
||||
|
||||
::testing::internal::CaptureStdout();
|
||||
const char *args[] = {"LAMMPS_test", "-log", "none", "-echo", "screen", "-nocite"};
|
||||
char **argv = (char **)args;
|
||||
int argc = sizeof(args) / sizeof(char *);
|
||||
void *lmp = lammps_open(argc, argv, MPI_COMM_WORLD, nullptr);
|
||||
lammps_command(lmp, "units lj");
|
||||
lammps_command(lmp, "atom_style atomic");
|
||||
lammps_command(lmp, "region box block 0 2 0 2 0 2");
|
||||
lammps_command(lmp, "create_box 1 box");
|
||||
|
||||
lammps_extract_box(lmp, boxlo, boxhi, &xy, &yz, &xz, pflags, &boxflag);
|
||||
::testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_EQ(boxlo[0], 0.0);
|
||||
EXPECT_EQ(boxlo[1], 0.0);
|
||||
EXPECT_EQ(boxlo[2], 0.0);
|
||||
|
||||
EXPECT_EQ(boxhi[0], 2.0);
|
||||
EXPECT_EQ(boxhi[1], 2.0);
|
||||
EXPECT_EQ(boxhi[2], 2.0);
|
||||
|
||||
::testing::internal::CaptureStdout();
|
||||
lammps_close(lmp);
|
||||
::testing::internal::GetCapturedStdout();
|
||||
};
|
||||
|
||||
TEST(MPI, sub_box)
|
||||
{
|
||||
int nprocs, me;
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &me);
|
||||
EXPECT_EQ(nprocs, 4);
|
||||
EXPECT_GT(me, -1);
|
||||
EXPECT_LT(me, 5);
|
||||
|
||||
double boxlo[3];
|
||||
double boxhi[3];
|
||||
double xy = 0.0;
|
||||
double yz = 0.0;
|
||||
double xz = 0.0;
|
||||
int pflags[3];
|
||||
int boxflag;
|
||||
|
||||
::testing::internal::CaptureStdout();
|
||||
const char *args[] = {"LAMMPS_test", "-log", "none", "-echo", "screen", "-nocite"};
|
||||
char **argv = (char **)args;
|
||||
int argc = sizeof(args) / sizeof(char *);
|
||||
void *lmp = lammps_open(argc, argv, MPI_COMM_WORLD, nullptr);
|
||||
lammps_command(lmp, "units lj");
|
||||
lammps_command(lmp, "atom_style atomic");
|
||||
lammps_command(lmp, "region box block 0 2 0 2 0 2");
|
||||
lammps_command(lmp, "create_box 1 box");
|
||||
|
||||
lammps_extract_box(lmp, boxlo, boxhi, &xy, &yz, &xz, pflags, &boxflag);
|
||||
::testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_EQ(boxlo[0], 0.0);
|
||||
EXPECT_EQ(boxlo[1], 0.0);
|
||||
EXPECT_EQ(boxlo[2], 0.0);
|
||||
|
||||
EXPECT_EQ(boxhi[0], 2.0);
|
||||
EXPECT_EQ(boxhi[1], 2.0);
|
||||
EXPECT_EQ(boxhi[2], 2.0);
|
||||
|
||||
double *sublo = (double *)lammps_extract_global(lmp, "sublo");
|
||||
double *subhi = (double *)lammps_extract_global(lmp, "subhi");
|
||||
|
||||
ASSERT_NE(sublo, nullptr);
|
||||
ASSERT_NE(subhi, nullptr);
|
||||
|
||||
EXPECT_GE(sublo[0], boxlo[0]);
|
||||
EXPECT_GE(sublo[1], boxlo[1]);
|
||||
EXPECT_GE(sublo[2], boxlo[2]);
|
||||
EXPECT_LE(subhi[0], boxhi[0]);
|
||||
EXPECT_LE(subhi[1], boxhi[1]);
|
||||
EXPECT_LE(subhi[2], boxhi[2]);
|
||||
|
||||
::testing::internal::CaptureStdout();
|
||||
lammps_command(lmp, "change_box all triclinic");
|
||||
::testing::internal::GetCapturedStdout();
|
||||
|
||||
sublo = (double *)lammps_extract_global(lmp, "sublo_lambda");
|
||||
subhi = (double *)lammps_extract_global(lmp, "subhi_lambda");
|
||||
ASSERT_NE(sublo, nullptr);
|
||||
ASSERT_NE(subhi, nullptr);
|
||||
|
||||
EXPECT_GE(sublo[0], 0.0);
|
||||
EXPECT_GE(sublo[1], 0.0);
|
||||
EXPECT_GE(sublo[2], 0.0);
|
||||
EXPECT_LE(subhi[0], 1.0);
|
||||
EXPECT_LE(subhi[1], 1.0);
|
||||
EXPECT_LE(subhi[2], 1.0);
|
||||
|
||||
::testing::internal::CaptureStdout();
|
||||
lammps_close(lmp);
|
||||
::testing::internal::GetCapturedStdout();
|
||||
};
|
||||
|
||||
TEST(MPI, split_comm)
|
||||
{
|
||||
int nprocs, me, color, key;
|
||||
MPI_Comm newcomm;
|
||||
lammps_mpi_init();
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &me);
|
||||
color = me % 2;
|
||||
key = me;
|
||||
|
||||
MPI_Comm_split(MPI_COMM_WORLD, color, key, &newcomm);
|
||||
|
||||
const char *args[] = {"LAMMPS_test", "-log", "none", "-echo", "screen", "-nocite"};
|
||||
char **argv = (char **)args;
|
||||
int argc = sizeof(args) / sizeof(char *);
|
||||
void *lmp = lammps_open(argc, argv, newcomm, nullptr);
|
||||
lammps_command(lmp, "units lj");
|
||||
lammps_command(lmp, "atom_style atomic");
|
||||
lammps_command(lmp, "region box block 0 2 0 2 0 2");
|
||||
lammps_command(lmp, "create_box 1 box");
|
||||
|
||||
MPI_Comm_size(newcomm, &nprocs);
|
||||
MPI_Comm_rank(newcomm, &me);
|
||||
EXPECT_EQ(nprocs, 2);
|
||||
EXPECT_GT(me, -1);
|
||||
EXPECT_LT(me, 2);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "universe_size"), nprocs);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "universe_rank"), me);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "world_size"), nprocs);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "world_rank"), me);
|
||||
|
||||
lammps_close(lmp);
|
||||
};
|
||||
|
||||
TEST(MPI, multi_partition)
|
||||
{
|
||||
FILE *fp;
|
||||
int nprocs, me;
|
||||
lammps_mpi_init();
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &me);
|
||||
|
||||
const char *args[] = {"LAMMPS_test", "-log", "none", "-partition", "2x2",
|
||||
"-echo", "screen", "-nocite", "-in", "none"};
|
||||
char **argv = (char **)args;
|
||||
int argc = sizeof(args) / sizeof(char *);
|
||||
void *lmp = lammps_open(argc, argv, MPI_COMM_WORLD, nullptr);
|
||||
|
||||
lammps_command(lmp, "units lj");
|
||||
lammps_command(lmp, "atom_style atomic");
|
||||
lammps_command(lmp, "region box block 0 2 0 2 0 2");
|
||||
lammps_command(lmp, "create_box 1 box");
|
||||
lammps_command(lmp, "variable partition universe 1 2");
|
||||
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "universe_size"), nprocs);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "universe_rank"), me);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "world_size"), nprocs / 2);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "world_rank"), me % 2);
|
||||
|
||||
char *part_id = (char *)lammps_extract_variable(lmp, "partition", nullptr);
|
||||
if (me < 2) {
|
||||
ASSERT_THAT(part_id, StrEq("1"));
|
||||
} else {
|
||||
ASSERT_THAT(part_id, StrEq("2"));
|
||||
}
|
||||
|
||||
lammps_close(lmp);
|
||||
};
|
||||
|
||||
class MPITest : public ::testing::Test {
|
||||
public:
|
||||
void command(const std::string &line) { lammps_command(lmp, line.c_str()); }
|
||||
|
||||
protected:
|
||||
const char *testbinary = "LAMMPSTest";
|
||||
void *lmp;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
const char *args[] = {testbinary, "-log", "none", "-echo", "screen", "-nocite"};
|
||||
char **argv = (char **)args;
|
||||
int argc = sizeof(args) / sizeof(char *);
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
lmp = lammps_open(argc, argv, MPI_COMM_WORLD, nullptr);
|
||||
InitSystem();
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
}
|
||||
|
||||
virtual void InitSystem()
|
||||
{
|
||||
command("units lj");
|
||||
command("atom_style atomic");
|
||||
command("atom_modify map yes");
|
||||
|
||||
command("lattice fcc 0.8442");
|
||||
command("region box block 0 2 0 2 0 2");
|
||||
command("create_box 1 box");
|
||||
command("create_atoms 1 box");
|
||||
command("mass 1 1.0");
|
||||
|
||||
command("velocity all create 3.0 87287");
|
||||
|
||||
command("pair_style lj/cut 2.5");
|
||||
command("pair_coeff 1 1 1.0 1.0 2.5");
|
||||
|
||||
command("neighbor 0.3 bin");
|
||||
command("neigh_modify every 20 delay 0 check no");
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
if (!verbose) ::testing::internal::CaptureStdout();
|
||||
lammps_close(lmp);
|
||||
lmp = nullptr;
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(MPITest, size_rank)
|
||||
{
|
||||
int nprocs, me;
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &me);
|
||||
|
||||
EXPECT_EQ(nprocs, lammps_extract_setting(lmp, "world_size"));
|
||||
EXPECT_EQ(me, lammps_extract_setting(lmp, "world_rank"));
|
||||
}
|
||||
|
||||
#if !defined(LAMMPS_BIGBIG)
|
||||
|
||||
TEST_F(MPITest, gather)
|
||||
{
|
||||
int64_t natoms = (int64_t)lammps_get_natoms(lmp);
|
||||
ASSERT_EQ(natoms, 32);
|
||||
int *p_nlocal = (int *)lammps_extract_global(lmp, "nlocal");
|
||||
int nlocal = *p_nlocal;
|
||||
EXPECT_LT(nlocal, 32);
|
||||
EXPECT_EQ(nlocal, 8);
|
||||
|
||||
// get the entire x on all procs
|
||||
double *x = new double[natoms * 3];
|
||||
lammps_gather(lmp, (char *)"x", 1, 3, x);
|
||||
|
||||
int *tag = (int *)lammps_extract_atom(lmp, "id");
|
||||
double **x_local = (double **)lammps_extract_atom(lmp, "x");
|
||||
|
||||
// each proc checks its local atoms
|
||||
for (int i = 0; i < nlocal; i++) {
|
||||
int64_t j = tag[i] - 1;
|
||||
double *x_i = x_local[i];
|
||||
double *x_g = &x[j * 3];
|
||||
EXPECT_DOUBLE_EQ(x_g[0], x_i[0]);
|
||||
EXPECT_DOUBLE_EQ(x_g[1], x_i[1]);
|
||||
EXPECT_DOUBLE_EQ(x_g[2], x_i[2]);
|
||||
}
|
||||
|
||||
delete[] x;
|
||||
}
|
||||
|
||||
TEST_F(MPITest, scatter)
|
||||
{
|
||||
int *p_nlocal = (int *)lammps_extract_global(lmp, "nlocal");
|
||||
int nlocal = *p_nlocal;
|
||||
double *x_orig = new double[3 * nlocal];
|
||||
double **x_local = (double **)lammps_extract_atom(lmp, "x");
|
||||
|
||||
// make copy of original local x vector
|
||||
for (int i = 0; i < nlocal; i++) {
|
||||
int j = 3 * i;
|
||||
x_orig[j] = x_local[i][0];
|
||||
x_orig[j + 1] = x_local[i][1];
|
||||
x_orig[j + 2] = x_local[i][2];
|
||||
}
|
||||
|
||||
// get the entire x on all procs
|
||||
int64_t natoms = (int64_t)lammps_get_natoms(lmp);
|
||||
double *x = new double[natoms * 3];
|
||||
lammps_gather(lmp, (char *)"x", 1, 3, x);
|
||||
|
||||
// shift all coordinates by 0.001
|
||||
const double delta = 0.001;
|
||||
for (int64_t i = 0; i < 3 * natoms; i++)
|
||||
x[i] += delta;
|
||||
|
||||
// update positions of all atoms
|
||||
lammps_scatter(lmp, (char *)"x", 1, 3, x);
|
||||
delete[] x;
|
||||
x = nullptr;
|
||||
|
||||
// get new nlocal and x_local
|
||||
p_nlocal = (int *)lammps_extract_global(lmp, "nlocal");
|
||||
nlocal = *p_nlocal;
|
||||
x_local = (double **)lammps_extract_atom(lmp, "x");
|
||||
|
||||
ASSERT_EQ(nlocal, 8);
|
||||
|
||||
// each proc checks its local atoms for shift
|
||||
for (int i = 0; i < nlocal; i++) {
|
||||
double *x_a = x_local[i];
|
||||
double *x_b = &x_orig[i * 3];
|
||||
EXPECT_DOUBLE_EQ(x_a[0], x_b[0] + delta);
|
||||
EXPECT_DOUBLE_EQ(x_a[1], x_b[1] + delta);
|
||||
EXPECT_DOUBLE_EQ(x_a[2], x_b[2] + delta);
|
||||
}
|
||||
|
||||
delete[] x_orig;
|
||||
}
|
||||
#endif
|
||||
@ -199,6 +199,12 @@ TEST_F(LibraryProperties, setting)
|
||||
lammps_command(lmp, "dimension 3");
|
||||
if (!verbose) ::testing::internal::GetCapturedStdout();
|
||||
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "world_size"), 1);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "world_rank"), 0);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "universe_size"), 1);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "universe_rank"), 0);
|
||||
EXPECT_GT(lammps_extract_setting(lmp, "nthreads"), 0);
|
||||
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "ntypes"), 0);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "nbondtypes"), 0);
|
||||
EXPECT_EQ(lammps_extract_setting(lmp, "nangletypes"), 0);
|
||||
|
||||
228
unittest/testing/mpitesting.h
Normal file
228
unittest/testing/mpitesting.h
Normal file
@ -0,0 +1,228 @@
|
||||
/* ----------------------------------------------------------------------
|
||||
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
||||
http://lammps.sandia.gov, Sandia National Laboratories
|
||||
Steve Plimpton, sjplimp@sandia.gov
|
||||
|
||||
Copyright (2003) Sandia Corporation. Under the terms of Contract
|
||||
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
|
||||
certain rights in this software. This software is distributed under
|
||||
the GNU General Public License.
|
||||
|
||||
See the README file in the top-level LAMMPS directory.
|
||||
------------------------------------------------------------------------- */
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <deque>
|
||||
#include <mpi.h>
|
||||
|
||||
using ::testing::TestEventListener;
|
||||
using ::testing::TestCase;
|
||||
using ::testing::TestSuite;
|
||||
using ::testing::UnitTest;
|
||||
using ::testing::TestPartResult;
|
||||
using ::testing::TestInfo;
|
||||
|
||||
class MPIPrinter : public TestEventListener {
|
||||
MPI_Comm comm;
|
||||
TestEventListener * default_listener;
|
||||
int me;
|
||||
int nprocs;
|
||||
char * buffer;
|
||||
size_t buffer_size;
|
||||
std::deque<TestPartResult> results;
|
||||
bool finalize_test;
|
||||
public:
|
||||
MPIPrinter(TestEventListener * default_listener) : default_listener(default_listener) {
|
||||
comm = MPI_COMM_WORLD;
|
||||
MPI_Comm_rank(comm, &me);
|
||||
MPI_Comm_size(comm, &nprocs);
|
||||
buffer_size = 1024;
|
||||
buffer = new char[buffer_size];
|
||||
finalize_test = false;
|
||||
}
|
||||
|
||||
~MPIPrinter() override {
|
||||
delete default_listener;
|
||||
default_listener = nullptr;
|
||||
|
||||
delete [] buffer;
|
||||
buffer = nullptr;
|
||||
buffer_size = 0;
|
||||
}
|
||||
|
||||
virtual void OnTestProgramStart(const UnitTest& unit_test) override {
|
||||
if(me == 0) default_listener->OnTestProgramStart(unit_test);
|
||||
}
|
||||
|
||||
virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration) override {
|
||||
if(me == 0) default_listener->OnTestIterationStart(unit_test, iteration);
|
||||
}
|
||||
|
||||
virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) override {
|
||||
if(me == 0) default_listener->OnEnvironmentsSetUpStart(unit_test);
|
||||
}
|
||||
|
||||
virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) override {
|
||||
if(me == 0) default_listener->OnEnvironmentsSetUpEnd(unit_test);
|
||||
}
|
||||
|
||||
virtual void OnTestSuiteStart(const TestSuite& test_suite) override {
|
||||
if(me == 0) default_listener->OnTestSuiteStart(test_suite);
|
||||
}
|
||||
|
||||
// Legacy API is deprecated but still available
|
||||
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||
virtual void OnTestCaseStart(const TestCase& test_case) override {
|
||||
if(me == 0) default_listener->OnTestSuiteStart(test_case);
|
||||
}
|
||||
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||
|
||||
|
||||
virtual void OnTestStart(const TestInfo& test_info) override {
|
||||
// Called before a test starts.
|
||||
if(me == 0) default_listener->OnTestStart(test_info);
|
||||
results.clear();
|
||||
finalize_test = false;
|
||||
}
|
||||
|
||||
|
||||
virtual void OnTestPartResult(const TestPartResult& test_part_result) override {
|
||||
// Called after a failed assertion or a SUCCESS().
|
||||
// test_part_result()
|
||||
|
||||
if (me == 0 && finalize_test) {
|
||||
default_listener->OnTestPartResult(test_part_result);
|
||||
} else {
|
||||
std::stringstream proc_message;
|
||||
std::istringstream msg(test_part_result.message());
|
||||
std::string line;
|
||||
|
||||
while(std::getline(msg, line)) {
|
||||
proc_message << "[Rank " << me << "] " << line << std::endl;
|
||||
}
|
||||
|
||||
results.push_back(TestPartResult(test_part_result.type(), test_part_result.file_name(), test_part_result.line_number(), proc_message.str().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnTestEnd(const TestInfo& test_info) override {
|
||||
// Called after a test ends.
|
||||
MPI_Barrier(comm);
|
||||
|
||||
// other procs send their test part results
|
||||
if(me != 0) {
|
||||
int nresults = results.size();
|
||||
MPI_Send(&nresults, 1, MPI_INT, 0, 0, comm);
|
||||
|
||||
for(auto& test_part_result : results) {
|
||||
|
||||
int type = test_part_result.type();
|
||||
MPI_Send(&type, 1, MPI_INT, 0, 0, comm);
|
||||
|
||||
const char * str = test_part_result.file_name();
|
||||
int length = 0;
|
||||
if(str) length = strlen(str)+1;
|
||||
MPI_Send(&length, 1, MPI_INT, 0, 0, comm);
|
||||
if(str) MPI_Send(str, length, MPI_CHAR, 0, 0, comm);
|
||||
|
||||
int lineno = test_part_result.line_number();
|
||||
MPI_Send(&lineno, 1, MPI_INT, 0, 0, comm);
|
||||
|
||||
str = test_part_result.message();
|
||||
length = 0;
|
||||
if(str) length = strlen(str)+1;
|
||||
MPI_Send(&length, 1, MPI_INT, 0, 0, comm);
|
||||
if(str) MPI_Send(str, length, MPI_CHAR, 0, 0, comm);
|
||||
}
|
||||
}
|
||||
|
||||
if(me == 0) {
|
||||
// collect results from other procs
|
||||
for(int p = 1; p < nprocs; p++) {
|
||||
int nresults = 0;
|
||||
MPI_Recv(&nresults, 1, MPI_INT, p, 0, comm, MPI_STATUS_IGNORE);
|
||||
|
||||
for(int r = 0; r < nresults; r++) {
|
||||
|
||||
int type;
|
||||
MPI_Recv(&type, 1, MPI_INT, p, 0, comm, MPI_STATUS_IGNORE);
|
||||
|
||||
int length = 0;
|
||||
MPI_Recv(&length, 1, MPI_INT, p, 0, comm, MPI_STATUS_IGNORE);
|
||||
std::string file_name;
|
||||
|
||||
if (length > 0) {
|
||||
if (length > buffer_size) {
|
||||
delete [] buffer;
|
||||
buffer = new char[length];
|
||||
buffer_size = length;
|
||||
}
|
||||
MPI_Recv(buffer, length, MPI_CHAR, p, 0, comm, MPI_STATUS_IGNORE);
|
||||
file_name = buffer;
|
||||
}
|
||||
|
||||
int lineno;
|
||||
MPI_Recv(&lineno, 1, MPI_INT, p, 0, comm, MPI_STATUS_IGNORE);
|
||||
|
||||
MPI_Recv(&length, 1, MPI_INT, p, 0, comm, MPI_STATUS_IGNORE);
|
||||
std::string message;
|
||||
|
||||
if (length > 0) {
|
||||
if (length > buffer_size) {
|
||||
delete [] buffer;
|
||||
buffer = new char[length];
|
||||
buffer_size = length;
|
||||
}
|
||||
MPI_Recv(buffer, length, MPI_CHAR, p, 0, comm, MPI_STATUS_IGNORE);
|
||||
message = std::string(buffer);
|
||||
}
|
||||
|
||||
results.push_back(TestPartResult((TestPartResult::Type)type, file_name.c_str(), lineno, message.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// ensure failures are reported
|
||||
finalize_test = true;
|
||||
|
||||
// add all failures
|
||||
while(!results.empty()) {
|
||||
auto result = results.front();
|
||||
if(result.failed()) {
|
||||
ADD_FAILURE_AT(result.file_name(), result.line_number()) << result.message();
|
||||
} else {
|
||||
default_listener->OnTestPartResult(result);
|
||||
}
|
||||
results.pop_front();
|
||||
}
|
||||
|
||||
default_listener->OnTestEnd(test_info);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnTestSuiteEnd(const TestSuite& test_suite) override {
|
||||
if(me == 0) default_listener->OnTestSuiteEnd(test_suite);
|
||||
}
|
||||
|
||||
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||
virtual void OnTestCaseEnd(const TestCase& test_case) override {
|
||||
if(me == 0) default_listener->OnTestCaseEnd(test_case);
|
||||
}
|
||||
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||
|
||||
virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) override {
|
||||
if(me == 0) default_listener->OnEnvironmentsTearDownStart(unit_test);
|
||||
}
|
||||
|
||||
virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) override {
|
||||
if(me == 0) default_listener->OnEnvironmentsTearDownEnd(unit_test);
|
||||
}
|
||||
|
||||
virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override {
|
||||
if(me == 0) default_listener->OnTestIterationEnd(unit_test, iteration);
|
||||
}
|
||||
|
||||
virtual void OnTestProgramEnd(const UnitTest& unit_test) override {
|
||||
if(me == 0) default_listener->OnTestProgramEnd(unit_test);
|
||||
}
|
||||
};
|
||||
67
unittest/testing/test_mpi_main.h
Normal file
67
unittest/testing/test_mpi_main.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* -*- c++ -*- ----------------------------------------------------------
|
||||
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
||||
http://lammps.sandia.gov, Sandia National Laboratories
|
||||
Steve Plimpton, sjplimp@sandia.gov
|
||||
|
||||
Copyright (2003) Sandia Corporation. Under the terms of Contract
|
||||
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
|
||||
certain rights in this software. This software is distributed under
|
||||
the GNU General Public License.
|
||||
|
||||
See the README file in the top-level LAMMPS directory.
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
#include "utils.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "mpitesting.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <mpi.h>
|
||||
|
||||
// whether to print verbose output (i.e. not capturing LAMMPS screen output).
|
||||
bool verbose = false;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
MPI_Init(&argc, &argv);
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
|
||||
if (argc < 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// handle arguments passed via environment variable
|
||||
if (const char *var = getenv("TEST_ARGS")) {
|
||||
std::vector<std::string> env = LAMMPS_NS::utils::split_words(var);
|
||||
for (auto arg : env) {
|
||||
if (arg == "-v") {
|
||||
verbose = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int iarg = 1;
|
||||
while (iarg < argc) {
|
||||
if (strcmp(argv[iarg], "-v") == 0) {
|
||||
verbose = true;
|
||||
++iarg;
|
||||
} else {
|
||||
std::cerr << "unknown option: " << argv[iarg] << "\n\n";
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto & listeners = UnitTest::GetInstance()->listeners();
|
||||
|
||||
// Remove default listener
|
||||
auto default_listener = listeners.Release(listeners.default_result_printer());
|
||||
|
||||
// Adds a listener to the end. googletest takes the ownership.
|
||||
listeners.Append(new MPIPrinter(default_listener));
|
||||
|
||||
int rv = RUN_ALL_TESTS();
|
||||
MPI_Finalize();
|
||||
return rv;
|
||||
}
|
||||
Reference in New Issue
Block a user