b59434dd6d333d99ab6b14cea63d12eead03609b
[alexxy/gromacs.git] / src / testutils / testfilemanager.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, 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 gmx::test::TestFileManager.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_testutils
41  */
42 #include "gmxpre.h"
43
44 #include "testfilemanager.h"
45
46 #include <cstdio>
47
48 #include <algorithm>
49 #include <set>
50 #include <string>
51
52 #include <gtest/gtest.h>
53
54 #include "gromacs/options/basicoptions.h"
55 #include "gromacs/options/ioptionscontainer.h"
56 #include "gromacs/utility/gmxassert.h"
57 #include "gromacs/utility/path.h"
58
59 #include "testutils/testoptions.h"
60
61 namespace gmx
62 {
63 namespace test
64 {
65
66 /********************************************************************
67  * TestFileManager::Impl
68  */
69
70 /*! \internal \brief
71  * Private implementation class for TestFileManager.
72  *
73  * \ingroup module_testutils
74  */
75 class TestFileManager::Impl
76 {
77 public:
78     //! Global test input data path set with setDataInputDirectory().
79     static std::string s_inputDirectory;
80
81     //! Global path to simulation input database set with setTestSimulationDataBaseDirectory().
82     static std::string s_simulationDatabaseDirectory;
83
84     //! Global temporary output directory for tests, set with setGlobalOutputTempDirectory().
85     static const char* s_globalOutputTempDirectory;
86
87     //! Container type for names of temporary files.
88     typedef std::set<std::string> FileNameList;
89
90     /*! \brief Constructor
91      *
92      * \param path Value for the outputTempDirectory, typically
93      * set by default from s_globalOutputTempDirectory */
94     explicit Impl(const char* path) : outputTempDirectory_(path)
95     {
96         GMX_RELEASE_ASSERT(Directory::exists(outputTempDirectory_),
97                            "Directory for tests' temporary files does not exist");
98     }
99
100     /*! \brief
101      * Try to remove all temporary files.
102      *
103      * Does not throw; errors (e.g., missing files) are silently ignored.
104      */
105     void removeFiles();
106
107     //! List of unique paths returned by getTemporaryFilePath().
108     FileNameList files_;
109
110     /*! \brief Temporary output directory local to the current
111      * test, set by a test with setOutputTempDirectory() if the
112      * global default is inappropriate. */
113     std::string outputTempDirectory_;
114 };
115
116 std::string TestFileManager::Impl::s_inputDirectory;
117 std::string TestFileManager::Impl::s_simulationDatabaseDirectory;
118 const char* TestFileManager::Impl::s_globalOutputTempDirectory = nullptr;
119 /** Controls whether TestFileManager should delete temporary files
120     after the test finishes. */
121 static bool g_bDeleteFilesAfterTest = true;
122
123 //! \cond
124 GMX_TEST_OPTIONS(TestFileManagerOptions, options)
125 {
126     options->addOption(
127             BooleanOption("delete-temporary-files")
128                     .store(&g_bDeleteFilesAfterTest)
129                     .description(
130                             "At the end of each test case, delete temporary and output files"));
131 }
132 //! \endcond
133
134 void TestFileManager::Impl::removeFiles()
135 {
136     FileNameList::const_iterator i;
137     for (i = files_.begin(); i != files_.end(); ++i)
138     {
139         std::remove(i->c_str());
140     }
141     files_.clear();
142 }
143
144 /********************************************************************
145  * TestFileManager
146  */
147
148 TestFileManager::TestFileManager() : impl_(new Impl(Impl::s_globalOutputTempDirectory)) {}
149
150 TestFileManager::~TestFileManager()
151 {
152     if (g_bDeleteFilesAfterTest)
153     {
154         impl_->removeFiles();
155     }
156 }
157
158 std::string TestFileManager::getTemporaryFilePath(const char* suffix)
159 {
160     /* Configure a temporary directory from CMake, so that temporary
161      * output from a test goes to a location relevant to that
162      * test. Currently, files whose names are returned by this method
163      * get cleaned up (by default) at the end of all tests.
164      */
165     std::string filename = Path::join(getOutputTempDirectory(), getTestSpecificFileName(suffix));
166     impl_->files_.insert(filename);
167     return filename;
168 }
169
170 std::string TestFileManager::getTemporaryFilePath(const std::string& suffix)
171 {
172     return getTemporaryFilePath(suffix.c_str());
173 }
174
175 std::string TestFileManager::getTestSpecificFileNameRoot()
176 {
177     const ::testing::TestInfo* test_info = ::testing::UnitTest::GetInstance()->current_test_info();
178     std::string                filenameRoot;
179     if (test_info)
180     {
181         filenameRoot = std::string(test_info->test_case_name()) + "_" + test_info->name();
182     }
183     else
184     {
185         const ::testing::TestCase* test_case_info =
186                 ::testing::UnitTest::GetInstance()->current_test_case();
187         filenameRoot = std::string(test_case_info->name());
188     }
189     std::replace(filenameRoot.begin(), filenameRoot.end(), '/', '_');
190     return filenameRoot;
191 }
192
193 std::string TestFileManager::getTestSpecificFileName(const char* suffix)
194 {
195     std::string filename = getTestSpecificFileNameRoot();
196     if (suffix[0] != '.')
197     {
198         filename.append("_");
199     }
200     filename.append(suffix);
201     return filename;
202 }
203
204 std::string TestFileManager::getInputFilePath(const char* filename)
205 {
206     // Check if file is present in local directory.
207     if (File::exists(Path::join(getInputDataDirectory(), filename), File::returnFalseOnError))
208     {
209         return Path::join(getInputDataDirectory(), filename);
210     }
211     else if (File::exists(Path::join(getTestSimulationDatabaseDirectory(), filename), File::returnFalseOnError))
212     {
213         // Assume file is in global directory for simulation input files.
214         return Path::join(getTestSimulationDatabaseDirectory(), filename);
215     }
216     else
217     {
218         // Assume file is present locally without full name (e.g. extension).
219         return Path::join(getInputDataDirectory(), filename);
220     }
221 }
222
223 std::string TestFileManager::getInputFilePath(const std::string& filename)
224 {
225     return getInputFilePath(filename.c_str());
226 }
227
228 const char* TestFileManager::getInputDataDirectory()
229 {
230     GMX_RELEASE_ASSERT(!Impl::s_inputDirectory.empty(), "Path for test input files is not set");
231     return Impl::s_inputDirectory.c_str();
232 }
233
234 const char* TestFileManager::getGlobalOutputTempDirectory()
235 {
236     GMX_RELEASE_ASSERT(Impl::s_globalOutputTempDirectory != nullptr,
237                        "Global path for temporary output files from tests is not set");
238     return Impl::s_globalOutputTempDirectory;
239 }
240
241 const char* TestFileManager::getOutputTempDirectory() const
242 {
243     return impl_->outputTempDirectory_.c_str();
244 }
245
246 const char* TestFileManager::getTestSimulationDatabaseDirectory()
247 {
248     GMX_RELEASE_ASSERT(!Impl::s_simulationDatabaseDirectory.empty(),
249                        "Path for simulation input database directory is not set");
250     return Impl::s_simulationDatabaseDirectory.c_str();
251 }
252
253 void TestFileManager::setInputDataDirectory(const std::string& path)
254 {
255     // There is no need to protect this by a mutex, as this is called in early
256     // initialization of the tests.
257     GMX_RELEASE_ASSERT(Directory::exists(path), "Test data directory does not exist");
258     Impl::s_inputDirectory = path;
259 }
260
261 void TestFileManager::setTestSimulationDatabaseDirectory(const std::string& path)
262 {
263     // There is no need to protect this by a mutex, as this is called in early
264     // initialization of the tests.
265     GMX_RELEASE_ASSERT(Directory::exists(path), "Simulation database directory does not exist");
266     Impl::s_simulationDatabaseDirectory = path;
267 }
268
269 void TestFileManager::setGlobalOutputTempDirectory(const char* path)
270 {
271     // There is no need to protect this by a mutex, as this is called in early
272     // initialization of the tests.
273     GMX_RELEASE_ASSERT(Directory::exists(path),
274                        "Directory for tests' temporary files does not exist");
275     Impl::s_globalOutputTempDirectory = path;
276 }
277
278 void TestFileManager::setOutputTempDirectory(const std::string& path)
279 {
280     // There could be a need to protect this with a mutex, since it is
281     // intended to be used in test fixtures, not just during setup.
282     GMX_RELEASE_ASSERT(Directory::exists(path),
283                        "Directory for tests' temporary files does not exist");
284     impl_->outputTempDirectory_ = path;
285 }
286
287 } // namespace test
288 } // namespace gmx