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