f38c38ee121c20eca2309fe5496bbf2b745fda2e
[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,2015,2016,2017,2018, 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/path.h"
55 #include "gromacs/utility/stringutil.h"
56 #include "gromacs/utility/textwriter.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_(nullptr), 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, nullptr, 0, nullptr, &oenv_);
97             EXPECT_TRUE(bOk);
98         }
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)
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::TextWriter::writeFileFromString(filename, "Dummy file");
112             if (name != nullptr)
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         gmx_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     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[] = {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" }
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     gmx::ArrayRef<const std::string> files = opt2fns("-om", nfile(), fnm);
327     EXPECT_EQ(2, files.size());
328     if (files.size() != 2)
329     {
330         EXPECT_STREQ("test1.xvg", files[0].c_str());
331         EXPECT_STREQ("test2.xvg", files[1].c_str());
332     }
333     EXPECT_STREQ("outm2.xvg", opt2fn("-om2", nfile(), fnm));
334 }
335
336 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaults)
337 {
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 }
344     };
345     const char *const cmdline[] = {
346         "test"
347     };
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_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));
355 }
356
357 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaultFileName)
358 {
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 }
365     };
366     const char *const cmdline[] = {
367         "test", "-deffnm", "def", "-f2", "other", "-o"
368     };
369     parseFromArray(cmdline, PCA_CAN_SET_DEFFNM, fnm, gmx::EmptyArrayRef());
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));
375 }
376
377 TEST_F(ParseCommonArgsTest, ParseFileArgsWithCustomDefaultExtension)
378 {
379     t_filenm          fnm[] = {
380         { efTRX, "-o1", "conf1.gro", ffWRITE },
381         { efTRX, "-o2", "conf2.pdb", ffWRITE },
382         { efTRX, "-o3", "conf3.gro", ffWRITE }
383     };
384     const char *const cmdline[] = {
385         "test", "-o2", "-o3", "test"
386     };
387     parseFromArray(cmdline, PCA_CAN_SET_DEFFNM, fnm, gmx::EmptyArrayRef());
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));
391 }
392
393 /********************************************************************
394  * Tests for file name options (input files, dependent on file system contents)
395  */
396
397 TEST_F(ParseCommonArgsTest, HandlesNonExistentInputFiles)
398 {
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 }
407     };
408     const char *const cmdline[] = {
409         "test", "-f2", "-f3", "other", "-f4", "trj.gro", "-g2", "foo"
410     };
411     parseFromArray(cmdline, PCA_DISABLE_INPUT_FILE_CHECKING, fnm, gmx::EmptyArrayRef());
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));
419 }
420
421 TEST_F(ParseCommonArgsTest, HandlesNonExistentOptionalInputFiles)
422 {
423     t_filenm          fnm[] = {
424         { efTPS, "-s",  nullptr,   ffOPTRD },
425         { efTRX, "-f",  "trj",  ffOPTRD }
426     };
427     const char *const cmdline[] = {
428         "test"
429     };
430     parseFromArray(cmdline, 0, fnm, gmx::EmptyArrayRef());
431     EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
432     EXPECT_STREQ("trj.xtc", opt2fn("-f", nfile(), fnm));
433 }
434
435 TEST_F(ParseCommonArgsTest, AcceptsNonExistentInputFilesIfSpecified)
436 {
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 }
443     };
444     const char *const cmdline[] = {
445         "test", "-c2", "-c3", "nonexistent", "-c4", "nonexistent.cpt", "-f", "nonexistent"
446     };
447     parseFromArray(cmdline, 0, fnm, gmx::EmptyArrayRef());
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));
453 }
454
455 TEST_F(ParseCommonArgsTest, HandlesCompressedFiles)
456 {
457     t_filenm          fnm[] = {
458         { efTRX, "-f", nullptr, ffREAD },
459         { efGRO, "-g", nullptr, ffREAD }
460     };
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, gmx::EmptyArrayRef());
467     EXPECT_EQ(expectedF, opt2fn("-f", nfile(), fnm));
468     EXPECT_EQ(expectedG, opt2fn("-g", nfile(), fnm));
469 }
470
471 TEST_F(ParseCommonArgsTest, AcceptsUnknownTrajectoryExtension)
472 {
473     t_filenm          fnm[] = {
474         { efTRX, "-f", nullptr, ffREAD }
475     };
476     args_.append("test");
477     std::string       expected = addFileArg("-f", ".foo", efFull);
478     parseFromArgs(0, fnm, gmx::EmptyArrayRef());
479     EXPECT_EQ(expected, opt2fn("-f", nfile(), fnm));
480 }
481
482 TEST_F(ParseCommonArgsTest, CompletesExtensionFromExistingFile)
483 {
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 }
495     };
496     parseFromArgs(0, fnm, gmx::EmptyArrayRef());
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));
501 }
502
503 TEST_F(ParseCommonArgsTest, CompletesExtensionFromExistingFileWithDefaultFileName)
504 {
505     t_filenm          fnm[] = {
506         { efTRX, "-f1", nullptr,  ffREAD },
507         { efSTO, "-f2", "foo", ffREAD },
508         { efTRX, "-f3", nullptr,  ffREAD },
509         { efSTX, "-f4", nullptr,  ffREAD }
510     };
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, gmx::EmptyArrayRef());
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));
524 }
525
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)
529 {
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" },
535     };
536     t_filenm          fnm[] = {
537         { efXVG, "-o1", "out1", ffOPTWR },
538         { efXVG, "-o2", "out2", ffOPTWR }
539     };
540     const char *const cmdline[] = {
541         "test", "foo", "-unk", "-o1", "-unk2", "-o2", "test",
542         "-i", "2", "-unk3", "-b", "-unk4"
543     };
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));
556 }
557
558 } // namespace