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.
37 * Implements classes from cmdlinetest.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_testutils
44 #include "cmdlinetest.h"
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"
74 /********************************************************************
78 class CommandLine::Impl
81 Impl(const char *const cmdline
[], size_t count
);
84 std::vector
<char *> args_
;
85 std::vector
<char *> argv_
;
89 CommandLine::Impl::Impl(const char *const cmdline
[], size_t 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
]);
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
)
115 /********************************************************************
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
);
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
);
165 //! Helper function for converting values to strings
166 template <typename T
>
167 std::string
value2string(T value
)
169 std::stringstream ss
;
176 void CommandLine::addOption(const char *name
, const char *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
)
190 append(value2string(value
));
193 void CommandLine::addOption(const char *name
, double value
)
196 append(value2string(value
));
199 void CommandLine::merge(const CommandLine
&args
)
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
)
212 int &CommandLine::argc()
216 char **CommandLine::argv()
218 return &impl_
->argv_
[0];
220 int CommandLine::argc() const
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
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
);
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
287 int CommandLineTestHelper::runModuleDirect(
288 ICommandLineModule
*module
, CommandLine
*commandLine
)
290 CommandLineModuleSettings settings
;
291 module
->init(&settings
);
292 return module
->run(commandLine
->argc(), commandLine
->argv());
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
);
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
)
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_
.push_back(
362 Impl::OutputFileInfo(option
, fullFilename
, matcher
.createMatcher()));
365 void CommandLineTestHelper::checkOutputFiles(TestReferenceChecker checker
) const
367 if (!impl_
->outputFiles_
.empty())
369 TestReferenceChecker
outputChecker(
370 checker
.checkCompound("OutputFiles", "Files"));
371 Impl::OutputFileList::const_iterator outfile
;
372 for (outfile
= impl_
->outputFiles_
.begin();
373 outfile
!= impl_
->outputFiles_
.end();
376 TestReferenceChecker
fileChecker(
377 outputChecker
.checkCompound("File", outfile
->option
.c_str()));
378 TextInputFile
stream(outfile
->path
);
379 outfile
->matcher
->checkStream(&stream
, &fileChecker
);
385 /********************************************************************
386 * CommandLineTestBase::Impl
389 class CommandLineTestBase::Impl
392 Impl() : helper_(&tempFiles_
)
394 cmdline_
.append("module");
397 TestReferenceData data_
;
398 TestFileManager tempFiles_
;
399 CommandLineTestHelper helper_
;
400 CommandLine cmdline_
;
403 /********************************************************************
404 * CommandLineTestBase
407 CommandLineTestBase::CommandLineTestBase()
412 CommandLineTestBase::~CommandLineTestBase()
416 void CommandLineTestBase::setInputFile(
417 const char *option
, const char *filename
)
419 impl_
->cmdline_
.addOption(option
, TestFileManager::getInputFilePath(filename
));
422 void CommandLineTestBase::setInputFileContents(
423 const char *option
, const char *extension
, const std::string
&contents
)
425 impl_
->helper_
.setInputFileContents(&impl_
->cmdline_
, option
, extension
,
429 void CommandLineTestBase::setInputFileContents(
430 const char *option
, const char *extension
,
431 const ConstArrayRef
<const char *> &contents
)
433 impl_
->helper_
.setInputFileContents(&impl_
->cmdline_
, option
, extension
,
437 void CommandLineTestBase::setOutputFile(
438 const char *option
, const char *filename
,
439 const ITextBlockMatcherSettings
&matcher
)
441 impl_
->helper_
.setOutputFile(&impl_
->cmdline_
, option
, filename
, matcher
);
444 CommandLine
&CommandLineTestBase::commandLine()
446 return impl_
->cmdline_
;
449 TestFileManager
&CommandLineTestBase::fileManager()
451 return impl_
->tempFiles_
;
454 TestReferenceChecker
CommandLineTestBase::rootChecker()
456 return impl_
->data_
.rootChecker();
459 void CommandLineTestBase::testWriteHelp(ICommandLineModule
*module
)
461 StringOutputStream stream
;
462 TextWriter
writer(&stream
);
463 CommandLineHelpContext
context(&writer
, eHelpOutputFormat_Console
, nullptr, "test");
464 context
.setModuleDisplayName(formatString("%s %s", "test", module
->name()));
465 module
->writeHelp(context
);
466 TestReferenceChecker
checker(rootChecker());
467 checker
.checkTextBlock(stream
.toString(), "HelpOutput");
470 void CommandLineTestBase::checkOutputFiles()
472 impl_
->helper_
.checkOutputFiles(rootChecker());