2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2013,2014, 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.
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 "gromacs/commandline/pargs.h"
49 #include <gtest/gtest.h>
51 #include "gromacs/utility/arrayref.h"
52 #include "gromacs/utility/file.h"
53 #include "gromacs/utility/path.h"
54 #include "gromacs/utility/stringutil.h"
56 #include "testutils/cmdlinetest.h"
57 #include "testutils/testasserts.h"
58 #include "testutils/testfilemanager.h"
63 using gmx::test::CommandLine;
65 class ParseCommonArgsTest : public ::testing::Test
76 : oenv_(NULL), fileCount_(0)
79 virtual ~ParseCommonArgsTest()
81 output_env_done(oenv_);
84 int nfile() const { return fileCount_; }
86 void parseFromArgs(unsigned long flags,
87 gmx::ArrayRef<t_filenm> fnm,
88 gmx::ArrayRef<t_pargs> pa)
90 fileCount_ = fnm.size();
91 bool bOk = parse_common_args(&args_.argc(), args_.argv(), flags,
92 fnm.size(), fnm.data(),
94 0, NULL, 0, NULL, &oenv_);
97 void parseFromArray(gmx::ConstArrayRef<const char *> cmdline,
99 gmx::ArrayRef<t_filenm> fnm,
100 gmx::ArrayRef<t_pargs> pa)
102 args_.initFromArray(cmdline);
103 parseFromArgs(flags, fnm, pa);
105 std::string addFileArg(const char *name, const char *extension,
106 FileArgumentType type)
108 std::string filename(tempFiles_.getTemporaryFilePath(extension));
109 gmx::File::writeFileFromString(filename, "Dummy file");
116 args_.append(filename);
119 args_.append(gmx::Path::stripExtension(filename));
128 // This must be a member that persists until the end of the test,
129 // because string arguments are not duplicated in the output.
135 gmx::test::TestFileManager tempFiles_;
138 /********************************************************************
139 * Tests for different types of options
142 TEST_F(ParseCommonArgsTest, ParsesIntegerArgs)
144 int value1 = 0, value2 = 0, value3 = 3;
146 { "-i1", FALSE, etINT, {&value1}, "Description" },
147 { "-i2", FALSE, etINT, {&value2}, "Description" },
148 { "-i3", FALSE, etINT, {&value3}, "Description" }
150 const char *const cmdline[] = {
151 "test", "-i1", "2", "-i2", "-3"
153 parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
154 EXPECT_EQ( 2, value1);
155 EXPECT_EQ(-3, value2);
156 EXPECT_EQ( 3, value3);
159 TEST_F(ParseCommonArgsTest, ParsesInt64Args)
161 gmx_int64_t value1 = 0, value2 = 0, value3 = 3;
163 { "-i1", FALSE, etINT64, {&value1}, "Description" },
164 { "-i2", FALSE, etINT64, {&value2}, "Description" },
165 { "-i3", FALSE, etINT64, {&value3}, "Description" }
167 const char *const cmdline[] = {
168 "test", "-i1", "2", "-i2", "-3"
170 parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
171 EXPECT_EQ( 2, value1);
172 EXPECT_EQ(-3, value2);
173 EXPECT_EQ( 3, value3);
176 TEST_F(ParseCommonArgsTest, ParsesRealArgs)
178 real value1 = 0.0, value2 = 0.0, value3 = 2.5;
180 { "-r1", FALSE, etREAL, {&value1}, "Description" },
181 { "-r2", FALSE, etREAL, {&value2}, "Description" },
182 { "-r3", FALSE, etREAL, {&value3}, "Description" }
184 const char *const cmdline[] = {
185 "test", "-r1", "2", "-r2", "-.5"
187 parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
188 EXPECT_EQ( 2.0, value1);
189 EXPECT_EQ(-0.5, value2);
190 EXPECT_EQ( 2.5, value3);
193 TEST_F(ParseCommonArgsTest, ParsesStringArgs)
195 const char *value1 = "def", *value2 = "", *value3 = "default";
197 { "-s1", FALSE, etSTR, {&value1}, "Description" },
198 { "-s2", FALSE, etSTR, {&value2}, "Description" },
199 { "-s3", FALSE, etSTR, {&value3}, "Description" }
201 const char *const cmdline[] = {
202 "test", "-s1", "", "-s2", "test"
204 parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
205 EXPECT_STREQ("", value1);
206 EXPECT_STREQ("test", value2);
207 EXPECT_STREQ("default", value3);
210 TEST_F(ParseCommonArgsTest, ParsesBooleanArgs)
212 gmx_bool value1 = TRUE, value2 = FALSE, value3 = TRUE;
214 { "-b1", FALSE, etBOOL, {&value1}, "Description" },
215 { "-b2", FALSE, etBOOL, {&value2}, "Description" },
216 { "-b3", FALSE, etBOOL, {&value3}, "Description" }
218 const char *const cmdline[] = {
219 "test", "-nob1", "-b2"
221 parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
222 EXPECT_FALSE(value1);
227 TEST_F(ParseCommonArgsTest, ParsesVectorArgs)
229 rvec value1 = {0, 0, 0}, value2 = {0, 0, 0}, value3 = {1, 2, 3};
231 { "-v1", FALSE, etRVEC, {&value1}, "Description" },
232 { "-v2", FALSE, etRVEC, {&value2}, "Description" },
233 { "-v3", FALSE, etRVEC, {&value3}, "Description" }
235 const char *const cmdline[] = {
236 "test", "-v1", "2", "1", "3", "-v2", "1"
238 parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
239 EXPECT_EQ(2.0, value1[XX]);
240 EXPECT_EQ(1.0, value1[YY]);
241 EXPECT_EQ(3.0, value1[ZZ]);
242 EXPECT_EQ(1.0, value2[XX]);
243 EXPECT_EQ(1.0, value2[YY]);
244 EXPECT_EQ(1.0, value2[ZZ]);
245 EXPECT_EQ(1.0, value3[XX]);
246 EXPECT_EQ(2.0, value3[YY]);
247 EXPECT_EQ(3.0, value3[ZZ]);
250 TEST_F(ParseCommonArgsTest, ParsesTimeArgs)
252 real value1 = 1.0, value2 = 2.0, value3 = 2.5;
254 { "-t1", FALSE, etTIME, {&value1}, "Description" },
255 { "-t2", FALSE, etTIME, {&value2}, "Description" },
256 { "-t3", FALSE, etTIME, {&value3}, "Description" }
258 const char *const cmdline[] = {
259 "test", "-t1", "2", "-t2", "-.5"
261 parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
262 EXPECT_EQ( 2.0, value1);
263 EXPECT_EQ(-0.5, value2);
264 EXPECT_EQ( 2.5, value3);
267 TEST_F(ParseCommonArgsTest, ParsesTimeArgsWithTimeUnit)
269 real value1 = 1.0, value2 = 2.0, value3 = 2.5;
271 { "-t1", FALSE, etTIME, {&value1}, "Description" },
272 { "-t2", FALSE, etTIME, {&value2}, "Description" },
273 { "-t3", FALSE, etTIME, {&value3}, "Description" }
275 const char *const cmdline[] = {
276 "test", "-t1", "2", "-t2", "-.5", "-tu", "ns"
278 parseFromArray(cmdline, PCA_TIME_UNIT, gmx::EmptyArrayRef(), pa);
279 EXPECT_EQ( 2000.0, value1);
280 EXPECT_EQ(-500.0, value2);
281 EXPECT_EQ( 2.5, value3);
284 TEST_F(ParseCommonArgsTest, ParsesEnumArgs)
286 const char *value1[] = {NULL, "none", "on", "off", NULL };
287 const char *value2[] = {NULL, "def", "value", "value_other", NULL };
288 const char *value3[] = {NULL, "default", "value", NULL };
290 { "-s1", FALSE, etENUM, {value1}, "Description" },
291 { "-s2", FALSE, etENUM, {value2}, "Description" },
292 { "-s3", FALSE, etENUM, {value3}, "Description" }
294 const char *const cmdline[] = {
295 "test", "-s1", "off", "-s2", "val"
297 parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
298 EXPECT_STREQ("off", value1[0]);
299 EXPECT_STREQ("value", value2[0]);
300 EXPECT_STREQ("default", value3[0]);
301 EXPECT_EQ(value1[nenum(value1)], value1[0]);
302 EXPECT_EQ(value2[nenum(value2)], value2[0]);
303 EXPECT_EQ(value3[nenum(value3)], value3[0]);
306 /********************************************************************
307 * Tests for file name options (output files, not dependent on file system)
310 TEST_F(ParseCommonArgsTest, ParsesFileArgs)
313 { efXVG, "-o1", "out1", ffOPTWR },
314 { efXVG, "-o2", "out2", ffOPTWR },
315 { efXVG, "-om", "outm", ffWRMULT },
316 { efXVG, "-om2", "outm2", ffWRMULT }
318 const char *const cmdline[] = {
319 "test", "-o1", "-o2", "test", "-om", "test1", "test2.xvg", "-om2"
321 parseFromArray(cmdline, 0, fnm, gmx::EmptyArrayRef());
322 EXPECT_STREQ("out1.xvg", opt2fn_null("-o1", nfile(), fnm));
323 EXPECT_STREQ("test.xvg", opt2fn_null("-o2", nfile(), fnm));
325 EXPECT_EQ(2, opt2fns(&files, "-om", nfile(), fnm));
326 EXPECT_TRUE(files != NULL);
329 EXPECT_STREQ("test1.xvg", files[0]);
330 EXPECT_STREQ("test2.xvg", files[1]);
332 EXPECT_STREQ("outm2.xvg", opt2fn("-om2", nfile(), fnm));
333 done_filenms(nfile(), fnm);
336 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaults)
339 { efTPS, NULL, NULL, ffWRITE },
340 { efTRX, "-f2", NULL, ffOPTWR },
341 { efTRX, "-f3", "trj3", ffWRITE },
342 { efXVG, "-o", "out", ffWRITE },
343 { efXVG, "-om", "outm", ffWRMULT }
345 const char *const cmdline[] = {
348 parseFromArray(cmdline, 0, fnm, gmx::EmptyArrayRef());
349 EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
350 EXPECT_STREQ("traj.xtc", opt2fn("-f2", nfile(), fnm));
351 EXPECT_NULL(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));
355 done_filenms(nfile(), fnm);
358 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaultFileName)
361 { efTPS, "-s", NULL, ffWRITE },
362 { efTRX, "-f2", NULL, ffWRITE },
363 { efTRX, "-f3", "trj3", ffWRITE },
364 { efXVG, "-o", "out", ffWRITE },
365 { efXVG, "-om", "outm", ffWRMULT }
367 const char *const cmdline[] = {
368 "test", "-deffnm", "def", "-f2", "other", "-o"
370 parseFromArray(cmdline, PCA_CAN_SET_DEFFNM, fnm, gmx::EmptyArrayRef());
371 EXPECT_STREQ("def.tpr", ftp2fn(efTPS, nfile(), fnm));
372 EXPECT_STREQ("other.xtc", opt2fn("-f2", nfile(), fnm));
373 EXPECT_STREQ("def.xtc", opt2fn("-f3", nfile(), fnm));
374 EXPECT_STREQ("def.xvg", opt2fn("-o", nfile(), fnm));
375 EXPECT_STREQ("def.xvg", opt2fn("-om", nfile(), fnm));
376 done_filenms(nfile(), fnm);
379 /********************************************************************
380 * Tests for file name options (input files, dependent on file system contents)
383 TEST_F(ParseCommonArgsTest, HandlesNonExistentInputFiles)
386 { efTPS, "-s", NULL, ffREAD },
387 { efTRX, "-f", "trj", ffREAD },
388 { efTRX, "-f2", "trj2", ffREAD },
389 { efTRX, "-f3", "trj3", ffREAD },
390 { efTRX, "-f4", "trj4", ffREAD },
391 { efGRO, "-g", "cnf", ffREAD },
392 { efGRO, "-g2", "cnf2", ffREAD }
394 const char *const cmdline[] = {
395 "test", "-f2", "-f3", "other", "-f4", "trj.gro", "-g2", "foo"
397 parseFromArray(cmdline, 0, fnm, gmx::EmptyArrayRef());
398 EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
399 EXPECT_STREQ("trj.xtc", opt2fn("-f", nfile(), fnm));
400 EXPECT_STREQ("trj2.xtc", opt2fn("-f2", nfile(), fnm));
401 EXPECT_STREQ("other.xtc", opt2fn("-f3", nfile(), fnm));
402 EXPECT_STREQ("trj.gro", opt2fn("-f4", nfile(), fnm));
403 EXPECT_STREQ("cnf.gro", opt2fn("-g", nfile(), fnm));
404 EXPECT_STREQ("foo.gro", opt2fn("-g2", nfile(), fnm));
405 done_filenms(nfile(), fnm);
408 TEST_F(ParseCommonArgsTest, HandlesCompressedFiles)
411 { efTRX, "-f", NULL, ffREAD },
412 { efGRO, "-g", NULL, ffREAD }
414 args_.append("test");
415 std::string expectedF = addFileArg("-f", ".pdb.gz", efFull);
416 std::string expectedG = addFileArg("-g", ".gro.Z", efFull);
417 expectedF = gmx::Path::stripExtension(expectedF);
418 expectedG = gmx::Path::stripExtension(expectedG);
419 parseFromArgs(0, fnm, gmx::EmptyArrayRef());
420 EXPECT_EQ(expectedF, opt2fn("-f", nfile(), fnm));
421 EXPECT_EQ(expectedG, opt2fn("-g", nfile(), fnm));
422 done_filenms(nfile(), fnm);
425 TEST_F(ParseCommonArgsTest, AcceptsUnknownTrajectoryExtension)
428 { efTRX, "-f", NULL, ffREAD }
430 args_.append("test");
431 std::string expected = addFileArg("-f", ".foo", efFull);
432 parseFromArgs(0, fnm, gmx::EmptyArrayRef());
433 EXPECT_EQ(expected, opt2fn("-f", nfile(), fnm));
434 done_filenms(nfile(), fnm);
437 TEST_F(ParseCommonArgsTest, CompletesExtensionFromExistingFile)
440 { efTRX, "-f1", NULL, ffREAD },
441 { efTRX, "-f2", NULL, ffREAD },
442 { efTRX, "-f3", NULL, ffREAD },
443 { efTRX, "-f4", NULL, ffREAD }
445 args_.append("test");
446 std::string expected1 = addFileArg("-f1", "1.xtc", efNoExtension);
447 std::string expected2 = addFileArg("-f2", "2.gro", efNoExtension);
448 std::string expected3 = addFileArg("-f3", "3.tng", efNoExtension);
449 std::string expected4 = addFileArg("-f4", ".gro", efEmptyValue);
450 std::string def4 = gmx::Path::stripExtension(expected4);
451 fnm[3].fn = def4.c_str();
452 parseFromArgs(0, fnm, gmx::EmptyArrayRef());
453 EXPECT_EQ(expected1, opt2fn("-f1", nfile(), fnm));
454 EXPECT_EQ(expected2, opt2fn("-f2", nfile(), fnm));
455 EXPECT_EQ(expected3, opt2fn("-f3", nfile(), fnm));
456 EXPECT_EQ(expected4, opt2fn("-f4", nfile(), fnm));
457 done_filenms(nfile(), fnm);
460 TEST_F(ParseCommonArgsTest, CompletesExtensionFromExistingFileWithDefaultFileName)
463 { efTRX, "-f1", NULL, ffREAD },
464 { efTPX, "-f2", "foo", ffREAD },
465 { efTRX, "-f3", NULL, ffREAD },
466 { efSTX, "-f4", NULL, ffREAD }
468 args_.append("test");
469 std::string expected1 = addFileArg("-f1", "1.trr", efNoExtension);
470 std::string expected2 = addFileArg("-f2", ".tpa", efEmptyValue);
471 std::string expected3 = addFileArg("-f3", ".trr", efEmptyValue);
472 std::string expected4 = addFileArg(NULL, ".pdb", efEmptyValue);
473 std::string deffnm = gmx::Path::stripExtension(expected3);
474 args_.append("-deffnm");
475 args_.append(deffnm);
476 parseFromArgs(PCA_CAN_SET_DEFFNM, fnm, gmx::EmptyArrayRef());
477 EXPECT_EQ(expected1, opt2fn("-f1", nfile(), fnm));
478 EXPECT_EQ(expected2, opt2fn("-f2", nfile(), fnm));
479 EXPECT_EQ(expected3, opt2fn("-f3", nfile(), fnm));
480 EXPECT_EQ(expected4, opt2fn("-f4", nfile(), fnm));
481 done_filenms(nfile(), fnm);
484 /********************************************************************
485 * Tests for general behavior
488 TEST_F(ParseCommonArgsTest, HandlesNonReadNode)
491 { efTPS, "-s", NULL, ffREAD },
492 { efTRX, "-f", NULL, ffREAD },
493 { efTRX, "-f2", NULL, ffREAD }
495 const char *const cmdline[] = {
496 "test", "-f", "-f2", "other"
498 parseFromArray(cmdline, PCA_NOT_READ_NODE, fnm, gmx::EmptyArrayRef());
499 EXPECT_NULL(fnm[0].fns);
500 EXPECT_NULL(fnm[1].fns);
501 EXPECT_NULL(fnm[2].fns);
502 done_filenms(nfile(), fnm);
505 TEST_F(ParseCommonArgsTest, HandlesNonReadNodeWithDefaultFileName)
508 { efTPS, "-s", NULL, ffREAD },
509 { efTRX, "-f", NULL, ffREAD },
510 { efTRX, "-f2", NULL, ffREAD }
512 const char *const cmdline[] = {
513 "test", "-deffnm", "def", "-f", "-f2", "other"
515 parseFromArray(cmdline, PCA_CAN_SET_DEFFNM | PCA_NOT_READ_NODE, fnm, gmx::EmptyArrayRef());
516 EXPECT_NULL(fnm[0].fns);
517 EXPECT_NULL(fnm[1].fns);
518 EXPECT_NULL(fnm[2].fns);
519 done_filenms(nfile(), fnm);
522 TEST_F(ParseCommonArgsTest, CanKeepUnknownArgs)
525 gmx_bool bvalue = FALSE;
527 { "-i", FALSE, etINT, {&ivalue}, "Description" },
528 { "-b", FALSE, etBOOL, {&bvalue}, "Description" },
531 { efXVG, "-o1", "out1", ffOPTWR },
532 { efXVG, "-o2", "out2", ffOPTWR }
534 const char *const cmdline[] = {
535 "test", "foo", "-unk", "-o1", "-unk2", "-o2", "test",
536 "-i", "2", "-unk3", "-b", "-unk4"
538 parseFromArray(cmdline, PCA_NOEXIT_ON_ARGS, fnm, pa);
539 EXPECT_EQ(2, ivalue);
541 EXPECT_STREQ("out1.xvg", opt2fn_null("-o1", nfile(), fnm));
542 EXPECT_STREQ("test.xvg", opt2fn_null("-o2", nfile(), fnm));
543 EXPECT_EQ(6, args_.argc());
544 EXPECT_STREQ(cmdline[0], args_.arg(0));
545 EXPECT_STREQ(cmdline[1], args_.arg(1));
546 EXPECT_STREQ(cmdline[2], args_.arg(2));
547 EXPECT_STREQ(cmdline[4], args_.arg(3));
548 EXPECT_STREQ(cmdline[9], args_.arg(4));
549 EXPECT_STREQ(cmdline[11], args_.arg(5));
550 done_filenms(nfile(), fnm);