Merge branch release-5-1
[alexxy/gromacs.git] / src / gromacs / options / tests / optionsassigner.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2010,2011,2012,2013,2014,2015, 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 option assignment.
38  *
39  * In addition to testing gmx::OptionsAssigner, these are the main
40  * tests for the classes from basicoptions.h and basicoptionstorage.h (and
41  * their base classes) that actually implement the behavior, as well as for the
42  * internal implementation of the gmx::Options and gmx::AbstractOptionStorage
43  * classes.
44  *
45  * \author Teemu Murtola <teemu.murtola@gmail.com>
46  * \ingroup module_options
47  */
48 #include "gmxpre.h"
49
50 #include "gromacs/options/optionsassigner.h"
51
52 #include <limits>
53 #include <vector>
54
55 #include <gtest/gtest.h>
56
57 #include "gromacs/options/basicoptions.h"
58 #include "gromacs/options/options.h"
59 #include "gromacs/utility/exceptions.h"
60 #include "gromacs/utility/stringutil.h"
61
62 #include "testutils/testasserts.h"
63
64 namespace
65 {
66
67 /********************************************************************
68  * General assignment tests
69  */
70
71 TEST(OptionsAssignerTest, HandlesMissingRequiredParameter)
72 {
73     gmx::Options options(NULL, NULL);
74     int          value = 0;
75     using gmx::IntegerOption;
76     ASSERT_NO_THROW(options.addOption(
77                             IntegerOption("p").store(&value).required()));
78
79     EXPECT_THROW(options.finish(), gmx::InvalidInputError);
80 }
81
82 TEST(OptionsAssignerTest, HandlesRequiredParameterWithDefaultValue)
83 {
84     gmx::Options options(NULL, NULL);
85     int          value = 0;
86     using gmx::IntegerOption;
87     ASSERT_NO_THROW(options.addOption(
88                             IntegerOption("p").store(&value).required()
89                                 .defaultValue(1)));
90
91     EXPECT_NO_THROW(options.finish());
92     EXPECT_EQ(1, value);
93 }
94
95 TEST(OptionsAssignerTest, HandlesInvalidMultipleParameter)
96 {
97     gmx::Options     options(NULL, NULL);
98     std::vector<int> values;
99     bool             bIsSet;
100     using gmx::IntegerOption;
101     ASSERT_NO_THROW(options.addOption(
102                             IntegerOption("p")
103                                 .storeVector(&values).storeIsSet(&bIsSet)
104                                 .multiValue()));
105
106     gmx::OptionsAssigner assigner(&options);
107     EXPECT_NO_THROW(assigner.start());
108     ASSERT_NO_THROW(assigner.startOption("p"));
109     ASSERT_NO_THROW(assigner.appendValue("1"));
110     ASSERT_NO_THROW(assigner.finishOption());
111     EXPECT_THROW(assigner.startOption("p"), gmx::InvalidInputError);
112     EXPECT_NO_THROW(assigner.finish());
113     EXPECT_NO_THROW(options.finish());
114
115     EXPECT_TRUE(bIsSet);
116     ASSERT_EQ(1U, values.size());
117     EXPECT_EQ(1, values[0]);
118 }
119
120 TEST(OptionsAssignerTest, HandlesMultipleParameter)
121 {
122     gmx::Options     options(NULL, NULL);
123     std::vector<int> values;
124     bool             bIsSet;
125     using gmx::IntegerOption;
126     ASSERT_NO_THROW(options.addOption(
127                             IntegerOption("p")
128                                 .storeVector(&values).storeIsSet(&bIsSet)
129                                 .allowMultiple()));
130
131     gmx::OptionsAssigner assigner(&options);
132     EXPECT_NO_THROW_GMX(assigner.start());
133     ASSERT_NO_THROW_GMX(assigner.startOption("p"));
134     ASSERT_NO_THROW_GMX(assigner.appendValue("1"));
135     EXPECT_NO_THROW_GMX(assigner.finishOption());
136     ASSERT_NO_THROW_GMX(assigner.startOption("p"));
137     ASSERT_NO_THROW_GMX(assigner.appendValue("2"));
138     EXPECT_NO_THROW_GMX(assigner.finishOption());
139     EXPECT_NO_THROW_GMX(assigner.finish());
140     EXPECT_NO_THROW_GMX(options.finish());
141
142     EXPECT_TRUE(bIsSet);
143     ASSERT_EQ(2U, values.size());
144     EXPECT_EQ(1, values[0]);
145     EXPECT_EQ(2, values[1]);
146 }
147
148 TEST(OptionsAssignerTest, HandlesMissingValue)
149 {
150     gmx::Options options(NULL, NULL);
151     int          value1 = 0, value2 = 0;
152     using gmx::IntegerOption;
153     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value1)));
154     ASSERT_NO_THROW(options.addOption(IntegerOption("q").store(&value2)));
155
156     gmx::OptionsAssigner assigner(&options);
157     EXPECT_NO_THROW(assigner.start());
158     ASSERT_NO_THROW(assigner.startOption("p"));
159     EXPECT_THROW(assigner.finishOption(), gmx::InvalidInputError);
160     ASSERT_NO_THROW(assigner.startOption("q"));
161     ASSERT_NO_THROW(assigner.appendValue("2"));
162     EXPECT_NO_THROW(assigner.finishOption());
163     EXPECT_NO_THROW(assigner.finish());
164     EXPECT_NO_THROW(options.finish());
165
166     EXPECT_EQ(2, value2);
167 }
168
169 TEST(OptionsAssignerTest, HandlesExtraValue)
170 {
171     gmx::Options options(NULL, NULL);
172     int          value1 = 0;
173     using gmx::IntegerOption;
174     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value1)));
175
176     gmx::OptionsAssigner assigner(&options);
177     EXPECT_NO_THROW(assigner.start());
178     ASSERT_NO_THROW(assigner.startOption("p"));
179     ASSERT_NO_THROW(assigner.appendValue("2"));
180     EXPECT_THROW(assigner.appendValue("3"), gmx::InvalidInputError);
181     EXPECT_NO_THROW(assigner.finishOption());
182     EXPECT_NO_THROW(assigner.finish());
183     EXPECT_NO_THROW(options.finish());
184
185     EXPECT_EQ(0, value1);
186 }
187
188 TEST(OptionsAssignerTest, HandlesGroups)
189 {
190     gmx::Options            options(NULL, NULL);
191     gmx::IOptionsContainer &group1 = options.addGroup();
192     gmx::IOptionsContainer &group2 = options.addGroup();
193     int                     value  = 3;
194     int                     value1 = 1;
195     int                     value2 = 2;
196     using gmx::IntegerOption;
197     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
198     ASSERT_NO_THROW(group1.addOption(IntegerOption("q").store(&value1)));
199     ASSERT_NO_THROW(group2.addOption(IntegerOption("r").store(&value2)));
200
201     gmx::OptionsAssigner assigner(&options);
202     EXPECT_NO_THROW(assigner.start());
203     ASSERT_NO_THROW(assigner.startOption("p"));
204     EXPECT_NO_THROW(assigner.appendValue("5"));
205     EXPECT_NO_THROW(assigner.finishOption());
206     ASSERT_NO_THROW(assigner.startOption("q"));
207     EXPECT_NO_THROW(assigner.appendValue("4"));
208     EXPECT_NO_THROW(assigner.finishOption());
209     ASSERT_NO_THROW(assigner.startOption("r"));
210     EXPECT_NO_THROW(assigner.appendValue("6"));
211     EXPECT_NO_THROW(assigner.finishOption());
212     EXPECT_NO_THROW(assigner.finish());
213     EXPECT_NO_THROW(options.finish());
214
215     EXPECT_EQ(5, value);
216     EXPECT_EQ(4, value1);
217     EXPECT_EQ(6, value2);
218 }
219
220 TEST(OptionsAssignerTest, HandlesSubSections)
221 {
222     gmx::Options options(NULL, NULL);
223     gmx::Options sub1("section1", NULL);
224     gmx::Options sub2("section2", NULL);
225     int          value  = 3;
226     int          value1 = 1;
227     int          value2 = 2;
228     using gmx::IntegerOption;
229     ASSERT_NO_THROW(options.addSubSection(&sub1));
230     ASSERT_NO_THROW(options.addSubSection(&sub2));
231     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
232     ASSERT_NO_THROW(sub1.addOption(IntegerOption("p").store(&value1)));
233     ASSERT_NO_THROW(sub2.addOption(IntegerOption("p").store(&value2)));
234
235     gmx::OptionsAssigner assigner(&options);
236     EXPECT_NO_THROW(assigner.start());
237     ASSERT_NO_THROW(assigner.startSubSection("section1"));
238     ASSERT_NO_THROW(assigner.startOption("p"));
239     EXPECT_NO_THROW(assigner.appendValue("5"));
240     EXPECT_NO_THROW(assigner.finishOption());
241     EXPECT_NO_THROW(assigner.finishSubSection());
242     ASSERT_NO_THROW(assigner.startOption("p"));
243     EXPECT_NO_THROW(assigner.appendValue("4"));
244     EXPECT_NO_THROW(assigner.finishOption());
245     ASSERT_NO_THROW(assigner.startSubSection("section2"));
246     ASSERT_NO_THROW(assigner.startOption("p"));
247     EXPECT_NO_THROW(assigner.appendValue("6"));
248     EXPECT_NO_THROW(assigner.finishOption());
249     EXPECT_NO_THROW(assigner.finishSubSection());
250     EXPECT_NO_THROW(assigner.finish());
251     EXPECT_NO_THROW(options.finish());
252
253     EXPECT_EQ(4, value);
254     EXPECT_EQ(5, value1);
255     EXPECT_EQ(6, value2);
256 }
257
258 TEST(OptionsAssignerTest, HandlesMultipleSources)
259 {
260     gmx::Options options(NULL, NULL);
261     int          value = -1;
262     using gmx::IntegerOption;
263     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
264
265     {
266         gmx::OptionsAssigner assigner(&options);
267         EXPECT_NO_THROW(assigner.start());
268         ASSERT_NO_THROW(assigner.startOption("p"));
269         EXPECT_NO_THROW(assigner.appendValue("1"));
270         EXPECT_NO_THROW(assigner.finishOption());
271         EXPECT_NO_THROW(assigner.finish());
272     }
273     {
274         gmx::OptionsAssigner assigner2(&options);
275         EXPECT_NO_THROW(assigner2.start());
276         ASSERT_NO_THROW(assigner2.startOption("p"));
277         EXPECT_NO_THROW(assigner2.appendValue("2"));
278         EXPECT_NO_THROW(assigner2.finishOption());
279         EXPECT_NO_THROW(assigner2.finish());
280     }
281     EXPECT_NO_THROW(options.finish());
282
283     EXPECT_EQ(2, value);
284 }
285
286
287 /********************************************************************
288  * Tests for boolean assignment
289  */
290
291 TEST(OptionsAssignerBooleanTest, StoresYesValue)
292 {
293     gmx::Options options(NULL, NULL);
294     bool         value = false;
295     using gmx::BooleanOption;
296     ASSERT_NO_THROW(options.addOption(BooleanOption("p").store(&value)));
297
298     gmx::OptionsAssigner assigner(&options);
299     EXPECT_NO_THROW(assigner.start());
300     ASSERT_NO_THROW(assigner.startOption("p"));
301     EXPECT_NO_THROW(assigner.appendValue("yes"));
302     EXPECT_NO_THROW(assigner.finishOption());
303     EXPECT_NO_THROW(assigner.finish());
304     EXPECT_NO_THROW(options.finish());
305
306     EXPECT_TRUE(value);
307 }
308
309 TEST(OptionsAssignerBooleanTest, SetsBooleanWithoutExplicitValue)
310 {
311     gmx::Options options(NULL, NULL);
312     bool         value = false;
313     using gmx::BooleanOption;
314     ASSERT_NO_THROW(options.addOption(BooleanOption("p").store(&value)));
315
316     gmx::OptionsAssigner assigner(&options);
317     EXPECT_NO_THROW(assigner.start());
318     ASSERT_NO_THROW(assigner.startOption("p"));
319     EXPECT_NO_THROW(assigner.finishOption());
320     EXPECT_NO_THROW(assigner.finish());
321     EXPECT_NO_THROW(options.finish());
322
323     EXPECT_TRUE(value);
324 }
325
326 TEST(OptionsAssignerBooleanTest, ClearsBooleanWithPrefixNo)
327 {
328     gmx::Options options(NULL, NULL);
329     bool         value = true;
330     using gmx::BooleanOption;
331     ASSERT_NO_THROW(options.addOption(BooleanOption("p").store(&value)));
332
333     gmx::OptionsAssigner assigner(&options);
334     assigner.setAcceptBooleanNoPrefix(true);
335     EXPECT_NO_THROW(assigner.start());
336     ASSERT_NO_THROW(assigner.startOption("nop"));
337     EXPECT_NO_THROW(assigner.finishOption());
338     EXPECT_NO_THROW(assigner.finish());
339     EXPECT_NO_THROW(options.finish());
340
341     EXPECT_FALSE(value);
342 }
343
344 TEST(OptionsAssignerBooleanTest, HandlesBooleanWithPrefixAndValue)
345 {
346     gmx::Options options(NULL, NULL);
347     bool         value = false;
348     using gmx::BooleanOption;
349     ASSERT_NO_THROW(options.addOption(BooleanOption("p").store(&value)));
350
351     gmx::OptionsAssigner assigner(&options);
352     assigner.setAcceptBooleanNoPrefix(true);
353     EXPECT_NO_THROW(assigner.start());
354     ASSERT_NO_THROW(assigner.startOption("nop"));
355     // It's OK to fail, but if it doesn't, it should work.
356     try
357     {
358         assigner.appendValue("no");
359         assigner.finishOption();
360         EXPECT_NO_THROW(assigner.finish());
361         EXPECT_TRUE(value);
362     }
363     catch (const gmx::InvalidInputError &)
364     {
365     }
366 }
367
368
369 /********************************************************************
370  * Tests for integer assignment
371  *
372  * These tests also contain tests for general default value handling.
373  */
374
375 TEST(OptionsAssignerIntegerTest, StoresSingleValue)
376 {
377     gmx::Options options(NULL, NULL);
378     int          value = 1;
379     using gmx::IntegerOption;
380     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
381
382     gmx::OptionsAssigner assigner(&options);
383     EXPECT_NO_THROW(assigner.start());
384     ASSERT_NO_THROW(assigner.startOption("p"));
385     ASSERT_NO_THROW(assigner.appendValue("3"));
386     EXPECT_NO_THROW(assigner.finishOption());
387     EXPECT_NO_THROW(assigner.finish());
388     EXPECT_NO_THROW(options.finish());
389
390     EXPECT_EQ(3, value);
391 }
392
393 TEST(OptionsAssignerIntegerTest, HandlesEmptyValue)
394 {
395     gmx::Options options(NULL, NULL);
396     int          value = 1;
397     using gmx::IntegerOption;
398     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
399
400     gmx::OptionsAssigner assigner(&options);
401     EXPECT_NO_THROW(assigner.start());
402     ASSERT_NO_THROW(assigner.startOption("p"));
403     EXPECT_THROW(assigner.appendValue(""), gmx::InvalidInputError);
404     EXPECT_NO_THROW(assigner.finishOption());
405     EXPECT_NO_THROW(assigner.finish());
406     EXPECT_NO_THROW(options.finish());
407
408     EXPECT_EQ(1, value);
409 }
410
411 TEST(OptionsAssignerIntegerTest, HandlesInvalidValue)
412 {
413     gmx::Options options(NULL, NULL);
414     int          value = 1;
415     using gmx::IntegerOption;
416     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
417
418     gmx::OptionsAssigner assigner(&options);
419     EXPECT_NO_THROW(assigner.start());
420     ASSERT_NO_THROW(assigner.startOption("p"));
421     EXPECT_THROW(assigner.appendValue("2abc"), gmx::InvalidInputError);
422     EXPECT_NO_THROW(assigner.finishOption());
423     EXPECT_NO_THROW(assigner.finish());
424     EXPECT_NO_THROW(options.finish());
425
426     EXPECT_EQ(1, value);
427 }
428
429 TEST(OptionsAssignerIntegerTest, HandlesOverflow)
430 {
431     gmx::Options options(NULL, NULL);
432     int          value = 1;
433     using gmx::IntegerOption;
434     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
435
436     gmx::OptionsAssigner assigner(&options);
437     EXPECT_NO_THROW(assigner.start());
438     ASSERT_NO_THROW(assigner.startOption("p"));
439     std::string overflowValue(
440             gmx::formatString("%d0000", std::numeric_limits<int>::max()));
441     EXPECT_THROW(assigner.appendValue(overflowValue), gmx::InvalidInputError);
442     EXPECT_NO_THROW(assigner.finishOption());
443     EXPECT_NO_THROW(assigner.finish());
444     EXPECT_NO_THROW(options.finish());
445
446     EXPECT_EQ(1, value);
447 }
448
449 TEST(OptionsAssignerIntegerTest, StoresDefaultValue)
450 {
451     gmx::Options options(NULL, NULL);
452     int          value = -1;
453     using gmx::IntegerOption;
454     ASSERT_NO_THROW(options.addOption(
455                             IntegerOption("p").store(&value).defaultValue(2)));
456     EXPECT_EQ(2, value);
457
458     gmx::OptionsAssigner assigner(&options);
459     EXPECT_NO_THROW(assigner.start());
460     EXPECT_NO_THROW(assigner.finish());
461     EXPECT_NO_THROW(options.finish());
462
463     EXPECT_EQ(2, value);
464 }
465
466 TEST(OptionsAssignerIntegerTest, StoresDefaultValueIfSet)
467 {
468     gmx::Options options(NULL, NULL);
469     int          value = -1;
470     using gmx::IntegerOption;
471     ASSERT_NO_THROW(options.addOption(
472                             IntegerOption("p").store(&value).defaultValueIfSet(2)));
473     EXPECT_EQ(-1, value);
474
475     gmx::OptionsAssigner assigner(&options);
476     EXPECT_NO_THROW(assigner.start());
477     ASSERT_NO_THROW(assigner.startOption("p"));
478     EXPECT_NO_THROW(assigner.finishOption());
479     EXPECT_NO_THROW(assigner.finish());
480     EXPECT_NO_THROW(options.finish());
481
482     EXPECT_EQ(2, value);
483 }
484
485 TEST(OptionsAssignerIntegerTest, HandlesDefaultValueIfSetWhenNotSet)
486 {
487     gmx::Options options(NULL, NULL);
488     int          value = -1;
489     using gmx::IntegerOption;
490     ASSERT_NO_THROW(options.addOption(
491                             IntegerOption("p").store(&value).defaultValueIfSet(2)));
492     EXPECT_EQ(-1, value);
493
494     gmx::OptionsAssigner assigner(&options);
495     EXPECT_NO_THROW(assigner.start());
496     EXPECT_NO_THROW(assigner.finish());
497     EXPECT_NO_THROW(options.finish());
498
499     EXPECT_EQ(-1, value);
500 }
501
502 TEST(OptionsAssignerIntegerTest, HandlesBothDefaultValues)
503 {
504     gmx::Options options(NULL, NULL);
505     int          value = -1;
506     using gmx::IntegerOption;
507     ASSERT_NO_THROW(options.addOption(
508                             IntegerOption("p").store(&value)
509                                 .defaultValue(1).defaultValueIfSet(2)));
510     EXPECT_EQ(1, value);
511
512     gmx::OptionsAssigner assigner(&options);
513     EXPECT_NO_THROW(assigner.start());
514     ASSERT_NO_THROW(assigner.startOption("p"));
515     EXPECT_NO_THROW(assigner.finishOption());
516     EXPECT_NO_THROW(assigner.finish());
517     EXPECT_NO_THROW(options.finish());
518
519     EXPECT_EQ(2, value);
520 }
521
522 TEST(OptionsAssignerIntegerTest, StoresToVector)
523 {
524     gmx::Options          options(NULL, NULL);
525     std::vector<int>      values;
526     using gmx::IntegerOption;
527     ASSERT_NO_THROW(options.addOption(
528                             IntegerOption("p").storeVector(&values).multiValue()));
529
530     gmx::OptionsAssigner assigner(&options);
531     EXPECT_NO_THROW(assigner.start());
532     ASSERT_NO_THROW(assigner.startOption("p"));
533     ASSERT_NO_THROW(assigner.appendValue("-2"));
534     ASSERT_NO_THROW(assigner.appendValue("1"));
535     ASSERT_NO_THROW(assigner.appendValue("4"));
536     EXPECT_NO_THROW(assigner.finishOption());
537     EXPECT_NO_THROW(assigner.finish());
538     EXPECT_NO_THROW(options.finish());
539
540     EXPECT_EQ(3U, values.size());
541     EXPECT_EQ(-2, values[0]);
542     EXPECT_EQ(1, values[1]);
543     EXPECT_EQ(4, values[2]);
544 }
545
546 TEST(OptionsAssignerIntegerTest, HandlesVectors)
547 {
548     gmx::Options options(NULL, NULL);
549     int          vec[3] = {0, 0, 0};
550     using gmx::IntegerOption;
551     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(vec).vector()));
552
553     gmx::OptionsAssigner assigner(&options);
554     EXPECT_NO_THROW(assigner.start());
555     ASSERT_NO_THROW(assigner.startOption("p"));
556     ASSERT_NO_THROW(assigner.appendValue("-2"));
557     ASSERT_NO_THROW(assigner.appendValue("1"));
558     ASSERT_NO_THROW(assigner.appendValue("4"));
559     EXPECT_NO_THROW(assigner.finishOption());
560     EXPECT_NO_THROW(assigner.finish());
561     EXPECT_NO_THROW(options.finish());
562
563     EXPECT_EQ(-2, vec[0]);
564     EXPECT_EQ(1, vec[1]);
565     EXPECT_EQ(4, vec[2]);
566 }
567
568 TEST(OptionsAssignerIntegerTest, HandlesVectorFromSingleValue)
569 {
570     gmx::Options options(NULL, NULL);
571     int          vec[3] = {0, 0, 0};
572     using gmx::IntegerOption;
573     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(vec).vector()));
574
575     gmx::OptionsAssigner assigner(&options);
576     EXPECT_NO_THROW(assigner.start());
577     ASSERT_NO_THROW(assigner.startOption("p"));
578     ASSERT_NO_THROW(assigner.appendValue("2"));
579     EXPECT_NO_THROW(assigner.finishOption());
580     EXPECT_NO_THROW(assigner.finish());
581     EXPECT_NO_THROW(options.finish());
582
583     EXPECT_EQ(2, vec[0]);
584     EXPECT_EQ(2, vec[1]);
585     EXPECT_EQ(2, vec[2]);
586 }
587
588 TEST(OptionsAssignerIntegerTest, HandlesVectorsWithDefaultValue)
589 {
590     gmx::Options options(NULL, NULL);
591     int          vec[3] = {3, 2, 1};
592     using gmx::IntegerOption;
593     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(vec).vector()));
594
595     EXPECT_NO_THROW(options.finish());
596
597     EXPECT_EQ(3, vec[0]);
598     EXPECT_EQ(2, vec[1]);
599     EXPECT_EQ(1, vec[2]);
600 }
601
602 TEST(OptionsAssignerIntegerTest, HandlesVectorsWithDefaultValueWithInvalidAssignment)
603 {
604     gmx::Options     options(NULL, NULL);
605     int              vec[3] = {3, 2, 1};
606     std::vector<int> vec2(vec, vec+3);
607     using gmx::IntegerOption;
608     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(vec)
609                                           .storeVector(&vec2).vector()));
610
611     gmx::OptionsAssigner assigner(&options);
612     EXPECT_NO_THROW(assigner.start());
613     ASSERT_NO_THROW(assigner.startOption("p"));
614     EXPECT_NO_THROW(assigner.appendValue("1"));
615     EXPECT_NO_THROW(assigner.appendValue("3"));
616     EXPECT_THROW(assigner.finishOption(), gmx::InvalidInputError);
617     EXPECT_NO_THROW(assigner.finish());
618     EXPECT_NO_THROW(options.finish());
619
620     EXPECT_EQ(3, vec[0]);
621     EXPECT_EQ(2, vec[1]);
622     EXPECT_EQ(1, vec[2]);
623     ASSERT_EQ(3U, vec2.size());
624     EXPECT_EQ(3, vec2[0]);
625     EXPECT_EQ(2, vec2[1]);
626     EXPECT_EQ(1, vec2[2]);
627 }
628
629
630 /********************************************************************
631  * Tests for double assignment
632  */
633
634 TEST(OptionsAssignerDoubleTest, StoresSingleValue)
635 {
636     gmx::Options options(NULL, NULL);
637     double       value = 0.0;
638     using gmx::DoubleOption;
639     ASSERT_NO_THROW(options.addOption(DoubleOption("p").store(&value)));
640
641     gmx::OptionsAssigner assigner(&options);
642     EXPECT_NO_THROW(assigner.start());
643     ASSERT_NO_THROW(assigner.startOption("p"));
644     ASSERT_NO_THROW(assigner.appendValue("2.7"));
645     EXPECT_NO_THROW(assigner.finishOption());
646     EXPECT_NO_THROW(assigner.finish());
647     EXPECT_NO_THROW(options.finish());
648
649     EXPECT_DOUBLE_EQ(2.7, value);
650 }
651
652 TEST(OptionsAssignerDoubleTest, HandlesEmptyValue)
653 {
654     gmx::Options options(NULL, NULL);
655     double       value = 1.0;
656     using gmx::DoubleOption;
657     ASSERT_NO_THROW(options.addOption(DoubleOption("p").store(&value)));
658
659     gmx::OptionsAssigner assigner(&options);
660     EXPECT_NO_THROW(assigner.start());
661     ASSERT_NO_THROW(assigner.startOption("p"));
662     EXPECT_THROW(assigner.appendValue(""), gmx::InvalidInputError);
663     EXPECT_NO_THROW(assigner.finishOption());
664     EXPECT_NO_THROW(assigner.finish());
665     EXPECT_NO_THROW(options.finish());
666
667     EXPECT_DOUBLE_EQ(1.0, value);
668 }
669
670
671 /********************************************************************
672  * Tests for string assignment
673  */
674
675 TEST(OptionsAssignerStringTest, StoresSingleValue)
676 {
677     gmx::Options           options(NULL, NULL);
678     std::string            value;
679     using gmx::StringOption;
680     ASSERT_NO_THROW(options.addOption(StringOption("p").store(&value)));
681
682     gmx::OptionsAssigner assigner(&options);
683     EXPECT_NO_THROW(assigner.start());
684     ASSERT_NO_THROW(assigner.startOption("p"));
685     ASSERT_NO_THROW(assigner.appendValue("value"));
686     EXPECT_NO_THROW(assigner.finishOption());
687     EXPECT_NO_THROW(assigner.finish());
688     EXPECT_NO_THROW(options.finish());
689
690     EXPECT_EQ("value", value);
691 }
692
693 TEST(OptionsAssignerStringTest, HandlesEnumValue)
694 {
695     gmx::Options           options(NULL, NULL);
696     std::string            value;
697     const char * const     allowed[] = { "none", "test", "value" };
698     int                    index     = -1;
699     using gmx::StringOption;
700     ASSERT_NO_THROW(options.addOption(
701                             StringOption("p").store(&value)
702                                 .enumValue(allowed).storeEnumIndex(&index)));
703
704     gmx::OptionsAssigner assigner(&options);
705     EXPECT_NO_THROW(assigner.start());
706     ASSERT_NO_THROW(assigner.startOption("p"));
707     ASSERT_NO_THROW(assigner.appendValue("test"));
708     EXPECT_NO_THROW(assigner.finishOption());
709     EXPECT_NO_THROW(assigner.finish());
710     EXPECT_NO_THROW(options.finish());
711
712     EXPECT_EQ("test", value);
713     EXPECT_EQ(1, index);
714 }
715
716 TEST(OptionsAssignerStringTest, HandlesEnumValueFromNullTerminatedArray)
717 {
718     gmx::Options           options(NULL, NULL);
719     std::string            value;
720     const char * const     allowed[] = { "none", "test", "value", NULL };
721     int                    index     = -1;
722     using gmx::StringOption;
723     ASSERT_NO_THROW(options.addOption(
724                             StringOption("p").store(&value)
725                                 .enumValueFromNullTerminatedArray(allowed)
726                                 .storeEnumIndex(&index)));
727
728     gmx::OptionsAssigner assigner(&options);
729     EXPECT_NO_THROW(assigner.start());
730     ASSERT_NO_THROW(assigner.startOption("p"));
731     ASSERT_NO_THROW(assigner.appendValue("value"));
732     EXPECT_NO_THROW(assigner.finishOption());
733     EXPECT_NO_THROW(assigner.finish());
734     EXPECT_NO_THROW(options.finish());
735
736     EXPECT_EQ("value", value);
737     EXPECT_EQ(2, index);
738 }
739
740 TEST(OptionsAssignerStringTest, HandlesIncorrectEnumValue)
741 {
742     gmx::Options           options(NULL, NULL);
743     std::string            value;
744     const char * const     allowed[] = { "none", "test", "value" };
745     int                    index     = -1;
746     using gmx::StringOption;
747     ASSERT_NO_THROW(options.addOption(
748                             StringOption("p").store(&value)
749                                 .enumValue(allowed).storeEnumIndex(&index)));
750
751     gmx::OptionsAssigner assigner(&options);
752     EXPECT_NO_THROW(assigner.start());
753     ASSERT_NO_THROW(assigner.startOption("p"));
754     ASSERT_THROW(assigner.appendValue("unknown"), gmx::InvalidInputError);
755 }
756
757 TEST(OptionsAssignerStringTest, CompletesEnumValue)
758 {
759     gmx::Options           options(NULL, NULL);
760     std::string            value;
761     const char * const     allowed[] = { "none", "test", "value" };
762     int                    index     = -1;
763     using gmx::StringOption;
764     ASSERT_NO_THROW(options.addOption(
765                             StringOption("p").store(&value)
766                                 .enumValue(allowed).storeEnumIndex(&index)));
767
768     gmx::OptionsAssigner assigner(&options);
769     EXPECT_NO_THROW(assigner.start());
770     ASSERT_NO_THROW(assigner.startOption("p"));
771     ASSERT_NO_THROW(assigner.appendValue("te"));
772     EXPECT_NO_THROW(assigner.finishOption());
773     EXPECT_NO_THROW(assigner.finish());
774     EXPECT_NO_THROW(options.finish());
775
776     EXPECT_EQ("test", value);
777     EXPECT_EQ(1, index);
778 }
779
780 TEST(OptionsAssignerStringTest, HandlesEnumWithNoValue)
781 {
782     gmx::Options           options(NULL, NULL);
783     std::string            value;
784     const char * const     allowed[] = { "none", "test", "value" };
785     int                    index     = -3;
786     using gmx::StringOption;
787     ASSERT_NO_THROW(options.addOption(
788                             StringOption("p").store(&value)
789                                 .enumValue(allowed).storeEnumIndex(&index)));
790     EXPECT_TRUE(value.empty());
791     EXPECT_EQ(-1, index);
792
793     ASSERT_NO_THROW(options.finish());
794
795     EXPECT_TRUE(value.empty());
796     EXPECT_EQ(-1, index);
797 }
798
799 TEST(OptionsAssignerStringTest, HandlesEnumDefaultValue)
800 {
801     gmx::Options           options(NULL, NULL);
802     std::string            value;
803     const char * const     allowed[] = { "none", "test", "value" };
804     int                    index     = -1;
805     using gmx::StringOption;
806     ASSERT_NO_THROW(options.addOption(
807                             StringOption("p").store(&value)
808                                 .enumValue(allowed).defaultValue("test")
809                                 .storeEnumIndex(&index)));
810     EXPECT_EQ("test", value);
811     EXPECT_EQ(1, index);
812
813     gmx::OptionsAssigner assigner(&options);
814     EXPECT_NO_THROW(assigner.start());
815     EXPECT_NO_THROW(assigner.finish());
816     EXPECT_NO_THROW(options.finish());
817
818     EXPECT_EQ("test", value);
819     EXPECT_EQ(1, index);
820 }
821
822 TEST(OptionsAssignerStringTest, HandlesEnumDefaultValueFromVariable)
823 {
824     gmx::Options           options(NULL, NULL);
825     std::string            value("test");
826     const char * const     allowed[] = { "none", "test", "value" };
827     int                    index     = -1;
828     using gmx::StringOption;
829     ASSERT_NO_THROW(options.addOption(
830                             StringOption("p").store(&value)
831                                 .enumValue(allowed).storeEnumIndex(&index)));
832     EXPECT_EQ("test", value);
833     EXPECT_EQ(1, index);
834
835     gmx::OptionsAssigner assigner(&options);
836     EXPECT_NO_THROW(assigner.start());
837     EXPECT_NO_THROW(assigner.finish());
838     EXPECT_NO_THROW(options.finish());
839
840     EXPECT_EQ("test", value);
841     EXPECT_EQ(1, index);
842 }
843
844 TEST(OptionsAssignerStringTest, HandlesEnumDefaultValueFromVector)
845 {
846     gmx::Options             options(NULL, NULL);
847     std::vector<std::string> value;
848     value.push_back("test");
849     value.push_back("value");
850     const char * const       allowed[] = { "none", "test", "value" };
851     int                      index[2]  = {-1, -1};
852     using gmx::StringOption;
853     ASSERT_NO_THROW(options.addOption(
854                             StringOption("p").storeVector(&value).valueCount(2)
855                                 .enumValue(allowed).storeEnumIndex(index)));
856     EXPECT_EQ("test", value[0]);
857     EXPECT_EQ("value", value[1]);
858     EXPECT_EQ(1, index[0]);
859     EXPECT_EQ(2, index[1]);
860
861     gmx::OptionsAssigner assigner(&options);
862     EXPECT_NO_THROW(assigner.start());
863     EXPECT_NO_THROW(assigner.finish());
864     EXPECT_NO_THROW(options.finish());
865
866     EXPECT_EQ("test", value[0]);
867     EXPECT_EQ("value", value[1]);
868     EXPECT_EQ(1, index[0]);
869     EXPECT_EQ(2, index[1]);
870 }
871
872 TEST(OptionsAssignerStringTest, HandlesEnumDefaultIndex)
873 {
874     gmx::Options           options(NULL, NULL);
875     std::string            value;
876     const char * const     allowed[] = { "none", "test", "value" };
877     int                    index     = -1;
878     using gmx::StringOption;
879     ASSERT_NO_THROW(options.addOption(
880                             StringOption("p").store(&value)
881                                 .enumValue(allowed).defaultEnumIndex(1)
882                                 .storeEnumIndex(&index)));
883     EXPECT_EQ("test", value);
884     EXPECT_EQ(1, index);
885
886     gmx::OptionsAssigner assigner(&options);
887     EXPECT_NO_THROW(assigner.start());
888     EXPECT_NO_THROW(assigner.finish());
889     EXPECT_NO_THROW(options.finish());
890
891     EXPECT_EQ("test", value);
892     EXPECT_EQ(1, index);
893 }
894
895 TEST(OptionsAssignerStringTest, HandlesEnumDefaultIndexFromVariable)
896 {
897     gmx::Options           options(NULL, NULL);
898     const char * const     allowed[] = { "none", "test", "value" };
899     int                    index     = 1;
900     using gmx::StringOption;
901     ASSERT_NO_THROW(options.addOption(
902                             StringOption("p")
903                                 .enumValue(allowed).storeEnumIndex(&index)));
904     EXPECT_EQ(1, index);
905
906     gmx::OptionsAssigner assigner(&options);
907     EXPECT_NO_THROW(assigner.start());
908     EXPECT_NO_THROW(assigner.finish());
909     EXPECT_NO_THROW(options.finish());
910
911     EXPECT_EQ(1, index);
912 }
913
914 } // namespace