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