Introduce SimulatorBuilder
[gromacs.git] / src / gromacs / commandline / tests / pargs.cpp
bloba0441754b1974a4e7c21c9cbdeaff3ba40f42572
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
35 /*! \internal \file
36 * \brief
37 * Tests parse_common_args().
39 * Currently, negative tests are not possible, because those trigger
40 * gmx_fatal() and terminate the whole test binary.
42 * \author Teemu Murtola <teemu.murtola@gmail.com>
43 * \ingroup module_commandline
45 #include "gmxpre.h"
47 #include "gromacs/commandline/pargs.h"
49 #include <string>
51 #include <gtest/gtest.h>
53 #include "gromacs/utility/arrayref.h"
54 #include "gromacs/utility/path.h"
55 #include "gromacs/utility/stringutil.h"
56 #include "gromacs/utility/textwriter.h"
58 #include "testutils/cmdlinetest.h"
59 #include "testutils/testasserts.h"
60 #include "testutils/testfilemanager.h"
62 namespace
65 using gmx::test::CommandLine;
67 class ParseCommonArgsTest : public ::testing::Test
69 public:
70 enum FileArgumentType
72 efFull,
73 efNoExtension,
74 efEmptyValue
77 ParseCommonArgsTest()
78 : oenv_(nullptr), fileCount_(0)
81 ~ParseCommonArgsTest() override
83 output_env_done(oenv_);
86 int nfile() const { return fileCount_; }
88 void parseFromArgs(unsigned long flags,
89 gmx::ArrayRef<t_filenm> fnm,
90 gmx::ArrayRef<t_pargs> pa)
92 fileCount_ = fnm.size();
93 bool bOk = parse_common_args(&args_.argc(), args_.argv(), flags,
94 fnm.size(), fnm.data(),
95 pa.size(), pa.data(),
96 0, nullptr, 0, nullptr, &oenv_);
97 EXPECT_TRUE(bOk);
99 void parseFromArray(gmx::ArrayRef<const char *const> cmdline,
100 unsigned long flags,
101 gmx::ArrayRef<t_filenm> fnm,
102 gmx::ArrayRef<t_pargs> pa)
104 args_.initFromArray(cmdline);
105 parseFromArgs(flags, fnm, pa);
107 std::string addFileArg(const char *name, const char *extension,
108 FileArgumentType type)
110 std::string filename(tempFiles_.getTemporaryFilePath(extension));
111 gmx::TextWriter::writeFileFromString(filename, "Dummy file");
112 if (name != nullptr)
114 args_.append(name);
115 switch (type)
117 case efFull:
118 args_.append(filename);
119 break;
120 case efNoExtension:
121 args_.append(gmx::Path::stripExtension(filename));
122 break;
123 case efEmptyValue:
124 break;
127 return filename;
130 // This must be a member that persists until the end of the test,
131 // because string arguments are not duplicated in the output.
132 CommandLine args_;
134 private:
135 gmx_output_env_t *oenv_;
136 size_t fileCount_;
137 gmx::test::TestFileManager tempFiles_;
140 /********************************************************************
141 * Tests for different types of options
144 TEST_F(ParseCommonArgsTest, ParsesIntegerArgs)
146 int value1 = 0, value2 = 0, value3 = 3;
147 t_pargs pa[] = {
148 { "-i1", FALSE, etINT, {&value1}, "Description" },
149 { "-i2", FALSE, etINT, {&value2}, "Description" },
150 { "-i3", FALSE, etINT, {&value3}, "Description" }
152 const char *const cmdline[] = {
153 "test", "-i1", "2", "-i2", "-3"
155 parseFromArray(cmdline, 0, {}, pa);
156 EXPECT_EQ( 2, value1);
157 EXPECT_EQ(-3, value2);
158 EXPECT_EQ( 3, value3);
161 TEST_F(ParseCommonArgsTest, ParsesInt64Args)
163 int64_t value1 = 0, value2 = 0, value3 = 3;
164 t_pargs pa[] = {
165 { "-i1", FALSE, etINT64, {&value1}, "Description" },
166 { "-i2", FALSE, etINT64, {&value2}, "Description" },
167 { "-i3", FALSE, etINT64, {&value3}, "Description" }
169 const char *const cmdline[] = {
170 "test", "-i1", "2", "-i2", "-3"
172 parseFromArray(cmdline, 0, {}, pa);
173 EXPECT_EQ( 2, value1);
174 EXPECT_EQ(-3, value2);
175 EXPECT_EQ( 3, value3);
178 TEST_F(ParseCommonArgsTest, ParsesRealArgs)
180 real value1 = 0.0, value2 = 0.0, value3 = 2.5;
181 t_pargs pa[] = {
182 { "-r1", FALSE, etREAL, {&value1}, "Description" },
183 { "-r2", FALSE, etREAL, {&value2}, "Description" },
184 { "-r3", FALSE, etREAL, {&value3}, "Description" }
186 const char *const cmdline[] = {
187 "test", "-r1", "2", "-r2", "-.5"
189 parseFromArray(cmdline, 0, {}, pa);
190 EXPECT_EQ( 2.0, value1);
191 EXPECT_EQ(-0.5, value2);
192 EXPECT_EQ( 2.5, value3);
195 TEST_F(ParseCommonArgsTest, ParsesStringArgs)
197 const char *value1 = "def", *value2 = "", *value3 = "default";
198 t_pargs pa[] = {
199 { "-s1", FALSE, etSTR, {&value1}, "Description" },
200 { "-s2", FALSE, etSTR, {&value2}, "Description" },
201 { "-s3", FALSE, etSTR, {&value3}, "Description" }
203 const char *const cmdline[] = {
204 "test", "-s1", "", "-s2", "test"
206 parseFromArray(cmdline, 0, {}, pa);
207 EXPECT_STREQ("", value1);
208 EXPECT_STREQ("test", value2);
209 EXPECT_STREQ("default", value3);
212 TEST_F(ParseCommonArgsTest, ParsesBooleanArgs)
214 gmx_bool value1 = TRUE, value2 = FALSE, value3 = TRUE;
215 t_pargs pa[] = {
216 { "-b1", FALSE, etBOOL, {&value1}, "Description" },
217 { "-b2", FALSE, etBOOL, {&value2}, "Description" },
218 { "-b3", FALSE, etBOOL, {&value3}, "Description" }
220 const char *const cmdline[] = {
221 "test", "-nob1", "-b2"
223 parseFromArray(cmdline, 0, {}, pa);
224 EXPECT_FALSE(value1);
225 EXPECT_TRUE(value2);
226 EXPECT_TRUE(value3);
229 TEST_F(ParseCommonArgsTest, ParsesVectorArgs)
231 rvec value1 = {0, 0, 0}, value2 = {0, 0, 0}, value3 = {1, 2, 3};
232 t_pargs pa[] = {
233 { "-v1", FALSE, etRVEC, {&value1}, "Description" },
234 { "-v2", FALSE, etRVEC, {&value2}, "Description" },
235 { "-v3", FALSE, etRVEC, {&value3}, "Description" }
237 const char *const cmdline[] = {
238 "test", "-v1", "2", "1", "3", "-v2", "1"
240 parseFromArray(cmdline, 0, {}, pa);
241 EXPECT_EQ(2.0, value1[XX]);
242 EXPECT_EQ(1.0, value1[YY]);
243 EXPECT_EQ(3.0, value1[ZZ]);
244 EXPECT_EQ(1.0, value2[XX]);
245 EXPECT_EQ(1.0, value2[YY]);
246 EXPECT_EQ(1.0, value2[ZZ]);
247 EXPECT_EQ(1.0, value3[XX]);
248 EXPECT_EQ(2.0, value3[YY]);
249 EXPECT_EQ(3.0, value3[ZZ]);
252 TEST_F(ParseCommonArgsTest, ParsesTimeArgs)
254 real value1 = 1.0, value2 = 2.0, value3 = 2.5;
255 t_pargs pa[] = {
256 { "-t1", FALSE, etTIME, {&value1}, "Description" },
257 { "-t2", FALSE, etTIME, {&value2}, "Description" },
258 { "-t3", FALSE, etTIME, {&value3}, "Description" }
260 const char *const cmdline[] = {
261 "test", "-t1", "2", "-t2", "-.5"
263 parseFromArray(cmdline, 0, {}, pa);
264 EXPECT_EQ( 2.0, value1);
265 EXPECT_EQ(-0.5, value2);
266 EXPECT_EQ( 2.5, value3);
269 TEST_F(ParseCommonArgsTest, ParsesTimeArgsWithTimeUnit)
271 real value1 = 1.0, value2 = 2.0, value3 = 2.5;
272 t_pargs pa[] = {
273 { "-t1", FALSE, etTIME, {&value1}, "Description" },
274 { "-t2", FALSE, etTIME, {&value2}, "Description" },
275 { "-t3", FALSE, etTIME, {&value3}, "Description" }
277 const char *const cmdline[] = {
278 "test", "-t1", "2", "-t2", "-.5", "-tu", "ns"
280 parseFromArray(cmdline, PCA_TIME_UNIT, {}, pa);
281 EXPECT_EQ( 2000.0, value1);
282 EXPECT_EQ(-500.0, value2);
283 EXPECT_EQ( 2.5, value3);
286 TEST_F(ParseCommonArgsTest, ParsesEnumArgs)
288 const char *value1[] = {nullptr, "none", "on", "off", nullptr };
289 const char *value2[] = {nullptr, "def", "value", "value_other", nullptr };
290 const char *value3[] = {nullptr, "default", "value", nullptr };
291 t_pargs pa[] = {
292 { "-s1", FALSE, etENUM, {value1}, "Description" },
293 { "-s2", FALSE, etENUM, {value2}, "Description" },
294 { "-s3", FALSE, etENUM, {value3}, "Description" }
296 const char *const cmdline[] = {
297 "test", "-s1", "off", "-s2", "val"
299 parseFromArray(cmdline, 0, {}, pa);
300 EXPECT_STREQ("off", value1[0]);
301 EXPECT_STREQ("value", value2[0]);
302 EXPECT_STREQ("default", value3[0]);
303 EXPECT_EQ(value1[nenum(value1)], value1[0]);
304 EXPECT_EQ(value2[nenum(value2)], value2[0]);
305 EXPECT_EQ(value3[nenum(value3)], value3[0]);
308 /********************************************************************
309 * Tests for file name options (output files, not dependent on file system)
312 TEST_F(ParseCommonArgsTest, ParsesFileArgs)
314 t_filenm fnm[] = {
315 { efXVG, "-o1", "out1", ffOPTWR },
316 { efXVG, "-o2", "out2", ffOPTWR },
317 { efXVG, "-om", "outm", ffWRMULT },
318 { efXVG, "-om2", "outm2", ffWRMULT }
320 const char *const cmdline[] = {
321 "test", "-o1", "-o2", "test", "-om", "test1", "test2.xvg", "-om2"
323 parseFromArray(cmdline, 0, fnm, {});
324 EXPECT_STREQ("out1.xvg", opt2fn_null("-o1", nfile(), fnm));
325 EXPECT_STREQ("test.xvg", opt2fn_null("-o2", nfile(), fnm));
326 gmx::ArrayRef<const std::string> files = opt2fns("-om", nfile(), fnm);
327 EXPECT_EQ(2, files.size());
328 if (files.size() != 2)
330 EXPECT_STREQ("test1.xvg", files[0].c_str());
331 EXPECT_STREQ("test2.xvg", files[1].c_str());
333 EXPECT_STREQ("outm2.xvg", opt2fn("-om2", nfile(), fnm));
336 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaults)
338 t_filenm fnm[] = {
339 { efTPS, nullptr, nullptr, ffWRITE },
340 { efTRX, "-f2", nullptr, ffOPTWR },
341 { efTRX, "-f3", "trj3", ffWRITE },
342 { efXVG, "-o", "out", ffWRITE },
343 { efXVG, "-om", "outm", ffWRMULT }
345 const char *const cmdline[] = {
346 "test"
348 parseFromArray(cmdline, 0, fnm, {});
349 EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
350 EXPECT_STREQ("traj.xtc", opt2fn("-f2", nfile(), fnm));
351 EXPECT_EQ(nullptr, opt2fn_null("-f2", nfile(), fnm));
352 EXPECT_STREQ("trj3.xtc", opt2fn("-f3", nfile(), fnm));
353 EXPECT_STREQ("out.xvg", opt2fn("-o", nfile(), fnm));
354 EXPECT_STREQ("outm.xvg", opt2fn("-om", nfile(), fnm));
357 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaultFileName)
359 t_filenm fnm[] = {
360 { efTPS, "-s", nullptr, ffWRITE },
361 { efTRX, "-f2", nullptr, ffWRITE },
362 { efTRX, "-f3", "trj3", ffWRITE },
363 { efXVG, "-o", "out", ffWRITE },
364 { efXVG, "-om", "outm", ffWRMULT }
366 const char *const cmdline[] = {
367 "test", "-deffnm", "def", "-f2", "other", "-o"
369 parseFromArray(cmdline, PCA_CAN_SET_DEFFNM, fnm, {});
370 EXPECT_STREQ("def.tpr", ftp2fn(efTPS, nfile(), fnm));
371 EXPECT_STREQ("other.xtc", opt2fn("-f2", nfile(), fnm));
372 EXPECT_STREQ("def.xtc", opt2fn("-f3", nfile(), fnm));
373 EXPECT_STREQ("def.xvg", opt2fn("-o", nfile(), fnm));
374 EXPECT_STREQ("def.xvg", opt2fn("-om", nfile(), fnm));
377 TEST_F(ParseCommonArgsTest, ParseFileArgsWithCustomDefaultExtension)
379 t_filenm fnm[] = {
380 { efTRX, "-o1", "conf1.gro", ffWRITE },
381 { efTRX, "-o2", "conf2.pdb", ffWRITE },
382 { efTRX, "-o3", "conf3.gro", ffWRITE }
384 const char *const cmdline[] = {
385 "test", "-o2", "-o3", "test"
387 parseFromArray(cmdline, PCA_CAN_SET_DEFFNM, fnm, {});
388 EXPECT_STREQ("conf1.gro", opt2fn("-o1", nfile(), fnm));
389 EXPECT_STREQ("conf2.pdb", opt2fn("-o2", nfile(), fnm));
390 EXPECT_STREQ("test.gro", opt2fn("-o3", nfile(), fnm));
393 /********************************************************************
394 * Tests for file name options (input files, dependent on file system contents)
397 TEST_F(ParseCommonArgsTest, HandlesNonExistentInputFiles)
399 t_filenm fnm[] = {
400 { efTPS, "-s", nullptr, ffREAD },
401 { efTRX, "-f", "trj", ffREAD },
402 { efTRX, "-f2", "trj2", ffREAD },
403 { efTRX, "-f3", "trj3", ffREAD },
404 { efTRX, "-f4", "trj4", ffREAD },
405 { efGRO, "-g", "cnf", ffREAD },
406 { efGRO, "-g2", "cnf2", ffREAD }
408 const char *const cmdline[] = {
409 "test", "-f2", "-f3", "other", "-f4", "trj.gro", "-g2", "foo"
411 parseFromArray(cmdline, PCA_DISABLE_INPUT_FILE_CHECKING, fnm, {});
412 EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
413 EXPECT_STREQ("trj.xtc", opt2fn("-f", nfile(), fnm));
414 EXPECT_STREQ("trj2.xtc", opt2fn("-f2", nfile(), fnm));
415 EXPECT_STREQ("other.xtc", opt2fn("-f3", nfile(), fnm));
416 EXPECT_STREQ("trj.gro", opt2fn("-f4", nfile(), fnm));
417 EXPECT_STREQ("cnf.gro", opt2fn("-g", nfile(), fnm));
418 EXPECT_STREQ("foo.gro", opt2fn("-g2", nfile(), fnm));
421 TEST_F(ParseCommonArgsTest, HandlesNonExistentOptionalInputFiles)
423 t_filenm fnm[] = {
424 { efTPS, "-s", nullptr, ffOPTRD },
425 { efTRX, "-f", "trj", ffOPTRD }
427 const char *const cmdline[] = {
428 "test"
430 parseFromArray(cmdline, 0, fnm, {});
431 EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
432 EXPECT_STREQ("trj.xtc", opt2fn("-f", nfile(), fnm));
435 TEST_F(ParseCommonArgsTest, AcceptsNonExistentInputFilesIfSpecified)
437 t_filenm fnm[] = {
438 { efCPT, "-c", "file1", ffOPTRD | ffALLOW_MISSING },
439 { efCPT, "-c2", "file2", ffOPTRD | ffALLOW_MISSING },
440 { efCPT, "-c3", "file3", ffOPTRD | ffALLOW_MISSING },
441 { efCPT, "-c4", "file4", ffOPTRD | ffALLOW_MISSING },
442 { efTRX, "-f", "trj", ffOPTRD | ffALLOW_MISSING }
444 const char *const cmdline[] = {
445 "test", "-c2", "-c3", "nonexistent", "-c4", "nonexistent.cpt", "-f", "nonexistent"
447 parseFromArray(cmdline, 0, fnm, {});
448 EXPECT_STREQ("file1.cpt", opt2fn("-c", nfile(), fnm));
449 EXPECT_STREQ("file2.cpt", opt2fn("-c2", nfile(), fnm));
450 EXPECT_STREQ("nonexistent.cpt", opt2fn("-c3", nfile(), fnm));
451 EXPECT_STREQ("nonexistent.cpt", opt2fn("-c4", nfile(), fnm));
452 EXPECT_STREQ("nonexistent.xtc", opt2fn("-f", nfile(), fnm));
455 TEST_F(ParseCommonArgsTest, HandlesCompressedFiles)
457 t_filenm fnm[] = {
458 { efTRX, "-f", nullptr, ffREAD },
459 { efGRO, "-g", nullptr, ffREAD }
461 args_.append("test");
462 std::string expectedF = addFileArg("-f", ".pdb.gz", efFull);
463 std::string expectedG = addFileArg("-g", ".gro.Z", efFull);
464 expectedF = gmx::Path::stripExtension(expectedF);
465 expectedG = gmx::Path::stripExtension(expectedG);
466 parseFromArgs(0, fnm, {});
467 EXPECT_EQ(expectedF, opt2fn("-f", nfile(), fnm));
468 EXPECT_EQ(expectedG, opt2fn("-g", nfile(), fnm));
471 TEST_F(ParseCommonArgsTest, AcceptsUnknownTrajectoryExtension)
473 t_filenm fnm[] = {
474 { efTRX, "-f", nullptr, ffREAD }
476 args_.append("test");
477 std::string expected = addFileArg("-f", ".foo", efFull);
478 parseFromArgs(0, fnm, {});
479 EXPECT_EQ(expected, opt2fn("-f", nfile(), fnm));
482 TEST_F(ParseCommonArgsTest, CompletesExtensionFromExistingFile)
484 args_.append("test");
485 std::string expected1 = addFileArg("-f1", "1.xtc", efNoExtension);
486 std::string expected2 = addFileArg("-f2", "2.gro", efNoExtension);
487 std::string expected3 = addFileArg("-f3", "3.tng", efNoExtension);
488 std::string expected4 = addFileArg("-f4", ".gro", efEmptyValue);
489 std::string def4 = gmx::Path::stripExtension(expected4);
490 t_filenm fnm[] = {
491 { efTRX, "-f1", nullptr, ffREAD },
492 { efTRX, "-f2", nullptr, ffREAD },
493 { efTRX, "-f3", nullptr, ffREAD },
494 { efTRX, "-f4", def4.c_str(), ffREAD }
496 parseFromArgs(0, fnm, {});
497 EXPECT_EQ(expected1, opt2fn("-f1", nfile(), fnm));
498 EXPECT_EQ(expected2, opt2fn("-f2", nfile(), fnm));
499 EXPECT_EQ(expected3, opt2fn("-f3", nfile(), fnm));
500 EXPECT_EQ(expected4, opt2fn("-f4", nfile(), fnm));
503 TEST_F(ParseCommonArgsTest, CompletesExtensionFromExistingFileWithDefaultFileName)
505 t_filenm fnm[] = {
506 { efTRX, "-f1", nullptr, ffREAD },
507 { efSTO, "-f2", "foo", ffREAD },
508 { efTRX, "-f3", nullptr, ffREAD },
509 { efSTX, "-f4", nullptr, ffREAD }
511 args_.append("test");
512 std::string expected1 = addFileArg("-f1", "1.trr", efNoExtension);
513 std::string expected2 = addFileArg("-f2", ".pdb", efEmptyValue);
514 std::string expected3 = addFileArg("-f3", ".trr", efEmptyValue);
515 std::string expected4 = addFileArg(nullptr, ".pdb", efEmptyValue);
516 std::string deffnm = gmx::Path::stripExtension(expected3);
517 args_.append("-deffnm");
518 args_.append(deffnm);
519 parseFromArgs(PCA_CAN_SET_DEFFNM, fnm, {});
520 EXPECT_EQ(expected1, opt2fn("-f1", nfile(), fnm));
521 EXPECT_EQ(expected2, opt2fn("-f2", nfile(), fnm));
522 EXPECT_EQ(expected3, opt2fn("-f3", nfile(), fnm));
523 EXPECT_EQ(expected4, opt2fn("-f4", nfile(), fnm));
526 // This is needed e.g. for tune_pme, which passes unknown arguments on
527 // to child mdrun processes that it spawns.
528 TEST_F(ParseCommonArgsTest, CanKeepUnknownArgs)
530 int ivalue = 0;
531 gmx_bool bvalue = FALSE;
532 t_pargs pa[] = {
533 { "-i", FALSE, etINT, {&ivalue}, "Description" },
534 { "-b", FALSE, etBOOL, {&bvalue}, "Description" },
536 t_filenm fnm[] = {
537 { efXVG, "-o1", "out1", ffOPTWR },
538 { efXVG, "-o2", "out2", ffOPTWR }
540 const char *const cmdline[] = {
541 "test", "foo", "-unk", "-o1", "-unk2", "-o2", "test",
542 "-i", "2", "-unk3", "-b", "-unk4"
544 parseFromArray(cmdline, PCA_NOEXIT_ON_ARGS, fnm, pa);
545 EXPECT_EQ(2, ivalue);
546 EXPECT_TRUE(bvalue);
547 EXPECT_STREQ("out1.xvg", opt2fn_null("-o1", nfile(), fnm));
548 EXPECT_STREQ("test.xvg", opt2fn_null("-o2", nfile(), fnm));
549 EXPECT_EQ(6, args_.argc());
550 EXPECT_STREQ(cmdline[0], args_.arg(0));
551 EXPECT_STREQ(cmdline[1], args_.arg(1));
552 EXPECT_STREQ(cmdline[2], args_.arg(2));
553 EXPECT_STREQ(cmdline[4], args_.arg(3));
554 EXPECT_STREQ(cmdline[9], args_.arg(4));
555 EXPECT_STREQ(cmdline[11], args_.arg(5));
558 } // namespace