Merge "Fix SelectionParserParameter move constructor problem."
[alexxy/gromacs.git] / src / gromacs / options / tests / abstractoptionstorage.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
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.
13
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.
18  *
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.
25  *
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.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Tests proper handling of option storage.
34  *
35  * These tests check that methods in storage objects are called properly in all
36  * situations, and also that the OptionStorageTemplate class behaves properly.
37  *
38  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
39  * \ingroup module_options
40  */
41 #include <vector>
42 #include <string>
43
44 #include <gmock/gmock.h>
45 #include <gtest/gtest.h>
46
47 #include "gromacs/options/abstractoption.h"
48 #include "gromacs/options/optioninfo.h"
49 #include "gromacs/options/options.h"
50 #include "gromacs/options/optionstoragetemplate.h"
51 #include "gromacs/options/optionsassigner.h"
52 #include "gromacs/utility/exceptions.h"
53 #include "testutils/testexceptions.h"
54
55 namespace
56 {
57
58 class MockOption;
59 class MockOptionStorage;
60
61 class MockOptionInfo : public gmx::OptionInfo
62 {
63     public:
64         //! Creates an option info object for the given option.
65         explicit MockOptionInfo(MockOptionStorage *option);
66
67         MockOptionStorage &option();
68 };
69
70 /*! \internal \brief
71  * Mock implementation of an option storage class for unit testing.
72  *
73  * Provides facilities for checking that correct methods are called, and for
74  * controlling how they add values using the base class methods.
75  *
76  * \ingroup module_options
77  */
78 class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
79 {
80     public:
81         /*! \brief
82          * Initializes the storage from option settings.
83          *
84          * \param[in] settings   Storage settings.
85          */
86         MockOptionStorage(const MockOption &settings);
87
88         /*! \brief
89          * Calls addValue("dummy") in the base class.
90          */
91         void addDummyValue()
92         {
93             addValue("dummy");
94         }
95         // using MyBase::markAsSet;
96         // using MyBase::addValue;
97         // using MyBase::commitValues;
98         // "using" is correct but MSVC gives error C2248. Workaround:
99         void markAsSet()
100         {
101             MyBase::markAsSet();
102         }
103         void addValue(const std::string &value)
104         {
105             MyBase::addValue(value);
106         }
107         void commitValues()
108         {
109             MyBase::commitValues();
110         }
111
112         virtual gmx::OptionInfo &optionInfo() { return info_; }
113         // These are not used.
114         virtual const char *typeString() const { return "mock"; }
115         virtual std::string formatSingleValue(const std::string &/*value*/) const
116         {
117             return "";
118         }
119
120         MOCK_METHOD1(convertValue, void(const std::string &value));
121         MOCK_METHOD1(processSetValues, void(ValueList *values));
122         MOCK_METHOD0(processAll, void());
123
124     private:
125         MockOptionInfo          info_;
126 };
127
128 /*! \internal \brief
129  * Specifies an option that has a mock storage object for unit testing.
130  *
131  * \ingroup module_options
132  */
133 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
134 {
135     public:
136         //! OptionInfo subclass corresponding to this option type.
137         typedef MockOptionInfo InfoType;
138
139         //! Initializes an option with the given name.
140         explicit MockOption(const char *name)
141             : MyBase(name)
142         {
143         }
144
145     private:
146         virtual gmx::AbstractOptionStoragePointer createStorage() const
147         {
148             return gmx::AbstractOptionStoragePointer(new MockOptionStorage(*this));
149         }
150 };
151
152 MockOptionStorage::MockOptionStorage(const MockOption &settings)
153     : MyBase(settings), info_(this)
154 {
155     using ::testing::_;
156     using ::testing::Invoke;
157     using ::testing::WithArg;
158     ON_CALL(*this, convertValue(_))
159         .WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
160 }
161
162 MockOptionInfo::MockOptionInfo(MockOptionStorage *option)
163     : gmx::OptionInfo(option)
164 {
165 }
166
167 MockOptionStorage &MockOptionInfo::option()
168 {
169     return static_cast<MockOptionStorage &>(gmx::OptionInfo::option());
170 }
171
172 /*
173  * Tests that finish() can set a required option even if the user has not
174  * provided it.
175  */
176 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
177 {
178     gmx::Options                options(NULL, NULL);
179     std::vector<std::string>    values;
180     MockOptionInfo *info = options.addOption(
181             MockOption("name").required().storeVector(&values));
182     MockOptionStorage *mock = &info->option();
183     {
184         ::testing::InSequence dummy;
185         using ::testing::DoAll;
186         using ::testing::InvokeWithoutArgs;
187         EXPECT_CALL(*mock, processAll())
188             .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::markAsSet),
189                             InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
190                             InvokeWithoutArgs(mock, &MockOptionStorage::commitValues)));
191     }
192
193     gmx::OptionsAssigner assigner(&options);
194     EXPECT_NO_THROW(assigner.start());
195     EXPECT_NO_THROW(assigner.finish());
196     EXPECT_NO_THROW(options.finish());
197
198     ASSERT_EQ(1U, values.size());
199     EXPECT_EQ("dummy", values[0]);
200 }
201
202 /*
203  * Tests that storage works if the storage object does not add a value in a
204  * call to appendValue().
205  */
206 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
207 {
208     gmx::Options                options(NULL, NULL);
209     std::vector<std::string>    values;
210     MockOptionInfo *info = options.addOption(
211             MockOption("name").storeVector(&values).multiValue());
212     MockOptionStorage *mock = &info->option();
213     {
214         ::testing::InSequence dummy;
215         using ::testing::ElementsAre;
216         using ::testing::Pointee;
217         using ::testing::Return;
218         EXPECT_CALL(*mock, convertValue("a"));
219         EXPECT_CALL(*mock, convertValue("b"))
220             .WillOnce(Return());
221         EXPECT_CALL(*mock, convertValue("c"));
222         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
223         EXPECT_CALL(*mock, processAll());
224     }
225
226     gmx::OptionsAssigner assigner(&options);
227     EXPECT_NO_THROW(assigner.start());
228     ASSERT_NO_THROW(assigner.startOption("name"));
229     EXPECT_NO_THROW(assigner.appendValue("a"));
230     EXPECT_NO_THROW(assigner.appendValue("b"));
231     EXPECT_NO_THROW(assigner.appendValue("c"));
232     EXPECT_NO_THROW(assigner.finishOption());
233     EXPECT_NO_THROW(assigner.finish());
234     EXPECT_NO_THROW(options.finish());
235
236     ASSERT_EQ(2U, values.size());
237     EXPECT_EQ("a", values[0]);
238     EXPECT_EQ("c", values[1]);
239 }
240
241 /*
242  * Tests that storage works if the storage object adds more than one value in
243  * one call to appendValue().
244  */
245 TEST(AbstractOptionStorageTest, HandlesValueAddition)
246 {
247     gmx::Options                options(NULL, NULL);
248     std::vector<std::string>    values;
249     MockOptionInfo *info = options.addOption(
250             MockOption("name").storeVector(&values).multiValue());
251     MockOptionStorage *mock = &info->option();
252     {
253         ::testing::InSequence dummy;
254         using ::testing::DoAll;
255         using ::testing::ElementsAre;
256         using ::testing::InvokeWithoutArgs;
257         using ::testing::Pointee;
258         EXPECT_CALL(*mock, convertValue("a"));
259         EXPECT_CALL(*mock, convertValue("b"))
260             .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
261                             InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
262         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "dummy", "dummy"))));
263         EXPECT_CALL(*mock, processAll());
264     }
265
266     gmx::OptionsAssigner assigner(&options);
267     EXPECT_NO_THROW(assigner.start());
268     ASSERT_NO_THROW(assigner.startOption("name"));
269     EXPECT_NO_THROW(assigner.appendValue("a"));
270     EXPECT_NO_THROW(assigner.appendValue("b"));
271     EXPECT_NO_THROW(assigner.finishOption());
272     EXPECT_NO_THROW(assigner.finish());
273     EXPECT_NO_THROW(options.finish());
274
275     ASSERT_EQ(3U, values.size());
276     EXPECT_EQ("a", values[0]);
277     EXPECT_EQ("dummy", values[1]);
278     EXPECT_EQ("dummy", values[2]);
279 }
280
281 /*
282  * Tests that storage works if the storage object adds more than one value in
283  * one call to appendValue(), and this results in too many values.
284  */
285 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
286 {
287     gmx::Options                options(NULL, NULL);
288     std::vector<std::string>    values;
289     MockOptionInfo *info = options.addOption(
290             MockOption("name").storeVector(&values).valueCount(2));
291     MockOptionStorage *mock = &info->option();
292     {
293         ::testing::InSequence dummy;
294         using ::testing::DoAll;
295         using ::testing::ElementsAre;
296         using ::testing::InvokeWithoutArgs;
297         using ::testing::Pointee;
298         EXPECT_CALL(*mock, convertValue("a"));
299         EXPECT_CALL(*mock, convertValue("b"))
300             .WillOnce(DoAll(InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue),
301                             InvokeWithoutArgs(mock, &MockOptionStorage::addDummyValue)));
302         EXPECT_CALL(*mock, processAll());
303     }
304
305     gmx::OptionsAssigner assigner(&options);
306     EXPECT_NO_THROW(assigner.start());
307     ASSERT_NO_THROW(assigner.startOption("name"));
308     EXPECT_NO_THROW(assigner.appendValue("a"));
309     EXPECT_THROW(assigner.appendValue("b"), gmx::InvalidInputError);
310     EXPECT_NO_THROW(assigner.finishOption());
311     EXPECT_NO_THROW(assigner.finish());
312     EXPECT_NO_THROW(options.finish());
313
314     ASSERT_TRUE(values.empty());
315 }
316
317 /*
318  * Tests that the storage object is properly invoked even if no output values
319  * should be produced.
320  */
321 TEST(AbstractOptionStorageTest, AllowsEmptyValues)
322 {
323     gmx::Options                options(NULL, NULL);
324     std::vector<std::string>    values;
325     MockOptionInfo *info = options.addOption(
326             MockOption("name").storeVector(&values).valueCount(0));
327     MockOptionStorage *mock = &info->option();
328     {
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"))
335             .WillOnce(Return());
336         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre())));
337         EXPECT_CALL(*mock, processAll());
338     }
339
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());
347
348     ASSERT_EQ(0U, values.size());
349 }
350
351 } // namespace