2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012, by the GROMACS development team, led by
5 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6 * others, as listed in the AUTHORS file in the top-level source
7 * 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
48 #include <gmock/gmock.h>
49 #include <gtest/gtest.h>
51 #include "gromacs/options/abstractoption.h"
52 #include "gromacs/options/options.h"
53 #include "gromacs/options/optionstoragetemplate.h"
54 #include "gromacs/options/optionsassigner.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "testutils/testexceptions.h"
62 class MockOptionStorage;
64 class MockOptionInfo : public gmx::OptionInfo
67 //! Creates an option info object for the given option.
68 explicit MockOptionInfo(MockOptionStorage *option);
70 MockOptionStorage &option();
74 * Mock implementation of an option storage class for unit testing.
76 * Provides facilities for checking that correct methods are called, and for
77 * controlling how they add values using the base class methods.
79 * \ingroup module_options
81 class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
85 * Initializes the storage from option settings.
87 * \param[in] settings Storage settings.
89 MockOptionStorage(const MockOption &settings);
92 * Calls addValue("dummy") in the base class.
98 // using MyBase::markAsSet;
99 // using MyBase::addValue;
100 // using MyBase::commitValues;
101 // "using" is correct but MSVC gives error C2248. Workaround:
106 void addValue(const std::string &value)
108 MyBase::addValue(value);
112 MyBase::commitValues();
115 virtual gmx::OptionInfo &optionInfo() { return info_; }
116 // These are not used.
117 virtual const char *typeString() const { return "mock"; }
118 virtual std::string formatSingleValue(const std::string & /*value*/) const
123 MOCK_METHOD1(convertValue, void(const std::string &value));
124 MOCK_METHOD1(processSetValues, void(ValueList *values));
125 MOCK_METHOD0(processAll, void());
128 MockOptionInfo info_;
132 * Specifies an option that has a mock storage object for unit testing.
134 * \ingroup module_options
136 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
139 //! OptionInfo subclass corresponding to this option type.
140 typedef MockOptionInfo InfoType;
142 //! Initializes an option with the given name.
143 explicit MockOption(const char *name)
149 virtual gmx::AbstractOptionStoragePointer createStorage() const
151 return gmx::AbstractOptionStoragePointer(new MockOptionStorage(*this));
155 MockOptionStorage::MockOptionStorage(const MockOption &settings)
156 : MyBase(settings), info_(this)
159 using ::testing::Invoke;
160 using ::testing::WithArg;
161 ON_CALL(*this, convertValue(_))
162 .WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
165 MockOptionInfo::MockOptionInfo(MockOptionStorage *option)
166 : gmx::OptionInfo(option)
170 MockOptionStorage &MockOptionInfo::option()
172 return static_cast<MockOptionStorage &>(gmx::OptionInfo::option());
176 * Tests that finish() can set a required option even if the user has not
179 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
181 gmx::Options options(NULL, NULL);
182 std::vector<std::string> values;
183 MockOptionInfo *info = options.addOption(
184 MockOption("name").required().storeVector(&values));
185 MockOptionStorage *mock = &info->option();
187 ::testing::InSequence dummy;
188 using ::testing::DoAll;
189 using ::testing::InvokeWithoutArgs;
190 EXPECT_CALL(*mock, processAll())
191 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::markAsSet),
192 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
193 InvokeWithoutArgs(mock, &MockOptionStorage::commitValues)));
196 gmx::OptionsAssigner assigner(&options);
197 EXPECT_NO_THROW(assigner.start());
198 EXPECT_NO_THROW(assigner.finish());
199 EXPECT_NO_THROW(options.finish());
201 ASSERT_EQ(1U, values.size());
202 EXPECT_EQ("dummy", values[0]);
206 * Tests that storage works if the storage object does not add a value in a
207 * call to appendValue().
209 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
211 gmx::Options options(NULL, NULL);
212 std::vector<std::string> values;
213 MockOptionInfo *info = options.addOption(
214 MockOption("name").storeVector(&values).multiValue());
215 MockOptionStorage *mock = &info->option();
217 ::testing::InSequence dummy;
218 using ::testing::ElementsAre;
219 using ::testing::Pointee;
220 using ::testing::Return;
221 EXPECT_CALL(*mock, convertValue("a"));
222 EXPECT_CALL(*mock, convertValue("b"))
224 EXPECT_CALL(*mock, convertValue("c"));
225 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
226 EXPECT_CALL(*mock, processAll());
229 gmx::OptionsAssigner assigner(&options);
230 EXPECT_NO_THROW(assigner.start());
231 ASSERT_NO_THROW(assigner.startOption("name"));
232 EXPECT_NO_THROW(assigner.appendValue("a"));
233 EXPECT_NO_THROW(assigner.appendValue("b"));
234 EXPECT_NO_THROW(assigner.appendValue("c"));
235 EXPECT_NO_THROW(assigner.finishOption());
236 EXPECT_NO_THROW(assigner.finish());
237 EXPECT_NO_THROW(options.finish());
239 ASSERT_EQ(2U, values.size());
240 EXPECT_EQ("a", values[0]);
241 EXPECT_EQ("c", values[1]);
245 * Tests that storage works if the storage object adds more than one value in
246 * one call to appendValue().
248 TEST(AbstractOptionStorageTest, HandlesValueAddition)
250 gmx::Options options(NULL, NULL);
251 std::vector<std::string> values;
252 MockOptionInfo *info = options.addOption(
253 MockOption("name").storeVector(&values).multiValue());
254 MockOptionStorage *mock = &info->option();
256 ::testing::InSequence dummy;
257 using ::testing::DoAll;
258 using ::testing::ElementsAre;
259 using ::testing::InvokeWithoutArgs;
260 using ::testing::Pointee;
261 EXPECT_CALL(*mock, convertValue("a"));
262 EXPECT_CALL(*mock, convertValue("b"))
263 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
264 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
265 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy", "dummy"))));
266 EXPECT_CALL(*mock, processAll());
269 gmx::OptionsAssigner assigner(&options);
270 EXPECT_NO_THROW(assigner.start());
271 ASSERT_NO_THROW(assigner.startOption("name"));
272 EXPECT_NO_THROW(assigner.appendValue("a"));
273 EXPECT_NO_THROW(assigner.appendValue("b"));
274 EXPECT_NO_THROW(assigner.finishOption());
275 EXPECT_NO_THROW(assigner.finish());
276 EXPECT_NO_THROW(options.finish());
278 ASSERT_EQ(3U, values.size());
279 EXPECT_EQ("a", values[0]);
280 EXPECT_EQ("dummy", values[1]);
281 EXPECT_EQ("dummy", values[2]);
285 * Tests that storage works if the storage object adds more than one value in
286 * one call to appendValue(), and this results in too many values.
288 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
290 gmx::Options options(NULL, NULL);
291 std::vector<std::string> values;
292 MockOptionInfo *info = options.addOption(
293 MockOption("name").storeVector(&values).valueCount(2));
294 MockOptionStorage *mock = &info->option();
296 ::testing::InSequence dummy;
297 using ::testing::DoAll;
298 using ::testing::ElementsAre;
299 using ::testing::InvokeWithoutArgs;
300 using ::testing::Pointee;
301 EXPECT_CALL(*mock, convertValue("a"));
302 EXPECT_CALL(*mock, convertValue("b"))
303 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
304 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
305 EXPECT_CALL(*mock, processAll());
308 gmx::OptionsAssigner assigner(&options);
309 EXPECT_NO_THROW(assigner.start());
310 ASSERT_NO_THROW(assigner.startOption("name"));
311 EXPECT_NO_THROW(assigner.appendValue("a"));
312 EXPECT_THROW(assigner.appendValue("b"), gmx::InvalidInputError);
313 EXPECT_NO_THROW(assigner.finishOption());
314 EXPECT_NO_THROW(assigner.finish());
315 EXPECT_NO_THROW(options.finish());
317 ASSERT_TRUE(values.empty());
321 * Tests that the storage object is properly invoked even if no output values
322 * should be produced.
324 TEST(AbstractOptionStorageTest, AllowsEmptyValues)
326 gmx::Options options(NULL, NULL);
327 std::vector<std::string> values;
328 MockOptionInfo *info = options.addOption(
329 MockOption("name").storeVector(&values).valueCount(0));
330 MockOptionStorage *mock = &info->option();
332 ::testing::InSequence dummy;
333 using ::testing::DoAll;
334 using ::testing::ElementsAre;
335 using ::testing::Pointee;
336 using ::testing::Return;
337 EXPECT_CALL(*mock, convertValue("a"))
339 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre())));
340 EXPECT_CALL(*mock, processAll());
343 gmx::OptionsAssigner assigner(&options);
344 EXPECT_NO_THROW(assigner.start());
345 EXPECT_NO_THROW(assigner.startOption("name"));
346 EXPECT_NO_THROW(assigner.appendValue("a"));
347 EXPECT_NO_THROW(assigner.finishOption());
348 EXPECT_NO_THROW(assigner.finish());
349 EXPECT_NO_THROW(options.finish());
351 ASSERT_EQ(0U, values.size());