2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013,2014, 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 MockOptionStorage(const MockOption &settings);
96 * Calls addValue("dummy") in the base class.
102 // using MyBase::markAsSet;
103 // using MyBase::addValue;
104 // using MyBase::commitValues;
105 // "using" is correct but MSVC gives error C2248. Workaround:
106 //! \copydoc gmx::OptionStorageTemplate::markAsSet()
111 //! \copydoc gmx::OptionStorageTemplate::addValue()
112 void addValue(const std::string &value)
114 MyBase::addValue(value);
116 //! \copydoc gmx::OptionStorageTemplate::commitValues()
119 MyBase::commitValues();
122 virtual gmx::OptionInfo &optionInfo() { return info_; }
123 // These are not used.
124 virtual std::string typeString() const { return "mock"; }
125 virtual std::string formatSingleValue(const std::string & /*value*/) const
130 MOCK_METHOD1(convertValue, void(const std::string &value));
131 MOCK_METHOD1(processSetValues, void(ValueList *values));
132 MOCK_METHOD0(processAll, void());
135 MockOptionInfo info_;
139 * Specifies an option that has a mock storage object for unit testing.
141 * \ingroup module_options
143 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
146 //! OptionInfo subclass corresponding to this option type.
147 typedef MockOptionInfo InfoType;
149 //! Initializes an option with the given name.
150 explicit MockOption(const char *name)
156 virtual gmx::AbstractOptionStorage *createStorage(
157 const gmx::OptionManagerContainer & /*managers*/) const
159 return new MockOptionStorage(*this);
163 MockOptionStorage::MockOptionStorage(const MockOption &settings)
164 : MyBase(settings), info_(this)
167 using ::testing::Invoke;
168 using ::testing::WithArg;
169 ON_CALL(*this, convertValue(_))
170 .WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
173 MockOptionInfo::MockOptionInfo(MockOptionStorage *option)
174 : gmx::OptionInfo(option)
178 MockOptionStorage &MockOptionInfo::option()
180 return static_cast<MockOptionStorage &>(gmx::OptionInfo::option());
184 * Tests that finish() can set a required option even if the user has not
187 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
189 gmx::Options options(NULL, NULL);
190 std::vector<std::string> values;
191 MockOptionInfo *info = options.addOption(
192 MockOption("name").required().storeVector(&values));
193 MockOptionStorage *mock = &info->option();
195 ::testing::InSequence dummy;
196 using ::testing::DoAll;
197 using ::testing::InvokeWithoutArgs;
198 EXPECT_CALL(*mock, processAll())
199 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::markAsSet),
200 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
201 InvokeWithoutArgs(mock, &MockOptionStorage::commitValues)));
204 gmx::OptionsAssigner assigner(&options);
205 EXPECT_NO_THROW_GMX(assigner.start());
206 EXPECT_NO_THROW_GMX(assigner.finish());
207 EXPECT_NO_THROW_GMX(options.finish());
209 ASSERT_EQ(1U, values.size());
210 EXPECT_EQ("dummy", values[0]);
214 * Tests that storage works if the storage object does not add a value in a
215 * call to appendValue().
217 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
219 gmx::Options options(NULL, NULL);
220 std::vector<std::string> values;
221 MockOptionInfo *info = options.addOption(
222 MockOption("name").storeVector(&values).multiValue());
223 MockOptionStorage *mock = &info->option();
225 ::testing::InSequence dummy;
226 using ::testing::ElementsAre;
227 using ::testing::Pointee;
228 using ::testing::Return;
229 EXPECT_CALL(*mock, convertValue("a"));
230 EXPECT_CALL(*mock, convertValue("b"))
232 EXPECT_CALL(*mock, convertValue("c"));
233 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
234 EXPECT_CALL(*mock, processAll());
237 gmx::OptionsAssigner assigner(&options);
238 EXPECT_NO_THROW_GMX(assigner.start());
239 ASSERT_NO_THROW_GMX(assigner.startOption("name"));
240 EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
241 EXPECT_NO_THROW_GMX(assigner.appendValue("b"));
242 EXPECT_NO_THROW_GMX(assigner.appendValue("c"));
243 EXPECT_NO_THROW_GMX(assigner.finishOption());
244 EXPECT_NO_THROW_GMX(assigner.finish());
245 EXPECT_NO_THROW_GMX(options.finish());
247 ASSERT_EQ(2U, values.size());
248 EXPECT_EQ("a", values[0]);
249 EXPECT_EQ("c", values[1]);
253 * Tests that storage works if the storage object adds more than one value in
254 * one call to appendValue().
256 TEST(AbstractOptionStorageTest, HandlesValueAddition)
258 gmx::Options options(NULL, NULL);
259 std::vector<std::string> values;
260 MockOptionInfo *info = options.addOption(
261 MockOption("name").storeVector(&values).multiValue());
262 MockOptionStorage *mock = &info->option();
264 ::testing::InSequence dummy;
265 using ::testing::DoAll;
266 using ::testing::ElementsAre;
267 using ::testing::InvokeWithoutArgs;
268 using ::testing::Pointee;
269 EXPECT_CALL(*mock, convertValue("a"));
270 EXPECT_CALL(*mock, convertValue("b"))
271 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
272 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
273 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy", "dummy"))));
274 EXPECT_CALL(*mock, processAll());
277 gmx::OptionsAssigner assigner(&options);
278 EXPECT_NO_THROW_GMX(assigner.start());
279 ASSERT_NO_THROW_GMX(assigner.startOption("name"));
280 EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
281 EXPECT_NO_THROW_GMX(assigner.appendValue("b"));
282 EXPECT_NO_THROW_GMX(assigner.finishOption());
283 EXPECT_NO_THROW_GMX(assigner.finish());
284 EXPECT_NO_THROW_GMX(options.finish());
286 ASSERT_EQ(3U, values.size());
287 EXPECT_EQ("a", values[0]);
288 EXPECT_EQ("dummy", values[1]);
289 EXPECT_EQ("dummy", values[2]);
293 * Tests that storage works if the storage object adds more than one value in
294 * one call to appendValue(), and this results in too many values.
296 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
298 gmx::Options options(NULL, NULL);
299 std::vector<std::string> values;
300 MockOptionInfo *info = options.addOption(
301 MockOption("name").storeVector(&values).valueCount(2));
302 MockOptionStorage *mock = &info->option();
304 ::testing::InSequence dummy;
305 using ::testing::DoAll;
306 using ::testing::ElementsAre;
307 using ::testing::InvokeWithoutArgs;
308 using ::testing::Pointee;
309 EXPECT_CALL(*mock, convertValue("a"));
310 EXPECT_CALL(*mock, convertValue("b"))
311 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
312 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
313 EXPECT_CALL(*mock, processAll());
316 gmx::OptionsAssigner assigner(&options);
317 EXPECT_NO_THROW_GMX(assigner.start());
318 ASSERT_NO_THROW_GMX(assigner.startOption("name"));
319 EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
320 EXPECT_THROW_GMX(assigner.appendValue("b"), gmx::InvalidInputError);
321 EXPECT_NO_THROW_GMX(assigner.finishOption());
322 EXPECT_NO_THROW_GMX(assigner.finish());
323 EXPECT_NO_THROW_GMX(options.finish());
325 ASSERT_TRUE(values.empty());
329 * Tests that the storage object is properly invoked even if no output values
330 * should be produced.
332 TEST(AbstractOptionStorageTest, AllowsEmptyValues)
334 gmx::Options options(NULL, NULL);
335 std::vector<std::string> values;
336 MockOptionInfo *info = options.addOption(
337 MockOption("name").storeVector(&values).valueCount(0));
338 MockOptionStorage *mock = &info->option();
340 ::testing::InSequence dummy;
341 using ::testing::DoAll;
342 using ::testing::ElementsAre;
343 using ::testing::Pointee;
344 using ::testing::Return;
345 EXPECT_CALL(*mock, convertValue("a"))
347 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre())));
348 EXPECT_CALL(*mock, processAll());
351 gmx::OptionsAssigner assigner(&options);
352 EXPECT_NO_THROW_GMX(assigner.start());
353 EXPECT_NO_THROW_GMX(assigner.startOption("name"));
354 EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
355 EXPECT_NO_THROW_GMX(assigner.finishOption());
356 EXPECT_NO_THROW_GMX(assigner.finish());
357 EXPECT_NO_THROW_GMX(options.finish());
359 ASSERT_EQ(0U, values.size());