3630ed1ced836eabd0c04c1c999c1d635ed36236
[alexxy/gromacs.git] / src / gromacs / commandline / tests / cmdlinehelpwriter.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015,2016,2017,2019, 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 gmx::CommandLineHelpWriter.
38  *
39  * These tests fail for any change in the output, and it should be reviewed
40  * whether the change was intentional.
41  * For development, the tests can be run with a '-stdout' command-line option
42  * to print out the help to stdout instead of using the XML reference
43  * framework.
44  *
45  * \author Teemu Murtola <teemu.murtola@gmail.com>
46  * \ingroup module_commandline
47  */
48 #include "gmxpre.h"
49
50 #include "gromacs/commandline/cmdlinehelpwriter.h"
51
52 #include <gtest/gtest.h>
53
54 #include "gromacs/commandline/cmdlinehelpcontext.h"
55 #include "gromacs/math/vectypes.h"
56 #include "gromacs/options/basicoptions.h"
57 #include "gromacs/options/filenameoption.h"
58 #include "gromacs/options/options.h"
59 #include "gromacs/utility/arrayref.h"
60 #include "gromacs/utility/stringstream.h"
61 #include "gromacs/utility/textwriter.h"
62
63 #include "testutils/stringtest.h"
64
65 namespace
66 {
67
68 class CommandLineHelpWriterTest : public ::gmx::test::StringTestBase
69 {
70 public:
71     CommandLineHelpWriterTest() : bHidden_(false) {}
72
73     void checkHelp(gmx::CommandLineHelpWriter* writer);
74
75     bool bHidden_;
76 };
77
78 void CommandLineHelpWriterTest::checkHelp(gmx::CommandLineHelpWriter* writer)
79 {
80     gmx::StringOutputStream     stream;
81     gmx::TextWriter             streamWriter(&stream);
82     gmx::CommandLineHelpContext context(&streamWriter, gmx::eHelpOutputFormat_Console, nullptr,
83                                         "test");
84     context.setShowHidden(bHidden_);
85     writer->writeHelp(context);
86     stream.close();
87
88     checkText(stream.toString(), "HelpText");
89 }
90
91
92 /********************************************************************
93  * Tests start here
94  */
95
96 /*
97  * Tests help printing for each option type, but doesn't contain much
98  * variablity in the options.
99  */
100 TEST_F(CommandLineHelpWriterTest, HandlesOptionTypes)
101 {
102     using namespace gmx;
103
104     Options options;
105     options.addOption(BooleanOption("bool").description("Boolean option").defaultValue(true));
106     options.addOption(BooleanOption("hidden").description("Hidden option").hidden().defaultValue(true));
107     options.addOption(IntegerOption("int").description("Integer option").defaultValue(2));
108     ivec intvec = { 1, 2, 3 };
109     options.addOption(IntegerOption("ivec").description("Integer vector option").vector().store(intvec));
110     options.addOption(DoubleOption("double").description("Double option").defaultValue(2.5));
111     dvec dblvec = { 1.1, 2.3, 3.2 };
112     options.addOption(DoubleOption("dvec").description("Double vector option").vector().store(dblvec));
113     options.addOption(DoubleOption("time").description("Time option (%t)").timeValue().defaultValue(10.0));
114     options.addOption(StringOption("string").description("String option").defaultValue("test"));
115     const char* const enumValues[] = { "no", "opt1", "opt2" };
116     options.addOption(
117             StringOption("enum").description("Enum option").enumValue(enumValues).defaultEnumIndex(0));
118     options.addOption(
119             EnumIntOption("ienum").description("Enum option").enumValue(enumValues).defaultValue(1));
120
121     std::string filename;
122     options.addOption(FileNameOption("f")
123                               .description("Input file description")
124                               .filetype(eftTrajectory)
125                               .inputFile()
126                               .required()
127                               .defaultBasename("traj"));
128     options.addOption(FileNameOption("mult")
129                               .description("Multiple file description")
130                               .filetype(eftTrajectory)
131                               .inputFile()
132                               .multiValue()
133                               .defaultBasename("traj"));
134     options.addOption(FileNameOption("lib")
135                               .description("Library file description")
136                               .filetype(eftGenericData)
137                               .inputFile()
138                               .libraryFile()
139                               .defaultBasename("libdata"));
140     options.addOption(FileNameOption("io")
141                               .store(&filename)
142                               .description("Input/Output file description")
143                               .filetype(eftGenericData)
144                               .inputOutputFile()
145                               .defaultBasename("inout"));
146     options.addOption(
147             FileNameOption("o").description("Output file description").filetype(eftPlot).outputFile());
148
149     CommandLineHelpWriter writer(options);
150     bHidden_ = true;
151     checkHelp(&writer);
152 }
153
154 //! Enum value for testing.
155 enum TestEnum
156 {
157     eFoo,
158     eBar
159 };
160
161 /*
162  * Tests that default values taken from variables are properly visible in the
163  * help output.
164  */
165 TEST_F(CommandLineHelpWriterTest, HandlesDefaultValuesFromVariables)
166 {
167     using namespace gmx;
168
169     Options options;
170
171     bool bValue = true;
172     options.addOption(BooleanOption("bool").description("Boolean option").store(&bValue));
173
174     int ivalue = 3;
175     options.addOption(IntegerOption("int").description("Integer option").store(&ivalue));
176
177     int iavalue[] = { 2, 3 };
178     options.addOption(
179             IntegerOption("int2").description("Integer 2-value option").store(iavalue).valueCount(2));
180
181     std::vector<std::string> svalues;
182     svalues.emplace_back("foo");
183     options.addOption(StringOption("str").description("String option").storeVector(&svalues).multiValue());
184
185     TestEnum          evalue    = eBar;
186     const char* const allowed[] = { "foo", "bar" };
187     options.addOption(
188             EnumOption<TestEnum>("enum").description("Enum option").enumValue(allowed).store(&evalue));
189
190     CommandLineHelpWriter writer(options);
191     checkHelp(&writer);
192 }
193
194 /*
195  * Tests help printing with file name options with various values that don't
196  * fit into the allocated columns.
197  */
198 TEST_F(CommandLineHelpWriterTest, HandlesLongFileOptions)
199 {
200     using gmx::eftGenericData;
201     using gmx::eftTrajectory;
202     using gmx::FileNameOption;
203
204     gmx::Options options;
205     options.addOption(FileNameOption("f")
206                               .description("File name option with a long value")
207                               .filetype(eftTrajectory)
208                               .inputFile()
209                               .required()
210                               .defaultBasename("path/to/long/trajectory/name"));
211     options.addOption(FileNameOption("f2")
212                               .description("File name option with a long value")
213                               .filetype(eftTrajectory)
214                               .inputFile()
215                               .required()
216                               .defaultBasename("path/to/long/trajectory"));
217     options.addOption(FileNameOption("lib")
218                               .description("File name option with a long value and type")
219                               .filetype(eftTrajectory)
220                               .inputFile()
221                               .libraryFile()
222                               .defaultBasename("path/to/long/trajectory/name"));
223     options.addOption(FileNameOption("longfileopt")
224                               .description("File name option with a long name")
225                               .filetype(eftGenericData)
226                               .inputFile()
227                               .defaultBasename("deffile"));
228     options.addOption(FileNameOption("longfileopt2")
229                               .description("File name option with multiple long fields")
230                               .filetype(eftGenericData)
231                               .inputFile()
232                               .libraryFile()
233                               .defaultBasename("path/to/long/file/name"));
234
235     gmx::CommandLineHelpWriter writer(options);
236     checkHelp(&writer);
237 }
238
239 /*
240  * Tests help printing with general options with various values that don't
241  * fit into the allocated columns.
242  */
243 TEST_F(CommandLineHelpWriterTest, HandlesLongOptions)
244 {
245     using gmx::BooleanOption;
246     using gmx::DoubleOption;
247     using gmx::StringOption;
248
249     gmx::Options options;
250     options.addOption(
251             BooleanOption("longboolean").description("Boolean option with a long name").defaultValue(true));
252     dvec dblvec = { 1.135, 2.32, 3.2132 };
253     options.addOption(DoubleOption("dvec").description("Double vector option").vector().store(dblvec));
254     std::vector<std::string> values;
255     values.emplace_back("A very long string value that overflows even the description column");
256     values.emplace_back(
257             "Another very long string value that overflows even the description column");
258     options.addOption(StringOption("string")
259                               .description("String option with very long values (may "
260                                            "be less relevant with selections having "
261                                            "their own option type)")
262                               .storeVector(&values));
263
264     gmx::CommandLineHelpWriter writer(options);
265     checkHelp(&writer);
266 }
267
268 /* TODO: Add corresponding tests to either the selection module, or as part of
269  * trajectoryanalysis tests.
270  * Tests help printing with selection options with values.
271  */
272 #if 0
273 TEST_F(CommandLineHelpWriterTest, HandlesSelectionOptions)
274 {
275     using gmx::SelectionFileOption;
276     using gmx::SelectionOption;
277
278     gmx::Options                options;
279     gmx::SelectionCollection    selections;
280     gmx::SelectionOptionManager manager(&selections);
281     options.addManager(&manager);
282     options.addOption(SelectionFileOption("sf"));
283     options.addOption(SelectionOption("refsel").required()
284                           .description("Reference selection option"));
285     options.addOption(SelectionOption("sel").required().valueCount(2)
286                           .description("Selection option"));
287     options.finish();
288     manager.parseRequestedFromString(
289             "resname SOL;"
290             "surface = within 0.5 of resname SOL;"
291             "group \"Protein\" and surface;"
292             "group \"Protein\" and not surface;");
293
294     gmx::CommandLineHelpWriter writer(options);
295     checkHelp(&writer);
296 }
297 #endif
298
299 /*
300  * Tests help output with option groups.
301  */
302 TEST_F(CommandLineHelpWriterTest, HandlesOptionGroups)
303 {
304     using gmx::IntegerOption;
305
306     gmx::Options            options;
307     gmx::IOptionsContainer& group1 = options.addGroup();
308     gmx::IOptionsContainer& group2 = options.addGroup();
309     group2.addOption(IntegerOption("sub2").description("Option in group 2"));
310     group1.addOption(IntegerOption("sub11").description("Option in group 1"));
311     options.addOption(IntegerOption("main").description("Option in root group"));
312     group1.addOption(IntegerOption("sub12").description("Option in group 1"));
313
314     gmx::CommandLineHelpWriter writer(options);
315     checkHelp(&writer);
316 }
317
318 /*
319  * Tests help output using a help text.
320  */
321 TEST_F(CommandLineHelpWriterTest, HandlesHelpText)
322 {
323     const char* const help[] = { "Help text", "for testing." };
324     using gmx::IntegerOption;
325
326     gmx::Options options;
327     options.addOption(IntegerOption("int").description("Integer option").defaultValue(2));
328
329     gmx::CommandLineHelpWriter writer(options);
330     writer.setHelpText(help);
331     checkHelp(&writer);
332 }
333
334 /*
335  * Test known issue output.
336  */
337 TEST_F(CommandLineHelpWriterTest, HandlesKnownIssues)
338 {
339     const char* const bugs[] = { "This is a bug.", "And this is another one." };
340     using gmx::IntegerOption;
341
342     gmx::Options options;
343     options.addOption(IntegerOption("int").description("Integer option").defaultValue(2));
344
345     gmx::CommandLineHelpWriter writer(options);
346     writer.setKnownIssues(bugs);
347     checkHelp(&writer);
348 }
349
350 } // namespace