Work around memory leak in t_state
[gromacs.git] / src / testutils / cmdlinetest.cpp
blobeb05df8a9bf31eb6c8b91fc61e229ffb77b624a9
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016, 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.
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.
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.
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.
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.
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.
35 /*! \internal \file
36 * \brief
37 * Implements classes from cmdlinetest.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_testutils
42 #include "gmxpre.h"
44 #include "cmdlinetest.h"
46 #include <cstdlib>
47 #include <cstring>
49 #include <memory>
50 #include <new>
51 #include <sstream>
52 #include <vector>
54 #include "gromacs/commandline/cmdlinehelpcontext.h"
55 #include "gromacs/commandline/cmdlineoptionsmodule.h"
56 #include "gromacs/commandline/cmdlineprogramcontext.h"
57 #include "gromacs/utility/arrayref.h"
58 #include "gromacs/utility/filestream.h"
59 #include "gromacs/utility/gmxassert.h"
60 #include "gromacs/utility/stringstream.h"
61 #include "gromacs/utility/stringutil.h"
62 #include "gromacs/utility/textreader.h"
63 #include "gromacs/utility/textwriter.h"
65 #include "testutils/refdata.h"
66 #include "testutils/testfilemanager.h"
67 #include "testutils/textblockmatchers.h"
69 namespace gmx
71 namespace test
74 /********************************************************************
75 * CommandLine::Impl
78 class CommandLine::Impl
80 public:
81 Impl(const char *const cmdline[], size_t count);
82 ~Impl();
84 std::vector<char *> args_;
85 std::vector<char *> argv_;
86 int argc_;
89 CommandLine::Impl::Impl(const char *const cmdline[], size_t count)
91 args_.reserve(count);
92 argv_.reserve(count + 1);
93 argc_ = static_cast<int>(count);
94 for (size_t i = 0; i < count; ++i)
96 char *arg = strdup(cmdline[i]);
97 if (arg == NULL)
99 throw std::bad_alloc();
101 args_.push_back(arg);
102 argv_.push_back(arg);
104 argv_.push_back(NULL);
107 CommandLine::Impl::~Impl()
109 for (size_t i = 0; i < args_.size(); ++i)
111 std::free(args_[i]);
115 /********************************************************************
116 * CommandLine
119 CommandLine::CommandLine()
120 : impl_(new Impl(NULL, 0))
124 CommandLine::CommandLine(const ConstArrayRef<const char *> &cmdline)
125 : impl_(new Impl(cmdline.data(), cmdline.size()))
129 CommandLine::CommandLine(const CommandLine &other)
130 : impl_(new Impl(other.argv(), other.argc()))
134 CommandLine::~CommandLine()
138 void CommandLine::initFromArray(const ConstArrayRef<const char *> &cmdline)
140 impl_.reset(new Impl(cmdline.data(), cmdline.size()));
143 void CommandLine::append(const char *arg)
145 GMX_RELEASE_ASSERT(impl_->argc_ == static_cast<int>(impl_->args_.size()),
146 "Command-line has been modified externally");
147 size_t newSize = impl_->args_.size() + 1;
148 impl_->args_.reserve(newSize);
149 impl_->argv_.reserve(newSize + 1);
150 char *newArg = strdup(arg);
151 if (newArg == NULL)
153 throw std::bad_alloc();
155 impl_->args_.push_back(newArg);
156 impl_->argv_.pop_back(); // Remove the trailing NULL.
157 impl_->argv_.push_back(newArg);
158 impl_->argv_.push_back(NULL);
159 impl_->argc_ = static_cast<int>(newSize);
162 namespace
165 //! Helper function for converting values to strings
166 template <typename T>
167 std::string value2string(T value)
169 std::stringstream ss;
170 ss << value;
171 return ss.str();
174 } // namespace
176 void CommandLine::addOption(const char *name, const char *value)
178 append(name);
179 append(value);
182 void CommandLine::addOption(const char *name, const std::string &value)
184 addOption(name, value.c_str());
187 void CommandLine::addOption(const char *name, int value)
189 append(name);
190 append(value2string(value));
193 void CommandLine::addOption(const char *name, double value)
195 append(name);
196 append(value2string(value));
199 void CommandLine::merge(const CommandLine &args)
201 if (args.argc() > 0)
203 // Skip first argument if it is the module name.
204 const int firstArg = (args.arg(0)[0] == '-' ? 0 : 1);
205 for (int i = firstArg; i < args.argc(); ++i)
207 append(args.arg(i));
212 int &CommandLine::argc()
214 return impl_->argc_;
216 char **CommandLine::argv()
218 return &impl_->argv_[0];
220 int CommandLine::argc() const
222 return impl_->argc_;
224 const char *const *CommandLine::argv() const
226 return &impl_->argv_[0];
228 const char *CommandLine::arg(int i) const
230 return impl_->argv_[i];
233 std::string CommandLine::toString() const
235 return CommandLineProgramContext(argc(), argv()).commandLine();
238 /********************************************************************
239 * CommandLineTestHelper::Impl
242 class CommandLineTestHelper::Impl
244 public:
245 struct OutputFileInfo
247 OutputFileInfo(const char *option, const std::string &path,
248 TextBlockMatcherPointer matcher)
249 : option(option), path(path), matcher(move(matcher))
252 OutputFileInfo(OutputFileInfo &&other)
253 : option(std::move(other.option)), path(std::move(other.path)),
254 matcher(std::move(other.matcher))
258 OutputFileInfo &operator=(OutputFileInfo &&other)
260 option = std::move(other.option);
261 path = std::move(other.path);
262 matcher = std::move(other.matcher);
263 return *this;
266 std::string option;
267 std::string path;
268 TextBlockMatcherPointer matcher;
271 typedef std::vector<OutputFileInfo> OutputFileList;
273 explicit Impl(TestFileManager *fileManager)
274 : fileManager_(*fileManager)
278 TestFileManager &fileManager_;
279 OutputFileList outputFiles_;
282 /********************************************************************
283 * CommandLineTestHelper
286 // static
287 int CommandLineTestHelper::runModuleDirect(
288 ICommandLineModule *module, CommandLine *commandLine)
290 CommandLineModuleSettings settings;
291 module->init(&settings);
292 return module->run(commandLine->argc(), commandLine->argv());
295 // static
296 int CommandLineTestHelper::runModuleDirect(
297 std::unique_ptr<ICommandLineOptionsModule> module, CommandLine *commandLine)
299 // The name and description are not used in the tests, so they can be NULL.
300 const std::unique_ptr<ICommandLineModule> wrapperModule(
301 ICommandLineOptionsModule::createModule(NULL, NULL, std::move(module)));
302 return runModuleDirect(wrapperModule.get(), commandLine);
305 // static
306 int CommandLineTestHelper::runModuleFactory(
307 std::function<std::unique_ptr<ICommandLineOptionsModule>()> factory,
308 CommandLine *commandLine)
310 return runModuleDirect(factory(), commandLine);
313 CommandLineTestHelper::CommandLineTestHelper(TestFileManager *fileManager)
314 : impl_(new Impl(fileManager))
318 CommandLineTestHelper::~CommandLineTestHelper()
322 void CommandLineTestHelper::setInputFileContents(
323 CommandLine *args, const char *option, const char *extension,
324 const std::string &contents)
326 GMX_ASSERT(extension[0] != '.', "Extension should not contain a dot");
327 std::string fullFilename = impl_->fileManager_.getTemporaryFilePath(
328 formatString("%d.%s", args->argc(), extension));
329 TextWriter::writeFileFromString(fullFilename, contents);
330 args->addOption(option, fullFilename);
333 void CommandLineTestHelper::setInputFileContents(
334 CommandLine *args, const char *option, const char *extension,
335 const ConstArrayRef<const char *> &contents)
337 GMX_ASSERT(extension[0] != '.', "Extension should not contain a dot");
338 std::string fullFilename = impl_->fileManager_.getTemporaryFilePath(
339 formatString("%d.%s", args->argc(), extension));
340 TextWriter file(fullFilename);
341 ConstArrayRef<const char *>::const_iterator i;
342 for (i = contents.begin(); i != contents.end(); ++i)
344 file.writeLine(*i);
346 file.close();
347 args->addOption(option, fullFilename);
350 void CommandLineTestHelper::setOutputFile(
351 CommandLine *args, const char *option, const char *filename,
352 const ITextBlockMatcherSettings &matcher)
354 std::string suffix(filename);
355 if (startsWith(filename, "."))
357 suffix = formatString("%d.%s", args->argc(), filename);
359 std::string fullFilename = impl_->fileManager_.getTemporaryFilePath(suffix);
360 args->addOption(option, fullFilename);
361 impl_->outputFiles_.emplace_back(option, fullFilename, matcher.createMatcher());
364 void CommandLineTestHelper::checkOutputFiles(TestReferenceChecker checker) const
366 if (!impl_->outputFiles_.empty())
368 TestReferenceChecker outputChecker(
369 checker.checkCompound("OutputFiles", "Files"));
370 Impl::OutputFileList::const_iterator outfile;
371 for (outfile = impl_->outputFiles_.begin();
372 outfile != impl_->outputFiles_.end();
373 ++outfile)
375 TestReferenceChecker fileChecker(
376 outputChecker.checkCompound("File", outfile->option.c_str()));
377 TextInputFile stream(outfile->path);
378 outfile->matcher->checkStream(&stream, &fileChecker);
379 stream.close();
384 /********************************************************************
385 * CommandLineTestBase::Impl
388 class CommandLineTestBase::Impl
390 public:
391 Impl() : helper_(&tempFiles_)
393 cmdline_.append("module");
396 TestReferenceData data_;
397 TestFileManager tempFiles_;
398 CommandLineTestHelper helper_;
399 CommandLine cmdline_;
402 /********************************************************************
403 * CommandLineTestBase
406 CommandLineTestBase::CommandLineTestBase()
407 : impl_(new Impl)
411 CommandLineTestBase::~CommandLineTestBase()
415 void CommandLineTestBase::setInputFile(
416 const char *option, const char *filename)
418 impl_->cmdline_.addOption(option, TestFileManager::getInputFilePath(filename));
421 void CommandLineTestBase::setInputFileContents(
422 const char *option, const char *extension, const std::string &contents)
424 impl_->helper_.setInputFileContents(&impl_->cmdline_, option, extension,
425 contents);
428 void CommandLineTestBase::setInputFileContents(
429 const char *option, const char *extension,
430 const ConstArrayRef<const char *> &contents)
432 impl_->helper_.setInputFileContents(&impl_->cmdline_, option, extension,
433 contents);
436 void CommandLineTestBase::setOutputFile(
437 const char *option, const char *filename,
438 const ITextBlockMatcherSettings &matcher)
440 impl_->helper_.setOutputFile(&impl_->cmdline_, option, filename, matcher);
443 CommandLine &CommandLineTestBase::commandLine()
445 return impl_->cmdline_;
448 TestFileManager &CommandLineTestBase::fileManager()
450 return impl_->tempFiles_;
453 TestReferenceChecker CommandLineTestBase::rootChecker()
455 return impl_->data_.rootChecker();
458 void CommandLineTestBase::testWriteHelp(ICommandLineModule *module)
460 StringOutputStream stream;
461 TextWriter writer(&stream);
462 CommandLineHelpContext context(&writer, eHelpOutputFormat_Console, nullptr, "test");
463 context.setModuleDisplayName(formatString("%s %s", "test", module->name()));
464 module->writeHelp(context);
465 TestReferenceChecker checker(rootChecker());
466 checker.checkTextBlock(stream.toString(), "HelpOutput");
469 void CommandLineTestBase::checkOutputFiles()
471 impl_->helper_.checkOutputFiles(rootChecker());
474 } // namespace test
475 } // namespace gmx