Some tests for 'gmx help -export rst'
[alexxy/gromacs.git] / src / gromacs / commandline / tests / cmdlinemodulemanager.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015, 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::CommandLineModuleManager.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_commandline
41  */
42 #include "gmxpre.h"
43
44 #include "gromacs/commandline/cmdlinemodulemanager.h"
45
46 #include <cstdio>
47
48 #include <gmock/gmock.h>
49
50 #include "gromacs/commandline/cmdlinehelpcontext.h"
51 #include "gromacs/commandline/cmdlinemodule.h"
52 #include "gromacs/commandline/cmdlineoptionsmodule.h"
53 #include "gromacs/commandline/cmdlineprogramcontext.h"
54 #include "gromacs/options/basicoptions.h"
55 #include "gromacs/options/options.h"
56 #include "gromacs/utility/file.h"
57
58 #include "gromacs/onlinehelp/tests/mock_helptopic.h"
59 #include "testutils/cmdlinetest.h"
60 #include "testutils/stringtest.h"
61 #include "testutils/testasserts.h"
62 #include "testutils/testfilemanager.h"
63
64 namespace
65 {
66
67 using gmx::test::CommandLine;
68 using gmx::test::MockHelpTopic;
69
70 /*! \brief
71  * Helper method to disable nice() calls from CommandLineModuleManager.
72  *
73  * \ingroup module_commandline
74  */
75 void disableNice(gmx::CommandLineModuleSettings *settings)
76 {
77     settings->setDefaultNiceLevel(0);
78 }
79
80 /********************************************************************
81  * MockModule
82  */
83
84 /*! \internal \brief
85  * Mock implementation of gmx::CommandLineModuleInterface.
86  *
87  * \ingroup module_commandline
88  */
89 class MockModule : public gmx::CommandLineModuleInterface
90 {
91     public:
92         //! Creates a mock module with the given name and description.
93         MockModule(const char *name, const char *description);
94
95         virtual const char *name() const { return name_; }
96         virtual const char *shortDescription() const { return descr_; }
97
98         MOCK_METHOD1(init, void(gmx::CommandLineModuleSettings *settings));
99         MOCK_METHOD2(run, int(int argc, char *argv[]));
100         MOCK_CONST_METHOD1(writeHelp, void(const gmx::CommandLineHelpContext &context));
101
102         //! Sets the expected display name for writeHelp() calls.
103         void setExpectedDisplayName(const char *expected)
104         {
105             expectedDisplayName_ = expected;
106         }
107
108     private:
109         //! Checks the context passed to writeHelp().
110         void checkHelpContext(const gmx::CommandLineHelpContext &context) const;
111
112         const char             *name_;
113         const char             *descr_;
114         std::string             expectedDisplayName_;
115 };
116
117 MockModule::MockModule(const char *name, const char *description)
118     : name_(name), descr_(description)
119 {
120     using ::testing::_;
121     using ::testing::Invoke;
122     ON_CALL(*this, init(_))
123         .WillByDefault(Invoke(&disableNice));
124     ON_CALL(*this, writeHelp(_))
125         .WillByDefault(Invoke(this, &MockModule::checkHelpContext));
126 }
127
128 void MockModule::checkHelpContext(const gmx::CommandLineHelpContext &context) const
129 {
130     EXPECT_EQ(expectedDisplayName_, context.moduleDisplayName());
131
132     gmx::TextLineWrapperSettings settings;
133     std::string                  moduleName =
134         context.writerContext().substituteMarkupAndWrapToString(
135                 settings, "[THISMODULE]");
136     EXPECT_EQ(expectedDisplayName_, moduleName);
137 }
138
139 /********************************************************************
140  * MockOptionsModule
141  */
142
143 /*! \internal \brief
144  * Mock implementation of gmx::CommandLineOptionsModuleInterface.
145  *
146  * \ingroup module_commandline
147  */
148 class MockOptionsModule : public gmx::CommandLineOptionsModuleInterface
149 {
150     public:
151         MockOptionsModule();
152
153         MOCK_METHOD1(init, void(gmx::CommandLineModuleSettings *settings));
154         MOCK_METHOD1(initOptions, void(gmx::Options *options));
155         MOCK_METHOD1(optionsFinished, void(gmx::Options *options));
156         MOCK_METHOD0(run, int());
157 };
158
159 MockOptionsModule::MockOptionsModule()
160 {
161     using ::testing::_;
162     using ::testing::Invoke;
163     ON_CALL(*this, init(_))
164         .WillByDefault(Invoke(&disableNice));
165 }
166
167 /********************************************************************
168  * Test fixture for the tests
169  */
170
171 class CommandLineModuleManagerTest : public gmx::test::StringTestBase
172 {
173     public:
174         void initManager(const CommandLine &args, const char *realBinaryName);
175         MockModule        &addModule(const char *name, const char *description);
176         MockOptionsModule &addOptionsModule(const char *name, const char *description);
177         MockHelpTopic     &addHelpTopic(const char *name, const char *title);
178
179         gmx::CommandLineModuleManager &manager() { return *manager_; }
180
181         void redirectManagerOutput()
182         {
183             manager_->setOutputRedirector(&initOutputRedirector(&fileManager_));
184         }
185
186     private:
187         boost::scoped_ptr<gmx::CommandLineProgramContext> programContext_;
188         boost::scoped_ptr<gmx::CommandLineModuleManager>  manager_;
189         gmx::test::TestFileManager                        fileManager_;
190 };
191
192 void CommandLineModuleManagerTest::initManager(
193         const CommandLine &args, const char *realBinaryName)
194 {
195     manager_.reset();
196     programContext_.reset(
197             new gmx::CommandLineProgramContext(args.argc(), args.argv()));
198     manager_.reset(new gmx::CommandLineModuleManager(realBinaryName,
199                                                      programContext_.get()));
200     manager_->setQuiet(true);
201 }
202
203 MockModule &
204 CommandLineModuleManagerTest::addModule(const char *name, const char *description)
205 {
206     MockModule *module = new MockModule(name, description);
207     manager().addModule(gmx::CommandLineModulePointer(module));
208     return *module;
209 }
210
211 MockOptionsModule &
212 CommandLineModuleManagerTest::addOptionsModule(const char *name, const char *description)
213 {
214     MockOptionsModule *module = new MockOptionsModule();
215     gmx::CommandLineOptionsModuleInterface::registerModule(
216             &manager(), name, description, module);
217     return *module;
218 }
219
220 MockHelpTopic &
221 CommandLineModuleManagerTest::addHelpTopic(const char *name, const char *title)
222 {
223     MockHelpTopic *topic = new MockHelpTopic(name, title, "Help text");
224     manager().addHelpTopic(gmx::HelpTopicPointer(topic));
225     return *topic;
226 }
227
228 /********************************************************************
229  * Actual tests
230  */
231
232 TEST_F(CommandLineModuleManagerTest, RunsGeneralHelp)
233 {
234     const char *const cmdline[] = {
235         "test"
236     };
237     CommandLine       args(cmdline);
238     initManager(args, "test");
239     redirectManagerOutput();
240     addModule("module", "First module");
241     addModule("other", "Second module");
242     int rc = 0;
243     ASSERT_NO_THROW_GMX(rc = manager().run(args.argc(), args.argv()));
244     ASSERT_EQ(0, rc);
245     checkRedirectedOutputFiles();
246 }
247
248 TEST_F(CommandLineModuleManagerTest, RunsModule)
249 {
250     const char *const cmdline[] = {
251         "test", "module", "-flag", "yes"
252     };
253     CommandLine       args(cmdline);
254     initManager(args, "test");
255     MockModule       &mod1 = addModule("module", "First module");
256     addModule("other", "Second module");
257     using ::testing::_;
258     using ::testing::Args;
259     using ::testing::ElementsAreArray;
260     EXPECT_CALL(mod1, init(_));
261     EXPECT_CALL(mod1, run(_, _))
262         .With(Args<1, 0>(ElementsAreArray(args.argv() + 1, args.argc() - 1)));
263     int rc = 0;
264     ASSERT_NO_THROW_GMX(rc = manager().run(args.argc(), args.argv()));
265     ASSERT_EQ(0, rc);
266 }
267
268 TEST_F(CommandLineModuleManagerTest, RunsModuleHelp)
269 {
270     const char *const cmdline[] = {
271         "test", "help", "module"
272     };
273     CommandLine       args(cmdline);
274     initManager(args, "test");
275     MockModule       &mod1 = addModule("module", "First module");
276     addModule("other", "Second module");
277     using ::testing::_;
278     EXPECT_CALL(mod1, writeHelp(_));
279     mod1.setExpectedDisplayName("test module");
280     int rc = 0;
281     ASSERT_NO_THROW_GMX(rc = manager().run(args.argc(), args.argv()));
282     ASSERT_EQ(0, rc);
283 }
284
285 TEST_F(CommandLineModuleManagerTest, RunsModuleHelpWithDashH)
286 {
287     const char *const cmdline[] = {
288         "test", "module", "-h"
289     };
290     CommandLine       args(cmdline);
291     initManager(args, "test");
292     MockModule       &mod1 = addModule("module", "First module");
293     addModule("other", "Second module");
294     using ::testing::_;
295     EXPECT_CALL(mod1, writeHelp(_));
296     mod1.setExpectedDisplayName("test module");
297     int rc = 0;
298     ASSERT_NO_THROW_GMX(rc = manager().run(args.argc(), args.argv()));
299     ASSERT_EQ(0, rc);
300 }
301
302 TEST_F(CommandLineModuleManagerTest, RunsModuleHelpWithDashHWithSingleModule)
303 {
304     const char *const cmdline[] = {
305         "g_module", "-h"
306     };
307     CommandLine       args(cmdline);
308     initManager(args, "g_module");
309     MockModule        mod(NULL, NULL);
310     manager().setSingleModule(&mod);
311     using ::testing::_;
312     EXPECT_CALL(mod, writeHelp(_));
313     mod.setExpectedDisplayName("g_module");
314     int rc = 0;
315     ASSERT_NO_THROW_GMX(rc = manager().run(args.argc(), args.argv()));
316     ASSERT_EQ(0, rc);
317 }
318
319 TEST_F(CommandLineModuleManagerTest, PrintsHelpOnTopic)
320 {
321     const char *const cmdline[] = {
322         "test", "help", "topic"
323     };
324     CommandLine       args(cmdline);
325     initManager(args, "test");
326     addModule("module", "First module");
327     MockHelpTopic &topic = addHelpTopic("topic", "Test topic");
328     using ::testing::_;
329     EXPECT_CALL(topic, writeHelp(_));
330     int rc = 0;
331     ASSERT_NO_THROW_GMX(rc = manager().run(args.argc(), args.argv()));
332     ASSERT_EQ(0, rc);
333 }
334
335 TEST_F(CommandLineModuleManagerTest, HandlesConflictingBinaryAndModuleNames)
336 {
337     const char *const cmdline[] = {
338         "test", "test", "-flag", "yes"
339     };
340     CommandLine       args(cmdline);
341     initManager(args, "test");
342     MockModule       &mod1 = addModule("test", "Test module");
343     addModule("other", "Second module");
344     using ::testing::_;
345     using ::testing::Args;
346     using ::testing::ElementsAreArray;
347     EXPECT_CALL(mod1, init(_));
348     EXPECT_CALL(mod1, run(_, _))
349         .With(Args<1, 0>(ElementsAreArray(args.argv() + 1, args.argc() - 1)));
350     int rc = 0;
351     ASSERT_NO_THROW_GMX(rc = manager().run(args.argc(), args.argv()));
352     ASSERT_EQ(0, rc);
353 }
354
355 /*! \brief
356  * Initializes Options for help export tests.
357  *
358  * \ingroup module_commandline
359  */
360 void initOptionsBasic(gmx::Options *options)
361 {
362     const char *const desc[] = {
363         "Sample description",
364         "for testing [THISMODULE]."
365     };
366     options->setDescription(desc);
367     options->addOption(gmx::IntegerOption("int"));
368 }
369
370 TEST_F(CommandLineModuleManagerTest, ExportsHelp)
371 {
372     const char *const cmdline[] = {
373         "test", "help", "-export", "rst"
374     };
375     // TODO: Find a more elegant solution, or get rid of the links.dat altogether.
376     gmx::File::writeFileFromString("links.dat", "");
377     CommandLine       args(cmdline);
378     initManager(args, "test");
379     redirectManagerOutput();
380     MockOptionsModule &mod1 = addOptionsModule("module", "First module");
381     MockOptionsModule &mod2 = addOptionsModule("other", "Second module");
382     {
383         gmx::CommandLineModuleGroup group = manager().addModuleGroup("Group 1");
384         group.addModule("module");
385     }
386     {
387         gmx::CommandLineModuleGroup group = manager().addModuleGroup("Group 2");
388         group.addModule("other");
389     }
390     using ::testing::_;
391     using ::testing::Invoke;
392     EXPECT_CALL(mod1, initOptions(_)).WillOnce(Invoke(&initOptionsBasic));
393     EXPECT_CALL(mod2, initOptions(_));
394     int rc = 0;
395     ASSERT_NO_THROW_GMX(rc = manager().run(args.argc(), args.argv()));
396     ASSERT_EQ(0, rc);
397     checkRedirectedOutputFiles();
398     std::remove("links.dat");
399 }
400
401 } // namespace