Fix copyright notices for new C++ code.
[alexxy/gromacs.git] / src / gromacs / options / tests / abstractoptionstorage.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \internal \file
36  * \brief
37  * Tests proper handling of option storage.
38  *
39  * These tests check that methods in storage objects are called properly in all
40  * situations, and also that the OptionStorageTemplate class behaves properly.
41  *
42  * \author Teemu Murtola <teemu.murtola@gmail.com>
43  * \ingroup module_options
44  */
45 #include <vector>
46 #include <string>
47
48 #include <gmock/gmock.h>
49 #include <gtest/gtest.h>
50
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"
57
58 namespace
59 {
60
61 class MockOption;
62 class MockOptionStorage;
63
64 class MockOptionInfo : public gmx::OptionInfo
65 {
66     public:
67         //! Creates an option info object for the given option.
68         explicit MockOptionInfo(MockOptionStorage *option);
69
70         MockOptionStorage &option();
71 };
72
73 /*! \internal \brief
74  * Mock implementation of an option storage class for unit testing.
75  *
76  * Provides facilities for checking that correct methods are called, and for
77  * controlling how they add values using the base class methods.
78  *
79  * \ingroup module_options
80  */
81 class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
82 {
83     public:
84         /*! \brief
85          * Initializes the storage from option settings.
86          *
87          * \param[in] settings   Storage settings.
88          */
89         MockOptionStorage(const MockOption &settings);
90
91         /*! \brief
92          * Calls addValue("dummy") in the base class.
93          */
94         void addDummyValue()
95         {
96             addValue("dummy");
97         }
98         // using MyBase::markAsSet;
99         // using MyBase::addValue;
100         // using MyBase::commitValues;
101         // "using" is correct but MSVC gives error C2248. Workaround:
102         void markAsSet()
103         {
104             MyBase::markAsSet();
105         }
106         void addValue(const std::string &value)
107         {
108             MyBase::addValue(value);
109         }
110         void commitValues()
111         {
112             MyBase::commitValues();
113         }
114
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
119         {
120             return "";
121         }
122
123         MOCK_METHOD1(convertValue, void(const std::string &value));
124         MOCK_METHOD1(processSetValues, void(ValueList *values));
125         MOCK_METHOD0(processAll, void());
126
127     private:
128         MockOptionInfo          info_;
129 };
130
131 /*! \internal \brief
132  * Specifies an option that has a mock storage object for unit testing.
133  *
134  * \ingroup module_options
135  */
136 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
137 {
138     public:
139         //! OptionInfo subclass corresponding to this option type.
140         typedef MockOptionInfo InfoType;
141
142         //! Initializes an option with the given name.
143         explicit MockOption(const char *name)
144             : MyBase(name)
145         {
146         }
147
148     private:
149         virtual gmx::AbstractOptionStoragePointer createStorage() const
150         {
151             return gmx::AbstractOptionStoragePointer(new MockOptionStorage(*this));
152         }
153 };
154
155 MockOptionStorage::MockOptionStorage(const MockOption &settings)
156     : MyBase(settings), info_(this)
157 {
158     using ::testing::_;
159     using ::testing::Invoke;
160     using ::testing::WithArg;
161     ON_CALL(*this, convertValue(_))
162         .WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
163 }
164
165 MockOptionInfo::MockOptionInfo(MockOptionStorage *option)
166     : gmx::OptionInfo(option)
167 {
168 }
169
170 MockOptionStorage &MockOptionInfo::option()
171 {
172     return static_cast<MockOptionStorage &>(gmx::OptionInfo::option());
173 }
174
175 /*
176  * Tests that finish() can set a required option even if the user has not
177  * provided it.
178  */
179 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
180 {
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();
186     {
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)));
194     }
195
196     gmx::OptionsAssigner assigner(&options);
197     EXPECT_NO_THROW(assigner.start());
198     EXPECT_NO_THROW(assigner.finish());
199     EXPECT_NO_THROW(options.finish());
200
201     ASSERT_EQ(1U, values.size());
202     EXPECT_EQ("dummy", values[0]);
203 }
204
205 /*
206  * Tests that storage works if the storage object does not add a value in a
207  * call to appendValue().
208  */
209 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
210 {
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();
216     {
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"))
223             .WillOnce(Return());
224         EXPECT_CALL(*mock, convertValue("c"));
225         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
226         EXPECT_CALL(*mock, processAll());
227     }
228
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());
238
239     ASSERT_EQ(2U, values.size());
240     EXPECT_EQ("a", values[0]);
241     EXPECT_EQ("c", values[1]);
242 }
243
244 /*
245  * Tests that storage works if the storage object adds more than one value in
246  * one call to appendValue().
247  */
248 TEST(AbstractOptionStorageTest, HandlesValueAddition)
249 {
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();
255     {
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());
267     }
268
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());
277
278     ASSERT_EQ(3U, values.size());
279     EXPECT_EQ("a", values[0]);
280     EXPECT_EQ("dummy", values[1]);
281     EXPECT_EQ("dummy", values[2]);
282 }
283
284 /*
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.
287  */
288 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
289 {
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();
295     {
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());
306     }
307
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());
316
317     ASSERT_TRUE(values.empty());
318 }
319
320 /*
321  * Tests that the storage object is properly invoked even if no output values
322  * should be produced.
323  */
324 TEST(AbstractOptionStorageTest, AllowsEmptyValues)
325 {
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();
331     {
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"))
338             .WillOnce(Return());
339         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre())));
340         EXPECT_CALL(*mock, processAll());
341     }
342
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());
350
351     ASSERT_EQ(0U, values.size());
352 }
353
354 } // namespace