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