Merge branch release-4-6
[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  * \todo
43  * Add tests that exercise the machinery triggered by ffREAD to detect the
44  * extension for certain types of files.  Also some other paths in the file
45  * name logic may not get tested by the current set.
46  *
47  * \author Teemu Murtola <teemu.murtola@gmail.com>
48  * \ingroup module_commandline
49  */
50 #include <gtest/gtest.h>
51
52 #include "gromacs/commandline/pargs.h"
53 #include "gromacs/utility/arrayref.h"
54
55 #include "testutils/cmdlinetest.h"
56 #include "testutils/testasserts.h"
57
58 namespace
59 {
60
61 using gmx::test::CommandLine;
62
63 class ParseCommonArgsTest : public ::testing::Test
64 {
65     public:
66         ParseCommonArgsTest()
67             : oenv_(NULL), fileCount_(0)
68         {
69         }
70         virtual ~ParseCommonArgsTest()
71         {
72             output_env_done(oenv_);
73         }
74
75         int nfile() const { return fileCount_; }
76
77         void parse(gmx::ConstArrayRef<const char *> cmdline,
78                    unsigned long                    flags,
79                    gmx::ArrayRef<t_filenm>          fnm,
80                    gmx::ArrayRef<t_pargs>           pa)
81         {
82             args_.initFromArray(cmdline);
83             fileCount_ = fnm.size();
84             bool bOk = parse_common_args(&args_.argc(), args_.argv(), flags,
85                                          fnm.size(), fnm.data(),
86                                          pa.size(), pa.data(),
87                                          0, NULL, 0, NULL, &oenv_);
88             EXPECT_TRUE(bOk);
89         }
90
91         CommandLine              args_;
92         output_env_t             oenv_;
93
94     private:
95         size_t                   fileCount_;
96 };
97
98 /********************************************************************
99  * Tests for different types of options
100  */
101
102 TEST_F(ParseCommonArgsTest, ParsesIntegerArgs)
103 {
104     int               value1 = 0, value2 = 0, value3 = 3;
105     t_pargs           pa[]   = {
106         { "-i1", FALSE, etINT, {&value1}, "Description" },
107         { "-i2", FALSE, etINT, {&value2}, "Description" },
108         { "-i3", FALSE, etINT, {&value3}, "Description" }
109     };
110     const char *const cmdline[] = {
111         "test", "-i1", "2", "-i2", "-3"
112     };
113     parse(cmdline, 0, gmx::EmptyArrayRef(), pa);
114     EXPECT_EQ( 2, value1);
115     EXPECT_EQ(-3, value2);
116     EXPECT_EQ( 3, value3);
117 }
118
119 TEST_F(ParseCommonArgsTest, ParsesInt64Args)
120 {
121     gmx_int64_t       value1 = 0, value2 = 0, value3 = 3;
122     t_pargs           pa[]   = {
123         { "-i1", FALSE, etINT64, {&value1}, "Description" },
124         { "-i2", FALSE, etINT64, {&value2}, "Description" },
125         { "-i3", FALSE, etINT64, {&value3}, "Description" }
126     };
127     const char *const cmdline[] = {
128         "test", "-i1", "2", "-i2", "-3"
129     };
130     parse(cmdline, 0, gmx::EmptyArrayRef(), pa);
131     EXPECT_EQ( 2, value1);
132     EXPECT_EQ(-3, value2);
133     EXPECT_EQ( 3, value3);
134 }
135
136 TEST_F(ParseCommonArgsTest, ParsesRealArgs)
137 {
138     real              value1 = 0.0, value2 = 0.0, value3 = 2.5;
139     t_pargs           pa[]   = {
140         { "-r1", FALSE, etREAL, {&value1}, "Description" },
141         { "-r2", FALSE, etREAL, {&value2}, "Description" },
142         { "-r3", FALSE, etREAL, {&value3}, "Description" }
143     };
144     const char *const cmdline[] = {
145         "test", "-r1", "2", "-r2", "-.5"
146     };
147     parse(cmdline, 0, gmx::EmptyArrayRef(), pa);
148     EXPECT_EQ( 2.0, value1);
149     EXPECT_EQ(-0.5, value2);
150     EXPECT_EQ( 2.5, value3);
151 }
152
153 TEST_F(ParseCommonArgsTest, ParsesStringArgs)
154 {
155     const char       *value1 = "def", *value2 = "", *value3 = "default";
156     t_pargs           pa[]   = {
157         { "-s1", FALSE, etSTR, {&value1}, "Description" },
158         { "-s2", FALSE, etSTR, {&value2}, "Description" },
159         { "-s3", FALSE, etSTR, {&value3}, "Description" }
160     };
161     const char *const cmdline[] = {
162         "test", "-s1", "", "-s2", "test"
163     };
164     parse(cmdline, 0, gmx::EmptyArrayRef(), pa);
165     EXPECT_STREQ("", value1);
166     EXPECT_STREQ("test", value2);
167     EXPECT_STREQ("default", value3);
168 }
169
170 TEST_F(ParseCommonArgsTest, ParsesBooleanArgs)
171 {
172     gmx_bool          value1 = TRUE, value2 = FALSE, value3 = TRUE;
173     t_pargs           pa[]   = {
174         { "-b1", FALSE, etBOOL, {&value1}, "Description" },
175         { "-b2", FALSE, etBOOL, {&value2}, "Description" },
176         { "-b3", FALSE, etBOOL, {&value3}, "Description" }
177     };
178     const char *const cmdline[] = {
179         "test", "-nob1", "-b2"
180     };
181     parse(cmdline, 0, gmx::EmptyArrayRef(), pa);
182     EXPECT_FALSE(value1);
183     EXPECT_TRUE(value2);
184     EXPECT_TRUE(value3);
185 }
186
187 TEST_F(ParseCommonArgsTest, ParsesVectorArgs)
188 {
189     rvec              value1 = {0, 0, 0}, value2 = {0, 0, 0}, value3 = {1, 2, 3};
190     t_pargs           pa[]   = {
191         { "-v1", FALSE, etRVEC, {&value1}, "Description" },
192         { "-v2", FALSE, etRVEC, {&value2}, "Description" },
193         { "-v3", FALSE, etRVEC, {&value3}, "Description" }
194     };
195     const char *const cmdline[] = {
196         "test", "-v1", "2", "1", "3", "-v2", "1"
197     };
198     parse(cmdline, 0, gmx::EmptyArrayRef(), pa);
199     EXPECT_EQ(2.0, value1[XX]);
200     EXPECT_EQ(1.0, value1[YY]);
201     EXPECT_EQ(3.0, value1[ZZ]);
202     EXPECT_EQ(1.0, value2[XX]);
203     EXPECT_EQ(1.0, value2[YY]);
204     EXPECT_EQ(1.0, value2[ZZ]);
205     EXPECT_EQ(1.0, value3[XX]);
206     EXPECT_EQ(2.0, value3[YY]);
207     EXPECT_EQ(3.0, value3[ZZ]);
208 }
209
210 TEST_F(ParseCommonArgsTest, ParsesTimeArgs)
211 {
212     real              value1 = 1.0, value2 = 2.0, value3 = 2.5;
213     t_pargs           pa[]   = {
214         { "-t1", FALSE, etTIME, {&value1}, "Description" },
215         { "-t2", FALSE, etTIME, {&value2}, "Description" },
216         { "-t3", FALSE, etTIME, {&value3}, "Description" }
217     };
218     const char *const cmdline[] = {
219         "test", "-t1", "2", "-t2", "-.5"
220     };
221     parse(cmdline, 0, gmx::EmptyArrayRef(), pa);
222     EXPECT_EQ( 2.0, value1);
223     EXPECT_EQ(-0.5, value2);
224     EXPECT_EQ( 2.5, value3);
225 }
226
227 TEST_F(ParseCommonArgsTest, ParsesTimeArgsWithTimeUnit)
228 {
229     real              value1 = 1.0, value2 = 2.0, value3 = 2.5;
230     t_pargs           pa[]   = {
231         { "-t1", FALSE, etTIME, {&value1}, "Description" },
232         { "-t2", FALSE, etTIME, {&value2}, "Description" },
233         { "-t3", FALSE, etTIME, {&value3}, "Description" }
234     };
235     const char *const cmdline[] = {
236         "test", "-t1", "2", "-t2", "-.5", "-tu", "ns"
237     };
238     parse(cmdline, PCA_TIME_UNIT, gmx::EmptyArrayRef(), pa);
239     EXPECT_EQ( 2000.0, value1);
240     EXPECT_EQ(-500.0, value2);
241     EXPECT_EQ( 2.5, value3);
242 }
243
244 TEST_F(ParseCommonArgsTest, ParsesEnumArgs)
245 {
246     const char       *value1[] = {NULL, "none", "on", "off", NULL };
247     const char       *value2[] = {NULL, "def", "value", "value_other", NULL };
248     const char       *value3[] = {NULL, "default", "value", NULL };
249     t_pargs           pa[]     = {
250         { "-s1", FALSE, etENUM, {value1}, "Description" },
251         { "-s2", FALSE, etENUM, {value2}, "Description" },
252         { "-s3", FALSE, etENUM, {value3}, "Description" }
253     };
254     const char *const cmdline[] = {
255         "test", "-s1", "off", "-s2", "val"
256     };
257     parse(cmdline, 0, gmx::EmptyArrayRef(), pa);
258     EXPECT_STREQ("off", value1[0]);
259     EXPECT_STREQ("value", value2[0]);
260     EXPECT_STREQ("default", value3[0]);
261     EXPECT_EQ(value1[nenum(value1)], value1[0]);
262     EXPECT_EQ(value2[nenum(value2)], value2[0]);
263     EXPECT_EQ(value3[nenum(value3)], value3[0]);
264 }
265
266 /********************************************************************
267  * Tests for file name options
268  */
269
270 TEST_F(ParseCommonArgsTest, ParsesFileArgs)
271 {
272     t_filenm          fnm[] = {
273         { efXVG, "-o1", "out1", ffOPTWR },
274         { efXVG, "-o2", "out2", ffOPTWR },
275         { efXVG, "-om", "outm", ffWRMULT },
276         { efXVG, "-om2", "outm2", ffWRMULT },
277     };
278     const char *const cmdline[] = {
279         "test", "-o1", "-o2", "test", "-om", "test1", "test2.xvg", "-om2"
280     };
281     parse(cmdline, 0, fnm, gmx::EmptyArrayRef());
282     EXPECT_STREQ("out1.xvg", opt2fn_null("-o1", nfile(), fnm));
283     EXPECT_STREQ("test.xvg", opt2fn_null("-o2", nfile(), fnm));
284     char **files;
285     EXPECT_EQ(2, opt2fns(&files, "-om", nfile(), fnm));
286     EXPECT_STREQ("test1.xvg", files[0]);
287     EXPECT_STREQ("test2.xvg", files[1]);
288     EXPECT_STREQ("outm2.xvg", opt2fn("-om2", nfile(), fnm));
289     done_filenms(nfile(), fnm);
290 }
291
292 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaults)
293 {
294     t_filenm          fnm[] = {
295         { efTPS, NULL,  NULL,   ffWRITE },
296         { efTRX, "-f2", NULL,   ffOPTWR },
297         { efTRX, "-f3", "trj3", ffWRITE },
298         { efXVG, "-o",  "out",  ffWRITE },
299         { efXVG, "-om", "outm", ffWRMULT },
300     };
301     const char *const cmdline[] = {
302         "test"
303     };
304     parse(cmdline, 0, fnm, gmx::EmptyArrayRef());
305     EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
306     EXPECT_STREQ("traj.xtc", opt2fn("-f2", nfile(), fnm));
307     EXPECT_NULL(opt2fn_null("-f2", nfile(), fnm));
308     EXPECT_STREQ("trj3.xtc", opt2fn("-f3", nfile(), fnm));
309     EXPECT_STREQ("out.xvg", opt2fn("-o", nfile(), fnm));
310     EXPECT_STREQ("outm.xvg", opt2fn("-om", nfile(), fnm));
311     done_filenms(nfile(), fnm);
312 }
313
314 TEST_F(ParseCommonArgsTest, ParsesFileArgsWithDefaultFileName)
315 {
316     t_filenm          fnm[] = {
317         { efTPS, "-s",  NULL,   ffWRITE },
318         { efTRX, "-f2", NULL,   ffWRITE },
319         { efTRX, "-f3", "trj3", ffWRITE },
320         { efXVG, "-o",  "out",  ffWRITE },
321         { efXVG, "-om", "outm", ffWRMULT },
322     };
323     const char *const cmdline[] = {
324         "test", "-deffnm", "def", "-f2", "other", "-o"
325     };
326     parse(cmdline, PCA_CAN_SET_DEFFNM, fnm, gmx::EmptyArrayRef());
327     EXPECT_STREQ("def.tpr", ftp2fn(efTPS, nfile(), fnm));
328     EXPECT_STREQ("other.xtc", opt2fn("-f2", nfile(), fnm));
329     EXPECT_STREQ("def.xtc", opt2fn("-f3", nfile(), fnm));
330     EXPECT_STREQ("def.xvg", opt2fn("-o", nfile(), fnm));
331     EXPECT_STREQ("def.xvg", opt2fn("-om", nfile(), fnm));
332     done_filenms(nfile(), fnm);
333 }
334
335 /********************************************************************
336  * Tests for general behavior
337  */
338
339 TEST_F(ParseCommonArgsTest, CanKeepUnknownArgs)
340 {
341     int               ivalue = 0;
342     gmx_bool          bvalue = FALSE;
343     t_pargs           pa[]   = {
344         { "-i", FALSE, etINT, {&ivalue}, "Description" },
345         { "-b", FALSE, etBOOL, {&bvalue}, "Description" },
346     };
347     t_filenm          fnm[] = {
348         { efXVG, "-o1", "out1", ffOPTWR },
349         { efXVG, "-o2", "out2", ffOPTWR }
350     };
351     const char *const cmdline[] = {
352         "test", "foo", "-unk", "-o1", "-unk2", "-o2", "test",
353         "-i", "2", "-unk3", "-b", "-unk4"
354     };
355     parse(cmdline, PCA_NOEXIT_ON_ARGS, fnm, pa);
356     EXPECT_EQ(2, ivalue);
357     EXPECT_TRUE(bvalue);
358     EXPECT_STREQ("out1.xvg", opt2fn_null("-o1", nfile(), fnm));
359     EXPECT_STREQ("test.xvg", opt2fn_null("-o2", nfile(), fnm));
360     EXPECT_EQ(6, args_.argc());
361     EXPECT_STREQ(cmdline[0],  args_.arg(0));
362     EXPECT_STREQ(cmdline[1],  args_.arg(1));
363     EXPECT_STREQ(cmdline[2],  args_.arg(2));
364     EXPECT_STREQ(cmdline[4],  args_.arg(3));
365     EXPECT_STREQ(cmdline[9],  args_.arg(4));
366     EXPECT_STREQ(cmdline[11], args_.arg(5));
367     done_filenms(nfile(), fnm);
368 }
369
370 } // namespace