SYCL: Avoid using no_init read accessor in rocFFT
[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 by the GROMACS development team.
5  * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \internal \file
37  * \brief
38  * Tests parse_common_args().
39  *
40  * Currently, negative tests are not possible, because those trigger
41  * gmx_fatal() and terminate the whole test binary.
42  *
43  * \author Teemu Murtola <teemu.murtola@gmail.com>
44  * \ingroup module_commandline
45  */
46 #include "gmxpre.h"
47
48 #include "gromacs/commandline/pargs.h"
49
50 #include <string>
51
52 #include <gtest/gtest.h>
53
54 #include "gromacs/utility/arrayref.h"
55 #include "gromacs/utility/path.h"
56 #include "gromacs/utility/stringutil.h"
57 #include "gromacs/utility/textwriter.h"
58
59 #include "testutils/cmdlinetest.h"
60 #include "testutils/testasserts.h"
61 #include "testutils/testfilemanager.h"
62
63 namespace
64 {
65
66 using gmx::test::CommandLine;
67
68 class ParseCommonArgsTest : public ::testing::Test
69 {
70 public:
71     enum FileArgumentType
72     {
73         efFull,
74         efNoExtension,
75         efEmptyValue
76     };
77
78     ParseCommonArgsTest() : oenv_(nullptr), fileCount_(0) {}
79     ~ParseCommonArgsTest() override { output_env_done(oenv_); }
80
81     int nfile() const { return fileCount_; }
82
83     void parseFromArgs(unsigned long flags, gmx::ArrayRef<t_filenm> fnm, gmx::ArrayRef<t_pargs> pa)
84     {
85         fileCount_ = fnm.size();
86         bool bOk   = parse_common_args(
87                 &args_.argc(), args_.argv(), flags, fnm.size(), fnm.data(), pa.size(), pa.data(), 0, nullptr, 0, nullptr, &oenv_);
88         EXPECT_TRUE(bOk);
89     }
90     void parseFromArray(gmx::ArrayRef<const char* const> cmdline,
91                         unsigned long                    flags,
92                         gmx::ArrayRef<t_filenm>          fnm,
93                         gmx::ArrayRef<t_pargs>           pa)
94     {
95         args_.initFromArray(cmdline);
96         parseFromArgs(flags, fnm, pa);
97     }
98     std::string addFileArg(const char* name, const char* extension, FileArgumentType type)
99     {
100         std::string filename(tempFiles_.getTemporaryFilePath(extension));
101         gmx::TextWriter::writeFileFromString(filename, "Dummy file");
102         if (name != nullptr)
103         {
104             args_.append(name);
105             switch (type)
106             {
107                 case efFull: args_.append(filename); break;
108                 case efNoExtension: args_.append(gmx::Path::stripExtension(filename)); break;
109                 case efEmptyValue: break;
110             }
111         }
112         return filename;
113     }
114
115     // This must be a member that persists until the end of the test,
116     // because string arguments are not duplicated in the output.
117     CommandLine args_;
118
119 private:
120     gmx_output_env_t*          oenv_;
121     size_t                     fileCount_;
122     gmx::test::TestFileManager tempFiles_;
123 };
124
125 /********************************************************************
126  * Tests for different types of options
127  */
128
129 TEST_F(ParseCommonArgsTest, ParsesIntegerArgs)
130 {
131     int               value1 = 0, value2 = 0, value3 = 3;
132     t_pargs           pa[]      = { { "-i1", FALSE, etINT, { &value1 }, "Description" },
133                      { "-i2", FALSE, etINT, { &value2 }, "Description" },
134                      { "-i3", FALSE, etINT, { &value3 }, "Description" } };
135     const char* const cmdline[] = { "test", "-i1", "2", "-i2", "-3" };
136     parseFromArray(cmdline, 0, {}, pa);
137     EXPECT_EQ(2, value1);
138     EXPECT_EQ(-3, value2);
139     EXPECT_EQ(3, value3);
140 }
141
142 TEST_F(ParseCommonArgsTest, ParsesInt64Args)
143 {
144     int64_t           value1 = 0, value2 = 0, value3 = 3;
145     t_pargs           pa[]      = { { "-i1", FALSE, etINT64, { &value1 }, "Description" },
146                      { "-i2", FALSE, etINT64, { &value2 }, "Description" },
147                      { "-i3", FALSE, etINT64, { &value3 }, "Description" } };
148     const char* const cmdline[] = { "test", "-i1", "2", "-i2", "-3" };
149     parseFromArray(cmdline, 0, {}, pa);
150     EXPECT_EQ(2, value1);
151     EXPECT_EQ(-3, value2);
152     EXPECT_EQ(3, value3);
153 }
154
155 TEST_F(ParseCommonArgsTest, ParsesRealArgs)
156 {
157     real              value1 = 0.0, value2 = 0.0, value3 = 2.5;
158     t_pargs           pa[]      = { { "-r1", FALSE, etREAL, { &value1 }, "Description" },
159                      { "-r2", FALSE, etREAL, { &value2 }, "Description" },
160                      { "-r3", FALSE, etREAL, { &value3 }, "Description" } };
161     const char* const cmdline[] = { "test", "-r1", "2", "-r2", "-.5" };
162     parseFromArray(cmdline, 0, {}, pa);
163     EXPECT_EQ(2.0, value1);
164     EXPECT_EQ(-0.5, value2);
165     EXPECT_EQ(2.5, value3);
166 }
167
168 TEST_F(ParseCommonArgsTest, ParsesStringArgs)
169 {
170     const char *      value1 = "def", *value2 = "", *value3 = "default";
171     t_pargs           pa[]      = { { "-s1", FALSE, etSTR, { &value1 }, "Description" },
172                      { "-s2", FALSE, etSTR, { &value2 }, "Description" },
173                      { "-s3", FALSE, etSTR, { &value3 }, "Description" } };
174     const char* const cmdline[] = { "test", "-s1", "", "-s2", "test" };
175     parseFromArray(cmdline, 0, {}, pa);
176     EXPECT_STREQ("", value1);
177     EXPECT_STREQ("test", value2);
178     EXPECT_STREQ("default", value3);
179 }
180
181 TEST_F(ParseCommonArgsTest, ParsesBooleanArgs)
182 {
183     gmx_bool          value1 = TRUE, value2 = FALSE, value3 = TRUE;
184     t_pargs           pa[]      = { { "-b1", FALSE, etBOOL, { &value1 }, "Description" },
185                      { "-b2", FALSE, etBOOL, { &value2 }, "Description" },
186                      { "-b3", FALSE, etBOOL, { &value3 }, "Description" } };
187     const char* const cmdline[] = { "test", "-nob1", "-b2" };
188     parseFromArray(cmdline, 0, {}, pa);
189     EXPECT_FALSE(value1);
190     EXPECT_TRUE(value2);
191     EXPECT_TRUE(value3);
192 }
193
194 TEST_F(ParseCommonArgsTest, ParsesBooleanArgsToValuesOfSuitableEnum)
195 {
196     enum class LikeBool : bool
197     {
198         No  = false,
199         Yes = true
200     };
201     // Set up the default values
202     LikeBool value1 = LikeBool::No;
203     LikeBool value2 = LikeBool::No;
204     LikeBool value3 = LikeBool::No;
205     LikeBool value4 = LikeBool::Yes;
206     LikeBool value5 = LikeBool::Yes;
207     LikeBool value6 = LikeBool::Yes;
208     // Set up 6 options
209     t_pargs pa[] = { { "-b1", FALSE, etBOOL, { &value1 }, "Description" },
210                      { "-b2", FALSE, etBOOL, { &value2 }, "Description" },
211                      { "-b3", FALSE, etBOOL, { &value3 }, "Description" },
212                      { "-b4", FALSE, etBOOL, { &value4 }, "Description" },
213                      { "-b5", FALSE, etBOOL, { &value5 }, "Description" },
214                      { "-b6", FALSE, etBOOL, { &value6 }, "Description" } };
215     // Set some to yes and no, leave some as default
216     const char* const cmdline[] = { "test", "-b1", "-nob2", "-b4", "-nob5" };
217
218     parseFromArray(cmdline, 0, {}, pa);
219     // Expetactions
220     EXPECT_EQ(value1, LikeBool::Yes) << "set to yes";
221     EXPECT_EQ(value2, LikeBool::No) << "set to no";
222     EXPECT_EQ(value3, LikeBool::No) << "preserves the default of no";
223     EXPECT_EQ(value4, LikeBool::Yes) << "set to yes";
224     EXPECT_EQ(value5, LikeBool::No) << "set to no";
225     EXPECT_EQ(value6, LikeBool::Yes) << "preserves the default of yes";
226 }
227
228 TEST_F(ParseCommonArgsTest, ParsesVectorArgs)
229 {
230     rvec              value1 = { 0, 0, 0 }, value2 = { 0, 0, 0 }, value3 = { 1, 2, 3 };
231     t_pargs           pa[]      = { { "-v1", FALSE, etRVEC, { &value1 }, "Description" },
232                      { "-v2", FALSE, etRVEC, { &value2 }, "Description" },
233                      { "-v3", FALSE, etRVEC, { &value3 }, "Description" } };
234     const char* const cmdline[] = { "test", "-v1", "2", "1", "3", "-v2", "1" };
235     parseFromArray(cmdline, 0, {}, pa);
236     EXPECT_EQ(2.0, value1[XX]);
237     EXPECT_EQ(1.0, value1[YY]);
238     EXPECT_EQ(3.0, value1[ZZ]);
239     EXPECT_EQ(1.0, value2[XX]);
240     EXPECT_EQ(1.0, value2[YY]);
241     EXPECT_EQ(1.0, value2[ZZ]);
242     EXPECT_EQ(1.0, value3[XX]);
243     EXPECT_EQ(2.0, value3[YY]);
244     EXPECT_EQ(3.0, value3[ZZ]);
245 }
246
247 TEST_F(ParseCommonArgsTest, ParsesTimeArgs)
248 {
249     real              value1 = 1.0, value2 = 2.0, value3 = 2.5;
250     t_pargs           pa[]      = { { "-t1", FALSE, etTIME, { &value1 }, "Description" },
251                      { "-t2", FALSE, etTIME, { &value2 }, "Description" },
252                      { "-t3", FALSE, etTIME, { &value3 }, "Description" } };
253     const char* const cmdline[] = { "test", "-t1", "2", "-t2", "-.5" };
254     parseFromArray(cmdline, 0, {}, pa);
255     EXPECT_EQ(2.0, value1);
256     EXPECT_EQ(-0.5, value2);
257     EXPECT_EQ(2.5, value3);
258 }
259
260 TEST_F(ParseCommonArgsTest, ParsesTimeArgsWithTimeUnit)
261 {
262     real              value1 = 1.0, value2 = 2.0, value3 = 2.5;
263     t_pargs           pa[]      = { { "-t1", FALSE, etTIME, { &value1 }, "Description" },
264                      { "-t2", FALSE, etTIME, { &value2 }, "Description" },
265                      { "-t3", FALSE, etTIME, { &value3 }, "Description" } };
266     const char* const cmdline[] = { "test", "-t1", "2", "-t2", "-.5", "-tu", "ns" };
267     parseFromArray(cmdline, PCA_TIME_UNIT, {}, pa);
268     EXPECT_EQ(2000.0, value1);
269     EXPECT_EQ(-500.0, value2);
270     EXPECT_EQ(2.5, value3);
271 }
272
273 TEST_F(ParseCommonArgsTest, ParsesEnumArgs)
274 {
275     const char*       value1[]  = { nullptr, "none", "on", "off", nullptr };
276     const char*       value2[]  = { nullptr, "def", "value", "value_other", nullptr };
277     const char*       value3[]  = { nullptr, "default", "value", nullptr };
278     t_pargs           pa[]      = { { "-s1", FALSE, etENUM, { value1 }, "Description" },
279                      { "-s2", FALSE, etENUM, { value2 }, "Description" },
280                      { "-s3", FALSE, etENUM, { value3 }, "Description" } };
281     const char* const cmdline[] = { "test", "-s1", "off", "-s2", "val" };
282     parseFromArray(cmdline, 0, {}, pa);
283     EXPECT_STREQ("off", value1[0]);
284     EXPECT_STREQ("value", value2[0]);
285     EXPECT_STREQ("default", value3[0]);
286     EXPECT_EQ(value1[nenum(value1)], value1[0]);
287     EXPECT_EQ(value2[nenum(value2)], value2[0]);
288     EXPECT_EQ(value3[nenum(value3)], value3[0]);
289 }
290
291 /********************************************************************
292  * Tests for file name options (output files, not dependent on file system)
293  */
294
295 TEST_F(ParseCommonArgsTest, ParsesFileArgs)
296 {
297     t_filenm          fnm[]     = { { efXVG, "-o1", "out1", ffOPTWR },
298                        { efXVG, "-o2", "out2", ffOPTWR },
299                        { efXVG, "-om", "outm", ffWRMULT },
300                        { efXVG, "-om2", "outm2", ffWRMULT } };
301     const char* const cmdline[] = { "test", "-o1",   "-o2",       "test",
302                                     "-om",  "test1", "test2.xvg", "-om2" };
303     parseFromArray(cmdline, 0, fnm, {});
304     EXPECT_STREQ("out1.xvg", opt2fn_null("-o1", nfile(), fnm));
305     EXPECT_STREQ("test.xvg", opt2fn_null("-o2", nfile(), fnm));
306     gmx::ArrayRef<const std::string> files = opt2fns("-om", nfile(), fnm);
307     EXPECT_EQ(2, files.size());
308     if (files.size() != 2)
309     {
310         EXPECT_STREQ("test1.xvg", files[0].c_str());
311         EXPECT_STREQ("test2.xvg", files[1].c_str());
312     }
313     EXPECT_STREQ("outm2.xvg", opt2fn("-om2", nfile(), fnm));
314 }
315
316 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaults)
317 {
318     t_filenm          fnm[]     = { { efTPS, nullptr, nullptr, ffWRITE },
319                        { efTRX, "-f2", nullptr, ffOPTWR },
320                        { efTRX, "-f3", "trj3", ffWRITE },
321                        { efXVG, "-o", "out", ffWRITE },
322                        { efXVG, "-om", "outm", ffWRMULT } };
323     const char* const cmdline[] = { "test" };
324     parseFromArray(cmdline, 0, fnm, {});
325     EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
326     EXPECT_STREQ("traj.xtc", opt2fn("-f2", nfile(), fnm));
327     EXPECT_EQ(nullptr, opt2fn_null("-f2", nfile(), fnm));
328     EXPECT_STREQ("trj3.xtc", opt2fn("-f3", nfile(), fnm));
329     EXPECT_STREQ("out.xvg", opt2fn("-o", nfile(), fnm));
330     EXPECT_STREQ("outm.xvg", opt2fn("-om", nfile(), fnm));
331 }
332
333 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaultFileName)
334 {
335     t_filenm          fnm[]     = { { efTPS, "-s", nullptr, ffWRITE },
336                        { efTRX, "-f2", nullptr, ffWRITE },
337                        { efTRX, "-f3", "trj3", ffWRITE },
338                        { efXVG, "-o", "out", ffWRITE },
339                        { efXVG, "-om", "outm", ffWRMULT } };
340     const char* const cmdline[] = { "test", "-deffnm", "def", "-f2", "other", "-o" };
341     parseFromArray(cmdline, PCA_CAN_SET_DEFFNM, fnm, {});
342     EXPECT_STREQ("def.tpr", ftp2fn(efTPS, nfile(), fnm));
343     EXPECT_STREQ("other.xtc", opt2fn("-f2", nfile(), fnm));
344     EXPECT_STREQ("def.xtc", opt2fn("-f3", nfile(), fnm));
345     EXPECT_STREQ("def.xvg", opt2fn("-o", nfile(), fnm));
346     EXPECT_STREQ("def.xvg", opt2fn("-om", nfile(), fnm));
347 }
348
349 TEST_F(ParseCommonArgsTest, ParseFileArgsWithCustomDefaultExtension)
350 {
351     t_filenm          fnm[]     = { { efTRX, "-o1", "conf1.gro", ffWRITE },
352                        { efTRX, "-o2", "conf2.pdb", ffWRITE },
353                        { efTRX, "-o3", "conf3.gro", ffWRITE } };
354     const char* const cmdline[] = { "test", "-o2", "-o3", "test" };
355     parseFromArray(cmdline, PCA_CAN_SET_DEFFNM, fnm, {});
356     EXPECT_STREQ("conf1.gro", opt2fn("-o1", nfile(), fnm));
357     EXPECT_STREQ("conf2.pdb", opt2fn("-o2", nfile(), fnm));
358     EXPECT_STREQ("test.gro", opt2fn("-o3", nfile(), fnm));
359 }
360
361 /********************************************************************
362  * Tests for file name options (input files, dependent on file system contents)
363  */
364
365 TEST_F(ParseCommonArgsTest, HandlesNonExistentInputFiles)
366 {
367     t_filenm          fnm[] = { { efTPS, "-s", nullptr, ffREAD }, { efTRX, "-f", "trj", ffREAD },
368                        { efTRX, "-f2", "trj2", ffREAD }, { efTRX, "-f3", "trj3", ffREAD },
369                        { efTRX, "-f4", "trj4", ffREAD }, { efGRO, "-g", "cnf", ffREAD },
370                        { efGRO, "-g2", "cnf2", ffREAD } };
371     const char* const cmdline[] = { "test", "-f2", "-f3", "other", "-f4", "trj.gro", "-g2", "foo" };
372     parseFromArray(cmdline, PCA_DISABLE_INPUT_FILE_CHECKING, fnm, {});
373     EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
374     EXPECT_STREQ("trj.xtc", opt2fn("-f", nfile(), fnm));
375     EXPECT_STREQ("trj2.xtc", opt2fn("-f2", nfile(), fnm));
376     EXPECT_STREQ("other.xtc", opt2fn("-f3", nfile(), fnm));
377     EXPECT_STREQ("trj.gro", opt2fn("-f4", nfile(), fnm));
378     EXPECT_STREQ("cnf.gro", opt2fn("-g", nfile(), fnm));
379     EXPECT_STREQ("foo.gro", opt2fn("-g2", nfile(), fnm));
380 }
381
382 TEST_F(ParseCommonArgsTest, HandlesNonExistentOptionalInputFiles)
383 {
384     t_filenm fnm[] = { { efTPS, "-s", nullptr, ffOPTRD }, { efTRX, "-f", "trj", ffOPTRD } };
385     const char* const cmdline[] = { "test" };
386     parseFromArray(cmdline, 0, fnm, {});
387     EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
388     EXPECT_STREQ("trj.xtc", opt2fn("-f", nfile(), fnm));
389 }
390
391 TEST_F(ParseCommonArgsTest, AcceptsNonExistentInputFilesIfSpecified)
392 {
393     t_filenm          fnm[]     = { { efCPT, "-c", "file1", ffOPTRD | ffALLOW_MISSING },
394                        { efCPT, "-c2", "file2", ffOPTRD | ffALLOW_MISSING },
395                        { efCPT, "-c3", "file3", ffOPTRD | ffALLOW_MISSING },
396                        { efCPT, "-c4", "file4", ffOPTRD | ffALLOW_MISSING },
397                        { efTRX, "-f", "trj", ffOPTRD | ffALLOW_MISSING } };
398     const char* const cmdline[] = { "test",        "-c2",        "-c3",
399                                     "nonexistent", "-c4",        "nonexistent.cpt",
400                                     "-f",          "nonexistent" };
401     parseFromArray(cmdline, 0, fnm, {});
402     EXPECT_STREQ("file1.cpt", opt2fn("-c", nfile(), fnm));
403     EXPECT_STREQ("file2.cpt", opt2fn("-c2", nfile(), fnm));
404     EXPECT_STREQ("nonexistent.cpt", opt2fn("-c3", nfile(), fnm));
405     EXPECT_STREQ("nonexistent.cpt", opt2fn("-c4", nfile(), fnm));
406     EXPECT_STREQ("nonexistent.xtc", opt2fn("-f", nfile(), fnm));
407 }
408
409 TEST_F(ParseCommonArgsTest, HandlesCompressedFiles)
410 {
411     t_filenm fnm[] = { { efTRX, "-f", nullptr, ffREAD }, { efGRO, "-g", nullptr, ffREAD } };
412     args_.append("test");
413     std::string expectedF = addFileArg("-f", ".pdb.gz", efFull);
414     std::string expectedG = addFileArg("-g", ".gro.Z", efFull);
415     expectedF             = gmx::Path::stripExtension(expectedF);
416     expectedG             = gmx::Path::stripExtension(expectedG);
417     parseFromArgs(0, fnm, {});
418     EXPECT_EQ(expectedF, opt2fn("-f", nfile(), fnm));
419     EXPECT_EQ(expectedG, opt2fn("-g", nfile(), fnm));
420 }
421
422 TEST_F(ParseCommonArgsTest, AcceptsUnknownTrajectoryExtension)
423 {
424     t_filenm fnm[] = { { efTRX, "-f", nullptr, ffREAD } };
425     args_.append("test");
426     std::string expected = addFileArg("-f", ".foo", efFull);
427     parseFromArgs(0, fnm, {});
428     EXPECT_EQ(expected, opt2fn("-f", nfile(), fnm));
429 }
430
431 TEST_F(ParseCommonArgsTest, CompletesExtensionFromExistingFile)
432 {
433     args_.append("test");
434     std::string expected1 = addFileArg("-f1", "1.xtc", efNoExtension);
435     std::string expected2 = addFileArg("-f2", "2.gro", efNoExtension);
436     std::string expected3 = addFileArg("-f3", "3.tng", efNoExtension);
437     std::string expected4 = addFileArg("-f4", ".gro", efEmptyValue);
438     std::string def4      = gmx::Path::stripExtension(expected4);
439     t_filenm    fnm[]     = { { efTRX, "-f1", nullptr, ffREAD },
440                        { efTRX, "-f2", nullptr, ffREAD },
441                        { efTRX, "-f3", nullptr, ffREAD },
442                        { efTRX, "-f4", def4.c_str(), ffREAD } };
443     parseFromArgs(0, fnm, {});
444     EXPECT_EQ(expected1, opt2fn("-f1", nfile(), fnm));
445     EXPECT_EQ(expected2, opt2fn("-f2", nfile(), fnm));
446     EXPECT_EQ(expected3, opt2fn("-f3", nfile(), fnm));
447     EXPECT_EQ(expected4, opt2fn("-f4", nfile(), fnm));
448 }
449
450 TEST_F(ParseCommonArgsTest, CompletesExtensionFromExistingFileWithDefaultFileName)
451 {
452     t_filenm fnm[] = { { efTRX, "-f1", nullptr, ffREAD },
453                        { efSTO, "-f2", "foo", ffREAD },
454                        { efTRX, "-f3", nullptr, ffREAD },
455                        { efSTX, "-f4", nullptr, ffREAD } };
456     args_.append("test");
457     std::string expected1 = addFileArg("-f1", "1.trr", efNoExtension);
458     std::string expected2 = addFileArg("-f2", ".pdb", efEmptyValue);
459     std::string expected3 = addFileArg("-f3", ".trr", efEmptyValue);
460     std::string expected4 = addFileArg(nullptr, ".pdb", efEmptyValue);
461     std::string deffnm    = gmx::Path::stripExtension(expected3);
462     args_.append("-deffnm");
463     args_.append(deffnm);
464     parseFromArgs(PCA_CAN_SET_DEFFNM, fnm, {});
465     EXPECT_EQ(expected1, opt2fn("-f1", nfile(), fnm));
466     EXPECT_EQ(expected2, opt2fn("-f2", nfile(), fnm));
467     EXPECT_EQ(expected3, opt2fn("-f3", nfile(), fnm));
468     EXPECT_EQ(expected4, opt2fn("-f4", nfile(), fnm));
469 }
470
471 // This is needed e.g. for tune_pme, which passes unknown arguments on
472 // to child mdrun processes that it spawns.
473 TEST_F(ParseCommonArgsTest, CanKeepUnknownArgs)
474 {
475     int      ivalue = 0;
476     gmx_bool bvalue = FALSE;
477     t_pargs  pa[]   = {
478         { "-i", FALSE, etINT, { &ivalue }, "Description" },
479         { "-b", FALSE, etBOOL, { &bvalue }, "Description" },
480     };
481     t_filenm fnm[] = { { efXVG, "-o1", "out1", ffOPTWR }, { efXVG, "-o2", "out2", ffOPTWR } };
482     const char* const cmdline[] = { "test", "foo", "-unk", "-o1",   "-unk2", "-o2",
483                                     "test", "-i",  "2",    "-unk3", "-b",    "-unk4" };
484     parseFromArray(cmdline, PCA_NOEXIT_ON_ARGS, fnm, pa);
485     EXPECT_EQ(2, ivalue);
486     EXPECT_TRUE(bvalue);
487     EXPECT_STREQ("out1.xvg", opt2fn_null("-o1", nfile(), fnm));
488     EXPECT_STREQ("test.xvg", opt2fn_null("-o2", nfile(), fnm));
489     EXPECT_EQ(6, args_.argc());
490     EXPECT_STREQ(cmdline[0], args_.arg(0));
491     EXPECT_STREQ(cmdline[1], args_.arg(1));
492     EXPECT_STREQ(cmdline[2], args_.arg(2));
493     EXPECT_STREQ(cmdline[4], args_.arg(3));
494     EXPECT_STREQ(cmdline[9], args_.arg(4));
495     EXPECT_STREQ(cmdline[11], args_.arg(5));
496 }
497
498 } // namespace