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