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"
60 * Mock implementation of an option storage class for unit testing.
62 * Provides facilities for checking that correct methods are called, and for
63 * controlling how they add values using the base class methods.
65 * \ingroup module_options
67 class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
71 * Initializes the storage from option settings.
73 * \param[in] settings Storage settings.
75 MockOptionStorage(const MockOption &settings);
78 * Calls addValue("dummy") in the base class.
85 * Calls setFlag(efSet).
91 // using MyBase::addValue;
92 // using MyBase::commitValues;
93 // "using" is correct but MSVC gives error C2248. Workaround:
94 void addValue(const std::string &value)
96 MyBase::addValue(value);
100 MyBase::commitValues();
103 // These are not used.
104 virtual gmx::OptionInfo &optionInfo()
106 GMX_THROW(gmx::test::TestException("Not implemented"));
108 virtual const char *typeString() const { return "mock"; }
109 virtual std::string formatSingleValue(const std::string &/*value*/) const
114 MOCK_METHOD1(convertValue, void(const std::string &value));
115 MOCK_METHOD1(processSetValues, void(ValueList *values));
116 MOCK_METHOD0(processAll, void());
120 * Specifies an option that has a mock storage object for unit testing.
122 * \ingroup module_options
124 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
127 //! Initializes an option with the given name.
128 explicit MockOption(const char *name)
129 : MyBase(name), storagePtr_(NULL)
133 //! Sets an output pointer to give access to the created storage object.
134 MyClass &storageObject(MockOptionStorage **storagePtr)
135 { storagePtr_ = storagePtr; return me(); }
138 virtual gmx::AbstractOptionStoragePointer createStorage() const
140 MockOptionStorage *storage = new MockOptionStorage(*this);
141 if (storagePtr_ != NULL)
143 *storagePtr_ = storage;
145 return gmx::AbstractOptionStoragePointer(storage);
148 MockOptionStorage **storagePtr_;
151 MockOptionStorage::MockOptionStorage(const MockOption &settings)
155 using ::testing::Invoke;
156 using ::testing::WithArg;
157 ON_CALL(*this, convertValue(_))
158 .WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
162 * Tests that finish() can set a required option even if the user has not
165 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
167 gmx::Options options(NULL, NULL);
168 std::vector<std::string> values;
169 MockOptionStorage *mock = NULL;
170 ASSERT_NO_THROW(options.addOption(
171 MockOption("name").storageObject(&mock).required()
172 .storeVector(&values)));
173 ASSERT_TRUE(mock != NULL);
175 ::testing::InSequence dummy;
176 using ::testing::DoAll;
177 using ::testing::InvokeWithoutArgs;
178 EXPECT_CALL(*mock, processAll())
179 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::setOption),
180 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
181 InvokeWithoutArgs(mock, &MockOptionStorage::commitValues)));
184 gmx::OptionsAssigner assigner(&options);
185 EXPECT_NO_THROW(assigner.start());
186 EXPECT_NO_THROW(assigner.finish());
187 EXPECT_NO_THROW(options.finish());
189 ASSERT_EQ(1U, values.size());
190 EXPECT_EQ("dummy", values[0]);
194 * Tests that storage works if the storage object does not add a value in a
195 * call to appendValue().
197 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
199 gmx::Options options(NULL, NULL);
200 std::vector<std::string> values;
201 MockOptionStorage *mock;
202 ASSERT_NO_THROW(options.addOption(
203 MockOption("name").storageObject(&mock)
204 .storeVector(&values).multiValue()));
207 ::testing::InSequence dummy;
208 using ::testing::ElementsAre;
209 using ::testing::Pointee;
210 using ::testing::Return;
211 EXPECT_CALL(*mock, convertValue("a"));
212 EXPECT_CALL(*mock, convertValue("b"))
214 EXPECT_CALL(*mock, convertValue("c"));
215 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
216 EXPECT_CALL(*mock, processAll());
219 gmx::OptionsAssigner assigner(&options);
220 EXPECT_NO_THROW(assigner.start());
221 ASSERT_NO_THROW(assigner.startOption("name"));
222 EXPECT_NO_THROW(assigner.appendValue("a"));
223 EXPECT_NO_THROW(assigner.appendValue("b"));
224 EXPECT_NO_THROW(assigner.appendValue("c"));
225 EXPECT_NO_THROW(assigner.finishOption());
226 EXPECT_NO_THROW(assigner.finish());
227 EXPECT_NO_THROW(options.finish());
229 ASSERT_EQ(2U, values.size());
230 EXPECT_EQ("a", values[0]);
231 EXPECT_EQ("c", values[1]);
235 * Tests that storage works if the storage object adds more than one value in
236 * one call to appendValue().
238 TEST(AbstractOptionStorageTest, HandlesValueAddition)
240 gmx::Options options(NULL, NULL);
241 std::vector<std::string> values;
242 MockOptionStorage *mock=NULL;
243 ASSERT_NO_THROW(options.addOption(
244 MockOption("name").storageObject(&mock)
245 .storeVector(&values).multiValue()));
246 ASSERT_TRUE(mock != NULL);
248 ::testing::InSequence dummy;
249 using ::testing::DoAll;
250 using ::testing::ElementsAre;
251 using ::testing::InvokeWithoutArgs;
252 using ::testing::Pointee;
253 EXPECT_CALL(*mock, convertValue("a"));
254 EXPECT_CALL(*mock, convertValue("b"))
255 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
256 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
257 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy", "dummy"))));
258 EXPECT_CALL(*mock, processAll());
261 gmx::OptionsAssigner assigner(&options);
262 EXPECT_NO_THROW(assigner.start());
263 ASSERT_NO_THROW(assigner.startOption("name"));
264 EXPECT_NO_THROW(assigner.appendValue("a"));
265 EXPECT_NO_THROW(assigner.appendValue("b"));
266 EXPECT_NO_THROW(assigner.finishOption());
267 EXPECT_NO_THROW(assigner.finish());
268 EXPECT_NO_THROW(options.finish());
270 ASSERT_EQ(3U, values.size());
271 EXPECT_EQ("a", values[0]);
272 EXPECT_EQ("dummy", values[1]);
273 EXPECT_EQ("dummy", values[2]);
277 * Tests that storage works if the storage object adds more than one value in
278 * one call to appendValue(), and this results in too many values.
280 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
282 gmx::Options options(NULL, NULL);
283 std::vector<std::string> values;
284 MockOptionStorage *mock=NULL;
285 ASSERT_NO_THROW(options.addOption(
286 MockOption("name").storageObject(&mock)
287 .storeVector(&values).valueCount(2)));
288 ASSERT_TRUE(mock != NULL);
290 ::testing::InSequence dummy;
291 using ::testing::DoAll;
292 using ::testing::ElementsAre;
293 using ::testing::InvokeWithoutArgs;
294 using ::testing::Pointee;
295 EXPECT_CALL(*mock, convertValue("a"));
296 EXPECT_CALL(*mock, convertValue("b"))
297 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
298 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
299 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy"))));
300 EXPECT_CALL(*mock, processAll());
303 gmx::OptionsAssigner assigner(&options);
304 EXPECT_NO_THROW(assigner.start());
305 ASSERT_NO_THROW(assigner.startOption("name"));
306 EXPECT_NO_THROW(assigner.appendValue("a"));
307 EXPECT_THROW(assigner.appendValue("b"), gmx::InvalidInputError);
308 EXPECT_NO_THROW(assigner.finishOption());
309 EXPECT_NO_THROW(assigner.finish());
310 EXPECT_NO_THROW(options.finish());
312 ASSERT_EQ(2U, values.size());
313 EXPECT_EQ("a", values[0]);
314 EXPECT_EQ("dummy", values[1]);
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 MockOptionStorage *mock;
326 ASSERT_NO_THROW(options.addOption(
327 MockOption("name").storageObject(&mock)
328 .storeVector(&values).valueCount(0)));
331 ::testing::InSequence dummy;
332 using ::testing::DoAll;
333 using ::testing::ElementsAre;
334 using ::testing::Pointee;
335 using ::testing::Return;
336 EXPECT_CALL(*mock, convertValue("a"))
338 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre())));
339 EXPECT_CALL(*mock, processAll());
342 gmx::OptionsAssigner assigner(&options);
343 EXPECT_NO_THROW(assigner.start());
344 EXPECT_NO_THROW(assigner.startOption("name"));
345 EXPECT_NO_THROW(assigner.appendValue("a"));
346 EXPECT_NO_THROW(assigner.finishOption());
347 EXPECT_NO_THROW(assigner.finish());
348 EXPECT_NO_THROW(options.finish());
350 ASSERT_EQ(0U, values.size());