Sort all includes in src/gromacs
[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,2013,2014, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source 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 "gmxpre.h"
46
47 #include <string>
48 #include <vector>
49
50 #include <gmock/gmock.h>
51 #include <gtest/gtest.h>
52
53 #include "gromacs/options/abstractoption.h"
54 #include "gromacs/options/options.h"
55 #include "gromacs/options/optionsassigner.h"
56 #include "gromacs/options/optionstoragetemplate.h"
57 #include "gromacs/utility/exceptions.h"
58
59 #include "testutils/testasserts.h"
60 #include "testutils/testexceptions.h"
61
62 namespace
63 {
64
65 class MockOption;
66 class MockOptionStorage;
67
68 class MockOptionInfo : public gmx::OptionInfo
69 {
70     public:
71         //! Creates an option info object for the given option.
72         explicit MockOptionInfo(MockOptionStorage *option);
73
74         MockOptionStorage &option();
75 };
76
77 /*! \brief
78  * Mock implementation of an option storage class for unit testing.
79  *
80  * Provides facilities for checking that correct methods are called, and for
81  * controlling how they add values using the base class methods.
82  *
83  * \ingroup module_options
84  */
85 class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
86 {
87     public:
88         /*! \brief
89          * Initializes the storage from option settings.
90          *
91          * \param[in] settings   Storage settings.
92          */
93         MockOptionStorage(const MockOption &settings);
94
95         /*! \brief
96          * Calls addValue("dummy") in the base class.
97          */
98         void addDummyValue()
99         {
100             addValue("dummy");
101         }
102         // using MyBase::markAsSet;
103         // using MyBase::addValue;
104         // using MyBase::commitValues;
105         // "using" is correct but MSVC gives error C2248. Workaround:
106         //! \copydoc gmx::OptionStorageTemplate::markAsSet()
107         void markAsSet()
108         {
109             MyBase::markAsSet();
110         }
111         //! \copydoc gmx::OptionStorageTemplate::addValue()
112         void addValue(const std::string &value)
113         {
114             MyBase::addValue(value);
115         }
116         //! \copydoc gmx::OptionStorageTemplate::commitValues()
117         void commitValues()
118         {
119             MyBase::commitValues();
120         }
121
122         virtual gmx::OptionInfo &optionInfo() { return info_; }
123         // These are not used.
124         virtual std::string typeString() const { return "mock"; }
125         virtual std::string formatSingleValue(const std::string & /*value*/) const
126         {
127             return "";
128         }
129
130         MOCK_METHOD1(convertValue, void(const std::string &value));
131         MOCK_METHOD1(processSetValues, void(ValueList *values));
132         MOCK_METHOD0(processAll, void());
133
134     private:
135         MockOptionInfo          info_;
136 };
137
138 /*! \internal \brief
139  * Specifies an option that has a mock storage object for unit testing.
140  *
141  * \ingroup module_options
142  */
143 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
144 {
145     public:
146         //! OptionInfo subclass corresponding to this option type.
147         typedef MockOptionInfo InfoType;
148
149         //! Initializes an option with the given name.
150         explicit MockOption(const char *name)
151             : MyBase(name)
152         {
153         }
154
155     private:
156         virtual gmx::AbstractOptionStorage *createStorage(
157             const gmx::OptionManagerContainer & /*managers*/) const
158         {
159             return new MockOptionStorage(*this);
160         }
161 };
162
163 MockOptionStorage::MockOptionStorage(const MockOption &settings)
164     : MyBase(settings), info_(this)
165 {
166     using ::testing::_;
167     using ::testing::Invoke;
168     using ::testing::WithArg;
169     ON_CALL(*this, convertValue(_))
170         .WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
171 }
172
173 MockOptionInfo::MockOptionInfo(MockOptionStorage *option)
174     : gmx::OptionInfo(option)
175 {
176 }
177
178 MockOptionStorage &MockOptionInfo::option()
179 {
180     return static_cast<MockOptionStorage &>(gmx::OptionInfo::option());
181 }
182
183 /*
184  * Tests that finish() can set a required option even if the user has not
185  * provided it.
186  */
187 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
188 {
189     gmx::Options                options(NULL, NULL);
190     std::vector<std::string>    values;
191     MockOptionInfo             *info = options.addOption(
192                 MockOption("name").required().storeVector(&values));
193     MockOptionStorage          *mock = &info->option();
194     {
195         ::testing::InSequence dummy;
196         using ::testing::DoAll;
197         using ::testing::InvokeWithoutArgs;
198         EXPECT_CALL(*mock, processAll())
199             .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::markAsSet),
200                             InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
201                             InvokeWithoutArgs(mock, &MockOptionStorage::commitValues)));
202     }
203
204     gmx::OptionsAssigner assigner(&options);
205     EXPECT_NO_THROW_GMX(assigner.start());
206     EXPECT_NO_THROW_GMX(assigner.finish());
207     EXPECT_NO_THROW_GMX(options.finish());
208
209     ASSERT_EQ(1U, values.size());
210     EXPECT_EQ("dummy", values[0]);
211 }
212
213 /*
214  * Tests that storage works if the storage object does not add a value in a
215  * call to appendValue().
216  */
217 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
218 {
219     gmx::Options                options(NULL, NULL);
220     std::vector<std::string>    values;
221     MockOptionInfo             *info = options.addOption(
222                 MockOption("name").storeVector(&values).multiValue());
223     MockOptionStorage          *mock = &info->option();
224     {
225         ::testing::InSequence dummy;
226         using ::testing::ElementsAre;
227         using ::testing::Pointee;
228         using ::testing::Return;
229         EXPECT_CALL(*mock, convertValue("a"));
230         EXPECT_CALL(*mock, convertValue("b"))
231             .WillOnce(Return());
232         EXPECT_CALL(*mock, convertValue("c"));
233         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
234         EXPECT_CALL(*mock, processAll());
235     }
236
237     gmx::OptionsAssigner assigner(&options);
238     EXPECT_NO_THROW_GMX(assigner.start());
239     ASSERT_NO_THROW_GMX(assigner.startOption("name"));
240     EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
241     EXPECT_NO_THROW_GMX(assigner.appendValue("b"));
242     EXPECT_NO_THROW_GMX(assigner.appendValue("c"));
243     EXPECT_NO_THROW_GMX(assigner.finishOption());
244     EXPECT_NO_THROW_GMX(assigner.finish());
245     EXPECT_NO_THROW_GMX(options.finish());
246
247     ASSERT_EQ(2U, values.size());
248     EXPECT_EQ("a", values[0]);
249     EXPECT_EQ("c", values[1]);
250 }
251
252 /*
253  * Tests that storage works if the storage object adds more than one value in
254  * one call to appendValue().
255  */
256 TEST(AbstractOptionStorageTest, HandlesValueAddition)
257 {
258     gmx::Options                options(NULL, NULL);
259     std::vector<std::string>    values;
260     MockOptionInfo             *info = options.addOption(
261                 MockOption("name").storeVector(&values).multiValue());
262     MockOptionStorage          *mock = &info->option();
263     {
264         ::testing::InSequence dummy;
265         using ::testing::DoAll;
266         using ::testing::ElementsAre;
267         using ::testing::InvokeWithoutArgs;
268         using ::testing::Pointee;
269         EXPECT_CALL(*mock, convertValue("a"));
270         EXPECT_CALL(*mock, convertValue("b"))
271             .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
272                             InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
273         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy", "dummy"))));
274         EXPECT_CALL(*mock, processAll());
275     }
276
277     gmx::OptionsAssigner assigner(&options);
278     EXPECT_NO_THROW_GMX(assigner.start());
279     ASSERT_NO_THROW_GMX(assigner.startOption("name"));
280     EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
281     EXPECT_NO_THROW_GMX(assigner.appendValue("b"));
282     EXPECT_NO_THROW_GMX(assigner.finishOption());
283     EXPECT_NO_THROW_GMX(assigner.finish());
284     EXPECT_NO_THROW_GMX(options.finish());
285
286     ASSERT_EQ(3U, values.size());
287     EXPECT_EQ("a", values[0]);
288     EXPECT_EQ("dummy", values[1]);
289     EXPECT_EQ("dummy", values[2]);
290 }
291
292 /*
293  * Tests that storage works if the storage object adds more than one value in
294  * one call to appendValue(), and this results in too many values.
295  */
296 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
297 {
298     gmx::Options                options(NULL, NULL);
299     std::vector<std::string>    values;
300     MockOptionInfo             *info = options.addOption(
301                 MockOption("name").storeVector(&values).valueCount(2));
302     MockOptionStorage          *mock = &info->option();
303     {
304         ::testing::InSequence dummy;
305         using ::testing::DoAll;
306         using ::testing::ElementsAre;
307         using ::testing::InvokeWithoutArgs;
308         using ::testing::Pointee;
309         EXPECT_CALL(*mock, convertValue("a"));
310         EXPECT_CALL(*mock, convertValue("b"))
311             .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
312                             InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
313         EXPECT_CALL(*mock, processAll());
314     }
315
316     gmx::OptionsAssigner assigner(&options);
317     EXPECT_NO_THROW_GMX(assigner.start());
318     ASSERT_NO_THROW_GMX(assigner.startOption("name"));
319     EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
320     EXPECT_THROW_GMX(assigner.appendValue("b"), gmx::InvalidInputError);
321     EXPECT_NO_THROW_GMX(assigner.finishOption());
322     EXPECT_NO_THROW_GMX(assigner.finish());
323     EXPECT_NO_THROW_GMX(options.finish());
324
325     ASSERT_TRUE(values.empty());
326 }
327
328 /*
329  * Tests that the storage object is properly invoked even if no output values
330  * should be produced.
331  */
332 TEST(AbstractOptionStorageTest, AllowsEmptyValues)
333 {
334     gmx::Options                options(NULL, NULL);
335     std::vector<std::string>    values;
336     MockOptionInfo             *info = options.addOption(
337                 MockOption("name").storeVector(&values).valueCount(0));
338     MockOptionStorage          *mock = &info->option();
339     {
340         ::testing::InSequence dummy;
341         using ::testing::DoAll;
342         using ::testing::ElementsAre;
343         using ::testing::Pointee;
344         using ::testing::Return;
345         EXPECT_CALL(*mock, convertValue("a"))
346             .WillOnce(Return());
347         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre())));
348         EXPECT_CALL(*mock, processAll());
349     }
350
351     gmx::OptionsAssigner assigner(&options);
352     EXPECT_NO_THROW_GMX(assigner.start());
353     EXPECT_NO_THROW_GMX(assigner.startOption("name"));
354     EXPECT_NO_THROW_GMX(assigner.appendValue("a"));
355     EXPECT_NO_THROW_GMX(assigner.finishOption());
356     EXPECT_NO_THROW_GMX(assigner.finish());
357     EXPECT_NO_THROW_GMX(options.finish());
358
359     ASSERT_EQ(0U, values.size());
360 }
361
362 } // namespace