clang-tidy: override
[alexxy/gromacs.git] / src / testutils / cmdlinetest.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015,2016,2017,2018, 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 /*! \libinternal \file
36  * \brief
37  * Declares utilities testing command-line programs.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \inlibraryapi
41  * \ingroup module_testutils
42  */
43 #ifndef GMX_TESTUTILS_CMDLINETEST_H
44 #define GMX_TESTUTILS_CMDLINETEST_H
45
46 #include <functional>
47 #include <memory>
48 #include <string>
49
50 #include <gtest/gtest.h>
51
52 // arrayref.h is not strictly necessary for this header, but nearly all
53 // callers will need it to use the constructor that takes ArrayRef.
54 #include "gromacs/utility/arrayref.h"
55 #include "gromacs/utility/classhelpers.h"
56
57 namespace gmx
58 {
59
60 class ICommandLineModule;
61 class ICommandLineOptionsModule;
62
63 namespace test
64 {
65
66 class FloatingPointTolerance;
67 class IFileMatcherSettings;
68 class ITextBlockMatcherSettings;
69 class TestFileManager;
70 class TestReferenceChecker;
71
72 /*! \libinternal \brief
73  * Helper class for tests that need to construct command lines.
74  *
75  * This class helps in writing tests for command-line handling.
76  * The constructor method takes an array of const char pointers, specifying the
77  * command-line arguments, each as one array element.  It is also possible to
78  * construct the command line by adding individual arguments with append() and
79  * addOption().
80  * The argc() and argv() methods can then be used to obtain `argc` and `argv`
81  * (non-const char pointers) arrays for passing into methods that expect these.
82  *
83  * Note that although the interface allows passing the argc and argv pointers
84  * to methods that modify them (typically as \p f(&argc(), argv())), currently
85  * the CommandLine object is not in a consistent state internally if the
86  * parameters are actually modified.  Reading the command line is possible
87  * afterwards, but modification is not.
88  *
89  * If you need to construct command lines that refer to files on the file
90  * system, see CommandLineTestHelper and CommandLineTestBase for additional
91  * convenience utilities.
92  *
93  * All constructors and methods that modify this class may throw an
94  * std::bad_alloc.  Const methods and accessors do not throw.
95  *
96  * \inlibraryapi
97  * \ingroup module_testutils
98  */
99 class CommandLine
100 {
101     public:
102         //! Initializes an empty command-line object.
103         CommandLine();
104         /*! \brief
105          * Initializes a command-line object from an array.
106          *
107          * \param[in] cmdline  Array of command-line arguments.
108          *
109          * \p cmdline should include the binary name as the first element if
110          * that is desired in the output.
111          *
112          * This constructor is not explicit to make it possible to create a
113          * CommandLine object directly from a C array.
114          */
115         CommandLine(const ArrayRef<const char *const> &cmdline);
116         //! \copydoc CommandLine(const ArrayRef<const char *const> &)
117         CommandLine(const ArrayRef<const std::string> &cmdline);
118         //! Creates a deep copy of a command-line object.
119         CommandLine(const CommandLine &other);
120         ~CommandLine();
121
122         /*! \brief
123          * Initializes a command-line object in-place from an array.
124          *
125          * \param[in] cmdline  Array of command-line arguments.
126          *
127          * \p cmdline should include the binary name as the first element if
128          * that is desired in the output.
129          *
130          * This function does the same as the constructor that takes a
131          * ArrayRef.  Any earlier contents of the object are discarded.
132          *
133          * Strong exception safety.
134          */
135         void initFromArray(const ArrayRef<const char *const> &cmdline);
136
137         /*! \brief
138          * Appends an argument to the command line.
139          *
140          * \param[in] arg  Argument to append.
141          *
142          * Strong exception safety.
143          */
144         void append(const char *arg);
145         //! Convenience overload taking a std::string.
146         void append(const std::string &arg) { append(arg.c_str()); }
147         /*! \brief
148          * Adds an option to the command line, typically a boolean.
149          *
150          * \param[in] name   Name of the option to append, which
151          *                   should start with "-".
152          */
153         void addOption(const char *name);
154         /*! \brief
155          * Adds an option-value pair to the command line.
156          *
157          * \param[in] name   Name of the option to append, which
158          *                   should start with "-".
159          * \param[in] value  Value of the argument to append.
160          */
161         void addOption(const char *name, const char *value);
162         //! Convenience overload taking a std::string.
163         void addOption(const char *name, const std::string &value);
164         //! Overload taking an int.
165         void addOption(const char *name, int value);
166         //! Overload taking a double.
167         void addOption(const char *name, double value);
168         /*! \brief
169          * Appends all arguments from \p args to the command line.
170          *
171          * If the first argument of \p args does not start with a `-`, it is
172          * skipped, assuming it is a gmx module name and thus useless.
173          */
174         void merge(const CommandLine &args);
175
176         //! Returns argc for passing into C-style command-line handling.
177         int &argc();
178         //! Returns argv for passing into C-style command-line handling.
179         char **argv();
180         //! Returns argc for passing into C-style command-line handling.
181         int argc() const;
182         //! Returns argv for passing into C-style command-line handling.
183         const char *const *argv() const;
184         //! Returns a single argument.
185         const char *arg(int i) const;
186
187         //! Returns the command line formatted as a single string.
188         std::string toString() const;
189
190         //! Whether the command line contains the given option.
191         bool contains(const char *name) const;
192
193     private:
194         class Impl;
195
196         PrivateImplPointer<Impl> impl_;
197 };
198
199 /*! \libinternal \brief
200  * Helper class for tests that construct command lines that need to reference
201  * existing files.
202  *
203  * This class provides helper methods for:
204  *
205  *   1. Adding input files to a CommandLine instance by generating them from a
206  *      string provided in the test (setInputFileContents()).
207  *   2. Adding output files to a CommandLine instance (setOutputFile()).
208  *   3. Checking the contents of some of the output files using
209  *      TestReferenceData (setOutputFile() and checkOutputFiles()).
210  *   4. Static methods for easily executing command-line modules
211  *      (various overloads of runModule()).
212  *
213  * All files created during the test are cleaned up at the end of the test.
214  *
215  * All methods can throw std::bad_alloc.
216  *
217  * \see TestFileManager
218  * \inlibraryapi
219  * \ingroup module_testutils
220  */
221 class CommandLineTestHelper
222 {
223     public:
224         /*! \brief
225          * Runs a command-line program that implements ICommandLineModule.
226          *
227          * \param[in,out] module       Module to run.
228          *     The function does not take ownership.
229          * \param[in,out] commandLine  Command line parameters to pass.
230          *     This is only modified if \p module modifies it.
231          * \returns The return value of the module.
232          * \throws  unspecified  Any exception thrown by the module.
233          */
234         static int
235         runModuleDirect(ICommandLineModule *module, CommandLine *commandLine);
236         /*! \brief
237          * Runs a command-line program that implements
238          * ICommandLineOptionsModule.
239          *
240          * \param[in,out] module       Module to run.
241          * \param[in,out] commandLine  Command line parameters to pass.
242          *     This is only modified if \p module modifies it.
243          * \returns The return value of the module.
244          * \throws  unspecified  Any exception thrown by the module.
245          */
246         static int
247         runModuleDirect(std::unique_ptr<ICommandLineOptionsModule> module,
248                         CommandLine                               *commandLine);
249         /*! \brief
250          * Runs a command-line program that implements
251          * ICommandLineOptionsModule.
252          *
253          * \param[in] factory          Factory method for the module to run.
254          * \param[in,out] commandLine  Command line parameters to pass.
255          *     This is only modified if the module modifies it.
256          * \returns The return value of the module.
257          * \throws  unspecified  Any exception thrown by the factory or the
258          *     module.
259          */
260         static int
261         runModuleFactory(const std::function<std::unique_ptr<ICommandLineOptionsModule>()> &factory,
262                          CommandLine                                                       *commandLine);
263
264         /*! \brief
265          * Initializes an instance.
266          *
267          * \param  fileManager  File manager to use for generating temporary
268          *     file names and to track temporary files.
269          */
270         explicit CommandLineTestHelper(TestFileManager *fileManager);
271         ~CommandLineTestHelper();
272
273         /*! \brief
274          * Generates and sets an input file.
275          *
276          * \param[in,out] args      CommandLine to which to add the option.
277          * \param[in]     option    Option to set.
278          * \param[in]     extension Extension for the file to create.
279          * \param[in]     contents  Text to write to the input file.
280          *
281          * Creates a temporary file with contents from \p contents, and adds
282          * \p option to \p args with a value that points to the generated file.
283          */
284         void setInputFileContents(CommandLine *args, const char *option,
285                                   const char *extension,
286                                   const std::string &contents);
287         /*! \brief
288          * Generates and sets an input file.
289          *
290          * \param[in,out] args      CommandLine to which to add the option.
291          * \param[in]     option    Option to set.
292          * \param[in]     extension Extension for the file to create.
293          * \param[in]     contents  Text to write to the input file.
294          *
295          * Creates a temporary file with contents from \p contents (each array
296          * entry on its own line), and adds \p option to \p args with a value
297          * that points to the generated file.
298          */
299         void setInputFileContents(CommandLine *args, const char *option,
300                                   const char *extension,
301                                   const ArrayRef<const char *const> &contents);
302         /*! \brief
303          * Sets an output file parameter and adds it to the set of tested files.
304          *
305          * \param[in,out] args      CommandLine to which to add the option.
306          * \param[in]     option    Option to set.
307          * \param[in]     filename  Name of the output file.
308          * \param[in]     matcher   Specifies how the contents of the file are
309          *     tested.
310          *
311          * This method does the following:
312          *  - Adds \p option to \p args to point a temporary file name
313          *    constructed from \p filename.
314          *  - Makes checkOutputFiles() to check the contents of the file
315          *    against reference data, using \p matcher.
316          *  - Marks the temporary file for removal at test teardown.
317          *
318          * \p filename is given to TestTemporaryFileManager to make a unique
319          * filename for the temporary file.
320          * If \p filename starts with a dot, a unique number is prefixed (such
321          * that it is possible to create multiple files with the same extension
322          * by just specifying the extension for every call of setOutputFile()).
323          *
324          * If the output file is needed to trigger some computation, or is
325          * unconditionally produced by the code under test, but the contents
326          * are not interesting for the test, use NoContentsMatch as the matcher.
327          * Note that the existence of the output file is still verified.
328          */
329         void setOutputFile(CommandLine *args, const char *option,
330                            const char *filename,
331                            const ITextBlockMatcherSettings &matcher);
332         //! \copydoc setOutputFile(CommandLine *, const char *, const char *, const ITextBlockMatcherSettings &)
333         void setOutputFile(CommandLine *args, const char *option,
334                            const char *filename,
335                            const IFileMatcherSettings &matcher);
336
337         /*! \brief
338          * Checks output files added with setOutputFile() against reference
339          * data.
340          *
341          * \param     checker  Reference data root location where the reference
342          *     data is stored.
343          *
344          * The file contents are tested verbatim, using direct string
345          * comparison.  The text can be found verbatim in the reference data
346          * XML files for manual inspection.
347          *
348          * Generates non-fatal test failures if some output file contents do
349          * not match the reference data.
350          */
351         void checkOutputFiles(TestReferenceChecker checker) const;
352
353     private:
354         class Impl;
355
356         PrivateImplPointer<Impl> impl_;
357 };
358
359 /*! \libinternal \brief
360  * Test fixture for tests that call a single command-line program with
361  * input/output files.
362  *
363  * This class provides a convenient package for using CommandLineTestHelper in
364  * a test that do not need special customization.  It takes care of creating
365  * the other necessary objects (like TestFileManager, TestReferenceData, and
366  * CommandLine) and wrapping the methods from CommandLineTestHelper such that
367  * extra parameters are not needed.  Additionally, it provides setInputFile()
368  * as a convenience function for adding a fixed input file, pointing to a file
369  * that resides in the source tree.
370  *
371  * \see CommandLineTestHelper
372  * \inlibraryapi
373  * \ingroup module_testutils
374  */
375 class CommandLineTestBase : public ::testing::Test
376 {
377     public:
378         CommandLineTestBase();
379         ~CommandLineTestBase() override;
380
381         /*! \brief
382          * Sets an input file.
383          *
384          * \param[in]     option    Option to set.
385          * \param[in]     filename  Name of the input file.
386          *
387          * \see TestFileManager::getInputFilePath()
388          */
389         void setInputFile(const char *option, const char *filename);
390         //! \copydoc setInputFile(const char *, const char *);
391         void setInputFile(const char *option, const std::string &filename);
392         /*! \brief
393          * Generates and sets an input file.
394          *
395          * \see CommandLineTestHelper::setInputFileContents()
396          */
397         void setInputFileContents(const char        *option,
398                                   const char        *extension,
399                                   const std::string &contents);
400         /*! \brief
401          * Generates and sets an input file.
402          *
403          * \see CommandLineTestHelper::setInputFileContents()
404          */
405         void setInputFileContents(const char                        *option,
406                                   const char                        *extension,
407                                   const ArrayRef<const char *const> &contents);
408         /*! \brief
409          * Sets an output file parameter and adds it to the set of tested files.
410          *
411          * \see CommandLineTestHelper::setOutputFile()
412          */
413         void setOutputFile(const char *option, const char *filename,
414                            const ITextBlockMatcherSettings &matcher);
415         /*! \brief
416          * Sets an output file parameter and adds it to the set of tested files.
417          *
418          * \see CommandLineTestHelper::setOutputFile()
419          */
420         void setOutputFile(const char *option, const char *filename,
421                            const IFileMatcherSettings &matcher);
422
423         /*! \brief
424          * Returns the internal CommandLine object used to construct the
425          * command line for the test.
426          *
427          * Derived test fixtures can use this to add additional options, and
428          * to access the final command line to do the actual call that is being
429          * tested.
430          *
431          * Does not throw.
432          */
433         CommandLine &commandLine();
434         /*! \brief
435          * Returns the internal TestFileManager object used to manage the
436          * files.
437          *
438          * Derived test fixtures can use this to manage files in cases the
439          * canned methods are not sufficient.
440          *
441          * Does not throw.
442          */
443         TestFileManager &fileManager();
444         /*! \brief
445          * Returns the root reference data checker.
446          *
447          * Derived test fixtures can use this to check other things than output
448          * file contents.
449          */
450         TestReferenceChecker rootChecker();
451         /*! \brief
452          * Sets the tolerance for floating-point comparisons.
453          *
454          * All following floating-point comparisons using the checker will use
455          * the new tolerance.
456          *
457          * Does not throw.
458          */
459         void setDefaultTolerance(const FloatingPointTolerance &tolerance);
460         /*! \brief
461          * Checks the output of writeHelp() against reference data.
462          */
463         void testWriteHelp(ICommandLineModule *module);
464         /*! \brief
465          * Checks output files added with setOutputFile() against reference
466          * data.
467          *
468          * \see CommandLineTestHelper::checkOutputFiles()
469          */
470         void checkOutputFiles();
471
472     private:
473         class Impl;
474
475         PrivateImplPointer<Impl> impl_;
476 };
477
478 } // namespace test
479 } // namespace gmx
480
481 #endif