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