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/options.h"
49 #include "gromacs/options/optionstoragetemplate.h"
50 #include "gromacs/options/optionsassigner.h"
51 #include "gromacs/utility/exceptions.h"
52 #include "testutils/testexceptions.h"
58 class MockOptionStorage;
60 class MockOptionInfo : public gmx::OptionInfo
63 //! Creates an option info object for the given option.
64 explicit MockOptionInfo(MockOptionStorage *option);
66 MockOptionStorage &option();
70 * Mock implementation of an option storage class for unit testing.
72 * Provides facilities for checking that correct methods are called, and for
73 * controlling how they add values using the base class methods.
75 * \ingroup module_options
77 class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
81 * Initializes the storage from option settings.
83 * \param[in] settings Storage settings.
85 MockOptionStorage(const MockOption &settings);
88 * Calls addValue("dummy") in the base class.
94 // using MyBase::markAsSet;
95 // using MyBase::addValue;
96 // using MyBase::commitValues;
97 // "using" is correct but MSVC gives error C2248. Workaround:
102 void addValue(const std::string &value)
104 MyBase::addValue(value);
108 MyBase::commitValues();
111 virtual gmx::OptionInfo &optionInfo() { return info_; }
112 // These are not used.
113 virtual const char *typeString() const { return "mock"; }
114 virtual std::string formatSingleValue(const std::string &/*value*/) const
119 MOCK_METHOD1(convertValue, void(const std::string &value));
120 MOCK_METHOD1(processSetValues, void(ValueList *values));
121 MOCK_METHOD0(processAll, void());
124 MockOptionInfo info_;
128 * Specifies an option that has a mock storage object for unit testing.
130 * \ingroup module_options
132 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
135 //! OptionInfo subclass corresponding to this option type.
136 typedef MockOptionInfo InfoType;
138 //! Initializes an option with the given name.
139 explicit MockOption(const char *name)
145 virtual gmx::AbstractOptionStoragePointer createStorage() const
147 return gmx::AbstractOptionStoragePointer(new MockOptionStorage(*this));
151 MockOptionStorage::MockOptionStorage(const MockOption &settings)
152 : MyBase(settings), info_(this)
155 using ::testing::Invoke;
156 using ::testing::WithArg;
157 ON_CALL(*this, convertValue(_))
158 .WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
161 MockOptionInfo::MockOptionInfo(MockOptionStorage *option)
162 : gmx::OptionInfo(option)
166 MockOptionStorage &MockOptionInfo::option()
168 return static_cast<MockOptionStorage &>(gmx::OptionInfo::option());
172 * Tests that finish() can set a required option even if the user has not
175 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
177 gmx::Options options(NULL, NULL);
178 std::vector<std::string> values;
179 MockOptionInfo *info = options.addOption(
180 MockOption("name").required().storeVector(&values));
181 MockOptionStorage *mock = &info->option();
183 ::testing::InSequence dummy;
184 using ::testing::DoAll;
185 using ::testing::InvokeWithoutArgs;
186 EXPECT_CALL(*mock, processAll())
187 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::markAsSet),
188 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
189 InvokeWithoutArgs(mock, &MockOptionStorage::commitValues)));
192 gmx::OptionsAssigner assigner(&options);
193 EXPECT_NO_THROW(assigner.start());
194 EXPECT_NO_THROW(assigner.finish());
195 EXPECT_NO_THROW(options.finish());
197 ASSERT_EQ(1U, values.size());
198 EXPECT_EQ("dummy", values[0]);
202 * Tests that storage works if the storage object does not add a value in a
203 * call to appendValue().
205 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
207 gmx::Options options(NULL, NULL);
208 std::vector<std::string> values;
209 MockOptionInfo *info = options.addOption(
210 MockOption("name").storeVector(&values).multiValue());
211 MockOptionStorage *mock = &info->option();
213 ::testing::InSequence dummy;
214 using ::testing::ElementsAre;
215 using ::testing::Pointee;
216 using ::testing::Return;
217 EXPECT_CALL(*mock, convertValue("a"));
218 EXPECT_CALL(*mock, convertValue("b"))
220 EXPECT_CALL(*mock, convertValue("c"));
221 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
222 EXPECT_CALL(*mock, processAll());
225 gmx::OptionsAssigner assigner(&options);
226 EXPECT_NO_THROW(assigner.start());
227 ASSERT_NO_THROW(assigner.startOption("name"));
228 EXPECT_NO_THROW(assigner.appendValue("a"));
229 EXPECT_NO_THROW(assigner.appendValue("b"));
230 EXPECT_NO_THROW(assigner.appendValue("c"));
231 EXPECT_NO_THROW(assigner.finishOption());
232 EXPECT_NO_THROW(assigner.finish());
233 EXPECT_NO_THROW(options.finish());
235 ASSERT_EQ(2U, values.size());
236 EXPECT_EQ("a", values[0]);
237 EXPECT_EQ("c", values[1]);
241 * Tests that storage works if the storage object adds more than one value in
242 * one call to appendValue().
244 TEST(AbstractOptionStorageTest, HandlesValueAddition)
246 gmx::Options options(NULL, NULL);
247 std::vector<std::string> values;
248 MockOptionInfo *info = options.addOption(
249 MockOption("name").storeVector(&values).multiValue());
250 MockOptionStorage *mock = &info->option();
252 ::testing::InSequence dummy;
253 using ::testing::DoAll;
254 using ::testing::ElementsAre;
255 using ::testing::InvokeWithoutArgs;
256 using ::testing::Pointee;
257 EXPECT_CALL(*mock, convertValue("a"));
258 EXPECT_CALL(*mock, convertValue("b"))
259 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
260 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
261 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy", "dummy"))));
262 EXPECT_CALL(*mock, processAll());
265 gmx::OptionsAssigner assigner(&options);
266 EXPECT_NO_THROW(assigner.start());
267 ASSERT_NO_THROW(assigner.startOption("name"));
268 EXPECT_NO_THROW(assigner.appendValue("a"));
269 EXPECT_NO_THROW(assigner.appendValue("b"));
270 EXPECT_NO_THROW(assigner.finishOption());
271 EXPECT_NO_THROW(assigner.finish());
272 EXPECT_NO_THROW(options.finish());
274 ASSERT_EQ(3U, values.size());
275 EXPECT_EQ("a", values[0]);
276 EXPECT_EQ("dummy", values[1]);
277 EXPECT_EQ("dummy", values[2]);
281 * Tests that storage works if the storage object adds more than one value in
282 * one call to appendValue(), and this results in too many values.
284 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
286 gmx::Options options(NULL, NULL);
287 std::vector<std::string> values;
288 MockOptionInfo *info = options.addOption(
289 MockOption("name").storeVector(&values).valueCount(2));
290 MockOptionStorage *mock = &info->option();
292 ::testing::InSequence dummy;
293 using ::testing::DoAll;
294 using ::testing::ElementsAre;
295 using ::testing::InvokeWithoutArgs;
296 using ::testing::Pointee;
297 EXPECT_CALL(*mock, convertValue("a"));
298 EXPECT_CALL(*mock, convertValue("b"))
299 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
300 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
301 EXPECT_CALL(*mock, processAll());
304 gmx::OptionsAssigner assigner(&options);
305 EXPECT_NO_THROW(assigner.start());
306 ASSERT_NO_THROW(assigner.startOption("name"));
307 EXPECT_NO_THROW(assigner.appendValue("a"));
308 EXPECT_THROW(assigner.appendValue("b"), gmx::InvalidInputError);
309 EXPECT_NO_THROW(assigner.finishOption());
310 EXPECT_NO_THROW(assigner.finish());
311 EXPECT_NO_THROW(options.finish());
313 ASSERT_TRUE(values.empty());
317 * Tests that the storage object is properly invoked even if no output values
318 * should be produced.
320 TEST(AbstractOptionStorageTest, AllowsEmptyValues)
322 gmx::Options options(NULL, NULL);
323 std::vector<std::string> values;
324 MockOptionInfo *info = options.addOption(
325 MockOption("name").storeVector(&values).valueCount(0));
326 MockOptionStorage *mock = &info->option();
328 ::testing::InSequence dummy;
329 using ::testing::DoAll;
330 using ::testing::ElementsAre;
331 using ::testing::Pointee;
332 using ::testing::Return;
333 EXPECT_CALL(*mock, convertValue("a"))
335 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre())));
336 EXPECT_CALL(*mock, processAll());
339 gmx::OptionsAssigner assigner(&options);
340 EXPECT_NO_THROW(assigner.start());
341 EXPECT_NO_THROW(assigner.startOption("name"));
342 EXPECT_NO_THROW(assigner.appendValue("a"));
343 EXPECT_NO_THROW(assigner.finishOption());
344 EXPECT_NO_THROW(assigner.finish());
345 EXPECT_NO_THROW(options.finish());
347 ASSERT_EQ(0U, values.size());