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