Tests for interactive selection input
[alexxy/gromacs.git] / src / gromacs / selection / tests / selectioncollection.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2010,2011,2012,2013,2014,2015, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Tests selection parsing and compilation.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_selection
41  */
42 #include "gmxpre.h"
43
44 #include "gromacs/selection/selectioncollection.h"
45
46 #include <gtest/gtest.h>
47
48 #include "gromacs/fileio/trx.h"
49 #include "gromacs/options/basicoptions.h"
50 #include "gromacs/options/options.h"
51 #include "gromacs/selection/indexutil.h"
52 #include "gromacs/selection/selection.h"
53 #include "gromacs/topology/topology.h"
54 #include "gromacs/utility/arrayref.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/flags.h"
57 #include "gromacs/utility/gmxregex.h"
58 #include "gromacs/utility/stringutil.h"
59
60 #include "testutils/interactivetest.h"
61 #include "testutils/refdata.h"
62 #include "testutils/testasserts.h"
63 #include "testutils/testfilemanager.h"
64 #include "testutils/testoptions.h"
65
66 #include "toputils.h"
67
68 namespace
69 {
70
71 /********************************************************************
72  * Test fixture for selection testing
73  */
74
75 class SelectionCollectionTest : public ::testing::Test
76 {
77     public:
78         static int               s_debugLevel;
79
80         SelectionCollectionTest();
81         ~SelectionCollectionTest();
82
83         void setAtomCount(int natoms)
84         {
85             ASSERT_NO_THROW_GMX(sc_.setTopology(NULL, natoms));
86         }
87         void loadTopology(const char *filename);
88         void setTopology();
89         void loadIndexGroups(const char *filename);
90
91         gmx::test::TopologyManager  topManager_;
92         gmx::SelectionCollection    sc_;
93         gmx::SelectionList          sel_;
94         t_topology                 *top_;
95         t_trxframe                 *frame_;
96         gmx_ana_indexgrps_t        *grps_;
97 };
98
99 int SelectionCollectionTest::s_debugLevel = 0;
100
101 // \cond/\endcond do not seem to work here with Doxygen 1.8.5 parser.
102 #ifndef DOXYGEN
103 GMX_TEST_OPTIONS(SelectionCollectionTestOptions, options)
104 {
105     options->addOption(gmx::IntegerOption("seldebug")
106                            .store(&SelectionCollectionTest::s_debugLevel)
107                            .description("Set selection debug level"));
108 }
109 #endif
110
111 SelectionCollectionTest::SelectionCollectionTest()
112     : top_(NULL), frame_(NULL), grps_(NULL)
113 {
114     topManager_.requestFrame();
115     sc_.setDebugLevel(s_debugLevel);
116     sc_.setReferencePosType("atom");
117     sc_.setOutputPosType("atom");
118 }
119
120 SelectionCollectionTest::~SelectionCollectionTest()
121 {
122     if (grps_ != NULL)
123     {
124         gmx_ana_indexgrps_free(grps_);
125     }
126 }
127
128 void
129 SelectionCollectionTest::loadTopology(const char *filename)
130 {
131     topManager_.loadTopology(filename);
132     setTopology();
133 }
134
135 void
136 SelectionCollectionTest::setTopology()
137 {
138     top_   = topManager_.topology();
139     frame_ = topManager_.frame();
140
141     ASSERT_NO_THROW_GMX(sc_.setTopology(top_, -1));
142 }
143
144 void
145 SelectionCollectionTest::loadIndexGroups(const char *filename)
146 {
147     GMX_RELEASE_ASSERT(grps_ == NULL,
148                        "External groups can only be loaded once");
149     std::string fullpath =
150         gmx::test::TestFileManager::getInputFilePath(filename);
151     gmx_ana_indexgrps_init(&grps_, NULL, fullpath.c_str());
152     sc_.setIndexGroups(grps_);
153 }
154
155
156 /********************************************************************
157  * Test fixture for interactive SelectionCollection tests
158  */
159
160 class SelectionCollectionInteractiveTest : public SelectionCollectionTest
161 {
162     public:
163         SelectionCollectionInteractiveTest()
164             : helper_(data_.rootChecker())
165         {
166         }
167
168         void runTest(int count, bool bInteractive,
169                      const gmx::ConstArrayRef<const char *> &input);
170
171         gmx::test::TestReferenceData      data_;
172         gmx::test::InteractiveTestHelper  helper_;
173 };
174
175 void SelectionCollectionInteractiveTest::runTest(
176         int count, bool bInteractive,
177         const gmx::ConstArrayRef<const char *> &inputLines)
178 {
179     helper_.setInputLines(inputLines);
180     // TODO: Check something about the returned selections as well.
181     ASSERT_NO_THROW_GMX(sc_.parseInteractive(
182                                 count, &helper_.inputStream(),
183                                 bInteractive ? &helper_.outputStream() : NULL,
184                                 "for test context"));
185     helper_.checkSession();
186 }
187
188
189 /********************************************************************
190  * Test fixture for selection testing with reference data
191  */
192
193 class SelectionCollectionDataTest : public SelectionCollectionTest
194 {
195     public:
196         enum TestFlag
197         {
198             efTestEvaluation            = 1<<0,
199             efTestPositionAtoms         = 1<<1,
200             efTestPositionCoordinates   = 1<<2,
201             efTestPositionMapping       = 1<<3,
202             efTestPositionMasses        = 1<<4,
203             efTestPositionCharges       = 1<<5,
204             efTestSelectionNames        = 1<<6,
205             efDontTestCompiledAtoms     = 1<<8
206         };
207         typedef gmx::FlagsTemplate<TestFlag> TestFlags;
208
209         SelectionCollectionDataTest()
210             : checker_(data_.rootChecker()), count_(0), framenr_(0)
211         {
212         }
213
214         void setFlags(TestFlags flags) { flags_ = flags; }
215
216         void runParser(const gmx::ConstArrayRef<const char *> &selections);
217         void runCompiler();
218         void runEvaluate();
219         void runEvaluateFinal();
220
221         void runTest(int                                     natoms,
222                      const gmx::ConstArrayRef<const char *> &selections);
223         void runTest(const char                             *filename,
224                      const gmx::ConstArrayRef<const char *> &selections);
225
226     private:
227         static void checkSelection(gmx::test::TestReferenceChecker *checker,
228                                    const gmx::Selection &sel, TestFlags flags);
229
230         void checkCompiled();
231
232         gmx::test::TestReferenceData    data_;
233         gmx::test::TestReferenceChecker checker_;
234         size_t                          count_;
235         int                             framenr_;
236         TestFlags                       flags_;
237 };
238
239
240 void
241 SelectionCollectionDataTest::checkSelection(
242         gmx::test::TestReferenceChecker *checker,
243         const gmx::Selection &sel, TestFlags flags)
244 {
245     using gmx::test::TestReferenceChecker;
246
247     {
248         gmx::ConstArrayRef<int> atoms = sel.atomIndices();
249         checker->checkSequence(atoms.begin(), atoms.end(), "Atoms");
250     }
251     if (flags.test(efTestPositionAtoms)
252         || flags.test(efTestPositionCoordinates)
253         || flags.test(efTestPositionMapping)
254         || flags.test(efTestPositionMasses)
255         || flags.test(efTestPositionCharges))
256     {
257         TestReferenceChecker compound(
258                 checker->checkSequenceCompound("Positions", sel.posCount()));
259         for (int i = 0; i < sel.posCount(); ++i)
260         {
261             TestReferenceChecker          poscompound(compound.checkCompound("Position", NULL));
262             const gmx::SelectionPosition &p = sel.position(i);
263             if (flags.test(efTestPositionAtoms))
264             {
265                 gmx::ConstArrayRef<int> atoms = p.atomIndices();
266                 poscompound.checkSequence(atoms.begin(), atoms.end(), "Atoms");
267             }
268             if (flags.test(efTestPositionCoordinates))
269             {
270                 poscompound.checkVector(p.x(), "Coordinates");
271             }
272             if (flags.test(efTestPositionMapping))
273             {
274                 poscompound.checkInteger(p.refId(), "RefId");
275                 poscompound.checkInteger(p.mappedId(), "MappedId");
276             }
277             if (flags.test(efTestPositionMasses))
278             {
279                 poscompound.checkReal(p.mass(), "Mass");
280             }
281             if (flags.test(efTestPositionCharges))
282             {
283                 poscompound.checkReal(p.charge(), "Charge");
284             }
285         }
286     }
287 }
288
289
290 void
291 SelectionCollectionDataTest::runParser(
292         const gmx::ConstArrayRef<const char *> &selections)
293 {
294     using gmx::test::TestReferenceChecker;
295
296     TestReferenceChecker compound(checker_.checkCompound("ParsedSelections", "Parsed"));
297     size_t               varcount = 0;
298     count_ = 0;
299     for (size_t i = 0; i < selections.size(); ++i)
300     {
301         SCOPED_TRACE(std::string("Parsing selection \"")
302                      + selections[i] + "\"");
303         gmx::SelectionList result;
304         ASSERT_NO_THROW_GMX(result = sc_.parseFromString(selections[i]));
305         sel_.insert(sel_.end(), result.begin(), result.end());
306         if (sel_.size() == count_)
307         {
308             std::string          id = gmx::formatString("Variable%d", static_cast<int>(varcount + 1));
309             TestReferenceChecker varcompound(
310                     compound.checkCompound("ParsedVariable", id.c_str()));
311             varcompound.checkString(selections[i], "Input");
312             ++varcount;
313         }
314         else
315         {
316             std::string          id = gmx::formatString("Selection%d", static_cast<int>(count_ + 1));
317             TestReferenceChecker selcompound(
318                     compound.checkCompound("ParsedSelection", id.c_str()));
319             selcompound.checkString(selections[i], "Input");
320             if (flags_.test(efTestSelectionNames))
321             {
322                 selcompound.checkString(sel_[count_].name(), "Name");
323             }
324             selcompound.checkString(sel_[count_].selectionText(), "Text");
325             selcompound.checkBoolean(sel_[count_].isDynamic(), "Dynamic");
326             ++count_;
327         }
328     }
329 }
330
331
332 void
333 SelectionCollectionDataTest::runCompiler()
334 {
335     ASSERT_NO_THROW_GMX(sc_.compile());
336     ASSERT_EQ(count_, sel_.size());
337     checkCompiled();
338 }
339
340
341 void
342 SelectionCollectionDataTest::checkCompiled()
343 {
344     using gmx::test::TestReferenceChecker;
345     const TestFlags      mask = ~TestFlags(efTestPositionCoordinates);
346
347     TestReferenceChecker compound(checker_.checkCompound("CompiledSelections", "Compiled"));
348     for (size_t i = 0; i < count_; ++i)
349     {
350         SCOPED_TRACE(std::string("Checking selection \"") +
351                      sel_[i].selectionText() + "\"");
352         std::string          id = gmx::formatString("Selection%d", static_cast<int>(i + 1));
353         TestReferenceChecker selcompound(
354                 compound.checkCompound("Selection", id.c_str()));
355         if (flags_.test(efTestSelectionNames))
356         {
357             selcompound.checkString(sel_[i].name(), "Name");
358         }
359         if (!flags_.test(efDontTestCompiledAtoms))
360         {
361             checkSelection(&selcompound, sel_[i], flags_ & mask);
362         }
363     }
364 }
365
366
367 void
368 SelectionCollectionDataTest::runEvaluate()
369 {
370     using gmx::test::TestReferenceChecker;
371
372     ++framenr_;
373     ASSERT_NO_THROW_GMX(sc_.evaluate(frame_, NULL));
374     std::string          frame = gmx::formatString("Frame%d", framenr_);
375     TestReferenceChecker compound(
376             checker_.checkCompound("EvaluatedSelections", frame.c_str()));
377     for (size_t i = 0; i < count_; ++i)
378     {
379         SCOPED_TRACE(std::string("Checking selection \"") +
380                      sel_[i].selectionText() + "\"");
381         std::string          id = gmx::formatString("Selection%d", static_cast<int>(i + 1));
382         TestReferenceChecker selcompound(
383                 compound.checkCompound("Selection", id.c_str()));
384         checkSelection(&selcompound, sel_[i], flags_);
385     }
386 }
387
388
389 void
390 SelectionCollectionDataTest::runEvaluateFinal()
391 {
392     ASSERT_NO_THROW_GMX(sc_.evaluateFinal(framenr_));
393     checkCompiled();
394 }
395
396
397 void
398 SelectionCollectionDataTest::runTest(
399         int natoms, const gmx::ConstArrayRef<const char *> &selections)
400 {
401     ASSERT_NO_FATAL_FAILURE(runParser(selections));
402     ASSERT_NO_FATAL_FAILURE(setAtomCount(natoms));
403     ASSERT_NO_FATAL_FAILURE(runCompiler());
404 }
405
406
407 void
408 SelectionCollectionDataTest::runTest(
409         const char *filename, const gmx::ConstArrayRef<const char *> &selections)
410 {
411     ASSERT_NO_FATAL_FAILURE(runParser(selections));
412     ASSERT_NO_FATAL_FAILURE(loadTopology(filename));
413     ASSERT_NO_FATAL_FAILURE(runCompiler());
414     if (flags_.test(efTestEvaluation))
415     {
416         ASSERT_NO_FATAL_FAILURE(runEvaluate());
417         ASSERT_NO_FATAL_FAILURE(runEvaluateFinal());
418     }
419 }
420
421
422 /********************************************************************
423  * Tests for SelectionCollection functionality without reference data
424  */
425
426 TEST_F(SelectionCollectionTest, HandlesNoSelections)
427 {
428     EXPECT_FALSE(sc_.requiresTopology());
429     EXPECT_NO_THROW_GMX(sc_.compile());
430 }
431
432 TEST_F(SelectionCollectionTest, HandlesVelocityAndForceRequests)
433 {
434     ASSERT_NO_THROW_GMX(sel_ = sc_.parseFromString("atomnr 1 to 10; none"));
435     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
436     ASSERT_EQ(2U, sel_.size());
437     ASSERT_NO_THROW_GMX(sel_[0].setEvaluateVelocities(true));
438     ASSERT_NO_THROW_GMX(sel_[1].setEvaluateVelocities(true));
439     ASSERT_NO_THROW_GMX(sel_[0].setEvaluateForces(true));
440     ASSERT_NO_THROW_GMX(sel_[1].setEvaluateForces(true));
441     ASSERT_NO_THROW_GMX(sc_.compile());
442     EXPECT_TRUE(sel_[0].hasVelocities());
443     EXPECT_TRUE(sel_[1].hasVelocities());
444     EXPECT_TRUE(sel_[0].hasForces());
445     EXPECT_TRUE(sel_[1].hasForces());
446 }
447
448 TEST_F(SelectionCollectionTest, ParsesSelectionsFromFile)
449 {
450     ASSERT_NO_THROW_GMX(sel_ = sc_.parseFromFile(
451                                     gmx::test::TestFileManager::getInputFilePath("selfile.dat")));
452     // These should match the contents of selfile.dat
453     ASSERT_EQ(2U, sel_.size());
454     EXPECT_STREQ("resname RA RB", sel_[0].selectionText());
455     EXPECT_STREQ("resname RB RC", sel_[1].selectionText());
456 }
457
458 TEST_F(SelectionCollectionTest, HandlesAtypicalWhitespace)
459 {
460     ASSERT_NO_THROW_GMX(sel_ = sc_.parseFromString("atomnr\n1\r\nto\t10;\vatomnr 3\f to 14\r"));
461     ASSERT_EQ(2U, sel_.size());
462     EXPECT_STREQ("atomnr 1 to 10", sel_[0].selectionText());
463     // TODO: Get rid of the trailing whitespace.
464     EXPECT_STREQ("atomnr 3 to 14 ", sel_[1].selectionText());
465 }
466
467 TEST_F(SelectionCollectionTest, HandlesInvalidRegularExpressions)
468 {
469     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
470     EXPECT_THROW_GMX({
471                          sc_.parseFromString("resname ~ \"R[A\"");
472                          sc_.compile();
473                      }, gmx::InvalidInputError);
474 }
475
476 TEST_F(SelectionCollectionTest, HandlesUnsupportedRegularExpressions)
477 {
478     if (!gmx::Regex::isSupported())
479     {
480         ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
481         EXPECT_THROW_GMX({
482                              sc_.parseFromString("resname \"R[AD]\"");
483                              sc_.compile();
484                          }, gmx::InvalidInputError);
485     }
486 }
487
488 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue)
489 {
490     EXPECT_THROW_GMX(sc_.parseFromString("mindist from atomnr 1 cutoff"),
491                      gmx::InvalidInputError);
492 }
493
494 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue2)
495 {
496     EXPECT_THROW_GMX(sc_.parseFromString("within 1 of"),
497                      gmx::InvalidInputError);
498 }
499
500 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue3)
501 {
502     EXPECT_THROW_GMX(sc_.parseFromString("within of atomnr 1"),
503                      gmx::InvalidInputError);
504 }
505
506 // TODO: Tests for more parser errors
507
508 TEST_F(SelectionCollectionTest, HandlesUnknownGroupReferenceParser1)
509 {
510     ASSERT_NO_THROW_GMX(sc_.setIndexGroups(NULL));
511     EXPECT_THROW_GMX(sc_.parseFromString("group \"foo\""), gmx::InconsistentInputError);
512     EXPECT_THROW_GMX(sc_.parseFromString("4"), gmx::InconsistentInputError);
513 }
514
515 TEST_F(SelectionCollectionTest, HandlesUnknownGroupReferenceParser2)
516 {
517     ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
518     EXPECT_THROW_GMX(sc_.parseFromString("group \"foo\""), gmx::InconsistentInputError);
519     EXPECT_THROW_GMX(sc_.parseFromString("4"), gmx::InconsistentInputError);
520 }
521
522 TEST_F(SelectionCollectionTest, HandlesUnknownGroupReferenceDelayed1)
523 {
524     ASSERT_NO_THROW_GMX(sc_.parseFromString("group \"foo\""));
525     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
526     EXPECT_THROW_GMX(sc_.setIndexGroups(NULL), gmx::InconsistentInputError);
527     EXPECT_THROW_GMX(sc_.compile(), gmx::APIError);
528 }
529
530 TEST_F(SelectionCollectionTest, HandlesUnknownGroupReferenceDelayed2)
531 {
532     ASSERT_NO_THROW_GMX(sc_.parseFromString("group 4; group \"foo\""));
533     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
534     EXPECT_THROW_GMX(loadIndexGroups("simple.ndx"), gmx::InconsistentInputError);
535     EXPECT_THROW_GMX(sc_.compile(), gmx::APIError);
536 }
537
538 TEST_F(SelectionCollectionTest, HandlesUnsortedGroupReference)
539 {
540     ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
541     EXPECT_THROW_GMX(sc_.parseFromString("atomnr 1 to 3 and group \"GrpUnsorted\""),
542                      gmx::InconsistentInputError);
543     EXPECT_THROW_GMX(sc_.parseFromString("group 2 or atomnr 2 to 5"),
544                      gmx::InconsistentInputError);
545     EXPECT_THROW_GMX(sc_.parseFromString("within 1 of group 2"),
546                      gmx::InconsistentInputError);
547 }
548
549 TEST_F(SelectionCollectionTest, HandlesUnsortedGroupReferenceDelayed)
550 {
551     ASSERT_NO_THROW_GMX(sc_.parseFromString("atomnr 1 to 3 and group \"GrpUnsorted\""));
552     ASSERT_NO_THROW_GMX(sc_.parseFromString("atomnr 1 to 3 and group 2"));
553     EXPECT_THROW_GMX(loadIndexGroups("simple.ndx"), gmx::InconsistentInputError);
554     // TODO: Add a separate check in the selection compiler for a safer API
555     // (makes sense in the future if the compiler needs the information for
556     // other purposes as well).
557     // EXPECT_THROW_GMX(sc_.compile(), gmx::APIError);
558 }
559
560 TEST_F(SelectionCollectionTest, HandlesOutOfRangeAtomIndexInGroup)
561 {
562     ASSERT_NO_THROW_GMX(sc_.setTopology(NULL, 5));
563     ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
564     EXPECT_THROW_GMX(sc_.parseFromString("group \"GrpB\""), gmx::InconsistentInputError);
565 }
566
567 TEST_F(SelectionCollectionTest, HandlesOutOfRangeAtomIndexInGroupDelayed)
568 {
569     ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
570     ASSERT_NO_THROW_GMX(sc_.parseFromString("group \"GrpB\""));
571     EXPECT_THROW_GMX(sc_.setTopology(NULL, 5), gmx::InconsistentInputError);
572 }
573
574 TEST_F(SelectionCollectionTest, HandlesOutOfRangeAtomIndexInGroupDelayed2)
575 {
576     ASSERT_NO_THROW_GMX(sc_.setTopology(NULL, 5));
577     ASSERT_NO_THROW_GMX(sc_.parseFromString("group \"GrpB\""));
578     EXPECT_THROW_GMX(loadIndexGroups("simple.ndx"), gmx::InconsistentInputError);
579 }
580
581 TEST_F(SelectionCollectionTest, RecoversFromMissingMoleculeInfo)
582 {
583     ASSERT_NO_THROW_GMX(sc_.parseFromString("molindex 1 to 5"));
584     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
585     EXPECT_THROW_GMX(sc_.compile(), gmx::InconsistentInputError);
586 }
587
588 TEST_F(SelectionCollectionTest, RecoversFromMissingAtomTypes)
589 {
590     ASSERT_NO_THROW_GMX(sc_.parseFromString("type CA"));
591     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
592     EXPECT_THROW_GMX(sc_.compile(), gmx::InconsistentInputError);
593 }
594
595 TEST_F(SelectionCollectionTest, RecoversFromMissingPDBInfo)
596 {
597     ASSERT_NO_THROW_GMX(sc_.parseFromString("altloc A"));
598     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
599     EXPECT_THROW_GMX(sc_.compile(), gmx::InconsistentInputError);
600 }
601
602 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation)
603 {
604     ASSERT_NO_THROW_GMX(sc_.parseFromString("all permute 1 1"));
605     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
606     EXPECT_THROW_GMX(sc_.compile(), gmx::InvalidInputError);
607 }
608
609 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation2)
610 {
611     ASSERT_NO_THROW_GMX(sc_.parseFromString("all permute 3 2 1"));
612     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
613     EXPECT_THROW_GMX(sc_.compile(), gmx::InconsistentInputError);
614 }
615
616 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation3)
617 {
618     ASSERT_NO_THROW_GMX(sc_.parseFromString("x < 1.5 permute 3 2 1"));
619     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
620     ASSERT_NO_THROW_GMX(sc_.compile());
621     EXPECT_THROW_GMX(sc_.evaluate(frame_, NULL), gmx::InconsistentInputError);
622 }
623
624 TEST_F(SelectionCollectionTest, HandlesFramesWithTooSmallAtomSubsets)
625 {
626     ASSERT_NO_THROW_GMX(sc_.parseFromString("atomnr 3 to 10"));
627     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
628     ASSERT_NO_THROW_GMX(sc_.compile());
629     frame_->natoms = 8;
630     EXPECT_THROW_GMX(sc_.evaluate(frame_, NULL), gmx::InconsistentInputError);
631 }
632
633 TEST_F(SelectionCollectionTest, HandlesFramesWithTooSmallAtomSubsets2)
634 {
635     // Evaluating the positions will require atoms 1-9.
636     ASSERT_NO_THROW_GMX(sc_.parseFromString("whole_res_com of atomnr 2 5 7"));
637     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
638     ASSERT_NO_THROW_GMX(sc_.compile());
639     frame_->natoms = 8;
640     EXPECT_THROW_GMX(sc_.evaluate(frame_, NULL), gmx::InconsistentInputError);
641 }
642
643 TEST_F(SelectionCollectionTest, HandlesFramesWithTooSmallAtomSubsets3)
644 {
645     ASSERT_NO_THROW_GMX(sc_.parseFromString("mindistance from atomnr 1 to 5 < 2"));
646     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
647     ASSERT_NO_THROW_GMX(sc_.compile());
648     frame_->natoms = 10;
649     EXPECT_THROW_GMX(sc_.evaluate(frame_, NULL), gmx::InconsistentInputError);
650 }
651
652 // TODO: Tests for more evaluation errors
653
654 /********************************************************************
655  * Tests for interactive selection input
656  */
657
658 TEST_F(SelectionCollectionInteractiveTest, HandlesBasicInput)
659 {
660     const char *const input[] = {
661         "foo = resname RA",
662         "resname RB",
663         "\"Name\" resname RC"
664     };
665     runTest(-1, true, input);
666 }
667
668 TEST_F(SelectionCollectionInteractiveTest, HandlesContinuation)
669 {
670     const char *const input[] = {
671         "resname RB and \\",
672         "resname RC"
673     };
674     runTest(-1, true, input);
675 }
676
677 TEST_F(SelectionCollectionInteractiveTest, HandlesSingleSelectionInput)
678 {
679     const char *const input[] = {
680         "foo = resname RA",
681         "resname RA"
682     };
683     runTest(1, true, input);
684 }
685
686 TEST_F(SelectionCollectionInteractiveTest, HandlesTwoSelectionInput)
687 {
688     const char *const input[] = {
689         "resname RA",
690         "resname RB"
691     };
692     runTest(2, true, input);
693 }
694
695 TEST_F(SelectionCollectionInteractiveTest, HandlesStatusWithGroups)
696 {
697     const char *const input[] = {
698         "resname RA",
699         ""
700     };
701     loadIndexGroups("simple.ndx");
702     runTest(-1, true, input);
703 }
704
705 TEST_F(SelectionCollectionInteractiveTest, HandlesStatusWithExistingSelections)
706 {
707     const char *const input[] = {
708         "",
709         "bar = resname RC",
710         "resname RA",
711         ""
712     };
713     ASSERT_NO_THROW_GMX(sc_.parseFromString("foo = resname RA"));
714     ASSERT_NO_THROW_GMX(sc_.parseFromString("resname RB"));
715     runTest(-1, true, input);
716 }
717
718 TEST_F(SelectionCollectionInteractiveTest, HandlesSingleSelectionInputStatus)
719 {
720     const char *const input[] = {
721         "foo = resname RA",
722         "",
723         "resname RB"
724     };
725     runTest(1, true, input);
726 }
727
728 TEST_F(SelectionCollectionInteractiveTest, HandlesTwoSelectionInputStatus)
729 {
730     const char *const input[] = {
731         "\"Sel\" resname RA",
732         "",
733         "resname RB"
734     };
735     runTest(2, true, input);
736 }
737
738 TEST_F(SelectionCollectionInteractiveTest, HandlesMultiSelectionInputStatus)
739 {
740     const char *const input[] = {
741         "\"Sel\" resname RA",
742         "\"Sel2\" resname RB",
743         ""
744     };
745     runTest(-1, true, input);
746 }
747
748 TEST_F(SelectionCollectionInteractiveTest, HandlesNoFinalNewline)
749 {
750     // TODO: There is an extra prompt printed after the input is finished; it
751     // would be cleaner not to have it, but it's only a cosmetic issue.
752     const char *const input[] = {
753         "resname RA"
754     };
755     helper_.setLastNewline(false);
756     runTest(-1, true, input);
757 }
758
759 TEST_F(SelectionCollectionInteractiveTest, HandlesEmptySelections)
760 {
761     const char *const input[] = {
762         "resname RA;",
763         "; resname RB;;",
764         " ",
765         ";"
766     };
767     runTest(-1, true, input);
768 }
769
770 TEST_F(SelectionCollectionInteractiveTest, HandlesMultipleSelectionsOnLine)
771 {
772     const char *const input[] = {
773         "resname RA; resname RB and \\",
774         "resname RC"
775     };
776     runTest(2, true, input);
777 }
778
779 TEST_F(SelectionCollectionInteractiveTest, HandlesNoninteractiveInput)
780 {
781     const char *const input[] = {
782         "foo = resname RA",
783         "resname RB",
784         "\"Name\" resname RC"
785     };
786     runTest(-1, false, input);
787 }
788
789 TEST_F(SelectionCollectionInteractiveTest, HandlesSingleSelectionInputNoninteractively)
790 {
791     const char *const input[] = {
792         "foo = resname RA",
793         "resname RA"
794     };
795     runTest(1, false, input);
796 }
797
798
799 /********************************************************************
800  * Tests for selection keywords
801  */
802
803 TEST_F(SelectionCollectionDataTest, HandlesAllNone)
804 {
805     static const char * const selections[] = {
806         "all",
807         "none"
808     };
809     runTest(10, selections);
810 }
811
812 TEST_F(SelectionCollectionDataTest, HandlesAtomnr)
813 {
814     static const char * const selections[] = {
815         "atomnr 1 to 3 6 to 8",
816         "atomnr 4 2 5 to 7",
817         "atomnr <= 5"
818     };
819     runTest(10, selections);
820 }
821
822 TEST_F(SelectionCollectionDataTest, HandlesResnr)
823 {
824     static const char * const selections[] = {
825         "resnr 1 2 5",
826         "resid 4 to 3"
827     };
828     runTest("simple.gro", selections);
829 }
830
831 TEST_F(SelectionCollectionDataTest, HandlesResIndex)
832 {
833     static const char * const selections[] = {
834         "resindex 1 4",
835         "residue 1 3"
836     };
837     runTest("simple.pdb", selections);
838 }
839
840 TEST_F(SelectionCollectionDataTest, HandlesMolIndex)
841 {
842     static const char * const selections[] = {
843         "molindex 1 4",
844         "molecule 2 3 5"
845     };
846     ASSERT_NO_FATAL_FAILURE(runParser(selections));
847     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
848     topManager_.initUniformMolecules(3);
849     ASSERT_NO_FATAL_FAILURE(runCompiler());
850 }
851
852 TEST_F(SelectionCollectionDataTest, HandlesAtomname)
853 {
854     static const char * const selections[] = {
855         "name CB",
856         "atomname S1 S2"
857     };
858     runTest("simple.gro", selections);
859 }
860
861 TEST_F(SelectionCollectionDataTest, HandlesPdbAtomname)
862 {
863     static const char * const selections[] = {
864         "name HG21",
865         "name 1HG2",
866         "pdbname HG21 CB",
867         "pdbatomname 1HG2"
868     };
869     runTest("simple.pdb", selections);
870 }
871
872
873 TEST_F(SelectionCollectionDataTest, HandlesAtomtype)
874 {
875     static const char * const selections[] = {
876         "atomtype CA"
877     };
878     ASSERT_NO_FATAL_FAILURE(runParser(selections));
879     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
880     const char *const types[] = { "CA", "SA", "SB" };
881     topManager_.initAtomTypes(types);
882     ASSERT_NO_FATAL_FAILURE(runCompiler());
883 }
884
885 TEST_F(SelectionCollectionDataTest, HandlesChain)
886 {
887     static const char * const selections[] = {
888         "chain A",
889         "chain B"
890     };
891     runTest("simple.pdb", selections);
892 }
893
894 TEST_F(SelectionCollectionDataTest, HandlesMass)
895 {
896     static const char * const selections[] = {
897         "mass > 5"
898     };
899     ASSERT_NO_FATAL_FAILURE(runParser(selections));
900     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
901     for (int i = 0; i < top_->atoms.nr; ++i)
902     {
903         top_->atoms.atom[i].m = 1.0 + i;
904     }
905     ASSERT_NO_FATAL_FAILURE(runCompiler());
906 }
907
908 TEST_F(SelectionCollectionDataTest, HandlesCharge)
909 {
910     static const char * const selections[] = {
911         "charge < 0.5"
912     };
913     ASSERT_NO_FATAL_FAILURE(runParser(selections));
914     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
915     for (int i = 0; i < top_->atoms.nr; ++i)
916     {
917         top_->atoms.atom[i].q = i / 10.0;
918     }
919     ASSERT_NO_FATAL_FAILURE(runCompiler());
920 }
921
922 TEST_F(SelectionCollectionDataTest, HandlesAltLoc)
923 {
924     static const char * const selections[] = {
925         "altloc \" \"",
926         "altloc A"
927     };
928     runTest("simple.pdb", selections);
929 }
930
931 TEST_F(SelectionCollectionDataTest, HandlesInsertCode)
932 {
933     static const char * const selections[] = {
934         "insertcode \" \"",
935         "insertcode A"
936     };
937     runTest("simple.pdb", selections);
938 }
939
940 TEST_F(SelectionCollectionDataTest, HandlesOccupancy)
941 {
942     static const char * const selections[] = {
943         "occupancy 1",
944         "occupancy < .5"
945     };
946     runTest("simple.pdb", selections);
947 }
948
949 TEST_F(SelectionCollectionDataTest, HandlesBeta)
950 {
951     static const char * const selections[] = {
952         "beta 0",
953         "beta >= 0.3"
954     };
955     runTest("simple.pdb", selections);
956 }
957
958 TEST_F(SelectionCollectionDataTest, HandlesResname)
959 {
960     static const char * const selections[] = {
961         "resname RA",
962         "resname RB RC"
963     };
964     runTest("simple.gro", selections);
965 }
966
967 TEST_F(SelectionCollectionDataTest, HandlesCoordinateKeywords)
968 {
969     static const char * const selections[] = {
970         "x < 3",
971         "y >= 3",
972         "x {-1 to 2}"
973     };
974     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
975     runTest("simple.gro", selections);
976 }
977
978
979 TEST_F(SelectionCollectionDataTest, HandlesSameResidue)
980 {
981     static const char * const selections[] = {
982         "same residue as atomnr 1 4 12"
983     };
984     runTest("simple.gro", selections);
985 }
986
987
988 TEST_F(SelectionCollectionDataTest, HandlesSameResidueName)
989 {
990     static const char * const selections[] = {
991         "same resname as atomnr 1 14"
992     };
993     runTest("simple.gro", selections);
994 }
995
996
997 TEST_F(SelectionCollectionDataTest, HandlesPositionKeywords)
998 {
999     static const char * const selections[] = {
1000         "cog of resnr 1 3",
1001         "res_cog of name CB and resnr 1 3",
1002         "whole_res_cog of name CB and resnr 1 3",
1003         "part_res_cog of x < 3",
1004         "dyn_res_cog of x < 3"
1005     };
1006     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
1007              | efTestPositionAtoms);
1008     runTest("simple.gro", selections);
1009 }
1010
1011
1012 TEST_F(SelectionCollectionDataTest, HandlesDistanceKeyword)
1013 {
1014     static const char * const selections[] = {
1015         "distance from cog of resnr 1 < 2"
1016     };
1017     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
1018     runTest("simple.gro", selections);
1019 }
1020
1021
1022 TEST_F(SelectionCollectionDataTest, HandlesMinDistanceKeyword)
1023 {
1024     static const char * const selections[] = {
1025         "mindistance from resnr 1 < 2"
1026     };
1027     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
1028     runTest("simple.gro", selections);
1029 }
1030
1031
1032 TEST_F(SelectionCollectionDataTest, HandlesWithinKeyword)
1033 {
1034     static const char * const selections[] = {
1035         "within 1 of resnr 2"
1036     };
1037     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
1038     runTest("simple.gro", selections);
1039 }
1040
1041
1042 TEST_F(SelectionCollectionDataTest, HandlesInSolidAngleKeyword)
1043 {
1044     // Both of these should evaluate to empty on a correct implementation.
1045     static const char * const selections[] = {
1046         "resname TP and not insolidangle center cog of resname C span resname R cutoff 20",
1047         "resname TN and insolidangle center cog of resname C span resname R cutoff 20"
1048     };
1049     setFlags(TestFlags() | efDontTestCompiledAtoms | efTestEvaluation);
1050     runTest("sphere.gro", selections);
1051 }
1052
1053
1054 TEST_F(SelectionCollectionDataTest, HandlesPermuteModifier)
1055 {
1056     static const char * const selections[] = {
1057         "all permute 3 1 2",
1058         "res_cog of resnr 1 to 4 permute 2 1",
1059         "name CB S1 and res_cog x < 3 permute 2 1"
1060     };
1061     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
1062              | efTestPositionAtoms | efTestPositionMapping);
1063     runTest("simple.gro", selections);
1064 }
1065
1066
1067 TEST_F(SelectionCollectionDataTest, HandlesPlusModifier)
1068 {
1069     static const char * const selections[] = {
1070         "name S2 plus name S1",
1071         "res_cog of resnr 2 plus res_cog of resnr 1 plus res_cog of resnr 3",
1072         "name S1 and y < 3 plus res_cog of x < 2.5"
1073     };
1074     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
1075              | efTestPositionAtoms | efTestPositionMapping);
1076     runTest("simple.gro", selections);
1077 }
1078
1079
1080 TEST_F(SelectionCollectionDataTest, HandlesMergeModifier)
1081 {
1082     static const char * const selections[] = {
1083         "name S2 merge name S1",
1084         "resnr 1 2 and name S2 merge resnr 1 2 and name S1 merge res_cog of resnr 1 2",
1085         "name S1 and x < 2.5 merge res_cog of x < 2.5"
1086     };
1087     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
1088              | efTestPositionAtoms | efTestPositionMapping);
1089     runTest("simple.gro", selections);
1090 }
1091
1092
1093 /********************************************************************
1094  * Tests for generic selection evaluation
1095  */
1096
1097 TEST_F(SelectionCollectionDataTest, ComputesMassesAndCharges)
1098 {
1099     static const char * const selections[] = {
1100         "name CB",
1101         "y > 2",
1102         "res_cog of y > 2"
1103     };
1104     setFlags(TestFlags() | efTestEvaluation | efTestPositionAtoms
1105              | efTestPositionMasses | efTestPositionCharges);
1106     ASSERT_NO_FATAL_FAILURE(runParser(selections));
1107     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
1108     for (int i = 0; i < top_->atoms.nr; ++i)
1109     {
1110         top_->atoms.atom[i].m =   1.0 + i / 100.0;
1111         top_->atoms.atom[i].q = -(1.0 + i / 100.0);
1112     }
1113     ASSERT_NO_FATAL_FAILURE(runCompiler());
1114     ASSERT_NO_FATAL_FAILURE(runEvaluate());
1115     ASSERT_NO_FATAL_FAILURE(runEvaluateFinal());
1116 }
1117
1118 TEST_F(SelectionCollectionDataTest, ComputesMassesAndChargesWithoutTopology)
1119 {
1120     static const char * const selections[] = {
1121         "atomnr 1 to 3 8 to 9",
1122         "y > 2",
1123         "cog of (y > 2)"
1124     };
1125     setFlags(TestFlags() | efTestPositionAtoms
1126              | efTestPositionMasses | efTestPositionCharges);
1127     runTest(10, selections);
1128 }
1129
1130
1131 /********************************************************************
1132  * Tests for selection syntactic constructs
1133  */
1134
1135 TEST_F(SelectionCollectionDataTest, HandlesSelectionNames)
1136 {
1137     static const char * const selections[] = {
1138         "\"GroupSelection\" group \"GrpA\"",
1139         "\"DynamicSelection\" x < 5",
1140         "y < 3"
1141     };
1142     setFlags(TestFlags() | efTestSelectionNames);
1143     ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
1144     runTest(10, selections);
1145 }
1146
1147 TEST_F(SelectionCollectionDataTest, HandlesIndexGroupsInSelections)
1148 {
1149     static const char * const selections[] = {
1150         "group \"GrpA\"",
1151         "GrpB",
1152         "1",
1153         // These test that the name of the group is not too eagerly promoted to
1154         // the name of the selection.
1155         "group \"GrpB\" and resname RB",
1156         "group \"GrpA\" permute 5 3 2 1 4",
1157         "group \"GrpA\" plus group \"GrpB\"",
1158         "res_cog of group \"GrpA\""
1159     };
1160     setFlags(TestFlags() | efTestSelectionNames);
1161     ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
1162     runTest("simple.gro", selections);
1163 }
1164
1165 TEST_F(SelectionCollectionDataTest, HandlesIndexGroupsInSelectionsDelayed)
1166 {
1167     static const char * const selections[] = {
1168         "group \"GrpA\"",
1169         "GrpB",
1170         "1",
1171         "group \"GrpB\" and resname RB"
1172     };
1173     setFlags(TestFlags() | efTestSelectionNames);
1174     ASSERT_NO_FATAL_FAILURE(runParser(selections));
1175     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
1176     ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
1177     ASSERT_NO_FATAL_FAILURE(runCompiler());
1178 }
1179
1180 TEST_F(SelectionCollectionDataTest, HandlesUnsortedIndexGroupsInSelections)
1181 {
1182     static const char * const selections[] = {
1183         "foo = group \"GrpUnsorted\"",
1184         "group \"GrpUnsorted\"",
1185         "GrpUnsorted",
1186         "2",
1187         "res_cog of group \"GrpUnsorted\"",
1188         "group \"GrpUnsorted\" permute 2 1",
1189         "foo"
1190     };
1191     setFlags(TestFlags() | efTestPositionAtoms | efTestPositionMapping
1192              | efTestSelectionNames);
1193     ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
1194     runTest("simple.gro", selections);
1195 }
1196
1197 TEST_F(SelectionCollectionDataTest, HandlesUnsortedIndexGroupsInSelectionsDelayed)
1198 {
1199     static const char * const selections[] = {
1200         "foo = group \"GrpUnsorted\"",
1201         "group \"GrpUnsorted\"",
1202         "GrpUnsorted",
1203         "2",
1204         "res_cog of group \"GrpUnsorted\"",
1205         "group \"GrpUnsorted\" permute 2 1",
1206         "foo"
1207     };
1208     ASSERT_NO_FATAL_FAILURE(runParser(selections));
1209     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
1210     ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
1211     ASSERT_NO_FATAL_FAILURE(runCompiler());
1212 }
1213
1214
1215 TEST_F(SelectionCollectionDataTest, HandlesConstantPositions)
1216 {
1217     static const char * const selections[] = {
1218         "[1, -2, 3.5]"
1219     };
1220     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
1221              | efTestPositionMapping);
1222     runTest("simple.gro", selections);
1223 }
1224
1225
1226 TEST_F(SelectionCollectionDataTest, HandlesConstantPositionsWithModifiers)
1227 {
1228     static const char * const selections[] = {
1229         "[0, 0, 0] plus [0, 1, 0]"
1230     };
1231     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
1232              | efTestPositionMapping);
1233     runTest("simple.gro", selections);
1234 }
1235
1236
1237 TEST_F(SelectionCollectionDataTest, HandlesWithinConstantPositions)
1238 {
1239     static const char * const selections[] = {
1240         "within 1 of [2, 1, 0]"
1241     };
1242     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
1243     runTest("simple.gro", selections);
1244 }
1245
1246
1247 TEST_F(SelectionCollectionDataTest, HandlesOverlappingIntegerRanges)
1248 {
1249     static const char * const selections[] = {
1250         "atomnr 2 to 4 5 to 8",
1251         "atomnr 2 to 5 4 to 7"
1252     };
1253     ASSERT_NO_FATAL_FAILURE(runTest(10, selections));
1254 }
1255
1256
1257 TEST_F(SelectionCollectionDataTest, HandlesOverlappingRealRanges)
1258 {
1259     static const char * const selections[] = {
1260         "charge {-0.35 to -0.05 0.25 to 0.75}",
1261         "charge {0.05 to -0.3 -0.05 to 0.55}"
1262     };
1263     ASSERT_NO_FATAL_FAILURE(runParser(selections));
1264     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
1265     for (int i = 0; i < top_->atoms.nr; ++i)
1266     {
1267         top_->atoms.atom[i].q = i / 10.0 - 0.5;
1268     }
1269     ASSERT_NO_FATAL_FAILURE(runCompiler());
1270 }
1271
1272
1273 TEST_F(SelectionCollectionDataTest, HandlesForcedStringMatchingMode)
1274 {
1275     static const char * const selections[] = {
1276         "name = S1 \"C?\"",
1277         "name ? S1 \"C?\""
1278     };
1279     runTest("simple.gro", selections);
1280 }
1281
1282
1283 TEST_F(SelectionCollectionDataTest, HandlesWildcardMatching)
1284 {
1285     static const char * const selections[] = {
1286         "name \"S?\"",
1287         "name ? \"S?\""
1288     };
1289     runTest("simple.gro", selections);
1290 }
1291
1292
1293 TEST_F(SelectionCollectionDataTest, HandlesRegexMatching)
1294 {
1295     static const char * const selections[] = {
1296         "resname \"R[BD]\"",
1297         "resname ~ \"R[BD]\""
1298     };
1299     if (gmx::Regex::isSupported())
1300     {
1301         runTest("simple.gro", selections);
1302     }
1303 }
1304
1305
1306 TEST_F(SelectionCollectionDataTest, HandlesBasicBoolean)
1307 {
1308     static const char * const selections[] = {
1309         "atomnr 1 to 5 and atomnr 2 to 7",
1310         "atomnr 1 to 5 or not atomnr 3 to 8",
1311         "not not atomnr 1 to 5 and atomnr 2 to 6 and not not atomnr 3 to 7",
1312         "atomnr 1 to 5 and (atomnr 2 to 7 and atomnr 3 to 6)",
1313         "x < 5 and atomnr 1 to 5 and y < 3 and atomnr 2 to 4"
1314     };
1315     runTest(10, selections);
1316 }
1317
1318
1319 TEST_F(SelectionCollectionDataTest, HandlesDynamicAtomValuedParameters)
1320 {
1321     static const char * const selections[] = {
1322         "same residue as (atomnr 3 5 13 or y > 5)",
1323         "(resnr 1 3 5 or x > 10) and same residue as (atomnr 3 5 13 or z > 5)"
1324     };
1325     setFlags(TestFlags() | efTestEvaluation);
1326     runTest("simple.gro", selections);
1327 }
1328
1329
1330 TEST_F(SelectionCollectionDataTest, HandlesEmptySelectionWithUnevaluatedExpressions)
1331 {
1332     static const char * const selections[] = {
1333         "none and x > 2",
1334         "none and same resname as resnr 2"
1335     };
1336     runTest("simple.gro", selections);
1337 }
1338
1339
1340 TEST_F(SelectionCollectionDataTest, HandlesEmptyReferenceForSame)
1341 {
1342     static const char * const selections[] = {
1343         "same residue as none",
1344         "same resname as none"
1345     };
1346     runTest("simple.gro", selections);
1347 }
1348
1349
1350 TEST_F(SelectionCollectionDataTest, HandlesPositionModifiersForKeywords)
1351 {
1352     static const char * const selections[] = {
1353         "res_cog x > 2",
1354         "name CB and res_cog y > 2.5"
1355     };
1356     setFlags(TestFlags() | efTestEvaluation);
1357     runTest("simple.gro", selections);
1358 }
1359
1360
1361 TEST_F(SelectionCollectionDataTest, HandlesPositionModifiersForMethods)
1362 {
1363     static const char * const selections[] = {
1364         "res_cog distance from cog of resnr 1 < 2",
1365         "res_cog within 2 of cog of resnr 1"
1366     };
1367     setFlags(TestFlags() | efTestEvaluation);
1368     runTest("simple.gro", selections);
1369 }
1370
1371
1372 TEST_F(SelectionCollectionDataTest, HandlesKeywordOfPositions)
1373 {
1374     static const char * const selections[] = {
1375         "x < y of cog of resnr 2"
1376     };
1377     setFlags(TestFlags() | efTestEvaluation);
1378     runTest("simple.gro", selections);
1379 }
1380
1381 TEST_F(SelectionCollectionDataTest, HandlesKeywordOfPositionsInArithmetic)
1382 {
1383     static const char * const selections[] = {
1384         "x - y of cog of resnr 2 < 0"
1385     };
1386     setFlags(TestFlags() | efTestEvaluation);
1387     runTest("simple.gro", selections);
1388 }
1389
1390
1391 TEST_F(SelectionCollectionDataTest, HandlesNumericComparisons)
1392 {
1393     static const char * const selections[] = {
1394         "x > 2",
1395         "2 < x",
1396         "y > resnr",
1397         "resnr < 2.5",
1398         "2.5 > resnr"
1399     };
1400     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
1401     runTest("simple.gro", selections);
1402 }
1403
1404
1405 TEST_F(SelectionCollectionDataTest, HandlesArithmeticExpressions)
1406 {
1407     static const char * const selections[] = {
1408         "x+1 > 3",
1409         "(y-1)^2 <= 1",
1410         "x+--1 > 3",
1411         "-x+-1 < -3"
1412     };
1413     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
1414     runTest("simple.gro", selections);
1415 }
1416
1417
1418 TEST_F(SelectionCollectionDataTest, HandlesNumericVariables)
1419 {
1420     static const char * const selections[] = {
1421         "value = x + y",
1422         "value <= 4",
1423         "index = resnr",
1424         "index < 3"
1425     };
1426     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
1427     runTest("simple.gro", selections);
1428 }
1429
1430
1431 TEST_F(SelectionCollectionDataTest, HandlesComplexNumericVariables)
1432 {
1433     static const char * const selections[] = {
1434         "value = x + y",
1435         "resname RA and value <= 4",
1436         "resname RA RB and x < 3 and value <= 4",
1437         "index = atomnr",
1438         "resname RA and index < 3",
1439         "resname RB and y < 3 and index < 6"
1440     };
1441     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
1442     runTest("simple.gro", selections);
1443 }
1444
1445
1446 TEST_F(SelectionCollectionDataTest, HandlesPositionVariables)
1447 {
1448     static const char * const selections[] = {
1449         "foo = res_cog of resname RA",
1450         "foo",
1451         "within 1 of foo",
1452         "bar = cog of resname RA",
1453         "bar",
1454         "within 1 of bar"
1455     };
1456     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
1457     runTest("simple.gro", selections);
1458 }
1459
1460
1461 TEST_F(SelectionCollectionDataTest, HandlesConstantPositionInVariable)
1462 {
1463     static const char * const selections[] = {
1464         "constpos = [1.0, 2.5, 0.5]",
1465         "constpos",
1466         "within 2 of constpos"
1467     };
1468     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
1469              | efTestPositionAtoms);
1470     runTest("simple.gro", selections);
1471 }
1472
1473
1474 TEST_F(SelectionCollectionDataTest, HandlesNumericConstantsInVariables)
1475 {
1476     static const char * const selections[] = {
1477         "constint = 4",
1478         "constreal1 = 0.5",
1479         "constreal2 = 2.7",
1480         "resnr < constint",
1481         "x + constreal1 < constreal2"
1482     };
1483     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
1484     runTest("simple.gro", selections);
1485 }
1486
1487
1488 /********************************************************************
1489  * Tests for complex boolean syntax
1490  */
1491
1492 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysis)
1493 {
1494     static const char * const selections[] = {
1495         "atomnr 1 to 5 and atomnr 2 to 7 and x < 2",
1496         "atomnr 1 to 5 and (atomnr 4 to 7 or x < 2)",
1497         "atomnr 1 to 5 and y < 3 and (atomnr 4 to 7 or x < 2)",
1498         "atomnr 1 to 5 and not (atomnr 4 to 7 or x < 2)",
1499         "atomnr 1 to 5 or (atomnr 4 to 6 and (atomnr 5 to 7 or x < 2))"
1500     };
1501     runTest(10, selections);
1502 }
1503
1504
1505 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysisWithVariables)
1506 {
1507     static const char * const selections[] = {
1508         "foo = atomnr 4 to 7 or x < 2",
1509         "atomnr 1 to 4 and foo",
1510         "atomnr 2 to 6 and y < 3 and foo",
1511         "atomnr 6 to 10 and not foo"
1512     };
1513     runTest(10, selections);
1514 }
1515
1516
1517 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysisWithMoreVariables)
1518 {
1519     static const char * const selections[] = {
1520         "foo = atomnr 4 to 7",
1521         "bar = foo and x < 2",
1522         "bar2 = foo and y < 2",
1523         "atomnr 1 to 4 and bar",
1524         "atomnr 2 to 6 and y < 3 and bar2",
1525         "atomnr 6 to 10 and not foo"
1526     };
1527     runTest(10, selections);
1528 }
1529
1530
1531 /********************************************************************
1532  * Tests for complex subexpression cases
1533  *
1534  * These tests use some knowledge of the implementation to trigger different
1535  * paths in the code.
1536  */
1537
1538 TEST_F(SelectionCollectionDataTest, HandlesUnusedVariables)
1539 {
1540     static const char * const selections[] = {
1541         "unused1 = atomnr 1 to 3",
1542         "foo = atomnr 4 to 7",
1543         "atomnr 1 to 6 and foo",
1544         "unused2 = atomnr 3 to 5"
1545     };
1546     runTest(10, selections);
1547 }
1548
1549
1550 TEST_F(SelectionCollectionDataTest, HandlesVariablesWithStaticEvaluationGroups)
1551 {
1552     static const char * const selections[] = {
1553         "foo = atomnr 4 to 7 and x < 2",
1554         "atomnr 1 to 5 and foo",
1555         "atomnr 3 to 7 and foo"
1556     };
1557     runTest(10, selections);
1558 }
1559
1560
1561 TEST_F(SelectionCollectionDataTest, HandlesVariablesWithMixedEvaluationGroups)
1562 {
1563     static const char * const selections[] = {
1564         "foo = atomnr 4 to 7 and x < 2",
1565         "atomnr 1 to 6 and foo",
1566         "within 1 of foo",
1567         "foo"
1568     };
1569     runTest(10, selections);
1570 }
1571
1572
1573 TEST_F(SelectionCollectionDataTest, HandlesVariablesWithMixedEvaluationGroups2)
1574 {
1575     static const char * const selections[] = {
1576         "foo = atomnr 1 to 8 and x < 10",
1577         "atomnr 1 to 5 and y < 10 and foo",
1578         "foo"
1579     };
1580     setFlags(TestFlags() | efTestEvaluation);
1581     runTest("simple.gro", selections);
1582 }
1583
1584
1585 } // namespace