SYCL: Avoid using no_init read accessor in rocFFT
[alexxy/gromacs.git] / src / gromacs / options / tests / abstractoptionstorage.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
5  * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \internal \file
37  * \brief
38  * Tests proper handling of option storage.
39  *
40  * These tests check that methods in storage objects are called properly in all
41  * situations, and also that the OptionStorageTemplate class behaves properly.
42  *
43  * \author Teemu Murtola <teemu.murtola@gmail.com>
44  * \ingroup module_options
45  */
46 #include "gmxpre.h"
47
48 #include <string>
49 #include <vector>
50
51 #include <gmock/gmock.h>
52 #include <gtest/gtest.h>
53
54 #include "gromacs/options/abstractoption.h"
55 #include "gromacs/options/options.h"
56 #include "gromacs/options/optionsassigner.h"
57 #include "gromacs/options/optionstoragetemplate.h"
58 #include "gromacs/utility/exceptions.h"
59
60 #include "testutils/testasserts.h"
61 #include "testutils/testexceptions.h"
62
63 namespace
64 {
65
66 class MockOption;
67 class MockOptionStorage;
68
69 class MockOptionInfo : public gmx::OptionInfo
70 {
71 public:
72     //! Creates an option info object for the given option.
73     explicit MockOptionInfo(MockOptionStorage* option);
74
75     MockOptionStorage& option();
76 };
77
78 /*! \brief
79  * Mock implementation of an option storage class for unit testing.
80  *
81  * Provides facilities for checking that correct methods are called, and for
82  * controlling how they add values using the base class methods.
83  *
84  * \ingroup module_options
85  */
86 class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
87 {
88 public:
89     /*! \brief
90      * Initializes the storage from option settings.
91      *
92      * \param[in] settings   Storage settings.
93      */
94     explicit MockOptionStorage(const MockOption& settings);
95
96     /*! \brief
97      * Calls addValue("dummy") in the base class.
98      */
99     void addDummyValue() { addValue("dummy"); }
100     using MyBase::addValue;
101     using MyBase::commitValues;
102     using MyBase::markAsSet;
103
104     gmx::OptionInfo& optionInfo() override { return info_; }
105     // These are not used.
106     std::string typeString() const override { return "mock"; }
107     std::string formatSingleValue(const std::string& /*value*/) const override { return ""; }
108     std::vector<gmx::Any> normalizeValues(const std::vector<gmx::Any>& values) const override
109     {
110         return values;
111     }
112
113     void convertValue(const gmx::Any& value) override { convertValue(value.cast<std::string>()); }
114
115     MOCK_METHOD1(convertValue, void(const std::string& value));
116     MOCK_METHOD1(processSetValues, void(ValueList* values));
117     MOCK_METHOD0(processAll, void());
118
119 private:
120     MockOptionInfo info_;
121 };
122
123 /*! \internal \brief
124  * Specifies an option that has a mock storage object for unit testing.
125  *
126  * \ingroup module_options
127  */
128 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
129 {
130 public:
131     //! OptionInfo subclass corresponding to this option type.
132     typedef MockOptionInfo InfoType;
133
134     //! Initializes an option with the given name.
135     explicit MockOption(const char* name) : MyBase(name) {}
136
137 private:
138     gmx::AbstractOptionStorage* createStorage(const gmx::OptionManagerContainer& /*managers*/) const override
139     {
140         return new MockOptionStorage(*this);
141     }
142 };
143
144 MockOptionStorage::MockOptionStorage(const MockOption& settings) : MyBase(settings), info_(this)
145 {
146     using ::testing::_;
147     using ::testing::Invoke;
148     using ::testing::WithArg;
149     ON_CALL(*this, convertValue(_)).WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
150 }
151
152 MockOptionInfo::MockOptionInfo(MockOptionStorage* option) : gmx::OptionInfo(option) {}
153
154 MockOptionStorage& MockOptionInfo::option()
155 {
156     return static_cast<MockOptionStorage&>(gmx::OptionInfo::option());
157 }
158
159 /*
160  * Tests that finish() can set a required option even if the user has not
161  * provided it.
162  */
163 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
164 {
165     gmx::Options             options;
166     std::vector<std::string> values;
167     MockOptionInfo*    info = options.addOption(MockOption("name").required().storeVector(&values));
168     MockOptionStorage* mock = &info->option();
169     {
170         ::testing::InSequence dummy;
171         using ::testing::DoAll;
172         using ::testing::InvokeWithoutArgs;
173         EXPECT_CALL(*mock, processAll())
174                 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::markAsSet),
175                                 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
176                                 InvokeWithoutArgs(mock, &MockOptionStorage::commitValues)));
177     }
178
179     gmx::OptionsAssigner assigner(&options);
180     EXPECT_NO_THROW_GMX(assigner.start());
181     EXPECT_NO_THROW_GMX(assigner.finish());
182     EXPECT_NO_THROW_GMX(options.finish());
183
184     ASSERT_EQ(1U, values.size());
185     EXPECT_EQ("dummy", values[0]);
186 }
187
188 /*
189  * Tests that storage works if the storage object does not add a value in a
190  * call to appendValue().
191  */
192 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
193 {
194     gmx::Options             options;
195     std::vector<std::string> values;
196     MockOptionInfo* info = options.addOption(MockOption("name").storeVector(&values).multiValue());
197     MockOptionStorage* mock = &info->option();
198     {
199         ::testing::InSequence dummy;
200         using ::testing::ElementsAre;
201         using ::testing::Pointee;
202         using ::testing::Return;
203         EXPECT_CALL(*mock, convertValue("a"));
204         EXPECT_CALL(*mock, convertValue("b")).WillOnce(Return());
205         EXPECT_CALL(*mock, convertValue("c"));
206         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
207         EXPECT_CALL(*mock, processAll());
208     }
209
210     gmx::OptionsAssigner assigner(&options);
211     EXPECT_NO_THROW_GMX(assigner.start());
212     ASSERT_NO_THROW_GMX(assigner.startOption("name"));
213     EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
214     EXPECT_NO_THROW_GMX(assigner.appendValue("b"));
215     EXPECT_NO_THROW_GMX(assigner.appendValue("c"));
216     EXPECT_NO_THROW_GMX(assigner.finishOption());
217     EXPECT_NO_THROW_GMX(assigner.finish());
218     EXPECT_NO_THROW_GMX(options.finish());
219
220     ASSERT_EQ(2U, values.size());
221     EXPECT_EQ("a", values[0]);
222     EXPECT_EQ("c", values[1]);
223 }
224
225 /*
226  * Tests that storage works if the storage object adds more than one value in
227  * one call to appendValue().
228  */
229 TEST(AbstractOptionStorageTest, HandlesValueAddition)
230 {
231     gmx::Options             options;
232     std::vector<std::string> values;
233     MockOptionInfo* info = options.addOption(MockOption("name").storeVector(&values).multiValue());
234     MockOptionStorage* mock = &info->option();
235     {
236         ::testing::InSequence dummy;
237         using ::testing::DoAll;
238         using ::testing::ElementsAre;
239         using ::testing::InvokeWithoutArgs;
240         using ::testing::Pointee;
241         EXPECT_CALL(*mock, convertValue("a"));
242         EXPECT_CALL(*mock, convertValue("b"))
243                 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
244                                 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
245         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy", "dummy"))));
246         EXPECT_CALL(*mock, processAll());
247     }
248
249     gmx::OptionsAssigner assigner(&options);
250     EXPECT_NO_THROW_GMX(assigner.start());
251     ASSERT_NO_THROW_GMX(assigner.startOption("name"));
252     EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
253     EXPECT_NO_THROW_GMX(assigner.appendValue("b"));
254     EXPECT_NO_THROW_GMX(assigner.finishOption());
255     EXPECT_NO_THROW_GMX(assigner.finish());
256     EXPECT_NO_THROW_GMX(options.finish());
257
258     ASSERT_EQ(3U, values.size());
259     EXPECT_EQ("a", values[0]);
260     EXPECT_EQ("dummy", values[1]);
261     EXPECT_EQ("dummy", values[2]);
262 }
263
264 /*
265  * Tests that storage works if the storage object adds more than one value in
266  * one call to appendValue(), and this results in too many values.
267  */
268 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
269 {
270     gmx::Options             options;
271     std::vector<std::string> values;
272     MockOptionInfo* info = options.addOption(MockOption("name").storeVector(&values).valueCount(2));
273     MockOptionStorage* mock = &info->option();
274     {
275         ::testing::InSequence dummy;
276         using ::testing::DoAll;
277         using ::testing::ElementsAre;
278         using ::testing::InvokeWithoutArgs;
279         using ::testing::Pointee;
280         EXPECT_CALL(*mock, convertValue("a"));
281         EXPECT_CALL(*mock, convertValue("b"))
282                 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
283                                 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
284         EXPECT_CALL(*mock, processAll());
285     }
286
287     gmx::OptionsAssigner assigner(&options);
288     EXPECT_NO_THROW_GMX(assigner.start());
289     ASSERT_NO_THROW_GMX(assigner.startOption("name"));
290     EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
291     EXPECT_THROW_GMX(assigner.appendValue("b"), gmx::InvalidInputError);
292     EXPECT_NO_THROW_GMX(assigner.finishOption());
293     EXPECT_NO_THROW_GMX(assigner.finish());
294     EXPECT_NO_THROW_GMX(options.finish());
295
296     ASSERT_TRUE(values.empty());
297 }
298
299 /*
300  * Tests that the storage object is properly invoked even if no output values
301  * should be produced.
302  */
303 TEST(AbstractOptionStorageTest, AllowsEmptyValues)
304 {
305     gmx::Options             options;
306     std::vector<std::string> values;
307     MockOptionInfo* info = options.addOption(MockOption("name").storeVector(&values).valueCount(0));
308     MockOptionStorage* mock = &info->option();
309     {
310         ::testing::InSequence dummy;
311         using ::testing::DoAll;
312         using ::testing::ElementsAre;
313         using ::testing::Pointee;
314         using ::testing::Return;
315         EXPECT_CALL(*mock, convertValue("a")).WillOnce(Return());
316         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre())));
317         EXPECT_CALL(*mock, processAll());
318     }
319
320     gmx::OptionsAssigner assigner(&options);
321     EXPECT_NO_THROW_GMX(assigner.start());
322     EXPECT_NO_THROW_GMX(assigner.startOption("name"));
323     EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
324     EXPECT_NO_THROW_GMX(assigner.finishOption());
325     EXPECT_NO_THROW_GMX(assigner.finish());
326     EXPECT_NO_THROW_GMX(options.finish());
327
328     ASSERT_EQ(0U, values.size());
329 }
330
331 } // namespace