c11259b77d3e2c4fec3f3c8ded75dec6b8836fd6
[alexxy/gromacs.git] / src / gromacs / commandline / tests / pargs.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \internal \file
36  * \brief
37  * Tests parse_common_args().
38  *
39  * Currently, negative tests are not possible, because those trigger
40  * gmx_fatal() and terminate the whole test binary.
41  *
42  * \author Teemu Murtola <teemu.murtola@gmail.com>
43  * \ingroup module_commandline
44  */
45 #include "gmxpre.h"
46
47 #include "gromacs/commandline/pargs.h"
48
49 #include <string>
50
51 #include <gtest/gtest.h>
52
53 #include "gromacs/utility/arrayref.h"
54 #include "gromacs/utility/file.h"
55 #include "gromacs/utility/path.h"
56 #include "gromacs/utility/stringutil.h"
57
58 #include "testutils/cmdlinetest.h"
59 #include "testutils/testasserts.h"
60 #include "testutils/testfilemanager.h"
61
62 namespace
63 {
64
65 using gmx::test::CommandLine;
66
67 class ParseCommonArgsTest : public ::testing::Test
68 {
69     public:
70         enum FileArgumentType
71         {
72             efFull,
73             efNoExtension,
74             efEmptyValue
75         };
76
77         ParseCommonArgsTest()
78             : oenv_(NULL), fileCount_(0)
79         {
80         }
81         virtual ~ParseCommonArgsTest()
82         {
83             output_env_done(oenv_);
84         }
85
86         int nfile() const { return fileCount_; }
87
88         void parseFromArgs(unsigned long           flags,
89                            gmx::ArrayRef<t_filenm> fnm,
90                            gmx::ArrayRef<t_pargs>  pa)
91         {
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, NULL, 0, NULL, &oenv_);
97             EXPECT_TRUE(bOk);
98         }
99         void parseFromArray(gmx::ConstArrayRef<const char *> cmdline,
100                             unsigned long                    flags,
101                             gmx::ArrayRef<t_filenm>          fnm,
102                             gmx::ArrayRef<t_pargs>           pa)
103         {
104             args_.initFromArray(cmdline);
105             parseFromArgs(flags, fnm, pa);
106         }
107         std::string addFileArg(const char *name, const char *extension,
108                                FileArgumentType type)
109         {
110             std::string filename(tempFiles_.getTemporaryFilePath(extension));
111             gmx::File::writeFileFromString(filename, "Dummy file");
112             if (name != NULL)
113             {
114                 args_.append(name);
115                 switch (type)
116                 {
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;
125                 }
126             }
127             return filename;
128         }
129
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_;
133
134     private:
135         output_env_t                oenv_;
136         size_t                      fileCount_;
137         gmx::test::TestFileManager  tempFiles_;
138 };
139
140 /********************************************************************
141  * Tests for different types of options
142  */
143
144 TEST_F(ParseCommonArgsTest, ParsesIntegerArgs)
145 {
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" }
151     };
152     const char *const cmdline[] = {
153         "test", "-i1", "2", "-i2", "-3"
154     };
155     parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
156     EXPECT_EQ( 2, value1);
157     EXPECT_EQ(-3, value2);
158     EXPECT_EQ( 3, value3);
159 }
160
161 TEST_F(ParseCommonArgsTest, ParsesInt64Args)
162 {
163     gmx_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" }
168     };
169     const char *const cmdline[] = {
170         "test", "-i1", "2", "-i2", "-3"
171     };
172     parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
173     EXPECT_EQ( 2, value1);
174     EXPECT_EQ(-3, value2);
175     EXPECT_EQ( 3, value3);
176 }
177
178 TEST_F(ParseCommonArgsTest, ParsesRealArgs)
179 {
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" }
185     };
186     const char *const cmdline[] = {
187         "test", "-r1", "2", "-r2", "-.5"
188     };
189     parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
190     EXPECT_EQ( 2.0, value1);
191     EXPECT_EQ(-0.5, value2);
192     EXPECT_EQ( 2.5, value3);
193 }
194
195 TEST_F(ParseCommonArgsTest, ParsesStringArgs)
196 {
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" }
202     };
203     const char *const cmdline[] = {
204         "test", "-s1", "", "-s2", "test"
205     };
206     parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
207     EXPECT_STREQ("", value1);
208     EXPECT_STREQ("test", value2);
209     EXPECT_STREQ("default", value3);
210 }
211
212 TEST_F(ParseCommonArgsTest, ParsesBooleanArgs)
213 {
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" }
219     };
220     const char *const cmdline[] = {
221         "test", "-nob1", "-b2"
222     };
223     parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
224     EXPECT_FALSE(value1);
225     EXPECT_TRUE(value2);
226     EXPECT_TRUE(value3);
227 }
228
229 TEST_F(ParseCommonArgsTest, ParsesVectorArgs)
230 {
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" }
236     };
237     const char *const cmdline[] = {
238         "test", "-v1", "2", "1", "3", "-v2", "1"
239     };
240     parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), 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]);
250 }
251
252 TEST_F(ParseCommonArgsTest, ParsesTimeArgs)
253 {
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" }
259     };
260     const char *const cmdline[] = {
261         "test", "-t1", "2", "-t2", "-.5"
262     };
263     parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), pa);
264     EXPECT_EQ( 2.0, value1);
265     EXPECT_EQ(-0.5, value2);
266     EXPECT_EQ( 2.5, value3);
267 }
268
269 TEST_F(ParseCommonArgsTest, ParsesTimeArgsWithTimeUnit)
270 {
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" }
276     };
277     const char *const cmdline[] = {
278         "test", "-t1", "2", "-t2", "-.5", "-tu", "ns"
279     };
280     parseFromArray(cmdline, PCA_TIME_UNIT, gmx::EmptyArrayRef(), pa);
281     EXPECT_EQ( 2000.0, value1);
282     EXPECT_EQ(-500.0, value2);
283     EXPECT_EQ( 2.5, value3);
284 }
285
286 TEST_F(ParseCommonArgsTest, ParsesEnumArgs)
287 {
288     const char       *value1[] = {NULL, "none", "on", "off", NULL };
289     const char       *value2[] = {NULL, "def", "value", "value_other", NULL };
290     const char       *value3[] = {NULL, "default", "value", NULL };
291     t_pargs           pa[]     = {
292         { "-s1", FALSE, etENUM, {value1}, "Description" },
293         { "-s2", FALSE, etENUM, {value2}, "Description" },
294         { "-s3", FALSE, etENUM, {value3}, "Description" }
295     };
296     const char *const cmdline[] = {
297         "test", "-s1", "off", "-s2", "val"
298     };
299     parseFromArray(cmdline, 0, gmx::EmptyArrayRef(), 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]);
306 }
307
308 /********************************************************************
309  * Tests for file name options (output files, not dependent on file system)
310  */
311
312 TEST_F(ParseCommonArgsTest, ParsesFileArgs)
313 {
314     t_filenm          fnm[] = {
315         { efXVG, "-o1", "out1", ffOPTWR },
316         { efXVG, "-o2", "out2", ffOPTWR },
317         { efXVG, "-om", "outm", ffWRMULT },
318         { efXVG, "-om2", "outm2", ffWRMULT }
319     };
320     const char *const cmdline[] = {
321         "test", "-o1", "-o2", "test", "-om", "test1", "test2.xvg", "-om2"
322     };
323     parseFromArray(cmdline, 0, fnm, gmx::EmptyArrayRef());
324     EXPECT_STREQ("out1.xvg", opt2fn_null("-o1", nfile(), fnm));
325     EXPECT_STREQ("test.xvg", opt2fn_null("-o2", nfile(), fnm));
326     char **files;
327     EXPECT_EQ(2, opt2fns(&files, "-om", nfile(), fnm));
328     EXPECT_TRUE(files != NULL);
329     if (files != NULL)
330     {
331         EXPECT_STREQ("test1.xvg", files[0]);
332         EXPECT_STREQ("test2.xvg", files[1]);
333     }
334     EXPECT_STREQ("outm2.xvg", opt2fn("-om2", nfile(), fnm));
335     done_filenms(nfile(), fnm);
336 }
337
338 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaults)
339 {
340     t_filenm          fnm[] = {
341         { efTPS, NULL,  NULL,   ffWRITE },
342         { efTRX, "-f2", NULL,   ffOPTWR },
343         { efTRX, "-f3", "trj3", ffWRITE },
344         { efXVG, "-o",  "out",  ffWRITE },
345         { efXVG, "-om", "outm", ffWRMULT }
346     };
347     const char *const cmdline[] = {
348         "test"
349     };
350     parseFromArray(cmdline, 0, fnm, gmx::EmptyArrayRef());
351     EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
352     EXPECT_STREQ("traj.xtc", opt2fn("-f2", nfile(), fnm));
353     EXPECT_NULL(opt2fn_null("-f2", nfile(), fnm));
354     EXPECT_STREQ("trj3.xtc", opt2fn("-f3", nfile(), fnm));
355     EXPECT_STREQ("out.xvg", opt2fn("-o", nfile(), fnm));
356     EXPECT_STREQ("outm.xvg", opt2fn("-om", nfile(), fnm));
357     done_filenms(nfile(), fnm);
358 }
359
360 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaultFileName)
361 {
362     t_filenm          fnm[] = {
363         { efTPS, "-s",  NULL,   ffWRITE },
364         { efTRX, "-f2", NULL,   ffWRITE },
365         { efTRX, "-f3", "trj3", ffWRITE },
366         { efXVG, "-o",  "out",  ffWRITE },
367         { efXVG, "-om", "outm", ffWRMULT }
368     };
369     const char *const cmdline[] = {
370         "test", "-deffnm", "def", "-f2", "other", "-o"
371     };
372     parseFromArray(cmdline, PCA_CAN_SET_DEFFNM, fnm, gmx::EmptyArrayRef());
373     EXPECT_STREQ("def.tpr", ftp2fn(efTPS, nfile(), fnm));
374     EXPECT_STREQ("other.xtc", opt2fn("-f2", nfile(), fnm));
375     EXPECT_STREQ("def.xtc", opt2fn("-f3", nfile(), fnm));
376     EXPECT_STREQ("def.xvg", opt2fn("-o", nfile(), fnm));
377     EXPECT_STREQ("def.xvg", opt2fn("-om", nfile(), fnm));
378     done_filenms(nfile(), fnm);
379 }
380
381 /********************************************************************
382  * Tests for file name options (input files, dependent on file system contents)
383  */
384
385 TEST_F(ParseCommonArgsTest, HandlesNonExistentInputFiles)
386 {
387     t_filenm          fnm[] = {
388         { efTPS, "-s",  NULL,   ffREAD },
389         { efTRX, "-f",  "trj",  ffREAD },
390         { efTRX, "-f2", "trj2", ffREAD },
391         { efTRX, "-f3", "trj3", ffREAD },
392         { efTRX, "-f4", "trj4", ffREAD },
393         { efGRO, "-g",  "cnf",  ffREAD },
394         { efGRO, "-g2", "cnf2", ffREAD }
395     };
396     const char *const cmdline[] = {
397         "test", "-f2", "-f3", "other", "-f4", "trj.gro", "-g2", "foo"
398     };
399     parseFromArray(cmdline, PCA_DISABLE_INPUT_FILE_CHECKING, fnm, gmx::EmptyArrayRef());
400     EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
401     EXPECT_STREQ("trj.xtc", opt2fn("-f", nfile(), fnm));
402     EXPECT_STREQ("trj2.xtc", opt2fn("-f2", nfile(), fnm));
403     EXPECT_STREQ("other.xtc", opt2fn("-f3", nfile(), fnm));
404     EXPECT_STREQ("trj.gro", opt2fn("-f4", nfile(), fnm));
405     EXPECT_STREQ("cnf.gro", opt2fn("-g", nfile(), fnm));
406     EXPECT_STREQ("foo.gro", opt2fn("-g2", nfile(), fnm));
407     done_filenms(nfile(), fnm);
408 }
409
410 TEST_F(ParseCommonArgsTest, HandlesNonExistentOptionalInputFiles)
411 {
412     t_filenm          fnm[] = {
413         { efTPS, "-s",  NULL,   ffOPTRD },
414         { efTRX, "-f",  "trj",  ffOPTRD }
415     };
416     const char *const cmdline[] = {
417         "test"
418     };
419     parseFromArray(cmdline, 0, fnm, gmx::EmptyArrayRef());
420     EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
421     EXPECT_STREQ("trj.xtc", opt2fn("-f", nfile(), fnm));
422     done_filenms(nfile(), fnm);
423 }
424
425 TEST_F(ParseCommonArgsTest, HandlesCompressedFiles)
426 {
427     t_filenm          fnm[] = {
428         { efTRX, "-f", NULL, ffREAD },
429         { efGRO, "-g", NULL, ffREAD }
430     };
431     args_.append("test");
432     std::string       expectedF = addFileArg("-f", ".pdb.gz", efFull);
433     std::string       expectedG = addFileArg("-g", ".gro.Z", efFull);
434     expectedF = gmx::Path::stripExtension(expectedF);
435     expectedG = gmx::Path::stripExtension(expectedG);
436     parseFromArgs(0, fnm, gmx::EmptyArrayRef());
437     EXPECT_EQ(expectedF, opt2fn("-f", nfile(), fnm));
438     EXPECT_EQ(expectedG, opt2fn("-g", nfile(), fnm));
439     done_filenms(nfile(), fnm);
440 }
441
442 TEST_F(ParseCommonArgsTest, AcceptsUnknownTrajectoryExtension)
443 {
444     t_filenm          fnm[] = {
445         { efTRX, "-f", NULL, ffREAD }
446     };
447     args_.append("test");
448     std::string       expected = addFileArg("-f", ".foo", efFull);
449     parseFromArgs(0, fnm, gmx::EmptyArrayRef());
450     EXPECT_EQ(expected, opt2fn("-f", nfile(), fnm));
451     done_filenms(nfile(), fnm);
452 }
453
454 TEST_F(ParseCommonArgsTest, CompletesExtensionFromExistingFile)
455 {
456     t_filenm          fnm[] = {
457         { efTRX, "-f1", NULL, ffREAD },
458         { efTRX, "-f2", NULL, ffREAD },
459         { efTRX, "-f3", NULL, ffREAD },
460         { efTRX, "-f4", NULL, ffREAD }
461     };
462     args_.append("test");
463     std::string       expected1 = addFileArg("-f1", "1.xtc", efNoExtension);
464     std::string       expected2 = addFileArg("-f2", "2.gro", efNoExtension);
465     std::string       expected3 = addFileArg("-f3", "3.tng", efNoExtension);
466     std::string       expected4 = addFileArg("-f4", ".gro", efEmptyValue);
467     std::string       def4      = gmx::Path::stripExtension(expected4);
468     fnm[3].fn = def4.c_str();
469     parseFromArgs(0, fnm, gmx::EmptyArrayRef());
470     EXPECT_EQ(expected1, opt2fn("-f1", nfile(), fnm));
471     EXPECT_EQ(expected2, opt2fn("-f2", nfile(), fnm));
472     EXPECT_EQ(expected3, opt2fn("-f3", nfile(), fnm));
473     EXPECT_EQ(expected4, opt2fn("-f4", nfile(), fnm));
474     done_filenms(nfile(), fnm);
475 }
476
477 TEST_F(ParseCommonArgsTest, CompletesExtensionFromExistingFileWithDefaultFileName)
478 {
479     t_filenm          fnm[] = {
480         { efTRX, "-f1", NULL,  ffREAD },
481         { efTPX, "-f2", "foo", ffREAD },
482         { efTRX, "-f3", NULL,  ffREAD },
483         { efSTX, "-f4", NULL,  ffREAD }
484     };
485     args_.append("test");
486     std::string       expected1 = addFileArg("-f1", "1.trr", efNoExtension);
487     std::string       expected2 = addFileArg("-f2", ".tpa", efEmptyValue);
488     std::string       expected3 = addFileArg("-f3", ".trr", efEmptyValue);
489     std::string       expected4 = addFileArg(NULL, ".pdb", efEmptyValue);
490     std::string       deffnm    = gmx::Path::stripExtension(expected3);
491     args_.append("-deffnm");
492     args_.append(deffnm);
493     parseFromArgs(PCA_CAN_SET_DEFFNM, fnm, gmx::EmptyArrayRef());
494     EXPECT_EQ(expected1, opt2fn("-f1", nfile(), fnm));
495     EXPECT_EQ(expected2, opt2fn("-f2", nfile(), fnm));
496     EXPECT_EQ(expected3, opt2fn("-f3", nfile(), fnm));
497     EXPECT_EQ(expected4, opt2fn("-f4", nfile(), fnm));
498     done_filenms(nfile(), fnm);
499 }
500
501 /********************************************************************
502  * Tests for general behavior
503  */
504
505 TEST_F(ParseCommonArgsTest, HandlesNonReadNode)
506 {
507     t_filenm          fnm[] = {
508         { efTPS, "-s",  NULL,  ffREAD },
509         { efTRX, "-f",  NULL,  ffREAD },
510         { efTRX, "-f2", NULL,  ffREAD }
511     };
512     const char *const cmdline[] = {
513         "test", "-f", "-f2", "other"
514     };
515     parseFromArray(cmdline, 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);
520 }
521
522 TEST_F(ParseCommonArgsTest, HandlesNonReadNodeWithDefaultFileName)
523 {
524     t_filenm          fnm[] = {
525         { efTPS, "-s",  NULL,  ffREAD },
526         { efTRX, "-f",  NULL,  ffREAD },
527         { efTRX, "-f2", NULL,  ffREAD }
528     };
529     const char *const cmdline[] = {
530         "test", "-deffnm", "def", "-f", "-f2", "other"
531     };
532     parseFromArray(cmdline, PCA_CAN_SET_DEFFNM | PCA_NOT_READ_NODE, fnm, gmx::EmptyArrayRef());
533     EXPECT_NULL(fnm[0].fns);
534     EXPECT_NULL(fnm[1].fns);
535     EXPECT_NULL(fnm[2].fns);
536     done_filenms(nfile(), fnm);
537 }
538
539 TEST_F(ParseCommonArgsTest, CanKeepUnknownArgs)
540 {
541     int               ivalue = 0;
542     gmx_bool          bvalue = FALSE;
543     t_pargs           pa[]   = {
544         { "-i", FALSE, etINT, {&ivalue}, "Description" },
545         { "-b", FALSE, etBOOL, {&bvalue}, "Description" },
546     };
547     t_filenm          fnm[] = {
548         { efXVG, "-o1", "out1", ffOPTWR },
549         { efXVG, "-o2", "out2", ffOPTWR }
550     };
551     const char *const cmdline[] = {
552         "test", "foo", "-unk", "-o1", "-unk2", "-o2", "test",
553         "-i", "2", "-unk3", "-b", "-unk4"
554     };
555     parseFromArray(cmdline, PCA_NOEXIT_ON_ARGS, fnm, pa);
556     EXPECT_EQ(2, ivalue);
557     EXPECT_TRUE(bvalue);
558     EXPECT_STREQ("out1.xvg", opt2fn_null("-o1", nfile(), fnm));
559     EXPECT_STREQ("test.xvg", opt2fn_null("-o2", nfile(), fnm));
560     EXPECT_EQ(6, args_.argc());
561     EXPECT_STREQ(cmdline[0],  args_.arg(0));
562     EXPECT_STREQ(cmdline[1],  args_.arg(1));
563     EXPECT_STREQ(cmdline[2],  args_.arg(2));
564     EXPECT_STREQ(cmdline[4],  args_.arg(3));
565     EXPECT_STREQ(cmdline[9],  args_.arg(4));
566     EXPECT_STREQ(cmdline[11], args_.arg(5));
567     done_filenms(nfile(), fnm);
568 }
569
570 } // namespace