Merge remote-tracking branch 'origin/master' into varargs-log-error-functions
This commit is contained in:
6
.github/CONTRIBUTING.md
vendored
6
.github/CONTRIBUTING.md
vendored
@ -26,11 +26,11 @@ __
|
|||||||
|
|
||||||
## I don't want to read this whole thing I just have a question!
|
## I don't want to read this whole thing I just have a question!
|
||||||
|
|
||||||
> **Note:** Please do not file an issue to ask a general question about LAMMPS, its features, how to use specific commands, or how perform simulations or analysis in LAMMPS. Instead post your question to the ['lammps-users' mailing list](https://lammps.sandia.gov/mail.html). You do not need to be subscribed to post to the list (but a mailing list subscription avoids having your post delayed until it is approved by a mailing list moderator). Most posts to the mailing list receive a response within less than 24 hours. Before posting to the mailing list, please read the [mailing list guidelines](https://lammps.sandia.gov/guidelines.html). Following those guidelines will help greatly to get a helpful response. Always mention which LAMMPS version you are using.
|
> **Note:** Please do not file an issue to ask a general question about LAMMPS, its features, how to use specific commands, or how perform simulations or analysis in LAMMPS. Instead post your question to either the ['lammps-users' mailing list](https://lammps.sandia.gov/mail.html) or the [LAMMPS Material Science Discourse forum](https://matsci.org/lammps). You do not need to be subscribed to post to the list (but a mailing list subscription avoids having your post delayed until it is approved by a mailing list moderator). Most posts to the mailing list receive a response within less than 24 hours. Before posting to the mailing list, please read the [mailing list guidelines](https://lammps.sandia.gov/guidelines.html). Following those guidelines will help greatly to get a helpful response. Always mention which LAMMPS version you are using. The LAMMPS forum was recently created as part of a larger effort to build a materials science community and have discussions not just about using LAMMPS. Thus the forum may be also used for discussions that would be off-topic for the mailing list. Those will just have to be moved to a more general category.
|
||||||
|
|
||||||
## How Can I Contribute?
|
## How Can I Contribute?
|
||||||
|
|
||||||
There are several ways how you can actively contribute to the LAMMPS project: you can discuss compiling and using LAMMPS, and solving LAMMPS related problems with other LAMMPS users on the lammps-users mailing list, you can report bugs or suggest enhancements by creating issues on GitHub (or posting them to the lammps-users mailing list), and you can contribute by submitting pull requests on GitHub or e-mail your code
|
There are several ways how you can actively contribute to the LAMMPS project: you can discuss compiling and using LAMMPS, and solving LAMMPS related problems with other LAMMPS users on the lammps-users mailing list, you can report bugs or suggest enhancements by creating issues on GitHub (or posting them to the lammps-users mailing list or posting in the LAMMPS Materials Science Discourse forum), and you can contribute by submitting pull requests on GitHub or e-mail your code
|
||||||
to one of the [LAMMPS core developers](https://lammps.sandia.gov/authors.html). As you may see from the aforementioned developer page, the LAMMPS software package includes the efforts of a very large number of contributors beyond the principal authors and maintainers.
|
to one of the [LAMMPS core developers](https://lammps.sandia.gov/authors.html). As you may see from the aforementioned developer page, the LAMMPS software package includes the efforts of a very large number of contributors beyond the principal authors and maintainers.
|
||||||
|
|
||||||
### Discussing How To Use LAMMPS
|
### Discussing How To Use LAMMPS
|
||||||
@ -42,6 +42,8 @@ Anyone can browse/search previous questions/answers in the archives. You do not
|
|||||||
|
|
||||||
If you post a message and you are a subscriber, your message will appear immediately. If you are not a subscriber, your message will be moderated, which typically takes one business day. Either way, when someone replies the reply will usually be sent to both, your personal email address and the mailing list. When replying to people, that responded to your post to the list, please always included the mailing list in your replies (i.e. use "Reply All" and **not** "Reply"). Responses will appear on the list in a few minutes, but it can take a few hours for postings and replies to show up in the SourceForge archive. Sending replies also to the mailing list is important, so that responses are archived and people with a similar issue can search for possible solutions in the mailing list archive.
|
If you post a message and you are a subscriber, your message will appear immediately. If you are not a subscriber, your message will be moderated, which typically takes one business day. Either way, when someone replies the reply will usually be sent to both, your personal email address and the mailing list. When replying to people, that responded to your post to the list, please always included the mailing list in your replies (i.e. use "Reply All" and **not** "Reply"). Responses will appear on the list in a few minutes, but it can take a few hours for postings and replies to show up in the SourceForge archive. Sending replies also to the mailing list is important, so that responses are archived and people with a similar issue can search for possible solutions in the mailing list archive.
|
||||||
|
|
||||||
|
The LAMMPS Materials Science Discourse forum was created recently to facilitate discussion not just about LAMMPS and as part of a larger effort towards building a materials science community. The forum contains a read-only sub-category with the continually updated mailing list archive, so you won't miss anything by joining only the forum and not the mailing list.
|
||||||
|
|
||||||
### Reporting Bugs
|
### Reporting Bugs
|
||||||
|
|
||||||
While developers writing code for LAMMPS are careful to test their code, LAMMPS is such a large and complex software, that it is impossible to test for all combinations of features under all normal and not so normal circumstances. Thus bugs do happen, and if you suspect, that you have encountered one, please try to document it and report it as an [Issue](https://github.com/lammps/lammps/issues) on the LAMMPS GitHub project web page. However, before reporting a bug, you need to check whether this is something that may have already been corrected. The [Latest Features and Bug Fixes in LAMMPS](https://lammps.sandia.gov/bug.html) web page lists all significant changes to LAMMPS over the years. It also tells you what the current latest development version of LAMMPS is, and you should test whether your issue still applies to that version.
|
While developers writing code for LAMMPS are careful to test their code, LAMMPS is such a large and complex software, that it is impossible to test for all combinations of features under all normal and not so normal circumstances. Thus bugs do happen, and if you suspect, that you have encountered one, please try to document it and report it as an [Issue](https://github.com/lammps/lammps/issues) on the LAMMPS GitHub project web page. However, before reporting a bug, you need to check whether this is something that may have already been corrected. The [Latest Features and Bug Fixes in LAMMPS](https://lammps.sandia.gov/bug.html) web page lists all significant changes to LAMMPS over the years. It also tells you what the current latest development version of LAMMPS is, and you should test whether your issue still applies to that version.
|
||||||
|
|||||||
@ -79,9 +79,11 @@ if(INTEL_ARCH STREQUAL "KNL")
|
|||||||
else()
|
else()
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
foreach(_FLAG -O2 -fp-model fast=2 -no-prec-div -qoverride-limits -qopt-zmm-usage=high -qno-offload -fno-alias -ansi-alias -restrict)
|
foreach(_FLAG -O2 "-fp-model fast=2" -no-prec-div -qoverride-limits -qopt-zmm-usage=high -qno-offload -fno-alias -ansi-alias -restrict)
|
||||||
check_cxx_compiler_flag("${_FLAG}" COMPILER_SUPPORTS${_FLAG})
|
string(REGEX REPLACE "[ =\"]" "" _FLAGX ${_FLAG})
|
||||||
if(COMPILER_SUPPORTS${_FLAG})
|
check_cxx_compiler_flag("${_FLAG}" COMPILER_SUPPORTS${_FLAGX})
|
||||||
|
if(COMPILER_SUPPORTS${_FLAGX})
|
||||||
|
separate_arguments(_FLAG UNIX_COMMAND "${_FLAG}")
|
||||||
target_compile_options(lammps PRIVATE ${_FLAG})
|
target_compile_options(lammps PRIVATE ${_FLAG})
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|||||||
@ -17,7 +17,7 @@ if(GIT_FOUND AND EXISTS ${LAMMPS_DIR}/.git)
|
|||||||
ERROR_QUIET
|
ERROR_QUIET
|
||||||
WORKING_DIRECTORY ${LAMMPS_DIR}
|
WORKING_DIRECTORY ${LAMMPS_DIR}
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --dirty=-modified
|
execute_process(COMMAND ${GIT_EXECUTABLE} describe --dirty=-modified --always
|
||||||
OUTPUT_VARIABLE temp_git_describe
|
OUTPUT_VARIABLE temp_git_describe
|
||||||
ERROR_QUIET
|
ERROR_QUIET
|
||||||
WORKING_DIRECTORY ${LAMMPS_DIR}
|
WORKING_DIRECTORY ${LAMMPS_DIR}
|
||||||
|
|||||||
@ -9,8 +9,8 @@ reading or writing to files with error checking or translation of
|
|||||||
strings into specific types of numbers with checking for validity. This
|
strings into specific types of numbers with checking for validity. This
|
||||||
reduces redundant implementations and encourages consistent behavior.
|
reduces redundant implementations and encourages consistent behavior.
|
||||||
|
|
||||||
I/O with status check
|
I/O with status check and similar functions
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
The the first two functions are wrappers around the corresponding C
|
The the first two functions are wrappers around the corresponding C
|
||||||
library calls ``fgets()`` or ``fread()``. They will check if there
|
library calls ``fgets()`` or ``fread()``. They will check if there
|
||||||
@ -19,6 +19,14 @@ In that case, the functions will stop with an error message, indicating
|
|||||||
the name of the problematic file, if possible unless the *error* argument
|
the name of the problematic file, if possible unless the *error* argument
|
||||||
is a NULL pointer.
|
is a NULL pointer.
|
||||||
|
|
||||||
|
The :cpp:func:`fgets_trunc` function will work similar for ``fgets()``
|
||||||
|
but it will read in a whole line (i.e. until the end of line or end
|
||||||
|
of file), but store only as many characters as will fit into the buffer
|
||||||
|
including a final newline character and the terminating NULL byte.
|
||||||
|
If the line in the file is longer it will thus be truncated in the buffer.
|
||||||
|
This function is used by :cpp:func:`read_lines_from_file` to read individual
|
||||||
|
lines but make certain they follow the size constraints.
|
||||||
|
|
||||||
The :cpp:func:`read_lines_from_file` function will read the requested
|
The :cpp:func:`read_lines_from_file` function will read the requested
|
||||||
number of lines of a maximum length into a buffer and will return 0
|
number of lines of a maximum length into a buffer and will return 0
|
||||||
if successful or 1 if not. It also guarantees that all lines are
|
if successful or 1 if not. It also guarantees that all lines are
|
||||||
@ -33,6 +41,9 @@ NULL character.
|
|||||||
.. doxygenfunction:: sfread
|
.. doxygenfunction:: sfread
|
||||||
:project: progguide
|
:project: progguide
|
||||||
|
|
||||||
|
.. doxygenfunction:: fgets_trunc
|
||||||
|
:project: progguide
|
||||||
|
|
||||||
.. doxygenfunction:: read_lines_from_file
|
.. doxygenfunction:: read_lines_from_file
|
||||||
:project: progguide
|
:project: progguide
|
||||||
|
|
||||||
|
|||||||
@ -223,6 +223,9 @@ The structure of the data file is important, though many settings and
|
|||||||
sections are optional or can come in any order. See the examples
|
sections are optional or can come in any order. See the examples
|
||||||
directory for sample data files for different problems.
|
directory for sample data files for different problems.
|
||||||
|
|
||||||
|
The file will be read line by line, but there is a limit of 254
|
||||||
|
characters per line and characters beyond that limit will be ignored.
|
||||||
|
|
||||||
A data file has a header and a body. The header appears first. The
|
A data file has a header and a body. The header appears first. The
|
||||||
first line of the header is always skipped; it typically contains a
|
first line of the header is always skipped; it typically contains a
|
||||||
description of the file. Then lines are read one at a time. Lines
|
description of the file. Then lines are read one at a time. Lines
|
||||||
|
|||||||
@ -363,12 +363,14 @@ variable, as discussed below.
|
|||||||
|
|
||||||
The rules for formatting the file are as follows. Each time a set of
|
The rules for formatting the file are as follows. Each time a set of
|
||||||
per-atom values is read, a non-blank line is searched for in the file.
|
per-atom values is read, a non-blank line is searched for in the file.
|
||||||
A comment character "#" can be used anywhere on a line; text starting
|
The file is read line by line but only up to 254 characters are used.
|
||||||
with the comment character is stripped. Blank lines are skipped. The
|
The rest are ignored. A comment character "#" can be used anywhere
|
||||||
first "word" of a non-blank line, delimited by white-space, is read as
|
on a line and all text following and the "#" character are ignored;
|
||||||
the count N of per-atom lines to immediately follow. N can be the
|
text starting with the comment character is stripped. Blank lines
|
||||||
total number of atoms in the system, or only a subset. The next N
|
are skipped. The first "word" of a non-blank line, delimited by
|
||||||
lines have the following format
|
white-space, is read as the count N of per-atom lines to immediately
|
||||||
|
follow. N can be the total number of atoms in the system, or only a
|
||||||
|
subset. The next N lines have the following format
|
||||||
|
|
||||||
.. parsed-literal::
|
.. parsed-literal::
|
||||||
|
|
||||||
|
|||||||
@ -266,7 +266,7 @@ KokkosLMP::KokkosLMP(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp)
|
|||||||
#if defined(MPICH) && defined(MVAPICH2_VERSION)
|
#if defined(MPICH) && defined(MVAPICH2_VERSION)
|
||||||
char* str;
|
char* str;
|
||||||
gpu_aware_flag = 0;
|
gpu_aware_flag = 0;
|
||||||
if ((str = getenv("MV2_ENABLE_CUDA")))
|
if ((str = getenv("MV2_USE_CUDA")))
|
||||||
if ((strcmp(str,"1") == 0))
|
if ((strcmp(str,"1") == 0))
|
||||||
gpu_aware_flag = 1;
|
gpu_aware_flag = 1;
|
||||||
|
|
||||||
|
|||||||
@ -155,6 +155,7 @@ ComputeChunkAtom::ComputeChunkAtom(LAMMPS *lmp, int narg, char **arg) :
|
|||||||
if ((which == ArgInfo::UNKNOWN) || (which == ArgInfo::NONE)
|
if ((which == ArgInfo::UNKNOWN) || (which == ArgInfo::NONE)
|
||||||
|| (argi.get_dim() > 1))
|
|| (argi.get_dim() > 1))
|
||||||
error->all(FLERR,"Illegal compute chunk/atom command");
|
error->all(FLERR,"Illegal compute chunk/atom command");
|
||||||
|
iarg = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// optional args
|
// optional args
|
||||||
|
|||||||
@ -41,24 +41,25 @@
|
|||||||
|
|
||||||
using namespace LAMMPS_NS;
|
using namespace LAMMPS_NS;
|
||||||
|
|
||||||
#define MAXLINE 256
|
static constexpr int MAXLINE = 256;
|
||||||
#define LB_FACTOR 1.1
|
static constexpr double LB_FACTOR = 1.1;
|
||||||
#define CHUNK 1024
|
static constexpr int CHUNK = 1024;
|
||||||
#define DELTA 4 // must be 2 or larger
|
static constexpr int DELTA = 4; // must be 2 or larger
|
||||||
#define MAXBODY 32 // max # of lines in one body
|
static constexpr int MAXBODY = 32; // max # of lines in one body
|
||||||
|
|
||||||
// customize for new sections
|
// customize for new sections
|
||||||
#define NSECTIONS 25 // change when add to header::section_keywords
|
// change when add to header::section_keywords
|
||||||
|
static constexpr int NSECTIONS = 25;
|
||||||
|
|
||||||
enum{NONE,APPEND,VALUE,MERGE};
|
enum{NONE,APPEND,VALUE,MERGE};
|
||||||
|
|
||||||
// pair style suffixes to ignore
|
// pair style suffixes to ignore
|
||||||
// when matching Pair Coeffs comment to currently-defined pair style
|
// when matching Pair Coeffs comment to currently-defined pair style
|
||||||
|
|
||||||
const char *suffixes[] = {"/cuda","/gpu","/opt","/omp","/kk",
|
static const char *suffixes[] = {"/cuda","/gpu","/opt","/omp","/kk",
|
||||||
"/coul/cut","/coul/long","/coul/msm",
|
"/coul/cut","/coul/long","/coul/msm",
|
||||||
"/coul/dsf","/coul/debye","/coul/charmm",
|
"/coul/dsf","/coul/debye","/coul/charmm",
|
||||||
nullptr};
|
nullptr};
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
@ -304,6 +305,12 @@ void ReadData::command(int narg, char **arg)
|
|||||||
extra_dihedral_types || extra_improper_types))
|
extra_dihedral_types || extra_improper_types))
|
||||||
error->all(FLERR,"Cannot use read_data extra with add flag");
|
error->all(FLERR,"Cannot use read_data extra with add flag");
|
||||||
|
|
||||||
|
// check if data file is available and readable
|
||||||
|
|
||||||
|
if (!utils::file_is_readable(arg[0]))
|
||||||
|
error->all(FLERR,fmt::format("Cannot open file {}: {}",
|
||||||
|
arg[0], utils::getsyserror()));
|
||||||
|
|
||||||
// first time system initialization
|
// first time system initialization
|
||||||
|
|
||||||
if (addflag == NONE) {
|
if (addflag == NONE) {
|
||||||
@ -950,7 +957,7 @@ void ReadData::header(int firstpass)
|
|||||||
// skip 1st line of file
|
// skip 1st line of file
|
||||||
|
|
||||||
if (me == 0) {
|
if (me == 0) {
|
||||||
char *eof = fgets(line,MAXLINE,fp);
|
char *eof = utils::fgets_trunc(line,MAXLINE,fp);
|
||||||
if (eof == nullptr) error->one(FLERR,"Unexpected end of data file");
|
if (eof == nullptr) error->one(FLERR,"Unexpected end of data file");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -959,7 +966,7 @@ void ReadData::header(int firstpass)
|
|||||||
// read a line and bcast length
|
// read a line and bcast length
|
||||||
|
|
||||||
if (me == 0) {
|
if (me == 0) {
|
||||||
if (fgets(line,MAXLINE,fp) == nullptr) n = 0;
|
if (utils::fgets_trunc(line,MAXLINE,fp) == nullptr) n = 0;
|
||||||
else n = strlen(line) + 1;
|
else n = strlen(line) + 1;
|
||||||
}
|
}
|
||||||
MPI_Bcast(&n,1,MPI_INT,0,world);
|
MPI_Bcast(&n,1,MPI_INT,0,world);
|
||||||
@ -1662,7 +1669,7 @@ void ReadData::bodies(int firstpass, AtomVec *ptr)
|
|||||||
m = 0;
|
m = 0;
|
||||||
|
|
||||||
while (nchunk < nmax && nline <= CHUNK-MAXBODY) {
|
while (nchunk < nmax && nline <= CHUNK-MAXBODY) {
|
||||||
eof = fgets(&buffer[m],MAXLINE,fp);
|
eof = utils::fgets_trunc(&buffer[m],MAXLINE,fp);
|
||||||
if (eof == nullptr) error->one(FLERR,"Unexpected end of data file");
|
if (eof == nullptr) error->one(FLERR,"Unexpected end of data file");
|
||||||
rv = sscanf(&buffer[m],"%d %d %d",&tmp,&ninteger,&ndouble);
|
rv = sscanf(&buffer[m],"%d %d %d",&tmp,&ninteger,&ndouble);
|
||||||
if (rv != 3)
|
if (rv != 3)
|
||||||
@ -1676,7 +1683,7 @@ void ReadData::bodies(int firstpass, AtomVec *ptr)
|
|||||||
|
|
||||||
nword = 0;
|
nword = 0;
|
||||||
while (nword < ninteger) {
|
while (nword < ninteger) {
|
||||||
eof = fgets(&buffer[m],MAXLINE,fp);
|
eof = utils::fgets_trunc(&buffer[m],MAXLINE,fp);
|
||||||
if (eof == nullptr) error->one(FLERR,"Unexpected end of data file");
|
if (eof == nullptr) error->one(FLERR,"Unexpected end of data file");
|
||||||
ncount = utils::trim_and_count_words(&buffer[m]);
|
ncount = utils::trim_and_count_words(&buffer[m]);
|
||||||
if (ncount == 0)
|
if (ncount == 0)
|
||||||
@ -1690,7 +1697,7 @@ void ReadData::bodies(int firstpass, AtomVec *ptr)
|
|||||||
|
|
||||||
nword = 0;
|
nword = 0;
|
||||||
while (nword < ndouble) {
|
while (nword < ndouble) {
|
||||||
eof = fgets(&buffer[m],MAXLINE,fp);
|
eof = utils::fgets_trunc(&buffer[m],MAXLINE,fp);
|
||||||
if (eof == nullptr) error->one(FLERR,"Unexpected end of data file");
|
if (eof == nullptr) error->one(FLERR,"Unexpected end of data file");
|
||||||
ncount = utils::trim_and_count_words(&buffer[m]);
|
ncount = utils::trim_and_count_words(&buffer[m]);
|
||||||
if (ncount == 0)
|
if (ncount == 0)
|
||||||
@ -1994,15 +2001,15 @@ void ReadData::parse_keyword(int first)
|
|||||||
|
|
||||||
if (me == 0) {
|
if (me == 0) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
if (fgets(line,MAXLINE,fp) == nullptr) eof = 1;
|
if (utils::fgets_trunc(line,MAXLINE,fp) == nullptr) eof = 1;
|
||||||
}
|
}
|
||||||
while (eof == 0 && done == 0) {
|
while (eof == 0 && done == 0) {
|
||||||
int blank = strspn(line," \t\n\r");
|
int blank = strspn(line," \t\n\r");
|
||||||
if ((blank == (int)strlen(line)) || (line[blank] == '#')) {
|
if ((blank == (int)strlen(line)) || (line[blank] == '#')) {
|
||||||
if (fgets(line,MAXLINE,fp) == nullptr) eof = 1;
|
if (utils::fgets_trunc(line,MAXLINE,fp) == nullptr) eof = 1;
|
||||||
} else done = 1;
|
} else done = 1;
|
||||||
}
|
}
|
||||||
if (fgets(buffer,MAXLINE,fp) == nullptr) {
|
if (utils::fgets_trunc(buffer,MAXLINE,fp) == nullptr) {
|
||||||
eof = 1;
|
eof = 1;
|
||||||
buffer[0] = '\0';
|
buffer[0] = '\0';
|
||||||
}
|
}
|
||||||
@ -2056,7 +2063,7 @@ void ReadData::skip_lines(bigint n)
|
|||||||
if (me) return;
|
if (me) return;
|
||||||
if (n <= 0) return;
|
if (n <= 0) return;
|
||||||
char *eof = nullptr;
|
char *eof = nullptr;
|
||||||
for (bigint i = 0; i < n; i++) eof = fgets(line,MAXLINE,fp);
|
for (bigint i = 0; i < n; i++) eof = utils::fgets_trunc(line,MAXLINE,fp);
|
||||||
if (eof == nullptr) error->one(FLERR,"Unexpected end of data file");
|
if (eof == nullptr) error->one(FLERR,"Unexpected end of data file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ using namespace LAMMPS_NS;
|
|||||||
* \param filetype Description of file type for error messages */
|
* \param filetype Description of file type for error messages */
|
||||||
|
|
||||||
TextFileReader::TextFileReader(const std::string &filename, const std::string &filetype)
|
TextFileReader::TextFileReader(const std::string &filename, const std::string &filetype)
|
||||||
: filename(filename), filetype(filetype), ignore_comments(true)
|
: filetype(filetype), closefp(true), ignore_comments(true)
|
||||||
{
|
{
|
||||||
fp = fopen(filename.c_str(), "r");
|
fp = fopen(filename.c_str(), "r");
|
||||||
|
|
||||||
@ -51,10 +51,33 @@ TextFileReader::TextFileReader(const std::string &filename, const std::string &f
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \overload
|
||||||
|
*
|
||||||
|
\verbatim embed:rst
|
||||||
|
|
||||||
|
This function is useful in combination with :cpp:func:`utils::open_potential`.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The FILE pointer is not closed in the destructor, but will be advanced
|
||||||
|
when reading from it.
|
||||||
|
|
||||||
|
\endverbatim
|
||||||
|
*
|
||||||
|
* \param fp File descriptor of the already opened file
|
||||||
|
* \param filetype Description of file type for error messages */
|
||||||
|
|
||||||
|
TextFileReader::TextFileReader(FILE *fp, const std::string &filetype)
|
||||||
|
: filetype(filetype), closefp(false), fp(fp), ignore_comments(true)
|
||||||
|
{
|
||||||
|
if (fp == nullptr) throw FileReaderException("Invalid file descriptor");
|
||||||
|
}
|
||||||
|
|
||||||
/** Closes the file */
|
/** Closes the file */
|
||||||
|
|
||||||
TextFileReader::~TextFileReader() {
|
TextFileReader::~TextFileReader() {
|
||||||
fclose(fp);
|
if (closefp) fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read the next line and ignore it */
|
/** Read the next line and ignore it */
|
||||||
@ -163,5 +186,8 @@ void TextFileReader::next_dvector(double * list, int n) {
|
|||||||
* \return ValueTokenizer object for read in text */
|
* \return ValueTokenizer object for read in text */
|
||||||
|
|
||||||
ValueTokenizer TextFileReader::next_values(int nparams, const std::string &separators) {
|
ValueTokenizer TextFileReader::next_values(int nparams, const std::string &separators) {
|
||||||
return ValueTokenizer(next_line(nparams), separators);
|
char *ptr = next_line(nparams);
|
||||||
|
if (ptr == nullptr)
|
||||||
|
throw EOFException(fmt::format("Missing line in {} file!", filetype));
|
||||||
|
return ValueTokenizer(line, separators);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,8 +25,8 @@
|
|||||||
namespace LAMMPS_NS
|
namespace LAMMPS_NS
|
||||||
{
|
{
|
||||||
class TextFileReader {
|
class TextFileReader {
|
||||||
std::string filename;
|
|
||||||
std::string filetype;
|
std::string filetype;
|
||||||
|
bool closefp;
|
||||||
static constexpr int MAXLINE = 1024;
|
static constexpr int MAXLINE = 1024;
|
||||||
char line[MAXLINE];
|
char line[MAXLINE];
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
@ -35,6 +35,8 @@ namespace LAMMPS_NS
|
|||||||
bool ignore_comments; //!< Controls whether comments are ignored
|
bool ignore_comments; //!< Controls whether comments are ignored
|
||||||
|
|
||||||
TextFileReader(const std::string &filename, const std::string &filetype);
|
TextFileReader(const std::string &filename, const std::string &filetype);
|
||||||
|
TextFileReader(FILE *fp, const std::string &filetype);
|
||||||
|
|
||||||
~TextFileReader();
|
~TextFileReader();
|
||||||
|
|
||||||
void skip_line();
|
void skip_line();
|
||||||
|
|||||||
@ -68,6 +68,28 @@ Tokenizer::Tokenizer(Tokenizer && rhs) :
|
|||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tokenizer& Tokenizer::operator=(const Tokenizer& other)
|
||||||
|
{
|
||||||
|
Tokenizer tmp(other);
|
||||||
|
swap(tmp);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tokenizer& Tokenizer::operator=(Tokenizer&& other)
|
||||||
|
{
|
||||||
|
Tokenizer tmp(std::move(other));
|
||||||
|
swap(tmp);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tokenizer::swap(Tokenizer& other)
|
||||||
|
{
|
||||||
|
std::swap(text, other.text);
|
||||||
|
std::swap(separators, other.separators);
|
||||||
|
std::swap(start, other.start);
|
||||||
|
std::swap(ntokens, other.ntokens);
|
||||||
|
}
|
||||||
|
|
||||||
/*! Re-position the tokenizer state to the first word,
|
/*! Re-position the tokenizer state to the first word,
|
||||||
* i.e. the first non-separator character */
|
* i.e. the first non-separator character */
|
||||||
void Tokenizer::reset() {
|
void Tokenizer::reset() {
|
||||||
@ -181,6 +203,25 @@ ValueTokenizer::ValueTokenizer(const ValueTokenizer &rhs) : tokens(rhs.tokens) {
|
|||||||
ValueTokenizer::ValueTokenizer(ValueTokenizer &&rhs) : tokens(std::move(rhs.tokens)) {
|
ValueTokenizer::ValueTokenizer(ValueTokenizer &&rhs) : tokens(std::move(rhs.tokens)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ValueTokenizer& ValueTokenizer::operator=(const ValueTokenizer& other)
|
||||||
|
{
|
||||||
|
ValueTokenizer tmp(other);
|
||||||
|
swap(tmp);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueTokenizer& ValueTokenizer::operator=(ValueTokenizer&& other)
|
||||||
|
{
|
||||||
|
ValueTokenizer tmp(std::move(other));
|
||||||
|
swap(tmp);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueTokenizer::swap(ValueTokenizer& other)
|
||||||
|
{
|
||||||
|
std::swap(tokens, other.tokens);
|
||||||
|
}
|
||||||
|
|
||||||
/*! Indicate whether more tokens are available
|
/*! Indicate whether more tokens are available
|
||||||
*
|
*
|
||||||
* \return true if there are more tokens, false if not */
|
* \return true if there are more tokens, false if not */
|
||||||
|
|||||||
@ -37,8 +37,9 @@ public:
|
|||||||
Tokenizer(const std::string &str, const std::string &separators = TOKENIZER_DEFAULT_SEPARATORS);
|
Tokenizer(const std::string &str, const std::string &separators = TOKENIZER_DEFAULT_SEPARATORS);
|
||||||
Tokenizer(Tokenizer &&);
|
Tokenizer(Tokenizer &&);
|
||||||
Tokenizer(const Tokenizer &);
|
Tokenizer(const Tokenizer &);
|
||||||
Tokenizer& operator=(const Tokenizer&) = default;
|
Tokenizer& operator=(const Tokenizer&);
|
||||||
Tokenizer& operator=(Tokenizer&&) = default;
|
Tokenizer& operator=(Tokenizer&&);
|
||||||
|
void swap(Tokenizer &);
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
void skip(int n=1);
|
void skip(int n=1);
|
||||||
@ -93,8 +94,9 @@ public:
|
|||||||
ValueTokenizer(const std::string &str, const std::string &separators = TOKENIZER_DEFAULT_SEPARATORS);
|
ValueTokenizer(const std::string &str, const std::string &separators = TOKENIZER_DEFAULT_SEPARATORS);
|
||||||
ValueTokenizer(const ValueTokenizer &);
|
ValueTokenizer(const ValueTokenizer &);
|
||||||
ValueTokenizer(ValueTokenizer &&);
|
ValueTokenizer(ValueTokenizer &&);
|
||||||
ValueTokenizer& operator=(const ValueTokenizer&) = default;
|
ValueTokenizer& operator=(const ValueTokenizer&);
|
||||||
ValueTokenizer& operator=(ValueTokenizer&&) = default;
|
ValueTokenizer& operator=(ValueTokenizer&&);
|
||||||
|
void swap(ValueTokenizer &);
|
||||||
|
|
||||||
std::string next_string();
|
std::string next_string();
|
||||||
tagint next_tagint();
|
tagint next_tagint();
|
||||||
|
|||||||
@ -170,6 +170,42 @@ const char *utils::guesspath(char *buf, int len, FILE *fp)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read line into buffer. if line is too long keep reading until EOL or EOF
|
||||||
|
// but return only the first part with a newline at the end.
|
||||||
|
|
||||||
|
char *utils::fgets_trunc(char *buf, int size, FILE *fp)
|
||||||
|
{
|
||||||
|
constexpr int MAXDUMMY = 256;
|
||||||
|
char dummy[MAXDUMMY];
|
||||||
|
char *ptr = fgets(buf,size,fp);
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
if (!ptr) return nullptr;
|
||||||
|
|
||||||
|
int n = strlen(buf);
|
||||||
|
|
||||||
|
// line is shorter than buffer, append newline if needed,
|
||||||
|
if (n < size-2) {
|
||||||
|
if (buf[n-1] != '\n') {
|
||||||
|
buf[n] = '\n';
|
||||||
|
buf[n+1] = '\0';
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
|
||||||
|
// line fits exactly. overwrite last but one character.
|
||||||
|
} else buf[size-2] = '\n';
|
||||||
|
|
||||||
|
// continue reading into dummy buffer until end of line or file
|
||||||
|
do {
|
||||||
|
ptr = fgets(dummy,MAXDUMMY,fp);
|
||||||
|
if (ptr) n = strlen(ptr);
|
||||||
|
else n = 0;
|
||||||
|
} while (n == MAXDUMMY-1 && ptr[MAXDUMMY-1] != '\n');
|
||||||
|
|
||||||
|
// return first chunk
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
#define MAXPATHLENBUF 1024
|
#define MAXPATHLENBUF 1024
|
||||||
/* like fgets() but aborts with an error or EOF is encountered */
|
/* like fgets() but aborts with an error or EOF is encountered */
|
||||||
void utils::sfgets(const char *srcname, int srcline, char *s, int size,
|
void utils::sfgets(const char *srcname, int srcline, char *s, int size,
|
||||||
@ -240,13 +276,12 @@ int utils::read_lines_from_file(FILE *fp, int nlines, int nmax,
|
|||||||
if (me == 0) {
|
if (me == 0) {
|
||||||
if (fp) {
|
if (fp) {
|
||||||
for (int i = 0; i < nlines; i++) {
|
for (int i = 0; i < nlines; i++) {
|
||||||
ptr = fgets(ptr,nmax,fp);
|
ptr = fgets_trunc(ptr,nmax,fp);
|
||||||
if (!ptr) break; // EOF?
|
if (!ptr) break; // EOF?
|
||||||
// advance ptr to end of string and append newline char if needed.
|
// advance ptr to end of string
|
||||||
ptr += strlen(ptr);
|
ptr += strlen(ptr);
|
||||||
if (*(--ptr) != '\n') *(++ptr) = '\n';
|
|
||||||
// ensure buffer is null terminated. null char is start of next line.
|
// ensure buffer is null terminated. null char is start of next line.
|
||||||
*(++ptr) = '\0';
|
*ptr = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/utils.h
14
src/utils.h
@ -86,6 +86,20 @@ namespace LAMMPS_NS {
|
|||||||
|
|
||||||
std::string getsyserror();
|
std::string getsyserror();
|
||||||
|
|
||||||
|
/** Wrapper around fgets() which reads whole lines but truncates the
|
||||||
|
* data to the buffer size and ensures a newline char at the end.
|
||||||
|
*
|
||||||
|
* This function is useful for reading line based text files with
|
||||||
|
* possible comments that should be parsed later. This applies to
|
||||||
|
* data files, potential files, atomfile variable files and so on.
|
||||||
|
* It is used instead of fgets() by utils::read_lines_from_file().
|
||||||
|
*
|
||||||
|
* \param s buffer for storing the result of fgets()
|
||||||
|
* \param size size of buffer s (max number of bytes returned)
|
||||||
|
* \param fp file pointer used by fgets() */
|
||||||
|
|
||||||
|
char *fgets_trunc(char *s, int size, FILE *fp);
|
||||||
|
|
||||||
/** Safe wrapper around fgets() which aborts on errors
|
/** Safe wrapper around fgets() which aborts on errors
|
||||||
* or EOF and prints a suitable error message to help debugging.
|
* or EOF and prints a suitable error message to help debugging.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -10,6 +10,22 @@ set_tests_properties(RunLammps PROPERTIES
|
|||||||
ENVIRONMENT "TSAN_OPTIONS=ignore_noninstrumented_modules=1"
|
ENVIRONMENT "TSAN_OPTIONS=ignore_noninstrumented_modules=1"
|
||||||
PASS_REGULAR_EXPRESSION "^LAMMPS \\([0-9]+ [A-Za-z]+ 2[0-9][0-9][0-9]\\)")
|
PASS_REGULAR_EXPRESSION "^LAMMPS \\([0-9]+ [A-Za-z]+ 2[0-9][0-9][0-9]\\)")
|
||||||
|
|
||||||
|
# check if the compiled executable will print the help message
|
||||||
|
add_test(NAME HelpMessage
|
||||||
|
COMMAND $<TARGET_FILE:lmp> -h
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
set_tests_properties(HelpMessage PROPERTIES
|
||||||
|
ENVIRONMENT "TSAN_OPTIONS=ignore_noninstrumented_modules=1"
|
||||||
|
PASS_REGULAR_EXPRESSION ".*Large-scale Atomic/Molecular Massively Parallel Simulator -.*Usage example:.*")
|
||||||
|
|
||||||
|
# check if the compiled executable will error out on an invalid command line flag
|
||||||
|
add_test(NAME InvalidFlag
|
||||||
|
COMMAND $<TARGET_FILE:lmp> -xxx
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
set_tests_properties(InvalidFlag PROPERTIES
|
||||||
|
ENVIRONMENT "TSAN_OPTIONS=ignore_noninstrumented_modules=1"
|
||||||
|
PASS_REGULAR_EXPRESSION "ERROR: Invalid command-line argument.*")
|
||||||
|
|
||||||
if(BUILD_MPI)
|
if(BUILD_MPI)
|
||||||
function(add_mpi_test)
|
function(add_mpi_test)
|
||||||
set(MPI_TEST_NUM_PROCS 1)
|
set(MPI_TEST_NUM_PROCS 1)
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
#include "lammps.h"
|
#include "lammps.h"
|
||||||
|
|
||||||
#include "citeme.h"
|
#include "citeme.h"
|
||||||
|
#include "comm.h"
|
||||||
#include "force.h"
|
#include "force.h"
|
||||||
#include "info.h"
|
#include "info.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
@ -25,6 +26,7 @@
|
|||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "../testing/core.h"
|
#include "../testing/core.h"
|
||||||
|
#include "../testing/utils.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -174,6 +176,38 @@ TEST_F(SimpleCommandsTest, Partition)
|
|||||||
ASSERT_THAT(text, StrEq(""));
|
ASSERT_THAT(text, StrEq(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SimpleCommandsTest, Processors)
|
||||||
|
{
|
||||||
|
// default setting is "*" for all dimensions
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[0], 0);
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[1], 0);
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[2], 0);
|
||||||
|
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("processors 1 1 1");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[0], 1);
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[1], 1);
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[2], 1);
|
||||||
|
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("processors * 1 *");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[0], 0);
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[1], 1);
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[2], 0);
|
||||||
|
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("processors 0 0 0");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[0], 0);
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[1], 0);
|
||||||
|
ASSERT_EQ(lmp->comm->user_procgrid[2], 0);
|
||||||
|
|
||||||
|
TEST_FAILURE(".*ERROR: Illegal processors command .*", command("processors -1 0 0"););
|
||||||
|
TEST_FAILURE(".*ERROR: Specified processors != physical processors.*", command("processors 100 100 100"););
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SimpleCommandsTest, Quit)
|
TEST_F(SimpleCommandsTest, Quit)
|
||||||
{
|
{
|
||||||
BEGIN_HIDE_OUTPUT();
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
|||||||
@ -105,8 +105,8 @@ protected:
|
|||||||
"# comments only\n five\n#END\n",
|
"# comments only\n five\n#END\n",
|
||||||
fp);
|
fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
fp = fopen("test_variable.atomfile", "w");
|
|
||||||
|
|
||||||
|
fp = fopen("test_variable.atomfile", "w");
|
||||||
fputs("# test file for atomfile style variable\n\n"
|
fputs("# test file for atomfile style variable\n\n"
|
||||||
"4 # four lines\n4 0.5 #with comment\n"
|
"4 # four lines\n4 0.5 #with comment\n"
|
||||||
"2 -0.5 \n3 1.5\n1 -1.5\n\n"
|
"2 -0.5 \n3 1.5\n1 -1.5\n\n"
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
add_executable(test_lammps_class test_lammps_class.cpp)
|
add_executable(test_lammps_class test_lammps_class.cpp)
|
||||||
target_link_libraries(test_lammps_class PRIVATE lammps GTest::GMockMain GTest::GTest GTest::GMock)
|
target_link_libraries(test_lammps_class PRIVATE lammps GTest::GMockMain GTest::GTest GTest::GMock)
|
||||||
add_test(LammpsClass test_lammps_class)
|
add_test(LammpsClass test_lammps_class)
|
||||||
|
set_tests_properties(LammpsClass PROPERTIES ENVIRONMENT "OMP_NUM_THREADS=1")
|
||||||
|
|
||||||
add_executable(test_input_class test_input_class.cpp)
|
add_executable(test_input_class test_input_class.cpp)
|
||||||
target_link_libraries(test_input_class PRIVATE lammps GTest::GTest GTest::GTestMain)
|
target_link_libraries(test_input_class PRIVATE lammps GTest::GTest GTest::GTestMain)
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
// unit tests for the LAMMPS base class
|
// unit tests for the LAMMPS base class
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
#include "info.h"
|
||||||
#include "lammps.h"
|
#include "lammps.h"
|
||||||
#include <cstdio> // for stdin, stdout
|
#include <cstdio> // for stdin, stdout
|
||||||
|
#include <cstdlib> // for setenv
|
||||||
#include <mpi.h>
|
#include <mpi.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using ::testing::MatchesRegex;
|
||||||
using ::testing::StartsWith;
|
using ::testing::StartsWith;
|
||||||
|
|
||||||
namespace LAMMPS_NS {
|
namespace LAMMPS_NS {
|
||||||
@ -95,6 +99,7 @@ TEST_F(LAMMPS_plain, InitMembers)
|
|||||||
EXPECT_STREQ(LAMMPS::git_branch, "(unknown)");
|
EXPECT_STREQ(LAMMPS::git_branch, "(unknown)");
|
||||||
EXPECT_STREQ(LAMMPS::git_descriptor, "(unknown)");
|
EXPECT_STREQ(LAMMPS::git_descriptor, "(unknown)");
|
||||||
}
|
}
|
||||||
|
EXPECT_EQ(lmp->comm->nthreads, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LAMMPS_plain, TestStyles)
|
TEST_F(LAMMPS_plain, TestStyles)
|
||||||
@ -229,6 +234,9 @@ TEST_F(LAMMPS_omp, InitMembers)
|
|||||||
EXPECT_STREQ(LAMMPS::git_branch, "(unknown)");
|
EXPECT_STREQ(LAMMPS::git_branch, "(unknown)");
|
||||||
EXPECT_STREQ(LAMMPS::git_descriptor, "(unknown)");
|
EXPECT_STREQ(LAMMPS::git_descriptor, "(unknown)");
|
||||||
}
|
}
|
||||||
|
#if 0 // temporarily disabled. MacOS behaves different from Linux here.
|
||||||
|
EXPECT_EQ(lmp->comm->nthreads, 2);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// test fixture for Kokkos tests
|
// test fixture for Kokkos tests
|
||||||
@ -318,10 +326,49 @@ TEST_F(LAMMPS_kokkos, InitMembers)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check help message printing
|
// check if Comm::nthreads is initialized to either 1 or 2 (from the previous tests)
|
||||||
TEST(LAMMPS_help, HelpMessage)
|
TEST(LAMMPS_init, OpenMP)
|
||||||
{
|
{
|
||||||
const char *args[] = {"LAMMPS_test", "-h"};
|
if (!LAMMPS::is_installed_pkg("USER-OMP")) GTEST_SKIP();
|
||||||
|
|
||||||
|
FILE *fp = fopen("in.lammps_empty", "w");
|
||||||
|
fputs("\n", fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
const char *args[] = {"LAMMPS_init", "-in", "in.lammps_empty", "-log", "none", "-nocite"};
|
||||||
|
char **argv = (char **)args;
|
||||||
|
int argc = sizeof(args) / sizeof(char *);
|
||||||
|
|
||||||
|
::testing::internal::CaptureStdout();
|
||||||
|
LAMMPS *lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD);
|
||||||
|
std::string output = ::testing::internal::GetCapturedStdout();
|
||||||
|
EXPECT_THAT(output, MatchesRegex(".*using 2 OpenMP thread.*per MPI task.*"));
|
||||||
|
|
||||||
|
if (LAMMPS_NS::Info::has_accelerator_feature("USER-OMP", "api", "openmp"))
|
||||||
|
EXPECT_EQ(lmp->comm->nthreads, 2);
|
||||||
|
else
|
||||||
|
EXPECT_EQ(lmp->comm->nthreads, 1);
|
||||||
|
::testing::internal::CaptureStdout();
|
||||||
|
delete lmp;
|
||||||
|
::testing::internal::GetCapturedStdout();
|
||||||
|
|
||||||
|
remove("in.lammps_empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check no OMP_NUM_THREADS warning message printing. this must be the
|
||||||
|
// last OpenMP related test as threads will be locked to 1 from here on.
|
||||||
|
|
||||||
|
TEST(LAMMPS_init, NoOpenMP)
|
||||||
|
{
|
||||||
|
if (!LAMMPS_NS::Info::has_accelerator_feature("USER-OMP", "api", "openmp"))
|
||||||
|
GTEST_SKIP() << "No threading enabled";
|
||||||
|
|
||||||
|
FILE *fp = fopen("in.lammps_class_noomp", "w");
|
||||||
|
fputs("\n", fp);
|
||||||
|
fclose(fp);
|
||||||
|
unsetenv("OMP_NUM_THREADS");
|
||||||
|
|
||||||
|
const char *args[] = {"LAMMPS_init", "-in", "in.lammps_class_noomp", "-log", "none", "-nocite"};
|
||||||
char **argv = (char **)args;
|
char **argv = (char **)args;
|
||||||
int argc = sizeof(args) / sizeof(char *);
|
int argc = sizeof(args) / sizeof(char *);
|
||||||
|
|
||||||
@ -329,7 +376,11 @@ TEST(LAMMPS_help, HelpMessage)
|
|||||||
LAMMPS *lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD);
|
LAMMPS *lmp = new LAMMPS(argc, argv, MPI_COMM_WORLD);
|
||||||
std::string output = ::testing::internal::GetCapturedStdout();
|
std::string output = ::testing::internal::GetCapturedStdout();
|
||||||
EXPECT_THAT(output,
|
EXPECT_THAT(output,
|
||||||
StartsWith("\nLarge-scale Atomic/Molecular Massively Parallel Simulator -"));
|
MatchesRegex(".*OMP_NUM_THREADS environment is not set.*Defaulting to 1 thread.*"));
|
||||||
|
EXPECT_EQ(lmp->comm->nthreads, 1);
|
||||||
|
::testing::internal::CaptureStdout();
|
||||||
delete lmp;
|
delete lmp;
|
||||||
|
::testing::internal::GetCapturedStdout();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace LAMMPS_NS
|
} // namespace LAMMPS_NS
|
||||||
|
|||||||
@ -33,7 +33,7 @@ else()
|
|||||||
target_link_libraries(style_tests PUBLIC mpi_stubs)
|
target_link_libraries(style_tests PUBLIC mpi_stubs)
|
||||||
endif()
|
endif()
|
||||||
# propagate sanitizer options to test tools
|
# propagate sanitizer options to test tools
|
||||||
if (NOT ENABLE_SANITIZER STREQUAL "none")
|
if (ENABLE_SANITIZER AND (NOT ENABLE_SANITIZER STREQUAL "none"))
|
||||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
|
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
|
||||||
target_compile_options(style_tests PUBLIC -fsanitize=${ENABLE_SANITIZER})
|
target_compile_options(style_tests PUBLIC -fsanitize=${ENABLE_SANITIZER})
|
||||||
target_link_options(style_tests PUBLIC -fsanitize=${ENABLE_SANITIZER})
|
target_link_options(style_tests PUBLIC -fsanitize=${ENABLE_SANITIZER})
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
lammps_version: 10 Feb 2021
|
lammps_version: 10 Feb 2021
|
||||||
date_generated: Fri Feb 26 23:09:10 2021
|
date_generated: Fri Feb 26 23:09:10 2021
|
||||||
epsilon: 5e-14
|
epsilon: 2e-13
|
||||||
prerequisites: ! |
|
prerequisites: ! |
|
||||||
atom sphere
|
atom sphere
|
||||||
pair yukawa/colloid
|
pair yukawa/colloid
|
||||||
|
|||||||
@ -28,6 +28,11 @@ if(PKG_MANYBODY)
|
|||||||
set_tests_properties(EIMPotentialFileReader PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
|
set_tests_properties(EIMPotentialFileReader PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_executable(test_text_file_reader test_text_file_reader.cpp)
|
||||||
|
target_link_libraries(test_text_file_reader PRIVATE lammps GTest::GMock GTest::GTest)
|
||||||
|
add_test(NAME TextFileReader COMMAND test_text_file_reader WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
set_tests_properties(TextFileReader PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR}")
|
||||||
|
|
||||||
add_executable(test_file_operations test_file_operations.cpp)
|
add_executable(test_file_operations test_file_operations.cpp)
|
||||||
target_link_libraries(test_file_operations PRIVATE lammps GTest::GMock GTest::GTest)
|
target_link_libraries(test_file_operations PRIVATE lammps GTest::GMock GTest::GTest)
|
||||||
add_test(NAME FileOperations COMMAND test_file_operations WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
add_test(NAME FileOperations COMMAND test_file_operations WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|||||||
@ -75,6 +75,31 @@ TEST_F(DumpCfgTest, run0)
|
|||||||
delete_file("dump_cfg_run0.melt.cfg");
|
delete_file("dump_cfg_run0.melt.cfg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DumpCfgTest, write_dump)
|
||||||
|
{
|
||||||
|
auto dump_file = "dump_cfg_run*.melt.cfg";
|
||||||
|
auto fields = "mass type xs ys zs id proc procp1 x y z ix iy iz vx vy vz fx fy fz";
|
||||||
|
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command(std::string("write_dump all cfg dump_cfg.melt.cfg ") + fields);
|
||||||
|
command(std::string("write_dump all cfg dump_cfg*.melt.cfg ") + fields);
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
|
||||||
|
ASSERT_FILE_EXISTS("dump_cfg.melt.cfg");
|
||||||
|
auto lines = read_lines("dump_cfg.melt.cfg");
|
||||||
|
ASSERT_EQ(lines.size(), 124);
|
||||||
|
ASSERT_THAT(lines[0], Eq("Number of particles = 32"));
|
||||||
|
delete_file("dump_cfg.melt.cfg");
|
||||||
|
|
||||||
|
ASSERT_FILE_EXISTS("dump_cfg0.melt.cfg");
|
||||||
|
lines = read_lines("dump_cfg0.melt.cfg");
|
||||||
|
ASSERT_EQ(lines.size(), 124);
|
||||||
|
ASSERT_THAT(lines[0], Eq("Number of particles = 32"));
|
||||||
|
delete_file("dump_cfg0.melt.cfg");
|
||||||
|
|
||||||
|
TEST_FAILURE(".*ERROR: Unrecognized dump style 'xxx'.*", command("write_dump all xxx test.xxx"););
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(DumpCfgTest, unwrap_run0)
|
TEST_F(DumpCfgTest, unwrap_run0)
|
||||||
{
|
{
|
||||||
auto dump_file = "dump_cfg_unwrap_run*.melt.cfg";
|
auto dump_file = "dump_cfg_unwrap_run*.melt.cfg";
|
||||||
|
|||||||
@ -12,11 +12,14 @@
|
|||||||
------------------------------------------------------------------------- */
|
------------------------------------------------------------------------- */
|
||||||
|
|
||||||
#include "../testing/core.h"
|
#include "../testing/core.h"
|
||||||
|
#include "../testing/utils.h"
|
||||||
|
#include "atom.h"
|
||||||
|
#include "domain.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "info.h"
|
#include "info.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "lammps.h"
|
#include "lammps.h"
|
||||||
#include "utils.h"
|
#include "update.h"
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
@ -45,19 +48,29 @@ protected:
|
|||||||
LAMMPSTest::SetUp();
|
LAMMPSTest::SetUp();
|
||||||
ASSERT_NE(lmp, nullptr);
|
ASSERT_NE(lmp, nullptr);
|
||||||
|
|
||||||
FILE *fp = fopen("safe_file_read_test.txt", "wb");
|
std::ofstream out("safe_file_read_test.txt", std::ios_base::out | std::ios_base::binary);
|
||||||
ASSERT_NE(fp, nullptr);
|
ASSERT_TRUE(out.good());
|
||||||
fputs("one line\n", fp);
|
out << "one line\ntwo_lines\n\nno newline";
|
||||||
fputs("two_lines\n", fp);
|
out.close();
|
||||||
fputs("\n", fp);
|
|
||||||
fputs("no newline", fp);
|
out.open("file_with_long_lines_test.txt", std::ios_base::out | std::ios_base::binary);
|
||||||
fclose(fp);
|
ASSERT_TRUE(out.good());
|
||||||
|
out << "zero ##########################################################"
|
||||||
|
"##################################################################"
|
||||||
|
"##################################################################"
|
||||||
|
"############################################################\n";
|
||||||
|
out << "one line\ntwo_lines\n\n";
|
||||||
|
for (int i = 0; i < 100; ++i) out << "one two ";
|
||||||
|
out << "\nthree\nfour five #";
|
||||||
|
for (int i = 0; i < 1000; ++i) out << '#';
|
||||||
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override
|
void TearDown() override
|
||||||
{
|
{
|
||||||
LAMMPSTest::TearDown();
|
LAMMPSTest::TearDown();
|
||||||
remove("safe_file_read_test.txt");
|
remove("safe_file_read_test.txt");
|
||||||
|
remove("file_with_long_lines_test.txt");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -66,7 +79,7 @@ TEST_F(FileOperationsTest, safe_fgets)
|
|||||||
{
|
{
|
||||||
char buf[MAX_BUF_SIZE];
|
char buf[MAX_BUF_SIZE];
|
||||||
|
|
||||||
FILE *fp = fopen("safe_file_read_test.txt", "r");
|
FILE *fp = fopen("safe_file_read_test.txt", "rb");
|
||||||
ASSERT_NE(fp, nullptr);
|
ASSERT_NE(fp, nullptr);
|
||||||
|
|
||||||
memset(buf, 0, MAX_BUF_SIZE);
|
memset(buf, 0, MAX_BUF_SIZE);
|
||||||
@ -97,12 +110,74 @@ TEST_F(FileOperationsTest, safe_fgets)
|
|||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MAX_BUF_SIZE 128
|
||||||
|
TEST_F(FileOperationsTest, fgets_trunc)
|
||||||
|
{
|
||||||
|
char buf[MAX_BUF_SIZE];
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
FILE *fp = fopen("safe_file_read_test.txt", "rb");
|
||||||
|
ASSERT_NE(fp, nullptr);
|
||||||
|
|
||||||
|
memset(buf, 0, MAX_BUF_SIZE);
|
||||||
|
ptr = utils::fgets_trunc(buf, MAX_BUF_SIZE, fp);
|
||||||
|
ASSERT_THAT(buf, StrEq("one line\n"));
|
||||||
|
ASSERT_NE(ptr,nullptr);
|
||||||
|
|
||||||
|
memset(buf, 0, MAX_BUF_SIZE);
|
||||||
|
ptr = utils::fgets_trunc(buf, MAX_BUF_SIZE, fp);
|
||||||
|
ASSERT_THAT(buf, StrEq("two_lines\n"));
|
||||||
|
ASSERT_NE(ptr,nullptr);
|
||||||
|
|
||||||
|
memset(buf, 0, MAX_BUF_SIZE);
|
||||||
|
ptr = utils::fgets_trunc(buf, MAX_BUF_SIZE, fp);
|
||||||
|
ASSERT_THAT(buf, StrEq("\n"));
|
||||||
|
ASSERT_NE(ptr,nullptr);
|
||||||
|
|
||||||
|
memset(buf, 0, MAX_BUF_SIZE);
|
||||||
|
ptr = utils::fgets_trunc(buf, 4, fp);
|
||||||
|
ASSERT_THAT(buf, StrEq("no\n"));
|
||||||
|
ASSERT_NE(ptr,nullptr);
|
||||||
|
|
||||||
|
ptr = utils::fgets_trunc(buf, MAX_BUF_SIZE, fp);
|
||||||
|
ASSERT_EQ(ptr,nullptr);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
fp = fopen("file_with_long_lines_test.txt", "r");
|
||||||
|
ASSERT_NE(fp,nullptr);
|
||||||
|
|
||||||
|
memset(buf, 0, MAX_BUF_SIZE);
|
||||||
|
ptr = utils::fgets_trunc(buf, MAX_BUF_SIZE, fp);
|
||||||
|
ASSERT_NE(ptr,nullptr);
|
||||||
|
ASSERT_THAT(buf, StrEq("zero ##########################################################"
|
||||||
|
"###############################################################\n"));
|
||||||
|
|
||||||
|
ptr = utils::fgets_trunc(buf, MAX_BUF_SIZE, fp);
|
||||||
|
ASSERT_THAT(buf, StrEq("one line\n"));
|
||||||
|
ASSERT_NE(ptr,nullptr);
|
||||||
|
|
||||||
|
ptr = utils::fgets_trunc(buf, MAX_BUF_SIZE, fp);
|
||||||
|
ASSERT_THAT(buf, StrEq("two_lines\n"));
|
||||||
|
ASSERT_NE(ptr,nullptr);
|
||||||
|
|
||||||
|
ptr = utils::fgets_trunc(buf, MAX_BUF_SIZE, fp);
|
||||||
|
ASSERT_THAT(buf, StrEq("\n"));
|
||||||
|
ASSERT_NE(ptr,nullptr);
|
||||||
|
|
||||||
|
ptr = utils::fgets_trunc(buf, MAX_BUF_SIZE, fp);
|
||||||
|
ASSERT_NE(ptr,nullptr);
|
||||||
|
ASSERT_THAT(buf, StrEq("one two one two one two one two one two one two one two one two "
|
||||||
|
"one two one two one two one two one two one two one two one tw\n"));
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
#define MAX_BUF_SIZE 128
|
#define MAX_BUF_SIZE 128
|
||||||
TEST_F(FileOperationsTest, safe_fread)
|
TEST_F(FileOperationsTest, safe_fread)
|
||||||
{
|
{
|
||||||
char buf[MAX_BUF_SIZE];
|
char buf[MAX_BUF_SIZE];
|
||||||
|
|
||||||
FILE *fp = fopen("safe_file_read_test.txt", "r");
|
FILE *fp = fopen("safe_file_read_test.txt", "rb");
|
||||||
ASSERT_NE(fp, nullptr);
|
ASSERT_NE(fp, nullptr);
|
||||||
|
|
||||||
memset(buf, 0, MAX_BUF_SIZE);
|
memset(buf, 0, MAX_BUF_SIZE);
|
||||||
@ -128,7 +203,7 @@ TEST_F(FileOperationsTest, safe_fread)
|
|||||||
|
|
||||||
TEST_F(FileOperationsTest, read_lines_from_file)
|
TEST_F(FileOperationsTest, read_lines_from_file)
|
||||||
{
|
{
|
||||||
char *buf = new char[MAX_BUF_SIZE];
|
char *buf = new char[MAX_BUF_SIZE];
|
||||||
FILE *fp = nullptr;
|
FILE *fp = nullptr;
|
||||||
MPI_Comm world = MPI_COMM_WORLD;
|
MPI_Comm world = MPI_COMM_WORLD;
|
||||||
int me, rv;
|
int me, rv;
|
||||||
@ -225,6 +300,191 @@ TEST_F(FileOperationsTest, error_all_one)
|
|||||||
lmp->error->one("testme.cpp", 10, "exit {} {}", "too"););
|
lmp->error->one("testme.cpp", 10, "exit {} {}", "too"););
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(FileOperationsTest, write_restart)
|
||||||
|
{
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("echo none");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
TEST_FAILURE(".*ERROR: Write_restart command before simulation box is defined.*",
|
||||||
|
command("write_restart test.restart"););
|
||||||
|
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("region box block -2 2 -2 2 -2 2");
|
||||||
|
command("create_box 1 box");
|
||||||
|
command("create_atoms 1 single 0.0 0.0 0.0");
|
||||||
|
command("mass 1 1.0");
|
||||||
|
command("reset_timestep 333");
|
||||||
|
command("comm_modify cutoff 0.2");
|
||||||
|
command("write_restart noinit.restart noinit");
|
||||||
|
command("run 0 post no");
|
||||||
|
command("write_restart test.restart");
|
||||||
|
command("write_restart step*.restart");
|
||||||
|
command("write_restart multi-%.restart");
|
||||||
|
command("write_restart multi2-%.restart fileper 2");
|
||||||
|
command("write_restart multi3-%.restart nfile 1");
|
||||||
|
if (info->has_package("MPIIO")) command("write_restart test.restart.mpiio");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
|
||||||
|
ASSERT_FILE_EXISTS("noinit.restart");
|
||||||
|
ASSERT_FILE_EXISTS("test.restart");
|
||||||
|
ASSERT_FILE_EXISTS("step333.restart");
|
||||||
|
ASSERT_FILE_EXISTS("multi-base.restart");
|
||||||
|
ASSERT_FILE_EXISTS("multi-0.restart");
|
||||||
|
ASSERT_FILE_EXISTS("multi2-base.restart");
|
||||||
|
ASSERT_FILE_EXISTS("multi2-0.restart");
|
||||||
|
ASSERT_FILE_EXISTS("multi3-base.restart");
|
||||||
|
ASSERT_FILE_EXISTS("multi3-0.restart");
|
||||||
|
if (info->has_package("MPIIO")) ASSERT_FILE_EXISTS("test.restart.mpiio");
|
||||||
|
|
||||||
|
if (!info->has_package("MPIIO")) {
|
||||||
|
TEST_FAILURE(".*ERROR: Writing to MPI-IO filename when MPIIO package is not inst.*",
|
||||||
|
command("write_restart test.restart.mpiio"););
|
||||||
|
} else {
|
||||||
|
TEST_FAILURE(".*ERROR: Restart file MPI-IO output not allowed with % in filename.*",
|
||||||
|
command("write_restart test.restart-%.mpiio"););
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_FAILURE(".*ERROR: Illegal write_restart command.*", command("write_restart"););
|
||||||
|
TEST_FAILURE(".*ERROR: Illegal write_restart command.*",
|
||||||
|
command("write_restart test.restart xxxx"););
|
||||||
|
TEST_FAILURE(".*ERROR on proc 0: Cannot open restart file some_crazy_dir/test.restart:"
|
||||||
|
" No such file or directory.*",
|
||||||
|
command("write_restart some_crazy_dir/test.restart"););
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("clear");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
ASSERT_EQ(lmp->atom->natoms, 0);
|
||||||
|
ASSERT_EQ(lmp->update->ntimestep, 0);
|
||||||
|
ASSERT_EQ(lmp->domain->triclinic, 0);
|
||||||
|
|
||||||
|
TEST_FAILURE(
|
||||||
|
".*ERROR on proc 0: Cannot open restart file noexist.restart: No such file or directory.*",
|
||||||
|
command("read_restart noexist.restart"););
|
||||||
|
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("read_restart step333.restart");
|
||||||
|
command("change_box all triclinic");
|
||||||
|
command("write_restart triclinic.restart");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
ASSERT_EQ(lmp->atom->natoms, 1);
|
||||||
|
ASSERT_EQ(lmp->update->ntimestep, 333);
|
||||||
|
ASSERT_EQ(lmp->domain->triclinic, 1);
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("clear");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
ASSERT_EQ(lmp->atom->natoms, 0);
|
||||||
|
ASSERT_EQ(lmp->update->ntimestep, 0);
|
||||||
|
ASSERT_EQ(lmp->domain->triclinic, 0);
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("read_restart triclinic.restart");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
ASSERT_EQ(lmp->atom->natoms, 1);
|
||||||
|
ASSERT_EQ(lmp->update->ntimestep, 333);
|
||||||
|
ASSERT_EQ(lmp->domain->triclinic, 1);
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
delete_file("noinit.restart");
|
||||||
|
delete_file("test.restart");
|
||||||
|
delete_file("step333.restart");
|
||||||
|
delete_file("multi-base.restart");
|
||||||
|
delete_file("multi-0.restart");
|
||||||
|
delete_file("multi2-base.restart");
|
||||||
|
delete_file("multi2-0.restart");
|
||||||
|
delete_file("multi3-base.restart");
|
||||||
|
delete_file("multi3-0.restart");
|
||||||
|
delete_file("triclinic.restart");
|
||||||
|
if (info->has_package("MPIIO")) delete_file("test.restart.mpiio");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FileOperationsTest, write_data)
|
||||||
|
{
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("echo none");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
TEST_FAILURE(".*ERROR: Write_data command before simulation box is defined.*",
|
||||||
|
command("write_data test.data"););
|
||||||
|
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("region box block -2 2 -2 2 -2 2");
|
||||||
|
command("create_box 2 box");
|
||||||
|
command("create_atoms 1 single 0.5 0.0 0.0");
|
||||||
|
command("pair_style zero 1.0");
|
||||||
|
command("pair_coeff * *");
|
||||||
|
command("mass * 1.0");
|
||||||
|
command("reset_timestep 333");
|
||||||
|
command("write_data noinit.data noinit");
|
||||||
|
command("write_data nocoeff.data nocoeff");
|
||||||
|
command("run 0 post no");
|
||||||
|
command("write_data test.data");
|
||||||
|
command("write_data step*.data pair ij");
|
||||||
|
command("fix q all property/atom q");
|
||||||
|
command("set type 1 charge -0.5");
|
||||||
|
command("write_data charge.data");
|
||||||
|
command("write_data nofix.data nofix");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
|
||||||
|
ASSERT_FILE_EXISTS("noinit.data");
|
||||||
|
ASSERT_EQ(count_lines("noinit.data"), 26);
|
||||||
|
ASSERT_FILE_EXISTS("test.data");
|
||||||
|
ASSERT_EQ(count_lines("test.data"), 26);
|
||||||
|
ASSERT_FILE_EXISTS("step333.data");
|
||||||
|
ASSERT_EQ(count_lines("step333.data"), 27);
|
||||||
|
ASSERT_FILE_EXISTS("nocoeff.data");
|
||||||
|
ASSERT_EQ(count_lines("nocoeff.data"), 21);
|
||||||
|
ASSERT_FILE_EXISTS("nofix.data");
|
||||||
|
ASSERT_EQ(count_lines("nofix.data"), 26);
|
||||||
|
ASSERT_FILE_EXISTS("charge.data");
|
||||||
|
ASSERT_EQ(count_lines("charge.data"), 30);
|
||||||
|
|
||||||
|
TEST_FAILURE(".*ERROR: Illegal write_data command.*", command("write_data"););
|
||||||
|
TEST_FAILURE(".*ERROR: Illegal write_data command.*", command("write_data test.data xxxx"););
|
||||||
|
TEST_FAILURE(".*ERROR: Illegal write_data command.*", command("write_data test.data pair xx"););
|
||||||
|
TEST_FAILURE(".*ERROR on proc 0: Cannot open data file some_crazy_dir/test.data:"
|
||||||
|
" No such file or directory.*",
|
||||||
|
command("write_data some_crazy_dir/test.data"););
|
||||||
|
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("clear");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
ASSERT_EQ(lmp->domain->box_exist, 0);
|
||||||
|
ASSERT_EQ(lmp->atom->natoms, 0);
|
||||||
|
ASSERT_EQ(lmp->update->ntimestep, 0);
|
||||||
|
ASSERT_EQ(lmp->domain->triclinic, 0);
|
||||||
|
|
||||||
|
TEST_FAILURE(".*ERROR: Cannot open file noexist.data: No such file or directory.*",
|
||||||
|
command("read_data noexist.data"););
|
||||||
|
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("pair_style zero 1.0");
|
||||||
|
command("read_data step333.data");
|
||||||
|
command("change_box all triclinic");
|
||||||
|
command("write_data triclinic.data");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
ASSERT_EQ(lmp->atom->natoms, 1);
|
||||||
|
ASSERT_EQ(lmp->update->ntimestep, 0);
|
||||||
|
ASSERT_EQ(lmp->domain->triclinic, 1);
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("clear");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
ASSERT_EQ(lmp->atom->natoms, 0);
|
||||||
|
ASSERT_EQ(lmp->domain->triclinic, 0);
|
||||||
|
BEGIN_HIDE_OUTPUT();
|
||||||
|
command("pair_style zero 1.0");
|
||||||
|
command("read_data triclinic.data");
|
||||||
|
END_HIDE_OUTPUT();
|
||||||
|
ASSERT_EQ(lmp->atom->natoms, 1);
|
||||||
|
ASSERT_EQ(lmp->domain->triclinic, 1);
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
delete_file("charge.data");
|
||||||
|
delete_file("nocoeff.data");
|
||||||
|
delete_file("noinit.data");
|
||||||
|
delete_file("nofix.data");
|
||||||
|
delete_file("test.data");
|
||||||
|
delete_file("step333.data");
|
||||||
|
delete_file("triclinic.data");
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
MPI_Init(&argc, &argv);
|
MPI_Init(&argc, &argv);
|
||||||
|
|||||||
182
unittest/formats/test_text_file_reader.cpp
Normal file
182
unittest/formats/test_text_file_reader.cpp
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/* ----------------------------------------------------------------------
|
||||||
|
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
||||||
|
https://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 "info.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "text_file_reader.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "../testing/core.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <mpi.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace LAMMPS_NS;
|
||||||
|
using LAMMPS_NS::utils::split_words;
|
||||||
|
|
||||||
|
// whether to print verbose output (i.e. not capturing LAMMPS screen output).
|
||||||
|
bool verbose = false;
|
||||||
|
|
||||||
|
class TextFileReaderTest : public ::testing::Test {
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void TearDown() override
|
||||||
|
{
|
||||||
|
unlink("text_reader_one.file");
|
||||||
|
unlink("text_reader_two.file");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_files()
|
||||||
|
{
|
||||||
|
FILE *fp = fopen("text_reader_one.file", "w");
|
||||||
|
fputs("# test file 1 for text file reader\n\n\none\n two \n\n"
|
||||||
|
"three # with comment\nfour ! with non-comment\n"
|
||||||
|
"# comments only\n five\n#END\n",
|
||||||
|
fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
fp = fopen("text_reader_two.file", "w");
|
||||||
|
fputs("# test file for atomfile style variable\n\n"
|
||||||
|
"4 # four lines\n4 0.5 #with comment\n"
|
||||||
|
"2 -0.5 \n3 1.5\n1 -1.5\n\n"
|
||||||
|
"2\n10 1.0 # test\n13 1.0\n\n######\n"
|
||||||
|
"4\n1 4.0 # test\n2 3.0\n3 2.0\n4 1.0\n#END\n",
|
||||||
|
fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(TextFileReaderTest, nofile)
|
||||||
|
{
|
||||||
|
ASSERT_THROW({ TextFileReader reader("text_reader_noexist.file", "test"); },
|
||||||
|
FileReaderException);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TextFileReaderTest, permissions)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen("text_reader_noperms.file", "w");
|
||||||
|
fputs("word\n", fp);
|
||||||
|
fclose(fp);
|
||||||
|
chmod("text_reader_noperms.file", 0);
|
||||||
|
ASSERT_THROW({ TextFileReader reader("text_reader_noperms.file", "test"); },
|
||||||
|
FileReaderException);
|
||||||
|
unlink("text_reader_noperms.file");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TextFileReaderTest, nofp)
|
||||||
|
{
|
||||||
|
ASSERT_THROW({ TextFileReader reader(nullptr, "test"); },
|
||||||
|
FileReaderException);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TextFileReaderTest, usefp)
|
||||||
|
{
|
||||||
|
test_files();
|
||||||
|
FILE *fp = fopen("text_reader_two.file","r");
|
||||||
|
ASSERT_NE(fp,nullptr);
|
||||||
|
|
||||||
|
auto reader = new TextFileReader(fp, "test");
|
||||||
|
auto line = reader->next_line();
|
||||||
|
ASSERT_STREQ(line, "4 ");
|
||||||
|
line = reader->next_line(1);
|
||||||
|
ASSERT_STREQ(line, "4 0.5 ");
|
||||||
|
ASSERT_NO_THROW({ reader->skip_line(); });
|
||||||
|
auto values = reader->next_values(1);
|
||||||
|
ASSERT_EQ(values.count(), 2);
|
||||||
|
ASSERT_EQ(values.next_int(), 3);
|
||||||
|
ASSERT_STREQ(values.next_string().c_str(), "1.5");
|
||||||
|
ASSERT_NE(reader->next_line(), nullptr);
|
||||||
|
double data[20];
|
||||||
|
ASSERT_THROW({ reader->next_dvector(data,20); }, FileReaderException);
|
||||||
|
ASSERT_THROW({ reader->skip_line(); }, EOFException);
|
||||||
|
ASSERT_EQ(reader->next_line(), nullptr);
|
||||||
|
delete reader;
|
||||||
|
|
||||||
|
// check that we reached EOF and the destructor didn't close the file.
|
||||||
|
ASSERT_EQ(feof(fp),1);
|
||||||
|
ASSERT_EQ(fclose(fp),0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TextFileReaderTest, comments)
|
||||||
|
{
|
||||||
|
test_files();
|
||||||
|
TextFileReader reader("text_reader_two.file", "test");
|
||||||
|
reader.ignore_comments = true;
|
||||||
|
auto line = reader.next_line();
|
||||||
|
ASSERT_STREQ(line, "4 ");
|
||||||
|
line = reader.next_line(1);
|
||||||
|
ASSERT_STREQ(line, "4 0.5 ");
|
||||||
|
ASSERT_NO_THROW({ reader.skip_line(); });
|
||||||
|
auto values = reader.next_values(1);
|
||||||
|
ASSERT_EQ(values.count(), 2);
|
||||||
|
ASSERT_EQ(values.next_int(), 3);
|
||||||
|
ASSERT_STREQ(values.next_string().c_str(), "1.5");
|
||||||
|
ASSERT_NE(reader.next_line(), nullptr);
|
||||||
|
double data[20];
|
||||||
|
ASSERT_THROW({ reader.next_dvector(data,20); }, FileReaderException);
|
||||||
|
ASSERT_THROW({ reader.skip_line(); }, EOFException);
|
||||||
|
ASSERT_EQ(reader.next_line(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TextFileReaderTest, nocomments)
|
||||||
|
{
|
||||||
|
test_files();
|
||||||
|
TextFileReader reader("text_reader_one.file", "test");
|
||||||
|
reader.ignore_comments = false;
|
||||||
|
auto line = reader.next_line();
|
||||||
|
ASSERT_STREQ(line, "# test file 1 for text file reader\n");
|
||||||
|
line = reader.next_line(1);
|
||||||
|
ASSERT_STREQ(line, "one\n");
|
||||||
|
ASSERT_NO_THROW({ reader.skip_line(); });
|
||||||
|
auto values = reader.next_values(4);
|
||||||
|
ASSERT_EQ(values.count(), 4);
|
||||||
|
ASSERT_STREQ(values.next_string().c_str(), "three");
|
||||||
|
ASSERT_STREQ(values.next_string().c_str(), "#");
|
||||||
|
ASSERT_STREQ(values.next_string().c_str(), "with");
|
||||||
|
try {
|
||||||
|
reader.next_values(100);
|
||||||
|
FAIL() << "No exception thrown\n";
|
||||||
|
} catch (EOFException &e) {
|
||||||
|
ASSERT_STREQ(e.what(), "Incorrect format in test file! 9/100 parameters");
|
||||||
|
}
|
||||||
|
ASSERT_THROW({ reader.skip_line(); }, EOFException);
|
||||||
|
ASSERT_EQ(reader.next_line(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
MPI_Init(&argc, &argv);
|
||||||
|
::testing::InitGoogleMock(&argc, argv);
|
||||||
|
|
||||||
|
if (Info::get_mpi_vendor() == "Open MPI" && !LAMMPS_NS::Info::has_exceptions())
|
||||||
|
std::cout << "Warning: using OpenMPI without exceptions. "
|
||||||
|
"Death tests will be skipped\n";
|
||||||
|
|
||||||
|
// handle arguments passed via environment variable
|
||||||
|
if (const char *var = getenv("TEST_ARGS")) {
|
||||||
|
std::vector<std::string> env = split_words(var);
|
||||||
|
for (auto arg : env) {
|
||||||
|
if (arg == "-v") {
|
||||||
|
verbose = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = true;
|
||||||
|
|
||||||
|
int rv = RUN_ALL_TESTS();
|
||||||
|
MPI_Finalize();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
@ -10,14 +10,12 @@
|
|||||||
|
|
||||||
See the README file in the top-level LAMMPS directory.
|
See the README file in the top-level LAMMPS directory.
|
||||||
------------------------------------------------------------------------- */
|
------------------------------------------------------------------------- */
|
||||||
#ifndef TEST_EXTENSIONS__H
|
#ifndef LMP_TESTING_UTILS_H
|
||||||
#define TEST_EXTENSIONS__H
|
#define LMP_TESTING_UTILS_H
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
static void delete_file(const std::string &filename)
|
static void delete_file(const std::string &filename)
|
||||||
@ -65,8 +63,8 @@ static std::vector<std::string> read_lines(const std::string &filename)
|
|||||||
|
|
||||||
static bool file_exists(const std::string &filename)
|
static bool file_exists(const std::string &filename)
|
||||||
{
|
{
|
||||||
struct stat result;
|
std::ifstream infile(filename);
|
||||||
return stat(filename.c_str(), &result) == 0;
|
return infile.good();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ASSERT_FILE_EXISTS(NAME) ASSERT_TRUE(file_exists(NAME))
|
#define ASSERT_FILE_EXISTS(NAME) ASSERT_TRUE(file_exists(NAME))
|
||||||
|
|||||||
@ -53,6 +53,11 @@ TEST(Tokenizer, skip)
|
|||||||
ASSERT_FALSE(t.has_next());
|
ASSERT_FALSE(t.has_next());
|
||||||
ASSERT_EQ(t.count(), 2);
|
ASSERT_EQ(t.count(), 2);
|
||||||
ASSERT_THROW(t.skip(), TokenizerException);
|
ASSERT_THROW(t.skip(), TokenizerException);
|
||||||
|
try {
|
||||||
|
t.skip();
|
||||||
|
} catch (TokenizerException &e) {
|
||||||
|
ASSERT_STREQ(e.what(), "No more tokens");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Tokenizer, prefix_separators)
|
TEST(Tokenizer, prefix_separators)
|
||||||
@ -87,6 +92,47 @@ TEST(Tokenizer, copy_constructor)
|
|||||||
ASSERT_EQ(u.count(), 2);
|
ASSERT_EQ(u.count(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Tokenizer, move_constructor)
|
||||||
|
{
|
||||||
|
Tokenizer u = std::move(Tokenizer("test new word ", " "));
|
||||||
|
ASSERT_THAT(u.next(), Eq("test"));
|
||||||
|
ASSERT_THAT(u.next(), Eq("new"));
|
||||||
|
ASSERT_THAT(u.next(), Eq("word"));
|
||||||
|
ASSERT_EQ(u.count(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Tokenizer, copy_assignment)
|
||||||
|
{
|
||||||
|
Tokenizer t(" test word ", " ");
|
||||||
|
Tokenizer u(" test2 word2 other2 ", " ");
|
||||||
|
ASSERT_THAT(t.next(), Eq("test"));
|
||||||
|
ASSERT_THAT(t.next(), Eq("word"));
|
||||||
|
ASSERT_EQ(t.count(), 2);
|
||||||
|
Tokenizer v = u;
|
||||||
|
u = t;
|
||||||
|
ASSERT_THAT(u.next(), Eq("test"));
|
||||||
|
ASSERT_THAT(u.next(), Eq("word"));
|
||||||
|
ASSERT_EQ(u.count(), 2);
|
||||||
|
|
||||||
|
ASSERT_THAT(v.next(), Eq("test2"));
|
||||||
|
ASSERT_THAT(v.next(), Eq("word2"));
|
||||||
|
ASSERT_THAT(v.next(), Eq("other2"));
|
||||||
|
ASSERT_EQ(v.count(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Tokenizer, move_assignment)
|
||||||
|
{
|
||||||
|
Tokenizer t(" test word ", " ");
|
||||||
|
ASSERT_THAT(t.next(), Eq("test"));
|
||||||
|
ASSERT_THAT(t.next(), Eq("word"));
|
||||||
|
ASSERT_EQ(t.count(), 2);
|
||||||
|
t = Tokenizer("test new word ", " ");
|
||||||
|
ASSERT_THAT(t.next(), Eq("test"));
|
||||||
|
ASSERT_THAT(t.next(), Eq("new"));
|
||||||
|
ASSERT_THAT(t.next(), Eq("word"));
|
||||||
|
ASSERT_EQ(t.count(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Tokenizer, no_separator_path)
|
TEST(Tokenizer, no_separator_path)
|
||||||
{
|
{
|
||||||
Tokenizer t("one", ":");
|
Tokenizer t("one", ":");
|
||||||
@ -181,12 +227,72 @@ TEST(ValueTokenizer, skip)
|
|||||||
ASSERT_FALSE(t.has_next());
|
ASSERT_FALSE(t.has_next());
|
||||||
ASSERT_EQ(t.count(), 2);
|
ASSERT_EQ(t.count(), 2);
|
||||||
ASSERT_THROW(t.skip(), TokenizerException);
|
ASSERT_THROW(t.skip(), TokenizerException);
|
||||||
|
try {
|
||||||
|
t.skip();
|
||||||
|
} catch (TokenizerException &e) {
|
||||||
|
ASSERT_STREQ(e.what(), "No more tokens");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ValueTokenizer, copy_constructor)
|
||||||
|
{
|
||||||
|
ValueTokenizer t(" test word ", " ");
|
||||||
|
ASSERT_THAT(t.next_string(), Eq("test"));
|
||||||
|
ASSERT_THAT(t.next_string(), Eq("word"));
|
||||||
|
ASSERT_EQ(t.count(), 2);
|
||||||
|
ValueTokenizer u(t);
|
||||||
|
ASSERT_THAT(u.next_string(), Eq("test"));
|
||||||
|
ASSERT_THAT(u.next_string(), Eq("word"));
|
||||||
|
ASSERT_EQ(u.count(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ValueTokenizer, move_constructor)
|
||||||
|
{
|
||||||
|
ValueTokenizer u = std::move(ValueTokenizer(" test new word ", " "));
|
||||||
|
ASSERT_THAT(u.next_string(), Eq("test"));
|
||||||
|
ASSERT_THAT(u.next_string(), Eq("new"));
|
||||||
|
ASSERT_THAT(u.next_string(), Eq("word"));
|
||||||
|
ASSERT_EQ(u.count(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ValueTokenizer, copy_assignment)
|
||||||
|
{
|
||||||
|
ValueTokenizer t(" test word ", " ");
|
||||||
|
ValueTokenizer u(" test2 word2 other2 ", " ");
|
||||||
|
ASSERT_THAT(t.next_string(), Eq("test"));
|
||||||
|
ASSERT_THAT(t.next_string(), Eq("word"));
|
||||||
|
ASSERT_EQ(t.count(), 2);
|
||||||
|
ValueTokenizer v = u;
|
||||||
|
u = t;
|
||||||
|
ASSERT_THAT(u.next_string(), Eq("test"));
|
||||||
|
ASSERT_THAT(u.next_string(), Eq("word"));
|
||||||
|
ASSERT_EQ(u.count(), 2);
|
||||||
|
|
||||||
|
ASSERT_THAT(v.next_string(), Eq("test2"));
|
||||||
|
ASSERT_THAT(v.next_string(), Eq("word2"));
|
||||||
|
ASSERT_THAT(v.next_string(), Eq("other2"));
|
||||||
|
ASSERT_EQ(v.count(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ValueTokenizer, move_assignment)
|
||||||
|
{
|
||||||
|
ValueTokenizer t(" test word ", " ");
|
||||||
|
ASSERT_THAT(t.next_string(), Eq("test"));
|
||||||
|
ASSERT_THAT(t.next_string(), Eq("word"));
|
||||||
|
ASSERT_EQ(t.count(), 2);
|
||||||
|
t = ValueTokenizer("test new word ", " ");
|
||||||
|
ASSERT_THAT(t.next_string(), Eq("test"));
|
||||||
|
ASSERT_THAT(t.next_string(), Eq("new"));
|
||||||
|
ASSERT_THAT(t.next_string(), Eq("word"));
|
||||||
|
ASSERT_EQ(t.count(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ValueTokenizer, bad_integer)
|
TEST(ValueTokenizer, bad_integer)
|
||||||
{
|
{
|
||||||
ValueTokenizer values("f10");
|
ValueTokenizer values("f10 f11 f12");
|
||||||
ASSERT_THROW(values.next_int(), InvalidIntegerException);
|
ASSERT_THROW(values.next_int(), InvalidIntegerException);
|
||||||
|
ASSERT_THROW(values.next_bigint(), InvalidIntegerException);
|
||||||
|
ASSERT_THROW(values.next_tagint(), InvalidIntegerException);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ValueTokenizer, bad_double)
|
TEST(ValueTokenizer, bad_double)
|
||||||
|
|||||||
Reference in New Issue
Block a user