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