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