3d5a956fca8c09fd79a121b81dddc618572cb3f8
[alexxy/gromacs.git] / src / testutils / cmdlinetest.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,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  * Implements classes from cmdlinetest.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_testutils
41  */
42 #include "gmxpre.h"
43
44 #include "cmdlinetest.h"
45
46 #include <cstdlib>
47 #include <cstring>
48
49 #include <new>
50 #include <sstream>
51 #include <vector>
52
53 #include <boost/scoped_ptr.hpp>
54
55 #include "gromacs/commandline/cmdlineoptionsmodule.h"
56 #include "gromacs/commandline/cmdlineprogramcontext.h"
57 #include "gromacs/utility/arrayref.h"
58 #include "gromacs/utility/file.h"
59 #include "gromacs/utility/gmxassert.h"
60 #include "gromacs/utility/stringutil.h"
61
62 #include "testutils/refdata.h"
63 #include "testutils/testfilemanager.h"
64
65 namespace gmx
66 {
67 namespace test
68 {
69
70 /********************************************************************
71  * CommandLine::Impl
72  */
73
74 class CommandLine::Impl
75 {
76     public:
77         Impl(const char *const cmdline[], size_t count);
78         ~Impl();
79
80         std::vector<char *>     args_;
81         std::vector<char *>     argv_;
82         int                     argc_;
83 };
84
85 CommandLine::Impl::Impl(const char *const cmdline[], size_t count)
86 {
87     args_.reserve(count);
88     argv_.reserve(count + 1);
89     argc_ = static_cast<int>(count);
90     for (size_t i = 0; i < count; ++i)
91     {
92         char *arg = strdup(cmdline[i]);
93         if (arg == NULL)
94         {
95             throw std::bad_alloc();
96         }
97         args_.push_back(arg);
98         argv_.push_back(arg);
99     }
100     argv_.push_back(NULL);
101 }
102
103 CommandLine::Impl::~Impl()
104 {
105     for (size_t i = 0; i < args_.size(); ++i)
106     {
107         std::free(args_[i]);
108     }
109 }
110
111 /********************************************************************
112  * CommandLine
113  */
114
115 CommandLine::CommandLine()
116     : impl_(new Impl(NULL, 0))
117 {
118 }
119
120 CommandLine::CommandLine(const ConstArrayRef<const char *> &cmdline)
121     : impl_(new Impl(cmdline.data(), cmdline.size()))
122 {
123 }
124
125 CommandLine::CommandLine(const CommandLine &other)
126     : impl_(new Impl(other.argv(), other.argc()))
127 {
128 }
129
130 CommandLine::~CommandLine()
131 {
132 }
133
134 void CommandLine::initFromArray(const ConstArrayRef<const char *> &cmdline)
135 {
136     impl_.reset(new Impl(cmdline.data(), cmdline.size()));
137 }
138
139 void CommandLine::append(const char *arg)
140 {
141     GMX_RELEASE_ASSERT(impl_->argc_ == static_cast<int>(impl_->args_.size()),
142                        "Command-line has been modified externally");
143     size_t newSize = impl_->args_.size() + 1;
144     impl_->args_.reserve(newSize);
145     impl_->argv_.reserve(newSize + 1);
146     char *newArg = strdup(arg);
147     if (newArg == NULL)
148     {
149         throw std::bad_alloc();
150     }
151     impl_->args_.push_back(newArg);
152     impl_->argv_.pop_back(); // Remove the trailing NULL.
153     impl_->argv_.push_back(newArg);
154     impl_->argv_.push_back(NULL);
155     impl_->argc_ = static_cast<int>(newSize);
156 }
157
158 namespace
159 {
160
161 //! Helper function for converting values to strings
162 template <typename T>
163 std::string value2string(T value)
164 {
165     std::stringstream ss;
166     ss << value;
167     return ss.str();
168 }
169
170 }       // namespace
171
172 void CommandLine::addOption(const char *name, const char *value)
173 {
174     append(name);
175     append(value);
176 }
177
178 void CommandLine::addOption(const char *name, const std::string &value)
179 {
180     addOption(name, value.c_str());
181 }
182
183 void CommandLine::addOption(const char *name, int value)
184 {
185     append(name);
186     append(value2string(value));
187 }
188
189 void CommandLine::addOption(const char *name, double value)
190 {
191     append(name);
192     append(value2string(value));
193 }
194
195 void CommandLine::merge(const CommandLine &args)
196 {
197     // Skip first argument if it is the module name.
198     const int firstArg = (args.arg(0)[0] == '-' ? 0 : 1);
199     for (int i = firstArg; i < args.argc(); ++i)
200     {
201         append(args.arg(i));
202     }
203 }
204
205 int &CommandLine::argc()
206 {
207     return impl_->argc_;
208 }
209 char **CommandLine::argv()
210 {
211     return &impl_->argv_[0];
212 }
213 int CommandLine::argc() const
214 {
215     return impl_->argc_;
216 }
217 const char *const *CommandLine::argv() const
218 {
219     return &impl_->argv_[0];
220 }
221 const char *CommandLine::arg(int i) const
222 {
223     return impl_->argv_[i];
224 }
225
226 std::string CommandLine::toString() const
227 {
228     return CommandLineProgramContext(argc(), argv()).commandLine();
229 }
230
231 /********************************************************************
232  * CommandLineTestHelper::Impl
233  */
234
235 class CommandLineTestHelper::Impl
236 {
237     public:
238         struct OutputFileInfo
239         {
240             OutputFileInfo(const char *option, const std::string &path)
241                 : option(option), path(path)
242             {
243             }
244
245             std::string         option;
246             std::string         path;
247         };
248
249         typedef std::vector<OutputFileInfo>        OutputFileList;
250
251         explicit Impl(TestFileManager *fileManager)
252             : fileManager_(*fileManager)
253         {
254         }
255
256         TestFileManager &fileManager_;
257         OutputFileList   outputFiles_;
258 };
259
260 /********************************************************************
261  * CommandLineTestHelper
262  */
263
264 // static
265 int CommandLineTestHelper::runModule(
266         CommandLineModuleInterface *module, CommandLine *commandLine)
267 {
268     CommandLineModuleSettings settings;
269     module->init(&settings);
270     return module->run(commandLine->argc(), commandLine->argv());
271 }
272
273 // static
274 int CommandLineTestHelper::runModule(
275         CommandLineOptionsModuleInterface::FactoryMethod  factory,
276         CommandLine                                      *commandLine)
277 {
278     // The name and description are not used in the tests, so they can be NULL.
279     boost::scoped_ptr<CommandLineModuleInterface> module(
280             CommandLineOptionsModuleInterface::createModule(NULL, NULL, factory));
281     return runModule(module.get(), commandLine);
282 }
283
284 CommandLineTestHelper::CommandLineTestHelper(TestFileManager *fileManager)
285     : impl_(new Impl(fileManager))
286 {
287 }
288
289 CommandLineTestHelper::~CommandLineTestHelper()
290 {
291 }
292
293 void CommandLineTestHelper::setInputFileContents(
294         CommandLine *args, const char *option, const char *extension,
295         const std::string &contents)
296 {
297     GMX_ASSERT(extension[0] != '.', "Extension should not contain a dot");
298     std::string fullFilename = impl_->fileManager_.getTemporaryFilePath(
299                 formatString("%d.%s", args->argc(), extension));
300     File::writeFileFromString(fullFilename, contents);
301     args->addOption(option, fullFilename);
302 }
303
304 void CommandLineTestHelper::setInputFileContents(
305         CommandLine *args, const char *option, const char *extension,
306         const ConstArrayRef<const char *> &contents)
307 {
308     GMX_ASSERT(extension[0] != '.', "Extension should not contain a dot");
309     std::string fullFilename = impl_->fileManager_.getTemporaryFilePath(
310                 formatString("%d.%s", args->argc(), extension));
311     File        file(fullFilename, "w");
312     ConstArrayRef<const char *>::const_iterator i;
313     for (i = contents.begin(); i != contents.end(); ++i)
314     {
315         file.writeLine(*i);
316     }
317     file.close();
318     args->addOption(option, fullFilename);
319 }
320
321 void CommandLineTestHelper::setOutputFile(
322         CommandLine *args, const char *option, const char *filename)
323 {
324     std::string fullFilename = impl_->fileManager_.getTemporaryFilePath(filename);
325     args->addOption(option, fullFilename);
326     impl_->outputFiles_.push_back(Impl::OutputFileInfo(option, fullFilename));
327 }
328
329 void CommandLineTestHelper::setOutputFileNoTest(
330         CommandLine *args, const char *option, const char *extension)
331 {
332     std::string fullFilename = impl_->fileManager_.getTemporaryFilePath(
333                 formatString("%d.%s", args->argc(), extension));
334     args->addOption(option, fullFilename);
335 }
336
337 void CommandLineTestHelper::checkOutputFiles(TestReferenceChecker checker) const
338 {
339     if (!impl_->outputFiles_.empty())
340     {
341         TestReferenceChecker                 outputChecker(
342                 checker.checkCompound("OutputFiles", "Files"));
343         Impl::OutputFileList::const_iterator outfile;
344         for (outfile = impl_->outputFiles_.begin();
345              outfile != impl_->outputFiles_.end();
346              ++outfile)
347         {
348             std::string output = File::readToString(outfile->path);
349             outputChecker.checkStringBlock(output, outfile->option.c_str());
350         }
351     }
352 }
353
354 /********************************************************************
355  * CommandLineTestBase::Impl
356  */
357
358 class CommandLineTestBase::Impl
359 {
360     public:
361         Impl() : helper_(&tempFiles_)
362         {
363             cmdline_.append("module");
364         }
365
366         TestReferenceData     data_;
367         TestFileManager       tempFiles_;
368         CommandLineTestHelper helper_;
369         CommandLine           cmdline_;
370 };
371
372 /********************************************************************
373  * CommandLineTestBase
374  */
375
376 CommandLineTestBase::CommandLineTestBase()
377     : impl_(new Impl)
378 {
379 }
380
381 CommandLineTestBase::~CommandLineTestBase()
382 {
383 }
384
385 void CommandLineTestBase::setInputFile(
386         const char *option, const char *filename)
387 {
388     impl_->cmdline_.addOption(option, TestFileManager::getInputFilePath(filename));
389 }
390
391 void CommandLineTestBase::setInputFileContents(
392         const char *option, const char *extension, const std::string &contents)
393 {
394     impl_->helper_.setInputFileContents(&impl_->cmdline_, option, extension,
395                                         contents);
396 }
397
398 void CommandLineTestBase::setInputFileContents(
399         const char *option, const char *extension,
400         const ConstArrayRef<const char *> &contents)
401 {
402     impl_->helper_.setInputFileContents(&impl_->cmdline_, option, extension,
403                                         contents);
404 }
405
406 void CommandLineTestBase::setOutputFile(
407         const char *option, const char *filename)
408 {
409     impl_->helper_.setOutputFile(&impl_->cmdline_, option, filename);
410 }
411
412 void CommandLineTestBase::setOutputFileNoTest(
413         const char *option, const char *extension)
414 {
415     impl_->helper_.setOutputFileNoTest(&impl_->cmdline_, option, extension);
416 }
417
418 CommandLine &CommandLineTestBase::commandLine()
419 {
420     return impl_->cmdline_;
421 }
422
423 TestFileManager &CommandLineTestBase::fileManager()
424 {
425     return impl_->tempFiles_;
426 }
427
428 TestReferenceChecker CommandLineTestBase::rootChecker()
429 {
430     return impl_->data_.rootChecker();
431 }
432
433 void CommandLineTestBase::checkOutputFiles()
434 {
435     impl_->helper_.checkOutputFiles(rootChecker());
436 }
437
438 } // namespace test
439 } // namespace gmx