Remove (part of) underscore-prefixed C++ symbols.
[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/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"
53
54 namespace
55 {
56
57 class MockOption;
58
59 /*! \internal \brief
60  * Mock implementation of an option storage class for unit testing.
61  *
62  * Provides facilities for checking that correct methods are called, and for
63  * controlling how they add values using the base class methods.
64  *
65  * \ingroup module_options
66  */
67 class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
68 {
69     public:
70         /*! \brief
71          * Initializes the storage from option settings.
72          *
73          * \param[in] settings   Storage settings.
74          */
75         MockOptionStorage(const MockOption &settings);
76
77         /*! \brief
78          * Calls addValue("dummy") in the base class.
79          */
80         void addDummyValue()
81         {
82             addValue("dummy");
83         }
84         /*! \brief
85          * Calls setFlag(efSet).
86          */
87         void setOption()
88         {
89             setFlag(gmx::efSet);
90         }
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)
95         {
96             MyBase::addValue(value);
97         }
98         void commitValues()
99         {
100             MyBase::commitValues();
101         }
102
103         // These are not used.
104         virtual gmx::OptionInfo &optionInfo()
105         {
106             GMX_THROW(gmx::test::TestException("Not implemented"));
107         }
108         virtual const char *typeString() const { return "mock"; }
109         virtual std::string formatSingleValue(const std::string &/*value*/) const
110         {
111             return "";
112         }
113
114         MOCK_METHOD1(convertValue, void(const std::string &value));
115         MOCK_METHOD1(processSetValues, void(ValueList *values));
116         MOCK_METHOD0(processAll, void());
117 };
118
119 /*! \internal \brief
120  * Specifies an option that has a mock storage object for unit testing.
121  *
122  * \ingroup module_options
123  */
124 class MockOption : public gmx::OptionTemplate<std::string, MockOption>
125 {
126     public:
127         //! Initializes an option with the given name.
128         explicit MockOption(const char *name)
129             : MyBase(name), storagePtr_(NULL)
130         {
131         }
132
133         //! Sets an output pointer to give access to the created storage object.
134         MyClass &storageObject(MockOptionStorage **storagePtr)
135         { storagePtr_ = storagePtr; return me(); }
136
137     private:
138         virtual gmx::AbstractOptionStoragePointer createStorage() const
139         {
140             MockOptionStorage *storage = new MockOptionStorage(*this);
141             if (storagePtr_ != NULL)
142             {
143                 *storagePtr_ = storage;
144             }
145             return gmx::AbstractOptionStoragePointer(storage);
146         }
147
148         MockOptionStorage     **storagePtr_;
149 };
150
151 MockOptionStorage::MockOptionStorage(const MockOption &settings)
152     : MyBase(settings)
153 {
154     using ::testing::_;
155     using ::testing::Invoke;
156     using ::testing::WithArg;
157     ON_CALL(*this, convertValue(_))
158         .WillByDefault(WithArg<0>(Invoke(this, &MockOptionStorage::addValue)));
159 }
160
161 /*
162  * Tests that finish() can set a required option even if the user has not
163  * provided it.
164  */
165 TEST(AbstractOptionStorageTest, HandlesSetInFinish)
166 {
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);
174     {
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)));
182     }
183
184     gmx::OptionsAssigner assigner(&options);
185     EXPECT_NO_THROW(assigner.start());
186     EXPECT_NO_THROW(assigner.finish());
187     EXPECT_NO_THROW(options.finish());
188
189     ASSERT_EQ(1U, values.size());
190     EXPECT_EQ("dummy", values[0]);
191 }
192
193 /*
194  * Tests that storage works if the storage object does not add a value in a
195  * call to appendValue().
196  */
197 TEST(AbstractOptionStorageTest, HandlesValueRemoval)
198 {
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()));
205
206     {
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"))
213             .WillOnce(Return());
214         EXPECT_CALL(*mock, convertValue("c"));
215         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre("a", "c"))));
216         EXPECT_CALL(*mock, processAll());
217     }
218
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());
228
229     ASSERT_EQ(2U, values.size());
230     EXPECT_EQ("a", values[0]);
231     EXPECT_EQ("c", values[1]);
232 }
233
234 /*
235  * Tests that storage works if the storage object adds more than one value in
236  * one call to appendValue().
237  */
238 TEST(AbstractOptionStorageTest, HandlesValueAddition)
239 {
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);
247     {
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());
259     }
260
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());
269
270     ASSERT_EQ(3U, values.size());
271     EXPECT_EQ("a", values[0]);
272     EXPECT_EQ("dummy", values[1]);
273     EXPECT_EQ("dummy", values[2]);
274 }
275
276 /*
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.
279  */
280 TEST(AbstractOptionStorageTest, HandlesTooManyValueAddition)
281 {
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);
289     {
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());
301     }
302
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());
311
312     ASSERT_EQ(2U, values.size());
313     EXPECT_EQ("a", values[0]);
314     EXPECT_EQ("dummy", values[1]);
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     MockOptionStorage          *mock;
326     ASSERT_NO_THROW(options.addOption(
327                         MockOption("name").storageObject(&mock)
328                             .storeVector(&values).valueCount(0)));
329
330     {
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"))
337             .WillOnce(Return());
338         EXPECT_CALL(*mock, processSetValues(Pointee(ElementsAre())));
339         EXPECT_CALL(*mock, processAll());
340     }
341
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());
349
350     ASSERT_EQ(0U, values.size());
351 }
352
353 } // namespace