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