2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013,2014,2016,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.
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 * Tests proper handling of option storage.
39 * These tests check that methods in storage objects are called properly in all
40 * situations, and also that the OptionStorageTemplate class behaves properly.
42 * \author Teemu Murtola <teemu.murtola@gmail.com>
43 * \ingroup module_options
50 #include <gmock/gmock.h>
51 #include <gtest/gtest.h>
53 #include "gromacs/options/abstractoption.h"
54 #include "gromacs/options/options.h"
55 #include "gromacs/options/optionsassigner.h"
56 #include "gromacs/options/optionstoragetemplate.h"
57 #include "gromacs/utility/exceptions.h"
59 #include "testutils/testasserts.h"
60 #include "testutils/testexceptions.h"
66 class MockOptionStorage;
68 class MockOptionInfo : public gmx::OptionInfo
71 //! Creates an option info object for the given option.
72 explicit MockOptionInfo(MockOptionStorage* option);
74 MockOptionStorage& option();
78 * Mock implementation of an option storage class for unit testing.
80 * Provides facilities for checking that correct methods are called, and for
81 * controlling how they add values using the base class methods.
83 * \ingroup module_options
85 class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
89 * Initializes the storage from option settings.
91 * \param[in] settings Storage settings.
93 explicit MockOptionStorage(const MockOption& settings);
96 * Calls addValue("dummy") in the base class.
98 void addDummyValue() { addValue("dummy"); }
99 using MyBase::addValue;
100 using MyBase::commitValues;
101 using MyBase::markAsSet;
103 gmx::OptionInfo& optionInfo() override { return info_; }
104 // These are not used.
105 std::string typeString() const override { return "mock"; }
106 std::string formatSingleValue(const std::string& /*value*/) const override { return ""; }
107 std::vector<gmx::Any> normalizeValues(const std::vector<gmx::Any>& values) const override
112 void convertValue(const gmx::Any& value) override { convertValue(value.cast<std::string>()); }
114 MOCK_METHOD1(convertValue, void(const std::string& value));
115 MOCK_METHOD1(processSetValues, void(ValueList* values));
116 MOCK_METHOD0(processAll, void());
119 MockOptionInfo info_;
123 * Specifies an option that has a mock storage object for unit testing.
125 * \ingroup module_options
127 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
130 //! OptionInfo subclass corresponding to this option type.
131 typedef MockOptionInfo InfoType;
133 //! Initializes an option with the given name.
134 explicit MockOption(const char* name) : MyBase(name) {}
137 gmx::AbstractOptionStorage* createStorage(const gmx::OptionManagerContainer& /*managers*/) const override
139 return new MockOptionStorage(*this);
143 MockOptionStorage::MockOptionStorage(const MockOption& settings) : MyBase(settings), info_(this)
146 using ::testing::Invoke;
147 using ::testing::WithArg;
148 ON_CALL(*this, convertValue(_)).WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
151 MockOptionInfo::MockOptionInfo(MockOptionStorage* option) : gmx::OptionInfo(option) {}
153 MockOptionStorage& MockOptionInfo::option()
155 return static_cast<MockOptionStorage&>(gmx::OptionInfo::option());
159 * Tests that finish() can set a required option even if the user has not
162 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
164 gmx::Options options;
165 std::vector<std::string> values;
166 MockOptionInfo* info = options.addOption(MockOption("name").required().storeVector(&values));
167 MockOptionStorage* mock = &info->option();
169 ::testing::InSequence dummy;
170 using ::testing::DoAll;
171 using ::testing::InvokeWithoutArgs;
172 EXPECT_CALL(*mock, processAll())
173 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::markAsSet),
174 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
175 InvokeWithoutArgs(mock, &MockOptionStorage::commitValues)));
178 gmx::OptionsAssigner assigner(&options);
179 EXPECT_NO_THROW_GMX(assigner.start());
180 EXPECT_NO_THROW_GMX(assigner.finish());
181 EXPECT_NO_THROW_GMX(options.finish());
183 ASSERT_EQ(1U, values.size());
184 EXPECT_EQ("dummy", values[0]);
188 * Tests that storage works if the storage object does not add a value in a
189 * call to appendValue().
191 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
193 gmx::Options options;
194 std::vector<std::string> values;
195 MockOptionInfo* info = options.addOption(MockOption("name").storeVector(&values).multiValue());
196 MockOptionStorage* mock = &info->option();
198 ::testing::InSequence dummy;
199 using ::testing::ElementsAre;
200 using ::testing::Pointee;
201 using ::testing::Return;
202 EXPECT_CALL(*mock, convertValue("a"));
203 EXPECT_CALL(*mock, convertValue("b")).WillOnce(Return());
204 EXPECT_CALL(*mock, convertValue("c"));
205 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
206 EXPECT_CALL(*mock, processAll());
209 gmx::OptionsAssigner assigner(&options);
210 EXPECT_NO_THROW_GMX(assigner.start());
211 ASSERT_NO_THROW_GMX(assigner.startOption("name"));
212 EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
213 EXPECT_NO_THROW_GMX(assigner.appendValue("b"));
214 EXPECT_NO_THROW_GMX(assigner.appendValue("c"));
215 EXPECT_NO_THROW_GMX(assigner.finishOption());
216 EXPECT_NO_THROW_GMX(assigner.finish());
217 EXPECT_NO_THROW_GMX(options.finish());
219 ASSERT_EQ(2U, values.size());
220 EXPECT_EQ("a", values[0]);
221 EXPECT_EQ("c", values[1]);
225 * Tests that storage works if the storage object adds more than one value in
226 * one call to appendValue().
228 TEST(AbstractOptionStorageTest, HandlesValueAddition)
230 gmx::Options options;
231 std::vector<std::string> values;
232 MockOptionInfo* info = options.addOption(MockOption("name").storeVector(&values).multiValue());
233 MockOptionStorage* mock = &info->option();
235 ::testing::InSequence dummy;
236 using ::testing::DoAll;
237 using ::testing::ElementsAre;
238 using ::testing::InvokeWithoutArgs;
239 using ::testing::Pointee;
240 EXPECT_CALL(*mock, convertValue("a"));
241 EXPECT_CALL(*mock, convertValue("b"))
242 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
243 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
244 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy", "dummy"))));
245 EXPECT_CALL(*mock, processAll());
248 gmx::OptionsAssigner assigner(&options);
249 EXPECT_NO_THROW_GMX(assigner.start());
250 ASSERT_NO_THROW_GMX(assigner.startOption("name"));
251 EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
252 EXPECT_NO_THROW_GMX(assigner.appendValue("b"));
253 EXPECT_NO_THROW_GMX(assigner.finishOption());
254 EXPECT_NO_THROW_GMX(assigner.finish());
255 EXPECT_NO_THROW_GMX(options.finish());
257 ASSERT_EQ(3U, values.size());
258 EXPECT_EQ("a", values[0]);
259 EXPECT_EQ("dummy", values[1]);
260 EXPECT_EQ("dummy", values[2]);
264 * Tests that storage works if the storage object adds more than one value in
265 * one call to appendValue(), and this results in too many values.
267 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
269 gmx::Options options;
270 std::vector<std::string> values;
271 MockOptionInfo* info = options.addOption(MockOption("name").storeVector(&values).valueCount(2));
272 MockOptionStorage* mock = &info->option();
274 ::testing::InSequence dummy;
275 using ::testing::DoAll;
276 using ::testing::ElementsAre;
277 using ::testing::InvokeWithoutArgs;
278 using ::testing::Pointee;
279 EXPECT_CALL(*mock, convertValue("a"));
280 EXPECT_CALL(*mock, convertValue("b"))
281 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
282 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
283 EXPECT_CALL(*mock, processAll());
286 gmx::OptionsAssigner assigner(&options);
287 EXPECT_NO_THROW_GMX(assigner.start());
288 ASSERT_NO_THROW_GMX(assigner.startOption("name"));
289 EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
290 EXPECT_THROW_GMX(assigner.appendValue("b"), gmx::InvalidInputError);
291 EXPECT_NO_THROW_GMX(assigner.finishOption());
292 EXPECT_NO_THROW_GMX(assigner.finish());
293 EXPECT_NO_THROW_GMX(options.finish());
295 ASSERT_TRUE(values.empty());
299 * Tests that the storage object is properly invoked even if no output values
300 * should be produced.
302 TEST(AbstractOptionStorageTest, AllowsEmptyValues)
304 gmx::Options options;
305 std::vector<std::string> values;
306 MockOptionInfo* info = options.addOption(MockOption("name").storeVector(&values).valueCount(0));
307 MockOptionStorage* mock = &info->option();
309 ::testing::InSequence dummy;
310 using ::testing::DoAll;
311 using ::testing::ElementsAre;
312 using ::testing::Pointee;
313 using ::testing::Return;
314 EXPECT_CALL(*mock, convertValue("a")).WillOnce(Return());
315 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre())));
316 EXPECT_CALL(*mock, processAll());
319 gmx::OptionsAssigner assigner(&options);
320 EXPECT_NO_THROW_GMX(assigner.start());
321 EXPECT_NO_THROW_GMX(assigner.startOption("name"));
322 EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
323 EXPECT_NO_THROW_GMX(assigner.finishOption());
324 EXPECT_NO_THROW_GMX(assigner.finish());
325 EXPECT_NO_THROW_GMX(options.finish());
327 ASSERT_EQ(0U, values.size());