2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016,2017, 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/gmxassert.h"
59 #include "gromacs/utility/strconvert.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/filematchers.h"
66 #include "testutils/refdata.h"
67 #include "testutils/testfilemanager.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(nullptr);
107 CommandLine::Impl::~Impl()
109 for (size_t i
= 0; i
< args_
.size(); ++i
)
115 /********************************************************************
119 CommandLine::CommandLine()
120 : impl_(new Impl(nullptr, 0))
124 CommandLine::CommandLine(const ArrayRef
<const char *const> &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 ArrayRef
<const char *const> &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
== nullptr)
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(nullptr);
159 impl_
->argc_
= static_cast<int>(newSize
);
162 void CommandLine::addOption(const char *name
)
167 void CommandLine::addOption(const char *name
, const char *value
)
173 void CommandLine::addOption(const char *name
, const std::string
&value
)
175 addOption(name
, value
.c_str());
178 void CommandLine::addOption(const char *name
, int value
)
181 append(gmx::toString(value
));
184 void CommandLine::addOption(const char *name
, double value
)
187 append(gmx::toString(value
));
190 void CommandLine::merge(const CommandLine
&args
)
194 // Skip first argument if it is the module name.
195 const int firstArg
= (args
.arg(0)[0] == '-' ? 0 : 1);
196 for (int i
= firstArg
; i
< args
.argc(); ++i
)
203 int &CommandLine::argc()
207 char **CommandLine::argv()
209 return &impl_
->argv_
[0];
211 int CommandLine::argc() const
215 const char *const *CommandLine::argv() const
217 return &impl_
->argv_
[0];
219 const char *CommandLine::arg(int i
) const
221 return impl_
->argv_
[i
];
224 std::string
CommandLine::toString() const
226 return CommandLineProgramContext(argc(), argv()).commandLine();
229 bool CommandLine::contains(const char *name
) const
231 for (int i
= 0; i
< impl_
->argc_
; ++i
)
233 if (std::strcmp(arg(i
), name
) == 0)
241 /********************************************************************
242 * CommandLineTestHelper::Impl
245 class CommandLineTestHelper::Impl
248 struct OutputFileInfo
250 OutputFileInfo(const char *option
, const std::string
&path
,
251 FileMatcherPointer matcher
)
252 : option(option
), path(path
), matcher(move(matcher
))
258 FileMatcherPointer matcher
;
261 typedef std::vector
<OutputFileInfo
> OutputFileList
;
263 explicit Impl(TestFileManager
*fileManager
)
264 : fileManager_(*fileManager
)
268 TestFileManager
&fileManager_
;
269 OutputFileList outputFiles_
;
272 /********************************************************************
273 * CommandLineTestHelper
277 int CommandLineTestHelper::runModuleDirect(
278 ICommandLineModule
*module
, CommandLine
*commandLine
)
280 CommandLineModuleSettings settings
;
281 module
->init(&settings
);
282 return module
->run(commandLine
->argc(), commandLine
->argv());
286 int CommandLineTestHelper::runModuleDirect(
287 std::unique_ptr
<ICommandLineOptionsModule
> module
, CommandLine
*commandLine
)
289 // The name and description are not used in the tests, so they can be NULL.
290 const std::unique_ptr
<ICommandLineModule
> wrapperModule(
291 ICommandLineOptionsModule::createModule(nullptr, nullptr, std::move(module
)));
292 return runModuleDirect(wrapperModule
.get(), commandLine
);
296 int CommandLineTestHelper::runModuleFactory(
297 std::function
<std::unique_ptr
<ICommandLineOptionsModule
>()> factory
,
298 CommandLine
*commandLine
)
300 return runModuleDirect(factory(), commandLine
);
303 CommandLineTestHelper::CommandLineTestHelper(TestFileManager
*fileManager
)
304 : impl_(new Impl(fileManager
))
308 CommandLineTestHelper::~CommandLineTestHelper()
312 void CommandLineTestHelper::setInputFileContents(
313 CommandLine
*args
, const char *option
, const char *extension
,
314 const std::string
&contents
)
316 GMX_ASSERT(extension
[0] != '.', "Extension should not contain a dot");
317 std::string fullFilename
= impl_
->fileManager_
.getTemporaryFilePath(
318 formatString("%d.%s", args
->argc(), extension
));
319 TextWriter::writeFileFromString(fullFilename
, contents
);
320 args
->addOption(option
, fullFilename
);
323 void CommandLineTestHelper::setInputFileContents(
324 CommandLine
*args
, const char *option
, const char *extension
,
325 const ArrayRef
<const char *const> &contents
)
327 GMX_ASSERT(extension
[0] != '.', "Extension should not contain a dot");
328 std::string fullFilename
= impl_
->fileManager_
.getTemporaryFilePath(
329 formatString("%d.%s", args
->argc(), extension
));
330 TextWriter
file(fullFilename
);
331 ArrayRef
<const char *const>::const_iterator i
;
332 for (i
= contents
.begin(); i
!= contents
.end(); ++i
)
337 args
->addOption(option
, fullFilename
);
340 void CommandLineTestHelper::setOutputFile(
341 CommandLine
*args
, const char *option
, const char *filename
,
342 const ITextBlockMatcherSettings
&matcher
)
344 setOutputFile(args
, option
, filename
, TextFileMatch(matcher
));
347 void CommandLineTestHelper::setOutputFile(
348 CommandLine
*args
, const char *option
, const char *filename
,
349 const IFileMatcherSettings
&matcher
)
351 std::string
suffix(filename
);
352 if (startsWith(filename
, "."))
354 suffix
= formatString("%d.%s", args
->argc(), filename
);
356 std::string fullFilename
= impl_
->fileManager_
.getTemporaryFilePath(suffix
);
357 args
->addOption(option
, fullFilename
);
358 impl_
->outputFiles_
.emplace_back(option
, fullFilename
, matcher
.createFileMatcher());
361 void CommandLineTestHelper::checkOutputFiles(TestReferenceChecker checker
) const
363 if (!impl_
->outputFiles_
.empty())
365 TestReferenceChecker
outputChecker(
366 checker
.checkCompound("OutputFiles", "Files"));
367 Impl::OutputFileList::const_iterator outfile
;
368 for (const auto &outfile
: impl_
->outputFiles_
)
370 TestReferenceChecker
fileChecker(
371 outputChecker
.checkCompound("File", outfile
.option
.c_str()));
372 outfile
.matcher
->checkFile(outfile
.path
, &fileChecker
);
377 /********************************************************************
378 * CommandLineTestBase::Impl
381 class CommandLineTestBase::Impl
384 Impl() : helper_(&tempFiles_
)
386 cmdline_
.append("module");
389 TestReferenceData data_
;
390 TestFileManager tempFiles_
;
391 CommandLineTestHelper helper_
;
392 CommandLine cmdline_
;
395 /********************************************************************
396 * CommandLineTestBase
399 CommandLineTestBase::CommandLineTestBase()
404 CommandLineTestBase::~CommandLineTestBase()
408 void CommandLineTestBase::setInputFile(
409 const char *option
, const char *filename
)
411 impl_
->cmdline_
.addOption(option
, TestFileManager::getInputFilePath(filename
));
414 void CommandLineTestBase::setInputFileContents(
415 const char *option
, const char *extension
, const std::string
&contents
)
417 impl_
->helper_
.setInputFileContents(&impl_
->cmdline_
, option
, extension
,
421 void CommandLineTestBase::setInputFileContents(
422 const char *option
, const char *extension
,
423 const ArrayRef
<const char *const> &contents
)
425 impl_
->helper_
.setInputFileContents(&impl_
->cmdline_
, option
, extension
,
429 void CommandLineTestBase::setOutputFile(
430 const char *option
, const char *filename
,
431 const ITextBlockMatcherSettings
&matcher
)
433 impl_
->helper_
.setOutputFile(&impl_
->cmdline_
, option
, filename
, matcher
);
436 void CommandLineTestBase::setOutputFile(
437 const char *option
, const char *filename
,
438 const IFileMatcherSettings
&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());