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, 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 handling of selection options.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \ingroup module_selection
42  */
43 #include "gmxpre.h"
44 
45 #include "gromacs/selection/selectionoption.h"
46 
47 #include <gtest/gtest.h>
48 
49 #include "gromacs/options/options.h"
50 #include "gromacs/options/optionsassigner.h"
51 #include "gromacs/selection/selection.h"
52 #include "gromacs/selection/selectioncollection.h"
53 #include "gromacs/selection/selectionfileoption.h"
54 #include "gromacs/selection/selectionoptionmanager.h"
55 #include "gromacs/utility/exceptions.h"
56 
57 #include "testutils/testasserts.h"
58 #include "testutils/testfilemanager.h"
59 
60 #include "toputils.h"
61 
62 using gmx::test::TestFileManager;
63 
64 namespace
65 {
66 
67 /********************************************************************
68  * Base fixture for tests in this file.
69  */
70 
71 class SelectionOptionTestBase : public ::testing::Test
72 {
73 public:
74     SelectionOptionTestBase();
75 
76     void loadTopology(const char* filename);
77 
78     gmx::SelectionCollection    sc_;
79     gmx::SelectionOptionManager manager_;
80     gmx::Options                options_;
81 
82 private:
83     gmx::test::TopologyManager topManager_;
84 };
85 
SelectionOptionTestBase()86 SelectionOptionTestBase::SelectionOptionTestBase() : manager_(&sc_)
87 {
88     options_.addManager(&manager_);
89     sc_.setReferencePosType("atom");
90     sc_.setOutputPosType("atom");
91 }
92 
loadTopology(const char * filename)93 void SelectionOptionTestBase::loadTopology(const char* filename)
94 {
95     topManager_.loadTopology(filename);
96 
97     ASSERT_NO_THROW_GMX(sc_.setTopology(topManager_.topology(), -1));
98 }
99 
100 
101 /********************************************************************
102  * Tests for SelectionOption
103  */
104 
105 //! Test fixture for gmx::SelectionOption.
106 typedef SelectionOptionTestBase SelectionOptionTest;
107 
TEST_F(SelectionOptionTest,ParsesSimpleSelection)108 TEST_F(SelectionOptionTest, ParsesSimpleSelection)
109 {
110     gmx::Selection sel;
111     using gmx::SelectionOption;
112     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").store(&sel)));
113 
114     gmx::OptionsAssigner assigner(&options_);
115     EXPECT_NO_THROW_GMX(assigner.start());
116     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
117     EXPECT_NO_THROW_GMX(assigner.appendValue("resname RA RB"));
118     EXPECT_NO_THROW_GMX(assigner.finishOption());
119     EXPECT_NO_THROW_GMX(assigner.finish());
120     EXPECT_NO_THROW_GMX(options_.finish());
121 
122     ASSERT_TRUE(sel.isValid());
123 }
124 
125 
TEST_F(SelectionOptionTest,HandlesDynamicSelectionWhenStaticRequired)126 TEST_F(SelectionOptionTest, HandlesDynamicSelectionWhenStaticRequired)
127 {
128     gmx::Selection sel;
129     using gmx::SelectionOption;
130     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").store(&sel).onlyStatic()));
131 
132     gmx::OptionsAssigner assigner(&options_);
133     EXPECT_NO_THROW_GMX(assigner.start());
134     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
135     EXPECT_THROW_GMX(assigner.appendValue("resname RA RB and x < 5"), gmx::InvalidInputError);
136     EXPECT_NO_THROW_GMX(assigner.finishOption());
137     EXPECT_NO_THROW_GMX(assigner.finish());
138     EXPECT_NO_THROW_GMX(options_.finish());
139 }
140 
141 
TEST_F(SelectionOptionTest,HandlesNonAtomicSelectionWhenAtomsRequired)142 TEST_F(SelectionOptionTest, HandlesNonAtomicSelectionWhenAtomsRequired)
143 {
144     gmx::Selection sel;
145     using gmx::SelectionOption;
146     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").store(&sel).onlyAtoms()));
147 
148     gmx::OptionsAssigner assigner(&options_);
149     EXPECT_NO_THROW_GMX(assigner.start());
150     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
151     EXPECT_NO_THROW_GMX(assigner.appendValue("res_cog of resname RA RB"));
152     EXPECT_NO_THROW_GMX(assigner.finishOption());
153     EXPECT_NO_THROW_GMX(assigner.finish());
154     EXPECT_NO_THROW_GMX(options_.finish());
155 
156     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
157     EXPECT_THROW_GMX(sc_.compile(), gmx::InvalidInputError);
158 }
159 
160 
TEST_F(SelectionOptionTest,ChecksForSortedAtomsWhenRequired)161 TEST_F(SelectionOptionTest, ChecksForSortedAtomsWhenRequired)
162 {
163     gmx::Selection sel;
164     using gmx::SelectionOption;
165     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").store(&sel).onlySortedAtoms()));
166 
167     gmx::OptionsAssigner assigner(&options_);
168     EXPECT_NO_THROW_GMX(assigner.start());
169     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
170     EXPECT_NO_THROW_GMX(assigner.appendValue("resnr 1 2 permute 2 1"));
171     EXPECT_NO_THROW_GMX(assigner.finishOption());
172     EXPECT_NO_THROW_GMX(assigner.finish());
173     EXPECT_NO_THROW_GMX(options_.finish());
174 
175     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
176     EXPECT_THROW_GMX(sc_.compile(), gmx::InvalidInputError);
177 }
178 
179 
TEST_F(SelectionOptionTest,ChecksEmptySelections)180 TEST_F(SelectionOptionTest, ChecksEmptySelections)
181 {
182     gmx::Selection sel;
183     using gmx::SelectionOption;
184     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").store(&sel)));
185 
186     gmx::OptionsAssigner assigner(&options_);
187     EXPECT_NO_THROW_GMX(assigner.start());
188     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
189     EXPECT_NO_THROW_GMX(assigner.appendValue("none"));
190     EXPECT_NO_THROW_GMX(assigner.finishOption());
191     EXPECT_NO_THROW_GMX(assigner.finish());
192     EXPECT_NO_THROW_GMX(options_.finish());
193 
194     EXPECT_THROW_GMX(sc_.compile(), gmx::InvalidInputError);
195 }
196 
197 
TEST_F(SelectionOptionTest,ChecksEmptyDelayedSelections)198 TEST_F(SelectionOptionTest, ChecksEmptyDelayedSelections)
199 {
200     gmx::Selection sel;
201     using gmx::SelectionOption;
202     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").store(&sel)));
203 
204     gmx::OptionsAssigner assigner(&options_);
205     EXPECT_NO_THROW_GMX(assigner.start());
206     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
207     EXPECT_NO_THROW_GMX(assigner.finishOption());
208     EXPECT_NO_THROW_GMX(assigner.finish());
209     EXPECT_NO_THROW_GMX(options_.finish());
210     ASSERT_NO_THROW_GMX(manager_.parseRequestedFromString("none"));
211 
212     EXPECT_THROW_GMX(sc_.compile(), gmx::InvalidInputError);
213 }
214 
215 
TEST_F(SelectionOptionTest,HandlesTooManySelections)216 TEST_F(SelectionOptionTest, HandlesTooManySelections)
217 {
218     gmx::Selection sel;
219     using gmx::SelectionOption;
220     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").store(&sel)));
221 
222     gmx::OptionsAssigner assigner(&options_);
223     EXPECT_NO_THROW_GMX(assigner.start());
224     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
225     EXPECT_NO_THROW_GMX(assigner.appendValue("resname RA RB"));
226     EXPECT_THROW_GMX(assigner.appendValue("resname RB RC"), gmx::InvalidInputError);
227     EXPECT_NO_THROW_GMX(assigner.finishOption());
228     EXPECT_NO_THROW_GMX(assigner.finish());
229     EXPECT_NO_THROW_GMX(options_.finish());
230 }
231 
232 
TEST_F(SelectionOptionTest,HandlesTooFewSelections)233 TEST_F(SelectionOptionTest, HandlesTooFewSelections)
234 {
235     gmx::Selection sel[2];
236     using gmx::SelectionOption;
237     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").store(sel).valueCount(2)));
238 
239     gmx::OptionsAssigner assigner(&options_);
240     EXPECT_NO_THROW_GMX(assigner.start());
241     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
242     EXPECT_NO_THROW_GMX(assigner.appendValue("resname RA RB"));
243     EXPECT_THROW_GMX(assigner.finishOption(), gmx::InvalidInputError);
244     EXPECT_NO_THROW_GMX(assigner.finish());
245     EXPECT_NO_THROW_GMX(options_.finish());
246 }
247 
248 
TEST_F(SelectionOptionTest,HandlesDefaultSelectionText)249 TEST_F(SelectionOptionTest, HandlesDefaultSelectionText)
250 {
251     gmx::Selection sel;
252     using gmx::SelectionOption;
253     options_.addOption(SelectionOption("sel").store(&sel).defaultSelectionText("all"));
254 
255     EXPECT_NO_THROW_GMX(options_.finish());
256 
257     ASSERT_TRUE(sel.isValid());
258 
259     EXPECT_NO_THROW_GMX(sc_.setTopology(nullptr, 10));
260     EXPECT_NO_THROW_GMX(sc_.compile());
261 
262     EXPECT_STREQ("all", sel.selectionText());
263 }
264 
265 
TEST_F(SelectionOptionTest,HandlesAdjuster)266 TEST_F(SelectionOptionTest, HandlesAdjuster)
267 {
268     gmx::SelectionList sel;
269     using gmx::SelectionOption;
270     gmx::SelectionOptionInfo* info =
271             options_.addOption(SelectionOption("sel").storeVector(&sel).multiValue());
272 
273     gmx::OptionsAssigner assigner(&options_);
274     EXPECT_NO_THROW_GMX(assigner.start());
275     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
276     EXPECT_NO_THROW_GMX(assigner.appendValue("resname RA RB"));
277     EXPECT_NO_THROW_GMX(assigner.appendValue("resname RB RC"));
278     EXPECT_NO_THROW_GMX(assigner.finishOption());
279     EXPECT_NO_THROW_GMX(assigner.finish());
280     EXPECT_NO_THROW_GMX(options_.finish());
281     EXPECT_NO_THROW_GMX(info->setValueCount(2));
282 }
283 
284 
TEST_F(SelectionOptionTest,HandlesDynamicWhenStaticRequiredWithAdjuster)285 TEST_F(SelectionOptionTest, HandlesDynamicWhenStaticRequiredWithAdjuster)
286 {
287     gmx::Selection sel;
288     using gmx::SelectionOption;
289     gmx::SelectionOptionInfo* info = options_.addOption(SelectionOption("sel").store(&sel));
290 
291     gmx::OptionsAssigner assigner(&options_);
292     EXPECT_NO_THROW_GMX(assigner.start());
293     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
294     EXPECT_NO_THROW_GMX(assigner.appendValue("x < 5"));
295     EXPECT_NO_THROW_GMX(assigner.finishOption());
296     EXPECT_NO_THROW_GMX(assigner.finish());
297     EXPECT_NO_THROW_GMX(options_.finish());
298     EXPECT_THROW_GMX(info->setOnlyStatic(true), gmx::InvalidInputError);
299 }
300 
301 
TEST_F(SelectionOptionTest,HandlesTooManySelectionsWithAdjuster)302 TEST_F(SelectionOptionTest, HandlesTooManySelectionsWithAdjuster)
303 {
304     gmx::SelectionList sel;
305     using gmx::SelectionOption;
306     gmx::SelectionOptionInfo* info =
307             options_.addOption(SelectionOption("sel").storeVector(&sel).multiValue());
308 
309     gmx::OptionsAssigner assigner(&options_);
310     EXPECT_NO_THROW_GMX(assigner.start());
311     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
312     EXPECT_NO_THROW_GMX(assigner.appendValue("resname RA RB"));
313     EXPECT_NO_THROW_GMX(assigner.appendValue("resname RB RC"));
314     EXPECT_NO_THROW_GMX(assigner.finishOption());
315     EXPECT_NO_THROW_GMX(assigner.finish());
316     EXPECT_NO_THROW_GMX(options_.finish());
317     EXPECT_THROW_GMX(info->setValueCount(1), gmx::InvalidInputError);
318 }
319 
320 
TEST_F(SelectionOptionTest,HandlesTooFewSelectionsWithAdjuster)321 TEST_F(SelectionOptionTest, HandlesTooFewSelectionsWithAdjuster)
322 {
323     gmx::SelectionList sel;
324     using gmx::SelectionOption;
325     gmx::SelectionOptionInfo* info =
326             options_.addOption(SelectionOption("sel").storeVector(&sel).multiValue());
327 
328     gmx::OptionsAssigner assigner(&options_);
329     EXPECT_NO_THROW_GMX(assigner.start());
330     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
331     EXPECT_NO_THROW_GMX(assigner.appendValue("resname RA RB"));
332     EXPECT_NO_THROW_GMX(assigner.finishOption());
333     EXPECT_NO_THROW_GMX(assigner.finish());
334     EXPECT_NO_THROW_GMX(options_.finish());
335     EXPECT_THROW_GMX(info->setValueCount(2), gmx::InvalidInputError);
336 }
337 
338 
TEST_F(SelectionOptionTest,HandlesDelayedRequiredSelection)339 TEST_F(SelectionOptionTest, HandlesDelayedRequiredSelection)
340 {
341     gmx::Selection sel;
342     using gmx::SelectionOption;
343     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").store(&sel).required()));
344 
345     gmx::OptionsAssigner assigner(&options_);
346     EXPECT_NO_THROW_GMX(assigner.start());
347     EXPECT_NO_THROW_GMX(assigner.finish());
348     EXPECT_NO_THROW_GMX(options_.finish());
349     ASSERT_NO_THROW_GMX(manager_.parseRequestedFromString("resname RA RB"));
350     ASSERT_STREQ("resname RA RB", sel.selectionText());
351 }
352 
353 
TEST_F(SelectionOptionTest,HandlesTooFewDelayedRequiredSelections)354 TEST_F(SelectionOptionTest, HandlesTooFewDelayedRequiredSelections)
355 {
356     gmx::Selection sel[2];
357     using gmx::SelectionOption;
358     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").store(sel).required().valueCount(2)));
359 
360     gmx::OptionsAssigner assigner(&options_);
361     EXPECT_NO_THROW_GMX(assigner.start());
362     EXPECT_NO_THROW_GMX(assigner.finish());
363     EXPECT_NO_THROW_GMX(options_.finish());
364     EXPECT_THROW_GMX(manager_.parseRequestedFromString("resname RA RB"), gmx::InvalidInputError);
365 }
366 
367 
TEST_F(SelectionOptionTest,HandlesDelayedOptionalSelection)368 TEST_F(SelectionOptionTest, HandlesDelayedOptionalSelection)
369 {
370     gmx::Selection sel;
371     using gmx::SelectionOption;
372     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").store(&sel)));
373 
374     gmx::OptionsAssigner assigner(&options_);
375     EXPECT_NO_THROW_GMX(assigner.start());
376     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
377     EXPECT_NO_THROW_GMX(assigner.finishOption());
378     EXPECT_NO_THROW_GMX(assigner.finish());
379     EXPECT_NO_THROW_GMX(options_.finish());
380     ASSERT_NO_THROW_GMX(manager_.parseRequestedFromString("resname RA RB"));
381     ASSERT_STREQ("resname RA RB", sel.selectionText());
382 }
383 
384 
TEST_F(SelectionOptionTest,HandlesDelayedSelectionWithAdjuster)385 TEST_F(SelectionOptionTest, HandlesDelayedSelectionWithAdjuster)
386 {
387     gmx::SelectionList sel;
388     using gmx::SelectionOption;
389     gmx::SelectionOptionInfo* info =
390             options_.addOption(SelectionOption("sel").storeVector(&sel).valueCount(3));
391 
392     gmx::OptionsAssigner assigner(&options_);
393     EXPECT_NO_THROW_GMX(assigner.start());
394     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
395     EXPECT_NO_THROW_GMX(assigner.finishOption());
396     EXPECT_NO_THROW_GMX(assigner.finish());
397     EXPECT_NO_THROW_GMX(options_.finish());
398     EXPECT_NO_THROW_GMX(info->setValueCount(2));
399     EXPECT_NO_THROW_GMX(manager_.parseRequestedFromString("resname RA RB; resname RB RC"));
400 }
401 
402 
403 /********************************************************************
404  * Tests for SelectionFileOption
405  */
406 
407 class SelectionFileOptionTest : public SelectionOptionTestBase
408 {
409 public:
410     SelectionFileOptionTest();
411 };
412 
SelectionFileOptionTest()413 SelectionFileOptionTest::SelectionFileOptionTest()
414 {
415     options_.addOption(gmx::SelectionFileOption("sf"));
416 }
417 
418 
TEST_F(SelectionFileOptionTest,HandlesSingleSelectionOptionFromFile)419 TEST_F(SelectionFileOptionTest, HandlesSingleSelectionOptionFromFile)
420 {
421     gmx::SelectionList sel;
422     gmx::SelectionList reqsel;
423     using gmx::SelectionOption;
424     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").storeVector(&sel).multiValue()));
425     ASSERT_NO_THROW_GMX(
426             options_.addOption(SelectionOption("reqsel").storeVector(&reqsel).multiValue().required()));
427 
428     gmx::OptionsAssigner assigner(&options_);
429     EXPECT_NO_THROW_GMX(assigner.start());
430     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
431     EXPECT_NO_THROW_GMX(assigner.finishOption());
432     ASSERT_NO_THROW_GMX(assigner.startOption("sf"));
433     EXPECT_NO_THROW_GMX(assigner.appendValue(TestFileManager::getInputFilePath("selfile.dat")));
434     EXPECT_NO_THROW_GMX(assigner.finishOption());
435     EXPECT_NO_THROW_GMX(assigner.finish());
436     EXPECT_NO_THROW_GMX(options_.finish());
437 
438     // These should match the contents of selfile.dat
439     ASSERT_EQ(2U, sel.size());
440     EXPECT_STREQ("resname RA RB", sel[0].selectionText());
441     EXPECT_STREQ("resname RB RC", sel[1].selectionText());
442     ASSERT_EQ(0U, reqsel.size());
443 }
444 
445 
TEST_F(SelectionFileOptionTest,HandlesTwoSeparateSelectionOptions)446 TEST_F(SelectionFileOptionTest, HandlesTwoSeparateSelectionOptions)
447 {
448     gmx::SelectionList sel1;
449     gmx::SelectionList sel2;
450     using gmx::SelectionOption;
451     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel1").storeVector(&sel1).multiValue()));
452     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel2").storeVector(&sel2).multiValue()));
453 
454     gmx::OptionsAssigner assigner(&options_);
455     std::string          value(TestFileManager::getInputFilePath("selfile.dat"));
456     EXPECT_NO_THROW_GMX(assigner.start());
457     ASSERT_NO_THROW_GMX(assigner.startOption("sel1"));
458     EXPECT_NO_THROW_GMX(assigner.finishOption());
459     ASSERT_NO_THROW_GMX(assigner.startOption("sf"));
460     EXPECT_NO_THROW_GMX(assigner.appendValue(value));
461     EXPECT_NO_THROW_GMX(assigner.finishOption());
462     ASSERT_NO_THROW_GMX(assigner.startOption("sel2"));
463     EXPECT_NO_THROW_GMX(assigner.finishOption());
464     ASSERT_NO_THROW_GMX(assigner.startOption("sf"));
465     EXPECT_NO_THROW_GMX(assigner.appendValue(value));
466     EXPECT_NO_THROW_GMX(assigner.finishOption());
467     EXPECT_NO_THROW_GMX(assigner.finish());
468     EXPECT_NO_THROW_GMX(options_.finish());
469 
470     // These should match the contents of selfile.dat
471     ASSERT_EQ(2U, sel1.size());
472     EXPECT_STREQ("resname RA RB", sel1[0].selectionText());
473     EXPECT_STREQ("resname RB RC", sel1[1].selectionText());
474     ASSERT_EQ(2U, sel2.size());
475     EXPECT_STREQ("resname RA RB", sel2[0].selectionText());
476     EXPECT_STREQ("resname RB RC", sel2[1].selectionText());
477 }
478 
479 
TEST_F(SelectionFileOptionTest,HandlesTwoSelectionOptionsFromSingleFile)480 TEST_F(SelectionFileOptionTest, HandlesTwoSelectionOptionsFromSingleFile)
481 {
482     gmx::SelectionList sel1;
483     gmx::SelectionList sel2;
484     using gmx::SelectionOption;
485     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel1").storeVector(&sel1)));
486     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel2").storeVector(&sel2)));
487 
488     gmx::OptionsAssigner assigner(&options_);
489     std::string          value(TestFileManager::getInputFilePath("selfile.dat"));
490     EXPECT_NO_THROW_GMX(assigner.start());
491     ASSERT_NO_THROW_GMX(assigner.startOption("sel1"));
492     EXPECT_NO_THROW_GMX(assigner.finishOption());
493     ASSERT_NO_THROW_GMX(assigner.startOption("sel2"));
494     EXPECT_NO_THROW_GMX(assigner.finishOption());
495     ASSERT_NO_THROW_GMX(assigner.startOption("sf"));
496     EXPECT_NO_THROW_GMX(assigner.appendValue(value));
497     EXPECT_NO_THROW_GMX(assigner.finishOption());
498     EXPECT_NO_THROW_GMX(assigner.finish());
499     EXPECT_NO_THROW_GMX(options_.finish());
500 
501     // These should match the contents of selfile.dat
502     ASSERT_EQ(1U, sel1.size());
503     EXPECT_STREQ("resname RA RB", sel1[0].selectionText());
504     ASSERT_EQ(1U, sel2.size());
505     EXPECT_STREQ("resname RB RC", sel2[0].selectionText());
506 }
507 
508 
TEST_F(SelectionFileOptionTest,HandlesRequiredOptionFromFile)509 TEST_F(SelectionFileOptionTest, HandlesRequiredOptionFromFile)
510 {
511     gmx::SelectionList sel;
512     gmx::SelectionList optsel;
513     using gmx::SelectionOption;
514     ASSERT_NO_THROW_GMX(
515             options_.addOption(SelectionOption("sel").storeVector(&sel).multiValue().required()));
516     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("optsel").storeVector(&optsel).multiValue()));
517 
518     gmx::OptionsAssigner assigner(&options_);
519     EXPECT_NO_THROW_GMX(assigner.start());
520     ASSERT_NO_THROW_GMX(assigner.startOption("sf"));
521     EXPECT_NO_THROW_GMX(assigner.appendValue(TestFileManager::getInputFilePath("selfile.dat")));
522     EXPECT_NO_THROW_GMX(assigner.finishOption());
523     EXPECT_NO_THROW_GMX(assigner.startOption("optsel"));
524     EXPECT_NO_THROW_GMX(assigner.finishOption());
525     EXPECT_NO_THROW_GMX(assigner.finish());
526     EXPECT_NO_THROW_GMX(options_.finish());
527     EXPECT_NO_THROW_GMX(manager_.parseRequestedFromString("resname RC RD"));
528 
529     // These should match the contents of selfile.dat
530     ASSERT_EQ(2U, sel.size());
531     EXPECT_STREQ("resname RA RB", sel[0].selectionText());
532     EXPECT_STREQ("resname RB RC", sel[1].selectionText());
533     ASSERT_EQ(1U, optsel.size());
534     EXPECT_STREQ("resname RC RD", optsel[0].selectionText());
535 }
536 
537 
538 // TODO: Is this the best possible behavior, or should it error out?
TEST_F(SelectionFileOptionTest,HandlesRequiredOptionFromFileWithOtherOptionSet)539 TEST_F(SelectionFileOptionTest, HandlesRequiredOptionFromFileWithOtherOptionSet)
540 {
541     gmx::SelectionList sel1;
542     gmx::SelectionList sel2;
543     using gmx::SelectionOption;
544     ASSERT_NO_THROW_GMX(
545             options_.addOption(SelectionOption("sel1").storeVector(&sel1).multiValue().required()));
546     ASSERT_NO_THROW_GMX(
547             options_.addOption(SelectionOption("sel2").storeVector(&sel2).multiValue().required()));
548 
549     gmx::OptionsAssigner assigner(&options_);
550     EXPECT_NO_THROW_GMX(assigner.start());
551     EXPECT_NO_THROW_GMX(assigner.startOption("sel1"));
552     EXPECT_NO_THROW_GMX(assigner.appendValue("resname RC RD"));
553     EXPECT_NO_THROW_GMX(assigner.finishOption());
554     ASSERT_NO_THROW_GMX(assigner.startOption("sf"));
555     EXPECT_NO_THROW_GMX(assigner.appendValue(TestFileManager::getInputFilePath("selfile.dat")));
556     EXPECT_NO_THROW_GMX(assigner.finishOption());
557     EXPECT_NO_THROW_GMX(assigner.finish());
558     EXPECT_NO_THROW_GMX(options_.finish());
559 
560     // These should match the contents of selfile.dat
561     ASSERT_EQ(2U, sel2.size());
562     EXPECT_STREQ("resname RA RB", sel2[0].selectionText());
563     EXPECT_STREQ("resname RB RC", sel2[1].selectionText());
564     ASSERT_EQ(1U, sel1.size());
565     EXPECT_STREQ("resname RC RD", sel1[0].selectionText());
566 }
567 
568 
TEST_F(SelectionFileOptionTest,HandlesTwoRequiredOptionsFromSingleFile)569 TEST_F(SelectionFileOptionTest, HandlesTwoRequiredOptionsFromSingleFile)
570 {
571     gmx::SelectionList sel1;
572     gmx::SelectionList sel2;
573     using gmx::SelectionOption;
574     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel1").storeVector(&sel1).required()));
575     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel2").storeVector(&sel2).required()));
576 
577     gmx::OptionsAssigner assigner(&options_);
578     std::string          value(TestFileManager::getInputFilePath("selfile.dat"));
579     EXPECT_NO_THROW_GMX(assigner.start());
580     ASSERT_NO_THROW_GMX(assigner.startOption("sf"));
581     EXPECT_NO_THROW_GMX(assigner.appendValue(value));
582     EXPECT_NO_THROW_GMX(assigner.finishOption());
583     EXPECT_NO_THROW_GMX(assigner.finish());
584     EXPECT_NO_THROW_GMX(options_.finish());
585 
586     // These should match the contents of selfile.dat
587     ASSERT_EQ(1U, sel1.size());
588     EXPECT_STREQ("resname RA RB", sel1[0].selectionText());
589     ASSERT_EQ(1U, sel2.size());
590     EXPECT_STREQ("resname RB RC", sel2[0].selectionText());
591 }
592 
593 
TEST_F(SelectionFileOptionTest,GivesErrorWithNoFile)594 TEST_F(SelectionFileOptionTest, GivesErrorWithNoFile)
595 {
596     gmx::SelectionList sel;
597     using gmx::SelectionOption;
598     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").storeVector(&sel).multiValue()));
599 
600     gmx::OptionsAssigner assigner(&options_);
601     EXPECT_NO_THROW_GMX(assigner.start());
602     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
603     EXPECT_NO_THROW_GMX(assigner.finishOption());
604     ASSERT_NO_THROW_GMX(assigner.startOption("sf"));
605     EXPECT_THROW_GMX(assigner.finishOption(), gmx::InvalidInputError);
606     EXPECT_NO_THROW_GMX(assigner.finish());
607     EXPECT_NO_THROW_GMX(options_.finish());
608 }
609 
610 
TEST_F(SelectionFileOptionTest,GivesErrorWithNonExistentFile)611 TEST_F(SelectionFileOptionTest, GivesErrorWithNonExistentFile)
612 {
613     gmx::SelectionList sel;
614     using gmx::SelectionOption;
615     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").storeVector(&sel).multiValue()));
616 
617     gmx::OptionsAssigner assigner(&options_);
618     EXPECT_NO_THROW_GMX(assigner.start());
619     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
620     EXPECT_NO_THROW_GMX(assigner.finishOption());
621     ASSERT_NO_THROW_GMX(assigner.startOption("sf"));
622     // TODO: Should this be changed to an InvalidInputError?
623     EXPECT_THROW_GMX(assigner.appendValue("nonexistentfile"), gmx::FileIOError);
624     EXPECT_THROW_GMX(assigner.appendValue(TestFileManager::getInputFilePath("selfile.dat")),
625                      gmx::InvalidInputError);
626     EXPECT_NO_THROW_GMX(assigner.finishOption());
627     EXPECT_NO_THROW_GMX(assigner.finish());
628     EXPECT_NO_THROW_GMX(options_.finish());
629 }
630 
631 
TEST_F(SelectionFileOptionTest,GivesErrorWithMultipleFiles)632 TEST_F(SelectionFileOptionTest, GivesErrorWithMultipleFiles)
633 {
634     gmx::SelectionList sel;
635     using gmx::SelectionOption;
636     ASSERT_NO_THROW_GMX(options_.addOption(SelectionOption("sel").storeVector(&sel).multiValue()));
637 
638     gmx::OptionsAssigner assigner(&options_);
639     EXPECT_NO_THROW_GMX(assigner.start());
640     ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
641     EXPECT_NO_THROW_GMX(assigner.finishOption());
642     ASSERT_NO_THROW_GMX(assigner.startOption("sf"));
643     EXPECT_NO_THROW_GMX(assigner.appendValue(TestFileManager::getInputFilePath("selfile.dat")));
644     EXPECT_THROW_GMX(assigner.appendValue("nonexistentfile"), gmx::InvalidInputError);
645     EXPECT_NO_THROW_GMX(assigner.finishOption());
646     EXPECT_NO_THROW_GMX(assigner.finish());
647     EXPECT_NO_THROW_GMX(options_.finish());
648 }
649 
650 } // namespace
651