SYCL: Avoid using no_init read accessor in rocFFT
[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-2017, The GROMACS development team.
5  * Copyright (c) 2019,2020, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \internal \file
37  * \brief
38  * Tests option assignment.
39  *
40  * In addition to testing gmx::OptionsAssigner, these are the main
41  * tests for the classes from basicoptions.h and basicoptionstorage.h (and
42  * their base classes) that actually implement the behavior, as well as for the
43  * internal implementation of the gmx::Options and gmx::AbstractOptionStorage
44  * classes.
45  *
46  * \author Teemu Murtola <teemu.murtola@gmail.com>
47  * \ingroup module_options
48  */
49 #include "gmxpre.h"
50
51 #include "gromacs/options/optionsassigner.h"
52
53 #include <limits>
54 #include <vector>
55
56 #include <gtest/gtest.h>
57
58 #include "gromacs/options/basicoptions.h"
59 #include "gromacs/options/options.h"
60 #include "gromacs/options/optionsection.h"
61 #include "gromacs/utility/any.h"
62 #include "gromacs/utility/exceptions.h"
63 #include "gromacs/utility/stringutil.h"
64
65 #include "testutils/testasserts.h"
66
67 namespace
68 {
69
70 /********************************************************************
71  * General assignment tests
72  */
73
74 TEST(OptionsAssignerTest, HandlesMissingRequiredParameter)
75 {
76     gmx::Options options;
77     int          value = 0;
78     using gmx::IntegerOption;
79     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value).required()));
80
81     EXPECT_THROW(options.finish(), gmx::InvalidInputError);
82 }
83
84 TEST(OptionsAssignerTest, HandlesRequiredParameterWithDefaultValue)
85 {
86     gmx::Options options;
87     int          value = 0;
88     using gmx::IntegerOption;
89     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value).required().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;
98     std::vector<int> values;
99     bool             bIsSet = false;
100     using gmx::IntegerOption;
101     ASSERT_NO_THROW(options.addOption(
102             IntegerOption("p").storeVector(&values).storeIsSet(&bIsSet).multiValue()));
103
104     gmx::OptionsAssigner assigner(&options);
105     EXPECT_NO_THROW(assigner.start());
106     ASSERT_NO_THROW(assigner.startOption("p"));
107     ASSERT_NO_THROW(assigner.appendValue("1"));
108     ASSERT_NO_THROW(assigner.finishOption());
109     EXPECT_THROW(assigner.startOption("p"), gmx::InvalidInputError);
110     EXPECT_NO_THROW(assigner.finish());
111     EXPECT_NO_THROW(options.finish());
112
113     EXPECT_TRUE(bIsSet);
114     ASSERT_EQ(1U, values.size());
115     EXPECT_EQ(1, values[0]);
116 }
117
118 TEST(OptionsAssignerTest, HandlesMultipleParameter)
119 {
120     gmx::Options     options;
121     std::vector<int> values;
122     bool             bIsSet = false;
123     using gmx::IntegerOption;
124     ASSERT_NO_THROW(options.addOption(
125             IntegerOption("p").storeVector(&values).storeIsSet(&bIsSet).allowMultiple()));
126
127     gmx::OptionsAssigner assigner(&options);
128     EXPECT_NO_THROW_GMX(assigner.start());
129     ASSERT_NO_THROW_GMX(assigner.startOption("p"));
130     ASSERT_NO_THROW_GMX(assigner.appendValue("1"));
131     EXPECT_NO_THROW_GMX(assigner.finishOption());
132     ASSERT_NO_THROW_GMX(assigner.startOption("p"));
133     ASSERT_NO_THROW_GMX(assigner.appendValue("2"));
134     EXPECT_NO_THROW_GMX(assigner.finishOption());
135     EXPECT_NO_THROW_GMX(assigner.finish());
136     EXPECT_NO_THROW_GMX(options.finish());
137
138     EXPECT_TRUE(bIsSet);
139     ASSERT_EQ(2U, values.size());
140     EXPECT_EQ(1, values[0]);
141     EXPECT_EQ(2, values[1]);
142 }
143
144 TEST(OptionsAssignerTest, HandlesMissingValue)
145 {
146     gmx::Options options;
147     int          value1 = 0, value2 = 0;
148     using gmx::IntegerOption;
149     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value1)));
150     ASSERT_NO_THROW(options.addOption(IntegerOption("q").store(&value2)));
151
152     gmx::OptionsAssigner assigner(&options);
153     EXPECT_NO_THROW(assigner.start());
154     ASSERT_NO_THROW(assigner.startOption("p"));
155     EXPECT_THROW(assigner.finishOption(), gmx::InvalidInputError);
156     ASSERT_NO_THROW(assigner.startOption("q"));
157     ASSERT_NO_THROW(assigner.appendValue("2"));
158     EXPECT_NO_THROW(assigner.finishOption());
159     EXPECT_NO_THROW(assigner.finish());
160     EXPECT_NO_THROW(options.finish());
161
162     EXPECT_EQ(2, value2);
163 }
164
165 TEST(OptionsAssignerTest, HandlesExtraValue)
166 {
167     gmx::Options options;
168     int          value1 = 0;
169     using gmx::IntegerOption;
170     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value1)));
171
172     gmx::OptionsAssigner assigner(&options);
173     EXPECT_NO_THROW(assigner.start());
174     ASSERT_NO_THROW(assigner.startOption("p"));
175     ASSERT_NO_THROW(assigner.appendValue("2"));
176     EXPECT_THROW(assigner.appendValue("3"), gmx::InvalidInputError);
177     EXPECT_NO_THROW(assigner.finishOption());
178     EXPECT_NO_THROW(assigner.finish());
179     EXPECT_NO_THROW(options.finish());
180
181     EXPECT_EQ(0, value1);
182 }
183
184 TEST(OptionsAssignerTest, HandlesGroups)
185 {
186     gmx::Options            options;
187     gmx::IOptionsContainer& group1 = options.addGroup();
188     gmx::IOptionsContainer& group2 = options.addGroup();
189     int                     value  = 3;
190     int                     value1 = 1;
191     int                     value2 = 2;
192     using gmx::IntegerOption;
193     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
194     ASSERT_NO_THROW(group1.addOption(IntegerOption("q").store(&value1)));
195     ASSERT_NO_THROW(group2.addOption(IntegerOption("r").store(&value2)));
196
197     gmx::OptionsAssigner assigner(&options);
198     EXPECT_NO_THROW(assigner.start());
199     ASSERT_NO_THROW(assigner.startOption("p"));
200     EXPECT_NO_THROW(assigner.appendValue("5"));
201     EXPECT_NO_THROW(assigner.finishOption());
202     ASSERT_NO_THROW(assigner.startOption("q"));
203     EXPECT_NO_THROW(assigner.appendValue("4"));
204     EXPECT_NO_THROW(assigner.finishOption());
205     ASSERT_NO_THROW(assigner.startOption("r"));
206     EXPECT_NO_THROW(assigner.appendValue("6"));
207     EXPECT_NO_THROW(assigner.finishOption());
208     EXPECT_NO_THROW(assigner.finish());
209     EXPECT_NO_THROW(options.finish());
210
211     EXPECT_EQ(5, value);
212     EXPECT_EQ(4, value1);
213     EXPECT_EQ(6, value2);
214 }
215
216 TEST(OptionsAssignerTest, HandlesSections)
217 {
218     using gmx::OptionSection;
219     gmx::Options options;
220     auto         sub1   = options.addSection(OptionSection("section1"));
221     auto         sub2   = options.addSection(OptionSection("section2"));
222     int          value  = 3;
223     int          value1 = 1;
224     int          value2 = 2;
225     using gmx::IntegerOption;
226     ASSERT_NO_THROW_GMX(options.addOption(IntegerOption("p").store(&value)));
227     ASSERT_NO_THROW_GMX(sub1.addOption(IntegerOption("p").store(&value1)));
228     ASSERT_NO_THROW_GMX(sub2.addOption(IntegerOption("p").store(&value2)));
229
230     gmx::OptionsAssigner assigner(&options);
231     EXPECT_NO_THROW(assigner.start());
232     ASSERT_NO_THROW(assigner.startSection("section1"));
233     ASSERT_NO_THROW(assigner.startOption("p"));
234     EXPECT_NO_THROW(assigner.appendValue("5"));
235     EXPECT_NO_THROW(assigner.finishOption());
236     EXPECT_NO_THROW(assigner.finishSection());
237     ASSERT_NO_THROW(assigner.startOption("p"));
238     EXPECT_NO_THROW(assigner.appendValue("4"));
239     EXPECT_NO_THROW(assigner.finishOption());
240     ASSERT_NO_THROW(assigner.startSection("section2"));
241     ASSERT_NO_THROW(assigner.startOption("p"));
242     EXPECT_NO_THROW(assigner.appendValue("6"));
243     EXPECT_NO_THROW(assigner.finishOption());
244     EXPECT_NO_THROW(assigner.finishSection());
245     EXPECT_NO_THROW(assigner.finish());
246     EXPECT_NO_THROW(options.finish());
247
248     EXPECT_EQ(4, value);
249     EXPECT_EQ(5, value1);
250     EXPECT_EQ(6, value2);
251 }
252
253 TEST(OptionsAssignerTest, HandlesMultipleSources)
254 {
255     gmx::Options options;
256     int          value = -1;
257     using gmx::IntegerOption;
258     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
259
260     {
261         gmx::OptionsAssigner assigner(&options);
262         EXPECT_NO_THROW(assigner.start());
263         ASSERT_NO_THROW(assigner.startOption("p"));
264         EXPECT_NO_THROW(assigner.appendValue("1"));
265         EXPECT_NO_THROW(assigner.finishOption());
266         EXPECT_NO_THROW(assigner.finish());
267     }
268     {
269         gmx::OptionsAssigner assigner2(&options);
270         EXPECT_NO_THROW(assigner2.start());
271         ASSERT_NO_THROW(assigner2.startOption("p"));
272         EXPECT_NO_THROW(assigner2.appendValue("2"));
273         EXPECT_NO_THROW(assigner2.finishOption());
274         EXPECT_NO_THROW(assigner2.finish());
275     }
276     EXPECT_NO_THROW(options.finish());
277
278     EXPECT_EQ(2, value);
279 }
280
281
282 /********************************************************************
283  * Tests for boolean assignment
284  */
285
286 TEST(OptionsAssignerBooleanTest, StoresYesValue)
287 {
288     gmx::Options options;
289     bool         value = false;
290     using gmx::BooleanOption;
291     ASSERT_NO_THROW(options.addOption(BooleanOption("p").store(&value)));
292
293     gmx::OptionsAssigner assigner(&options);
294     EXPECT_NO_THROW(assigner.start());
295     ASSERT_NO_THROW(assigner.startOption("p"));
296     EXPECT_NO_THROW(assigner.appendValue("yes"));
297     EXPECT_NO_THROW(assigner.finishOption());
298     EXPECT_NO_THROW(assigner.finish());
299     EXPECT_NO_THROW(options.finish());
300
301     EXPECT_TRUE(value);
302 }
303
304 TEST(OptionsAssignerBooleanTest, SetsBooleanWithoutExplicitValue)
305 {
306     gmx::Options options;
307     bool         value = false;
308     using gmx::BooleanOption;
309     ASSERT_NO_THROW(options.addOption(BooleanOption("p").store(&value)));
310
311     gmx::OptionsAssigner assigner(&options);
312     EXPECT_NO_THROW(assigner.start());
313     ASSERT_NO_THROW(assigner.startOption("p"));
314     EXPECT_NO_THROW(assigner.finishOption());
315     EXPECT_NO_THROW(assigner.finish());
316     EXPECT_NO_THROW(options.finish());
317
318     EXPECT_TRUE(value);
319 }
320
321 TEST(OptionsAssignerBooleanTest, ClearsBooleanWithPrefixNo)
322 {
323     gmx::Options options;
324     bool         value = true;
325     using gmx::BooleanOption;
326     ASSERT_NO_THROW(options.addOption(BooleanOption("p").store(&value)));
327
328     gmx::OptionsAssigner assigner(&options);
329     assigner.setAcceptBooleanNoPrefix(true);
330     EXPECT_NO_THROW(assigner.start());
331     ASSERT_NO_THROW(assigner.startOption("nop"));
332     EXPECT_NO_THROW(assigner.finishOption());
333     EXPECT_NO_THROW(assigner.finish());
334     EXPECT_NO_THROW(options.finish());
335
336     EXPECT_FALSE(value);
337 }
338
339 TEST(OptionsAssignerBooleanTest, HandlesBooleanWithPrefixAndValue)
340 {
341     gmx::Options options;
342     bool         value = false;
343     using gmx::BooleanOption;
344     ASSERT_NO_THROW(options.addOption(BooleanOption("p").store(&value)));
345
346     gmx::OptionsAssigner assigner(&options);
347     assigner.setAcceptBooleanNoPrefix(true);
348     EXPECT_NO_THROW(assigner.start());
349     ASSERT_NO_THROW(assigner.startOption("nop"));
350     // It's OK to fail, but if it doesn't, it should work.
351     try
352     {
353         assigner.appendValue("no");
354         assigner.finishOption();
355         EXPECT_NO_THROW(assigner.finish());
356         EXPECT_TRUE(value);
357     }
358     catch (const gmx::InvalidInputError&)
359     {
360     }
361 }
362
363
364 /********************************************************************
365  * Tests for integer assignment
366  *
367  * These tests also contain tests for general default value handling.
368  */
369
370 TEST(OptionsAssignerIntegerTest, StoresSingleValue)
371 {
372     gmx::Options options;
373     int          value = 1;
374     using gmx::IntegerOption;
375     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
376
377     gmx::OptionsAssigner assigner(&options);
378     EXPECT_NO_THROW(assigner.start());
379     ASSERT_NO_THROW(assigner.startOption("p"));
380     ASSERT_NO_THROW(assigner.appendValue("3"));
381     EXPECT_NO_THROW(assigner.finishOption());
382     EXPECT_NO_THROW(assigner.finish());
383     EXPECT_NO_THROW(options.finish());
384
385     EXPECT_EQ(3, value);
386 }
387
388 TEST(OptionsAssignerIntegerTest, HandlesEmptyValue)
389 {
390     gmx::Options options;
391     int          value = 1;
392     using gmx::IntegerOption;
393     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
394
395     gmx::OptionsAssigner assigner(&options);
396     EXPECT_NO_THROW(assigner.start());
397     ASSERT_NO_THROW(assigner.startOption("p"));
398     EXPECT_THROW(assigner.appendValue(""), gmx::InvalidInputError);
399     EXPECT_NO_THROW(assigner.finishOption());
400     EXPECT_NO_THROW(assigner.finish());
401     EXPECT_NO_THROW(options.finish());
402
403     EXPECT_EQ(1, value);
404 }
405
406 TEST(OptionsAssignerIntegerTest, HandlesInvalidValue)
407 {
408     gmx::Options options;
409     int          value = 1;
410     using gmx::IntegerOption;
411     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
412
413     gmx::OptionsAssigner assigner(&options);
414     EXPECT_NO_THROW(assigner.start());
415     ASSERT_NO_THROW(assigner.startOption("p"));
416     EXPECT_THROW(assigner.appendValue("2abc"), gmx::InvalidInputError);
417     EXPECT_NO_THROW(assigner.finishOption());
418     EXPECT_NO_THROW(assigner.finish());
419     EXPECT_NO_THROW(options.finish());
420
421     EXPECT_EQ(1, value);
422 }
423
424 TEST(OptionsAssignerIntegerTest, HandlesOverflow)
425 {
426     gmx::Options options;
427     int          value = 1;
428     using gmx::IntegerOption;
429     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value)));
430
431     gmx::OptionsAssigner assigner(&options);
432     EXPECT_NO_THROW(assigner.start());
433     ASSERT_NO_THROW(assigner.startOption("p"));
434     std::string overflowValue(gmx::formatString("%d0000", std::numeric_limits<int>::max()));
435     EXPECT_THROW(assigner.appendValue(overflowValue), gmx::InvalidInputError);
436     EXPECT_NO_THROW(assigner.finishOption());
437     EXPECT_NO_THROW(assigner.finish());
438     EXPECT_NO_THROW(options.finish());
439
440     EXPECT_EQ(1, value);
441 }
442
443 TEST(OptionsAssignerIntegerTest, StoresDefaultValue)
444 {
445     gmx::Options options;
446     int          value = -1;
447     using gmx::IntegerOption;
448     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value).defaultValue(2)));
449     EXPECT_EQ(2, value);
450
451     gmx::OptionsAssigner assigner(&options);
452     EXPECT_NO_THROW(assigner.start());
453     EXPECT_NO_THROW(assigner.finish());
454     EXPECT_NO_THROW(options.finish());
455
456     EXPECT_EQ(2, value);
457 }
458
459 TEST(OptionsAssignerIntegerTest, StoresDefaultValueIfSet)
460 {
461     gmx::Options options;
462     int          value = -1;
463     using gmx::IntegerOption;
464     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value).defaultValueIfSet(2)));
465     EXPECT_EQ(-1, value);
466
467     gmx::OptionsAssigner assigner(&options);
468     EXPECT_NO_THROW(assigner.start());
469     ASSERT_NO_THROW(assigner.startOption("p"));
470     EXPECT_NO_THROW(assigner.finishOption());
471     EXPECT_NO_THROW(assigner.finish());
472     EXPECT_NO_THROW(options.finish());
473
474     EXPECT_EQ(2, value);
475 }
476
477 TEST(OptionsAssignerIntegerTest, HandlesDefaultValueIfSetWhenNotSet)
478 {
479     gmx::Options options;
480     int          value = -1;
481     using gmx::IntegerOption;
482     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(&value).defaultValueIfSet(2)));
483     EXPECT_EQ(-1, value);
484
485     gmx::OptionsAssigner assigner(&options);
486     EXPECT_NO_THROW(assigner.start());
487     EXPECT_NO_THROW(assigner.finish());
488     EXPECT_NO_THROW(options.finish());
489
490     EXPECT_EQ(-1, value);
491 }
492
493 TEST(OptionsAssignerIntegerTest, HandlesBothDefaultValues)
494 {
495     gmx::Options options;
496     int          value = -1;
497     using gmx::IntegerOption;
498     ASSERT_NO_THROW(
499             options.addOption(IntegerOption("p").store(&value).defaultValue(1).defaultValueIfSet(2)));
500     EXPECT_EQ(1, value);
501
502     gmx::OptionsAssigner assigner(&options);
503     EXPECT_NO_THROW(assigner.start());
504     ASSERT_NO_THROW(assigner.startOption("p"));
505     EXPECT_NO_THROW(assigner.finishOption());
506     EXPECT_NO_THROW(assigner.finish());
507     EXPECT_NO_THROW(options.finish());
508
509     EXPECT_EQ(2, value);
510 }
511
512 TEST(OptionsAssignerIntegerTest, StoresToVector)
513 {
514     gmx::Options     options;
515     std::vector<int> values;
516     using gmx::IntegerOption;
517     ASSERT_NO_THROW(options.addOption(IntegerOption("p").storeVector(&values).multiValue()));
518
519     gmx::OptionsAssigner assigner(&options);
520     EXPECT_NO_THROW(assigner.start());
521     ASSERT_NO_THROW(assigner.startOption("p"));
522     ASSERT_NO_THROW(assigner.appendValue("-2"));
523     ASSERT_NO_THROW(assigner.appendValue("1"));
524     ASSERT_NO_THROW(assigner.appendValue("4"));
525     EXPECT_NO_THROW(assigner.finishOption());
526     EXPECT_NO_THROW(assigner.finish());
527     EXPECT_NO_THROW(options.finish());
528
529     EXPECT_EQ(3U, values.size());
530     EXPECT_EQ(-2, values[0]);
531     EXPECT_EQ(1, values[1]);
532     EXPECT_EQ(4, values[2]);
533 }
534
535 TEST(OptionsAssignerIntegerTest, HandlesVectors)
536 {
537     gmx::Options options;
538     int          vec[3] = { 0, 0, 0 };
539     using gmx::IntegerOption;
540     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(vec).vector()));
541
542     gmx::OptionsAssigner assigner(&options);
543     EXPECT_NO_THROW(assigner.start());
544     ASSERT_NO_THROW(assigner.startOption("p"));
545     ASSERT_NO_THROW(assigner.appendValue("-2"));
546     ASSERT_NO_THROW(assigner.appendValue("1"));
547     ASSERT_NO_THROW(assigner.appendValue("4"));
548     EXPECT_NO_THROW(assigner.finishOption());
549     EXPECT_NO_THROW(assigner.finish());
550     EXPECT_NO_THROW(options.finish());
551
552     EXPECT_EQ(-2, vec[0]);
553     EXPECT_EQ(1, vec[1]);
554     EXPECT_EQ(4, vec[2]);
555 }
556
557 TEST(OptionsAssignerIntegerTest, HandlesVectorFromSingleValue)
558 {
559     gmx::Options options;
560     int          vec[3] = { 0, 0, 0 };
561     using gmx::IntegerOption;
562     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(vec).vector()));
563
564     gmx::OptionsAssigner assigner(&options);
565     EXPECT_NO_THROW(assigner.start());
566     ASSERT_NO_THROW(assigner.startOption("p"));
567     ASSERT_NO_THROW(assigner.appendValue("2"));
568     EXPECT_NO_THROW(assigner.finishOption());
569     EXPECT_NO_THROW(assigner.finish());
570     EXPECT_NO_THROW(options.finish());
571
572     EXPECT_EQ(2, vec[0]);
573     EXPECT_EQ(2, vec[1]);
574     EXPECT_EQ(2, vec[2]);
575 }
576
577 TEST(OptionsAssignerIntegerTest, HandlesVectorsWithDefaultValue)
578 {
579     gmx::Options options;
580     int          vec[3] = { 3, 2, 1 };
581     using gmx::IntegerOption;
582     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(vec).vector()));
583
584     EXPECT_NO_THROW(options.finish());
585
586     EXPECT_EQ(3, vec[0]);
587     EXPECT_EQ(2, vec[1]);
588     EXPECT_EQ(1, vec[2]);
589 }
590
591 TEST(OptionsAssignerIntegerTest, HandlesVectorsWithDefaultValueWithInvalidAssignment)
592 {
593     gmx::Options options;
594     int          vec[3] = { 3, 2, 1 };
595     using gmx::IntegerOption;
596     ASSERT_NO_THROW(options.addOption(IntegerOption("p").store(vec).vector()));
597
598     gmx::OptionsAssigner assigner(&options);
599     EXPECT_NO_THROW(assigner.start());
600     ASSERT_NO_THROW(assigner.startOption("p"));
601     EXPECT_NO_THROW(assigner.appendValue("1"));
602     EXPECT_NO_THROW(assigner.appendValue("3"));
603     EXPECT_THROW(assigner.finishOption(), gmx::InvalidInputError);
604     EXPECT_NO_THROW(assigner.finish());
605     EXPECT_NO_THROW(options.finish());
606
607     EXPECT_EQ(3, vec[0]);
608     EXPECT_EQ(2, vec[1]);
609     EXPECT_EQ(1, vec[2]);
610 }
611
612
613 /********************************************************************
614  * Tests for double assignment
615  */
616
617 TEST(OptionsAssignerDoubleTest, StoresSingleValue)
618 {
619     gmx::Options options;
620     double       value = 0.0;
621     using gmx::DoubleOption;
622     ASSERT_NO_THROW(options.addOption(DoubleOption("p").store(&value)));
623
624     gmx::OptionsAssigner assigner(&options);
625     EXPECT_NO_THROW(assigner.start());
626     ASSERT_NO_THROW(assigner.startOption("p"));
627     ASSERT_NO_THROW(assigner.appendValue("2.7"));
628     EXPECT_NO_THROW(assigner.finishOption());
629     EXPECT_NO_THROW(assigner.finish());
630     EXPECT_NO_THROW(options.finish());
631
632     EXPECT_DOUBLE_EQ(2.7, value);
633 }
634
635 TEST(OptionsAssignerDoubleTest, StoresValueFromFloat)
636 {
637     gmx::Options options;
638     double       value = 0.0;
639     using gmx::DoubleOption;
640     ASSERT_NO_THROW(options.addOption(DoubleOption("p").store(&value)));
641
642     gmx::OptionsAssigner assigner(&options);
643     EXPECT_NO_THROW(assigner.start());
644     ASSERT_NO_THROW(assigner.startOption("p"));
645     ASSERT_NO_THROW(assigner.appendValue(gmx::Any::create<float>(2.7)));
646     EXPECT_NO_THROW(assigner.finishOption());
647     EXPECT_NO_THROW(assigner.finish());
648     EXPECT_NO_THROW(options.finish());
649
650     EXPECT_FLOAT_EQ(2.7, value);
651 }
652
653 TEST(OptionsAssignerDoubleTest, HandlesEmptyValue)
654 {
655     gmx::Options options;
656     double       value = 1.0;
657     using gmx::DoubleOption;
658     ASSERT_NO_THROW(options.addOption(DoubleOption("p").store(&value)));
659
660     gmx::OptionsAssigner assigner(&options);
661     EXPECT_NO_THROW(assigner.start());
662     ASSERT_NO_THROW(assigner.startOption("p"));
663     EXPECT_THROW(assigner.appendValue(""), gmx::InvalidInputError);
664     EXPECT_NO_THROW(assigner.finishOption());
665     EXPECT_NO_THROW(assigner.finish());
666     EXPECT_NO_THROW(options.finish());
667
668     EXPECT_DOUBLE_EQ(1.0, value);
669 }
670
671 TEST(OptionsAssignerDoubleTest, HandlesPreSetScaleValue)
672 {
673     gmx::Options options;
674     double       value = 1.0;
675     using gmx::DoubleOption;
676     gmx::DoubleOptionInfo* info = options.addOption(DoubleOption("p").store(&value));
677     EXPECT_NO_THROW(info->setScaleFactor(10));
678
679     gmx::OptionsAssigner assigner(&options);
680     EXPECT_NO_THROW(assigner.start());
681     ASSERT_NO_THROW(assigner.startOption("p"));
682     ASSERT_NO_THROW(assigner.appendValue("2.7"));
683     EXPECT_NO_THROW(assigner.finishOption());
684     EXPECT_NO_THROW(assigner.finish());
685     EXPECT_NO_THROW(options.finish());
686
687     EXPECT_DOUBLE_EQ(27, value);
688 }
689
690 TEST(OptionsAssignerDoubleTest, HandlesPostSetScaleValue)
691 {
692     gmx::Options options;
693     double       value = 1.0;
694     using gmx::DoubleOption;
695     gmx::DoubleOptionInfo* info = options.addOption(DoubleOption("p").store(&value));
696
697     gmx::OptionsAssigner assigner(&options);
698     EXPECT_NO_THROW(assigner.start());
699     ASSERT_NO_THROW(assigner.startOption("p"));
700     ASSERT_NO_THROW(assigner.appendValue("2.7"));
701     EXPECT_NO_THROW(assigner.finishOption());
702     EXPECT_NO_THROW(assigner.finish());
703     EXPECT_NO_THROW(info->setScaleFactor(10));
704     EXPECT_NO_THROW(options.finish());
705
706     EXPECT_DOUBLE_EQ(27, value);
707 }
708
709
710 /********************************************************************
711  * Tests for string assignment
712  */
713
714 //! Set of allowed values for enum option tests.
715 const char* const c_allowed[] = { "none", "test", "value" };
716
717 TEST(OptionsAssignerStringTest, StoresSingleValue)
718 {
719     gmx::Options options;
720     std::string  value;
721     using gmx::StringOption;
722     ASSERT_NO_THROW(options.addOption(StringOption("p").store(&value)));
723
724     gmx::OptionsAssigner assigner(&options);
725     EXPECT_NO_THROW(assigner.start());
726     ASSERT_NO_THROW(assigner.startOption("p"));
727     ASSERT_NO_THROW(assigner.appendValue("value"));
728     EXPECT_NO_THROW(assigner.finishOption());
729     EXPECT_NO_THROW(assigner.finish());
730     EXPECT_NO_THROW(options.finish());
731
732     EXPECT_EQ("value", value);
733 }
734
735 TEST(OptionsAssignerStringTest, HandlesEnumValue)
736 {
737     gmx::Options options;
738     std::string  value;
739     using gmx::StringOption;
740     ASSERT_NO_THROW(options.addOption(StringOption("p").store(&value).enumValue(c_allowed)));
741
742     gmx::OptionsAssigner assigner(&options);
743     EXPECT_NO_THROW(assigner.start());
744     ASSERT_NO_THROW(assigner.startOption("p"));
745     ASSERT_NO_THROW(assigner.appendValue("test"));
746     EXPECT_NO_THROW(assigner.finishOption());
747     EXPECT_NO_THROW(assigner.finish());
748     EXPECT_NO_THROW(options.finish());
749
750     EXPECT_EQ("test", value);
751 }
752
753 TEST(OptionsAssignerStringTest, HandlesEnumValueFromNullTerminatedArray)
754 {
755     gmx::Options      options;
756     std::string       value;
757     const char* const allowed[] = { "none", "test", "value", nullptr };
758     using gmx::StringOption;
759     ASSERT_NO_THROW(options.addOption(
760             StringOption("p").store(&value).enumValueFromNullTerminatedArray(allowed)));
761
762     gmx::OptionsAssigner assigner(&options);
763     EXPECT_NO_THROW(assigner.start());
764     ASSERT_NO_THROW(assigner.startOption("p"));
765     ASSERT_NO_THROW(assigner.appendValue("value"));
766     EXPECT_NO_THROW(assigner.finishOption());
767     EXPECT_NO_THROW(assigner.finish());
768     EXPECT_NO_THROW(options.finish());
769
770     EXPECT_EQ("value", value);
771 }
772
773 TEST(OptionsAssignerStringTest, HandlesIncorrectEnumValue)
774 {
775     gmx::Options options;
776     std::string  value;
777     using gmx::StringOption;
778     ASSERT_NO_THROW(options.addOption(StringOption("p").store(&value).enumValue(c_allowed)));
779
780     gmx::OptionsAssigner assigner(&options);
781     EXPECT_NO_THROW(assigner.start());
782     ASSERT_NO_THROW(assigner.startOption("p"));
783     ASSERT_THROW(assigner.appendValue("unknown"), gmx::InvalidInputError);
784 }
785
786 TEST(OptionsAssignerStringTest, CompletesEnumValue)
787 {
788     gmx::Options options;
789     std::string  value;
790     using gmx::StringOption;
791     ASSERT_NO_THROW(options.addOption(StringOption("p").store(&value).enumValue(c_allowed)));
792
793     gmx::OptionsAssigner assigner(&options);
794     EXPECT_NO_THROW(assigner.start());
795     ASSERT_NO_THROW(assigner.startOption("p"));
796     ASSERT_NO_THROW(assigner.appendValue("te"));
797     EXPECT_NO_THROW(assigner.finishOption());
798     EXPECT_NO_THROW(assigner.finish());
799     EXPECT_NO_THROW(options.finish());
800
801     EXPECT_EQ("test", value);
802 }
803
804 TEST(OptionsAssignerStringTest, HandlesEnumWithNoValue)
805 {
806     gmx::Options options;
807     std::string  value;
808     using gmx::StringOption;
809     ASSERT_NO_THROW(options.addOption(StringOption("p").store(&value).enumValue(c_allowed)));
810     EXPECT_TRUE(value.empty());
811
812     ASSERT_NO_THROW(options.finish());
813
814     EXPECT_TRUE(value.empty());
815 }
816
817 TEST(OptionsAssignerStringTest, HandlesEnumDefaultValue)
818 {
819     gmx::Options options;
820     std::string  value;
821     using gmx::StringOption;
822     ASSERT_NO_THROW(options.addOption(
823             StringOption("p").store(&value).enumValue(c_allowed).defaultValue("test")));
824     EXPECT_EQ("test", value);
825
826     gmx::OptionsAssigner assigner(&options);
827     EXPECT_NO_THROW(assigner.start());
828     EXPECT_NO_THROW(assigner.finish());
829     EXPECT_NO_THROW(options.finish());
830
831     EXPECT_EQ("test", value);
832 }
833
834 TEST(OptionsAssignerStringTest, HandlesEnumDefaultValueFromVariable)
835 {
836     gmx::Options options;
837     std::string  value("test");
838     using gmx::StringOption;
839     ASSERT_NO_THROW(options.addOption(StringOption("p").store(&value).enumValue(c_allowed)));
840     EXPECT_EQ("test", value);
841
842     gmx::OptionsAssigner assigner(&options);
843     EXPECT_NO_THROW(assigner.start());
844     EXPECT_NO_THROW(assigner.finish());
845     EXPECT_NO_THROW(options.finish());
846
847     EXPECT_EQ("test", value);
848 }
849
850 TEST(OptionsAssignerStringTest, HandlesEnumDefaultValueFromVector)
851 {
852     gmx::Options             options;
853     std::vector<std::string> value;
854     value.emplace_back("test");
855     value.emplace_back("value");
856     using gmx::StringOption;
857     ASSERT_NO_THROW(options.addOption(
858             StringOption("p").storeVector(&value).valueCount(2).enumValue(c_allowed)));
859     ASSERT_EQ(2U, value.size());
860     EXPECT_EQ("test", value[0]);
861     EXPECT_EQ("value", value[1]);
862
863     gmx::OptionsAssigner assigner(&options);
864     EXPECT_NO_THROW(assigner.start());
865     EXPECT_NO_THROW(assigner.finish());
866     EXPECT_NO_THROW(options.finish());
867
868     ASSERT_EQ(2U, value.size());
869     EXPECT_EQ("test", value[0]);
870     EXPECT_EQ("value", value[1]);
871 }
872
873
874 /********************************************************************
875  * Tests for enum options
876  */
877
878 //! Enum type for EnumOption tests.
879 enum class TestEnum : int
880 {
881     None,
882     Test,
883     Value,
884     Count
885 };
886 //! Set of allowed values for enum option tests.
887 const gmx::EnumerationArray<TestEnum, const char*> c_testEnumNames = { { "none", "test", "value" } };
888
889 TEST(OptionsAssignerEnumTest, StoresSingleValue)
890 {
891     gmx::Options options;
892     TestEnum     value = TestEnum::None;
893     using gmx::EnumOption;
894     ASSERT_NO_THROW(options.addOption(EnumOption<TestEnum>("p").store(&value).enumValue(c_testEnumNames)));
895     EXPECT_EQ(TestEnum::None, value);
896
897     gmx::OptionsAssigner assigner(&options);
898     EXPECT_NO_THROW(assigner.start());
899     ASSERT_NO_THROW(assigner.startOption("p"));
900     ASSERT_NO_THROW(assigner.appendValue("test"));
901     EXPECT_NO_THROW(assigner.finishOption());
902     EXPECT_NO_THROW(assigner.finish());
903     EXPECT_NO_THROW(options.finish());
904
905     EXPECT_EQ(TestEnum::Test, value);
906 }
907
908 TEST(OptionsAssignerEnumTest, StoresVectorValues)
909 {
910     gmx::Options          options;
911     std::vector<TestEnum> values;
912     using gmx::EnumOption;
913     ASSERT_NO_THROW(options.addOption(
914             EnumOption<TestEnum>("p").storeVector(&values).multiValue().enumValue(c_testEnumNames)));
915     EXPECT_TRUE(values.empty());
916
917     gmx::OptionsAssigner assigner(&options);
918     EXPECT_NO_THROW(assigner.start());
919     ASSERT_NO_THROW(assigner.startOption("p"));
920     ASSERT_NO_THROW(assigner.appendValue("test"));
921     ASSERT_NO_THROW(assigner.appendValue("value"));
922     EXPECT_NO_THROW(assigner.finishOption());
923     EXPECT_NO_THROW(assigner.finish());
924     EXPECT_NO_THROW(options.finish());
925
926     ASSERT_EQ(2U, values.size());
927     EXPECT_EQ(TestEnum::Test, values[0]);
928     EXPECT_EQ(TestEnum::Value, values[1]);
929 }
930
931 TEST(OptionsAssignerEnumTest, HandlesInitialValueOutOfRange)
932 {
933     gmx::Options options;
934     TestEnum     value = TestEnum::Count;
935     using gmx::EnumOption;
936     ASSERT_NO_THROW(options.addOption(EnumOption<TestEnum>("p").store(&value).enumValue(c_testEnumNames)));
937     EXPECT_EQ(TestEnum::Count, value);
938
939     gmx::OptionsAssigner assigner(&options);
940     EXPECT_NO_THROW(assigner.start());
941     EXPECT_NO_THROW(assigner.finish());
942     EXPECT_NO_THROW(options.finish());
943
944     EXPECT_EQ(TestEnum::Count, value);
945 }
946
947 TEST(OptionsAssignerEnumTest, HandlesEnumDefaultValue)
948 {
949     gmx::Options options;
950     TestEnum     value = TestEnum::None;
951     using gmx::EnumOption;
952     ASSERT_NO_THROW(options.addOption(
953             EnumOption<TestEnum>("p").store(&value).enumValue(c_testEnumNames).defaultValue(TestEnum::Test)));
954     EXPECT_EQ(TestEnum::Test, value);
955
956     gmx::OptionsAssigner assigner(&options);
957     EXPECT_NO_THROW(assigner.start());
958     EXPECT_NO_THROW(assigner.finish());
959     EXPECT_NO_THROW(options.finish());
960
961     EXPECT_EQ(TestEnum::Test, value);
962 }
963
964 TEST(OptionsAssignerEnumTest, HandlesEnumDefaultValueFromVariable)
965 {
966     gmx::Options options;
967     TestEnum     value = TestEnum::Test;
968     using gmx::EnumOption;
969     ASSERT_NO_THROW(options.addOption(EnumOption<TestEnum>("p").store(&value).enumValue(c_testEnumNames)));
970     EXPECT_EQ(TestEnum::Test, value);
971
972     gmx::OptionsAssigner assigner(&options);
973     EXPECT_NO_THROW(assigner.start());
974     EXPECT_NO_THROW(assigner.finish());
975     EXPECT_NO_THROW(options.finish());
976
977     EXPECT_EQ(TestEnum::Test, value);
978 }
979
980 TEST(OptionsAssignerEnumTest, HandlesEnumDefaultValueFromVector)
981 {
982     gmx::Options          options;
983     std::vector<TestEnum> value;
984     value.push_back(TestEnum::None);
985     value.push_back(TestEnum::Test);
986     using gmx::EnumOption;
987     ASSERT_NO_THROW(options.addOption(
988             EnumOption<TestEnum>("p").storeVector(&value).valueCount(2).enumValue(c_testEnumNames)));
989     ASSERT_EQ(2U, value.size());
990     EXPECT_EQ(TestEnum::None, value[0]);
991     EXPECT_EQ(TestEnum::Test, value[1]);
992
993     gmx::OptionsAssigner assigner(&options);
994     EXPECT_NO_THROW(assigner.start());
995     EXPECT_NO_THROW(assigner.finish());
996     EXPECT_NO_THROW(options.finish());
997
998     ASSERT_EQ(2U, value.size());
999     EXPECT_EQ(TestEnum::None, value[0]);
1000     EXPECT_EQ(TestEnum::Test, value[1]);
1001 }
1002
1003 } // namespace