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.
84 // using MyBase::markAsSet;
85 // using MyBase::addValue;
86 // using MyBase::commitValues;
87 // "using" is correct but MSVC gives error C2248. Workaround:
92 void addValue(const std::string &value)
94 MyBase::addValue(value);
98 MyBase::commitValues();
101 // These are not used.
102 virtual gmx::OptionInfo &optionInfo()
104 GMX_THROW(gmx::test::TestException("Not implemented"));
106 virtual const char *typeString() const { return "mock"; }
107 virtual std::string formatSingleValue(const std::string &/*value*/) const
112 MOCK_METHOD1(convertValue, void(const std::string &value));
113 MOCK_METHOD1(processSetValues, void(ValueList *values));
114 MOCK_METHOD0(processAll, void());
118 * Specifies an option that has a mock storage object for unit testing.
120 * \ingroup module_options
122 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
125 //! Initializes an option with the given name.
126 explicit MockOption(const char *name)
127 : MyBase(name), storagePtr_(NULL)
131 //! Sets an output pointer to give access to the created storage object.
132 MyClass &storageObject(MockOptionStorage **storagePtr)
133 { storagePtr_ = storagePtr; return me(); }
136 virtual gmx::AbstractOptionStoragePointer createStorage() const
138 MockOptionStorage *storage = new MockOptionStorage(*this);
139 if (storagePtr_ != NULL)
141 *storagePtr_ = storage;
143 return gmx::AbstractOptionStoragePointer(storage);
146 MockOptionStorage **storagePtr_;
149 MockOptionStorage::MockOptionStorage(const MockOption &settings)
153 using ::testing::Invoke;
154 using ::testing::WithArg;
155 ON_CALL(*this, convertValue(_))
156 .WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
160 * Tests that finish() can set a required option even if the user has not
163 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
165 gmx::Options options(NULL, NULL);
166 std::vector<std::string> values;
167 MockOptionStorage *mock = NULL;
168 ASSERT_NO_THROW(options.addOption(
169 MockOption("name").storageObject(&mock).required()
170 .storeVector(&values)));
171 ASSERT_TRUE(mock != NULL);
173 ::testing::InSequence dummy;
174 using ::testing::DoAll;
175 using ::testing::InvokeWithoutArgs;
176 EXPECT_CALL(*mock, processAll())
177 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::markAsSet),
178 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
179 InvokeWithoutArgs(mock, &MockOptionStorage::commitValues)));
182 gmx::OptionsAssigner assigner(&options);
183 EXPECT_NO_THROW(assigner.start());
184 EXPECT_NO_THROW(assigner.finish());
185 EXPECT_NO_THROW(options.finish());
187 ASSERT_EQ(1U, values.size());
188 EXPECT_EQ("dummy", values[0]);
192 * Tests that storage works if the storage object does not add a value in a
193 * call to appendValue().
195 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
197 gmx::Options options(NULL, NULL);
198 std::vector<std::string> values;
199 MockOptionStorage *mock;
200 ASSERT_NO_THROW(options.addOption(
201 MockOption("name").storageObject(&mock)
202 .storeVector(&values).multiValue()));
205 ::testing::InSequence dummy;
206 using ::testing::ElementsAre;
207 using ::testing::Pointee;
208 using ::testing::Return;
209 EXPECT_CALL(*mock, convertValue("a"));
210 EXPECT_CALL(*mock, convertValue("b"))
212 EXPECT_CALL(*mock, convertValue("c"));
213 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
214 EXPECT_CALL(*mock, processAll());
217 gmx::OptionsAssigner assigner(&options);
218 EXPECT_NO_THROW(assigner.start());
219 ASSERT_NO_THROW(assigner.startOption("name"));
220 EXPECT_NO_THROW(assigner.appendValue("a"));
221 EXPECT_NO_THROW(assigner.appendValue("b"));
222 EXPECT_NO_THROW(assigner.appendValue("c"));
223 EXPECT_NO_THROW(assigner.finishOption());
224 EXPECT_NO_THROW(assigner.finish());
225 EXPECT_NO_THROW(options.finish());
227 ASSERT_EQ(2U, values.size());
228 EXPECT_EQ("a", values[0]);
229 EXPECT_EQ("c", values[1]);
233 * Tests that storage works if the storage object adds more than one value in
234 * one call to appendValue().
236 TEST(AbstractOptionStorageTest, HandlesValueAddition)
238 gmx::Options options(NULL, NULL);
239 std::vector<std::string> values;
240 MockOptionStorage *mock=NULL;
241 ASSERT_NO_THROW(options.addOption(
242 MockOption("name").storageObject(&mock)
243 .storeVector(&values).multiValue()));
244 ASSERT_TRUE(mock != NULL);
246 ::testing::InSequence dummy;
247 using ::testing::DoAll;
248 using ::testing::ElementsAre;
249 using ::testing::InvokeWithoutArgs;
250 using ::testing::Pointee;
251 EXPECT_CALL(*mock, convertValue("a"));
252 EXPECT_CALL(*mock, convertValue("b"))
253 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
254 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
255 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy", "dummy"))));
256 EXPECT_CALL(*mock, processAll());
259 gmx::OptionsAssigner assigner(&options);
260 EXPECT_NO_THROW(assigner.start());
261 ASSERT_NO_THROW(assigner.startOption("name"));
262 EXPECT_NO_THROW(assigner.appendValue("a"));
263 EXPECT_NO_THROW(assigner.appendValue("b"));
264 EXPECT_NO_THROW(assigner.finishOption());
265 EXPECT_NO_THROW(assigner.finish());
266 EXPECT_NO_THROW(options.finish());
268 ASSERT_EQ(3U, values.size());
269 EXPECT_EQ("a", values[0]);
270 EXPECT_EQ("dummy", values[1]);
271 EXPECT_EQ("dummy", values[2]);
275 * Tests that storage works if the storage object adds more than one value in
276 * one call to appendValue(), and this results in too many values.
278 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
280 gmx::Options options(NULL, NULL);
281 std::vector<std::string> values;
282 MockOptionStorage *mock=NULL;
283 ASSERT_NO_THROW(options.addOption(
284 MockOption("name").storageObject(&mock)
285 .storeVector(&values).valueCount(2)));
286 ASSERT_TRUE(mock != NULL);
288 ::testing::InSequence dummy;
289 using ::testing::DoAll;
290 using ::testing::ElementsAre;
291 using ::testing::InvokeWithoutArgs;
292 using ::testing::Pointee;
293 EXPECT_CALL(*mock, convertValue("a"));
294 EXPECT_CALL(*mock, convertValue("b"))
295 .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
296 InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
297 EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy"))));
298 EXPECT_CALL(*mock, processAll());
301 gmx::OptionsAssigner assigner(&options);
302 EXPECT_NO_THROW(assigner.start());
303 ASSERT_NO_THROW(assigner.startOption("name"));
304 EXPECT_NO_THROW(assigner.appendValue("a"));
305 EXPECT_THROW(assigner.appendValue("b"), gmx::InvalidInputError);
306 EXPECT_NO_THROW(assigner.finishOption());
307 EXPECT_NO_THROW(assigner.finish());
308 EXPECT_NO_THROW(options.finish());
310 ASSERT_EQ(2U, values.size());
311 EXPECT_EQ("a", values[0]);
312 EXPECT_EQ("dummy", values[1]);
316 * Tests that the storage object is properly invoked even if no output values
317 * should be produced.
319 TEST(AbstractOptionStorageTest, AllowsEmptyValues)
321 gmx::Options options(NULL, NULL);
322 std::vector<std::string> values;
323 MockOptionStorage *mock;
324 ASSERT_NO_THROW(options.addOption(
325 MockOption("name").storageObject(&mock)
326 .storeVector(&values).valueCount(0)));
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());