3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
9 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11 * Copyright (c) 2001-2009, The GROMACS development team,
12 * check out http://www.gromacs.org for more information.
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * If you want to redistribute modifications, please consider that
20 * scientific software is very special. Version control is crucial -
21 * bugs must be traceable. We will be happy to consider code for
22 * inclusion in the official distribution, but derived work must not
23 * be called official GROMACS. Details are found in the README & COPYING
24 * files - if they are missing, get the official version at www.gromacs.org.
26 * To help us fund GROMACS development, we humbly ask that you cite
27 * the papers on the package - you can find them in the top README file.
29 * For more info, check our website at http://www.gromacs.org
33 * Tests proper handling of option storage.
35 * These tests check that methods in storage objects are called properly in all
36 * situations, and also that the OptionStorageTemplate class behaves properly.
38 * \author Teemu Murtola <teemu.murtola@cbr.su.se>
39 * \ingroup module_options
44 #include <gmock/gmock.h>
45 #include <gtest/gtest.h>
47 #include "gromacs/options/abstractoption.h"
48 #include "gromacs/options/optioninfo.h"
49 #include "gromacs/options/options.h"
50 #include "gromacs/options/optionstoragetemplate.h"
51 #include "gromacs/options/optionsassigner.h"
52 #include "gromacs/utility/exceptions.h"
53 #include "testutils/testexceptions.h"
59 class MockOptionStorage;
61 class MockOptionInfo : public gmx::OptionInfo
64 //! Creates an option info object for the given option.
65 explicit MockOptionInfo(MockOptionStorage *option);
67 MockOptionStorage &option();
71 * Mock implementation of an option storage class for unit testing.
73 * Provides facilities for checking that correct methods are called, and for
74 * controlling how they add values using the base class methods.
76 * \ingroup module_options
78 class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
82 * Initializes the storage from option settings.
84 * \param[in] settings Storage settings.
86 MockOptionStorage(const MockOption &settings);
89 * Calls addValue("dummy") in the base class.
95 // using MyBase::markAsSet;
96 // using MyBase::addValue;
97 // using MyBase::commitValues;
98 // "using" is correct but MSVC gives error C2248. Workaround:
103 void addValue(const std::string &value)
105 MyBase::addValue(value);
109 MyBase::commitValues();
112 virtual gmx::OptionInfo &optionInfo() { return info_; }
113 // These are not used.
114 virtual const char *typeString() const { return "mock"; }
115 virtual std::string formatSingleValue(const std::string &/*value*/) const
120 MOCK_METHOD1(convertValue, void(const std::string &value));
121 MOCK_METHOD1(processSetValues, void(ValueList *values));
122 MOCK_METHOD0(processAll, void());
125 MockOptionInfo info_;
129 * Specifies an option that has a mock storage object for unit testing.
131 * \ingroup module_options
133 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
136 //! OptionInfo subclass corresponding to this option type.
137 typedef MockOptionInfo InfoType;
139 //! Initializes an option with the given name.
140 explicit MockOption(const char *name)
146 virtual gmx::AbstractOptionStoragePointer createStorage() const
148 return gmx::AbstractOptionStoragePointer(new MockOptionStorage(*this));
152 MockOptionStorage::MockOptionStorage(const MockOption &settings)
153 : MyBase(settings), info_(this)
156 using ::testing::Invoke;
157 using ::testing::WithArg;
158 ON_CALL(*this, convertValue(_))
159 .WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
162 MockOptionInfo::MockOptionInfo(MockOptionStorage *option)
163 : gmx::OptionInfo(option)
167 MockOptionStorage &MockOptionInfo::option()
169 return static_cast<MockOptionStorage &>(gmx::OptionInfo::option());
173 * Tests that finish() can set a required option even if the user has not
176 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
178 gmx::Options options(NULL, NULL);
179 std::vector<std::string> values;
180 MockOptionInfo *info = options.addOption(
181 MockOption("name").required().storeVector(&values));
182 MockOptionStorage *mock = &info->option();
184 ::testing::InSequence dummy;
185 using ::testing::DoAll;
186 using ::testing::InvokeWithoutArgs;
187 EXPECT_CALL(*mock, processAll())
188 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::markAsSet),
189 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
190 InvokeWithoutArgs(mock, &MockOptionStorage::commitValues)));
193 gmx::OptionsAssigner assigner(&options);
194 EXPECT_NO_THROW(assigner.start());
195 EXPECT_NO_THROW(assigner.finish());
196 EXPECT_NO_THROW(options.finish());
198 ASSERT_EQ(1U, values.size());
199 EXPECT_EQ("dummy", values[0]);
203 * Tests that storage works if the storage object does not add a value in a
204 * call to appendValue().
206 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
208 gmx::Options options(NULL, NULL);
209 std::vector<std::string> values;
210 MockOptionInfo *info = options.addOption(
211 MockOption("name").storeVector(&values).multiValue());
212 MockOptionStorage *mock = &info->option();
214 ::testing::InSequence dummy;
215 using ::testing::ElementsAre;
216 using ::testing::Pointee;
217 using ::testing::Return;
218 EXPECT_CALL(*mock, convertValue("a"));
219 EXPECT_CALL(*mock, convertValue("b"))
221 EXPECT_CALL(*mock, convertValue("c"));
222 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
223 EXPECT_CALL(*mock, processAll());
226 gmx::OptionsAssigner assigner(&options);
227 EXPECT_NO_THROW(assigner.start());
228 ASSERT_NO_THROW(assigner.startOption("name"));
229 EXPECT_NO_THROW(assigner.appendValue("a"));
230 EXPECT_NO_THROW(assigner.appendValue("b"));
231 EXPECT_NO_THROW(assigner.appendValue("c"));
232 EXPECT_NO_THROW(assigner.finishOption());
233 EXPECT_NO_THROW(assigner.finish());
234 EXPECT_NO_THROW(options.finish());
236 ASSERT_EQ(2U, values.size());
237 EXPECT_EQ("a", values[0]);
238 EXPECT_EQ("c", values[1]);
242 * Tests that storage works if the storage object adds more than one value in
243 * one call to appendValue().
245 TEST(AbstractOptionStorageTest, HandlesValueAddition)
247 gmx::Options options(NULL, NULL);
248 std::vector<std::string> values;
249 MockOptionInfo *info = options.addOption(
250 MockOption("name").storeVector(&values).multiValue());
251 MockOptionStorage *mock = &info->option();
253 ::testing::InSequence dummy;
254 using ::testing::DoAll;
255 using ::testing::ElementsAre;
256 using ::testing::InvokeWithoutArgs;
257 using ::testing::Pointee;
258 EXPECT_CALL(*mock, convertValue("a"));
259 EXPECT_CALL(*mock, convertValue("b"))
260 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
261 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
262 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy", "dummy"))));
263 EXPECT_CALL(*mock, processAll());
266 gmx::OptionsAssigner assigner(&options);
267 EXPECT_NO_THROW(assigner.start());
268 ASSERT_NO_THROW(assigner.startOption("name"));
269 EXPECT_NO_THROW(assigner.appendValue("a"));
270 EXPECT_NO_THROW(assigner.appendValue("b"));
271 EXPECT_NO_THROW(assigner.finishOption());
272 EXPECT_NO_THROW(assigner.finish());
273 EXPECT_NO_THROW(options.finish());
275 ASSERT_EQ(3U, values.size());
276 EXPECT_EQ("a", values[0]);
277 EXPECT_EQ("dummy", values[1]);
278 EXPECT_EQ("dummy", values[2]);
282 * Tests that storage works if the storage object adds more than one value in
283 * one call to appendValue(), and this results in too many values.
285 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
287 gmx::Options options(NULL, NULL);
288 std::vector<std::string> values;
289 MockOptionInfo *info = options.addOption(
290 MockOption("name").storeVector(&values).valueCount(2));
291 MockOptionStorage *mock = &info->option();
293 ::testing::InSequence dummy;
294 using ::testing::DoAll;
295 using ::testing::ElementsAre;
296 using ::testing::InvokeWithoutArgs;
297 using ::testing::Pointee;
298 EXPECT_CALL(*mock, convertValue("a"));
299 EXPECT_CALL(*mock, convertValue("b"))
300 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
301 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
302 EXPECT_CALL(*mock, processAll());
305 gmx::OptionsAssigner assigner(&options);
306 EXPECT_NO_THROW(assigner.start());
307 ASSERT_NO_THROW(assigner.startOption("name"));
308 EXPECT_NO_THROW(assigner.appendValue("a"));
309 EXPECT_THROW(assigner.appendValue("b"), gmx::InvalidInputError);
310 EXPECT_NO_THROW(assigner.finishOption());
311 EXPECT_NO_THROW(assigner.finish());
312 EXPECT_NO_THROW(options.finish());
314 ASSERT_TRUE(values.empty());
318 * Tests that the storage object is properly invoked even if no output values
319 * should be produced.
321 TEST(AbstractOptionStorageTest, AllowsEmptyValues)
323 gmx::Options options(NULL, NULL);
324 std::vector<std::string> values;
325 MockOptionInfo *info = options.addOption(
326 MockOption("name").storeVector(&values).valueCount(0));
327 MockOptionStorage *mock = &info->option();
329 ::testing::InSequence dummy;
330 using ::testing::DoAll;
331 using ::testing::ElementsAre;
332 using ::testing::Pointee;
333 using ::testing::Return;
334 EXPECT_CALL(*mock, convertValue("a"))
336 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre())));
337 EXPECT_CALL(*mock, processAll());
340 gmx::OptionsAssigner assigner(&options);
341 EXPECT_NO_THROW(assigner.start());
342 EXPECT_NO_THROW(assigner.startOption("name"));
343 EXPECT_NO_THROW(assigner.appendValue("a"));
344 EXPECT_NO_THROW(assigner.finishOption());
345 EXPECT_NO_THROW(assigner.finish());
346 EXPECT_NO_THROW(options.finish());
348 ASSERT_EQ(0U, values.size());