Merge release-4-6 into master
[alexxy/gromacs.git] / src / gromacs / selection / tests / selectioncollection.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Tests selection parsing and compilation.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_selection
37  */
38 #include <gtest/gtest.h>
39
40 #include "gromacs/legacyheaders/smalloc.h"
41 #include "gromacs/legacyheaders/statutil.h"
42 #include "gromacs/legacyheaders/tpxio.h"
43 #include "gromacs/legacyheaders/vec.h"
44
45 #include "gromacs/options/basicoptions.h"
46 #include "gromacs/options/options.h"
47 #include "gromacs/selection/selectioncollection.h"
48 #include "gromacs/selection/selection.h"
49 #include "gromacs/utility/exceptions.h"
50 #include "gromacs/utility/flags.h"
51 #include "gromacs/utility/gmxregex.h"
52 #include "gromacs/utility/stringutil.h"
53
54 #include "testutils/refdata.h"
55 #include "testutils/testfilemanager.h"
56 #include "testutils/testoptions.h"
57
58 namespace
59 {
60
61 /********************************************************************
62  * Test fixture for selection testing
63  */
64
65 class SelectionCollectionTest : public ::testing::Test
66 {
67     public:
68         static void SetUpTestCase();
69
70         static int               s_debugLevel;
71
72         SelectionCollectionTest();
73         ~SelectionCollectionTest();
74
75         void setAtomCount(int natoms)
76         {
77             ASSERT_NO_THROW(sc_.setTopology(NULL, natoms));
78         }
79         void loadTopology(const char *filename);
80
81         gmx::SelectionCollection sc_;
82         gmx::SelectionList       sel_;
83         t_topology              *top_;
84         t_trxframe              *frame_;
85 };
86
87 int SelectionCollectionTest::s_debugLevel = 0;
88
89 void SelectionCollectionTest::SetUpTestCase()
90 {
91     gmx::Options options(NULL, NULL);
92     options.addOption(gmx::IntegerOption("seldebug").store(&s_debugLevel));
93     gmx::test::parseTestOptions(&options);
94 }
95
96
97 SelectionCollectionTest::SelectionCollectionTest()
98     : top_(NULL), frame_(NULL)
99 {
100     sc_.setDebugLevel(s_debugLevel);
101     sc_.setReferencePosType("atom");
102     sc_.setOutputPosType("atom");
103 }
104
105
106 SelectionCollectionTest::~SelectionCollectionTest()
107 {
108     if (top_ != NULL)
109     {
110         free_t_atoms(&top_->atoms, TRUE);
111         done_top(top_);
112         sfree(top_);
113     }
114
115     if (frame_ != NULL)
116     {
117         sfree(frame_->x);
118         sfree(frame_);
119     }
120 }
121
122
123 void
124 SelectionCollectionTest::loadTopology(const char *filename)
125 {
126     char    title[STRLEN];
127     int     ePBC;
128     rvec   *xtop;
129     matrix  box;
130
131     snew(top_, 1);
132     read_tps_conf(gmx::test::TestFileManager::getInputFilePath(filename).c_str(),
133                   title, top_, &ePBC, &xtop, NULL, box, FALSE);
134
135     snew(frame_, 1);
136     frame_->flags  = TRX_NEED_X;
137     frame_->natoms = top_->atoms.nr;
138     frame_->bX     = TRUE;
139     snew(frame_->x, frame_->natoms);
140     memcpy(frame_->x, xtop, sizeof(*frame_->x) * frame_->natoms);
141     frame_->bBox   = TRUE;
142     copy_mat(box, frame_->box);
143
144     sfree(xtop);
145
146     ASSERT_NO_THROW(sc_.setTopology(top_, -1));
147 }
148
149
150 /********************************************************************
151  * Test fixture for selection testing with reference data
152  */
153
154 class SelectionCollectionDataTest : public SelectionCollectionTest
155 {
156     public:
157         enum TestFlag
158         {
159             efTestEvaluation            = 1<<0,
160             efTestPositionAtoms         = 1<<1,
161             efTestPositionCoordinates   = 1<<2,
162             efTestPositionMapping       = 1<<3,
163             efDontTestCompiledAtoms     = 1<<8
164         };
165         typedef gmx::FlagsTemplate<TestFlag> TestFlags;
166
167         SelectionCollectionDataTest()
168             : checker_(data_.rootChecker()), count_(0), framenr_(0)
169         {
170         }
171
172         void setFlags(TestFlags flags) { flags_ = flags; }
173
174         void runTest(int natoms, const char *const *selections, size_t count);
175         void runTest(const char *filename, const char *const *selections,
176                      size_t count);
177         template <size_t count>
178         void runTest(int natoms, const char *const (&selections)[count])
179         {
180             runTest(natoms, selections, count);
181         }
182         template <size_t count>
183         void runTest(const char *filename, const char *const (&selections)[count])
184         {
185             runTest(filename, selections, count);
186         }
187
188     private:
189         static void checkSelection(gmx::test::TestReferenceChecker *checker,
190                                    const gmx::Selection &sel, TestFlags flags);
191
192         void runParser(const char *const *selections, size_t count);
193         void runCompiler();
194         void checkCompiled();
195         void runEvaluate();
196         void runEvaluateFinal();
197
198         gmx::test::TestReferenceData    data_;
199         gmx::test::TestReferenceChecker checker_;
200         size_t                          count_;
201         int                             framenr_;
202         TestFlags                       flags_;
203 };
204
205
206 void
207 SelectionCollectionDataTest::checkSelection(
208         gmx::test::TestReferenceChecker *checker,
209         const gmx::Selection &sel, TestFlags flags)
210 {
211     using gmx::test::TestReferenceChecker;
212
213     {
214         gmx::ConstArrayRef<int> atoms = sel.atomIndices();
215         checker->checkSequence(atoms.begin(), atoms.end(), "Atoms");
216     }
217     if (flags.test(efTestPositionAtoms)
218         || flags.test(efTestPositionCoordinates)
219         || flags.test(efTestPositionMapping))
220     {
221         TestReferenceChecker compound(
222                 checker->checkSequenceCompound("Positions", sel.posCount()));
223         for (int i = 0; i < sel.posCount(); ++i)
224         {
225             TestReferenceChecker          poscompound(compound.checkCompound("Position", NULL));
226             const gmx::SelectionPosition &p = sel.position(i);
227             if (flags.test(efTestPositionAtoms))
228             {
229                 gmx::ConstArrayRef<int> atoms = p.atomIndices();
230                 poscompound.checkSequence(atoms.begin(), atoms.end(), "Atoms");
231             }
232             if (flags.test(efTestPositionCoordinates))
233             {
234                 poscompound.checkVector(p.x(), "Coordinates");
235             }
236             if (flags.test(efTestPositionMapping))
237             {
238                 poscompound.checkInteger(p.refId(), "RefId");
239                 poscompound.checkInteger(p.mappedId(), "MappedId");
240             }
241         }
242     }
243 }
244
245
246 void
247 SelectionCollectionDataTest::runParser(const char *const *selections,
248                                        size_t             count)
249 {
250     using gmx::test::TestReferenceChecker;
251
252     TestReferenceChecker compound(checker_.checkCompound("ParsedSelections", "Parsed"));
253     size_t               varcount = 0;
254     count_ = 0;
255     for (size_t i = 0; i < count; ++i)
256     {
257         SCOPED_TRACE(std::string("Parsing selection \"")
258                      + selections[i] + "\"");
259         gmx::SelectionList result;
260         ASSERT_NO_THROW(result = sc_.parseFromString(selections[i]));
261         sel_.insert(sel_.end(), result.begin(), result.end());
262         if (sel_.size() == count_)
263         {
264             std::string          id = gmx::formatString("Variable%d", static_cast<int>(varcount + 1));
265             TestReferenceChecker varcompound(
266                     compound.checkCompound("ParsedVariable", id.c_str()));
267             varcompound.checkString(selections[i], "Input");
268             ++varcount;
269         }
270         else
271         {
272             std::string          id = gmx::formatString("Selection%d", static_cast<int>(count_ + 1));
273             TestReferenceChecker selcompound(
274                     compound.checkCompound("ParsedSelection", id.c_str()));
275             selcompound.checkString(selections[i], "Input");
276             selcompound.checkString(sel_[count_].name(), "Name");
277             selcompound.checkString(sel_[count_].selectionText(), "Text");
278             selcompound.checkBoolean(sel_[count_].isDynamic(), "Dynamic");
279             ++count_;
280         }
281     }
282 }
283
284
285 void
286 SelectionCollectionDataTest::runCompiler()
287 {
288     ASSERT_NO_THROW(sc_.compile());
289     ASSERT_EQ(count_, sel_.size());
290     checkCompiled();
291 }
292
293
294 void
295 SelectionCollectionDataTest::checkCompiled()
296 {
297     using gmx::test::TestReferenceChecker;
298     const TestFlags      mask = ~TestFlags(efTestPositionCoordinates);
299
300     TestReferenceChecker compound(checker_.checkCompound("CompiledSelections", "Compiled"));
301     for (size_t i = 0; i < count_; ++i)
302     {
303         SCOPED_TRACE(std::string("Checking selection \"") +
304                      sel_[i].selectionText() + "\"");
305         std::string          id = gmx::formatString("Selection%d", static_cast<int>(i + 1));
306         TestReferenceChecker selcompound(
307                 compound.checkCompound("Selection", id.c_str()));
308         if (!flags_.test(efDontTestCompiledAtoms))
309         {
310             checkSelection(&selcompound, sel_[i], flags_ & mask);
311         }
312     }
313 }
314
315
316 void
317 SelectionCollectionDataTest::runEvaluate()
318 {
319     using gmx::test::TestReferenceChecker;
320
321     ++framenr_;
322     ASSERT_NO_THROW(sc_.evaluate(frame_, NULL));
323     std::string          frame = gmx::formatString("Frame%d", framenr_);
324     TestReferenceChecker compound(
325             checker_.checkCompound("EvaluatedSelections", frame.c_str()));
326     for (size_t i = 0; i < count_; ++i)
327     {
328         SCOPED_TRACE(std::string("Checking selection \"") +
329                      sel_[i].selectionText() + "\"");
330         std::string          id = gmx::formatString("Selection%d", static_cast<int>(i + 1));
331         TestReferenceChecker selcompound(
332                 compound.checkCompound("Selection", id.c_str()));
333         checkSelection(&selcompound, sel_[i], flags_);
334     }
335 }
336
337
338 void
339 SelectionCollectionDataTest::runEvaluateFinal()
340 {
341     ASSERT_NO_THROW(sc_.evaluateFinal(framenr_));
342     if (!checker_.isWriteMode())
343     {
344         checkCompiled();
345     }
346 }
347
348
349 void
350 SelectionCollectionDataTest::runTest(int natoms, const char * const *selections,
351                                      size_t count)
352 {
353     ASSERT_NO_FATAL_FAILURE(runParser(selections, count));
354     ASSERT_NO_FATAL_FAILURE(setAtomCount(natoms));
355     ASSERT_NO_FATAL_FAILURE(runCompiler());
356 }
357
358
359 void
360 SelectionCollectionDataTest::runTest(const char         *filename,
361                                      const char * const *selections,
362                                      size_t              count)
363 {
364     ASSERT_NO_FATAL_FAILURE(runParser(selections, count));
365     ASSERT_NO_FATAL_FAILURE(loadTopology(filename));
366     ASSERT_NO_FATAL_FAILURE(runCompiler());
367     if (flags_.test(efTestEvaluation))
368     {
369         ASSERT_NO_FATAL_FAILURE(runEvaluate());
370         ASSERT_NO_FATAL_FAILURE(runEvaluateFinal());
371     }
372 }
373
374
375 /********************************************************************
376  * Tests for SelectionCollection functionality without reference data
377  */
378
379 TEST_F(SelectionCollectionTest, HandlesNoSelections)
380 {
381     EXPECT_FALSE(sc_.requiresTopology());
382     EXPECT_NO_THROW(sc_.compile());
383 }
384
385 TEST_F(SelectionCollectionTest, ParsesSelectionsFromFile)
386 {
387     ASSERT_NO_THROW(sel_ = sc_.parseFromFile(
388                                 gmx::test::TestFileManager::getInputFilePath("selfile.dat")));
389     // These should match the contents of selfile.dat
390     ASSERT_EQ(2U, sel_.size());
391     EXPECT_STREQ("resname RA RB", sel_[0].selectionText());
392     EXPECT_STREQ("resname RB RC", sel_[1].selectionText());
393 }
394
395 TEST_F(SelectionCollectionTest, HandlesInvalidRegularExpressions)
396 {
397     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
398     EXPECT_THROW({
399                      sc_.parseFromString("resname ~ \"R[A\"");
400                      sc_.compile();
401                  }, gmx::InvalidInputError);
402 }
403
404 TEST_F(SelectionCollectionTest, HandlesUnsupportedRegularExpressions)
405 {
406     if (!gmx::Regex::isSupported())
407     {
408         ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
409         EXPECT_THROW({
410                          sc_.parseFromString("resname \"R[AD]\"");
411                          sc_.compile();
412                      }, gmx::InvalidInputError);
413     }
414 }
415
416 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue)
417 {
418     EXPECT_THROW(sc_.parseFromString("mindist from atomnr 1 cutoff"),
419                  gmx::InvalidInputError);
420 }
421
422 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue2)
423 {
424     EXPECT_THROW(sc_.parseFromString("within 1 of"),
425                  gmx::InvalidInputError);
426 }
427
428 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue3)
429 {
430     EXPECT_THROW(sc_.parseFromString("within of atomnr 1"),
431                  gmx::InvalidInputError);
432 }
433
434 TEST_F(SelectionCollectionTest, HandlesHelpKeywordInInvalidContext)
435 {
436     EXPECT_THROW(sc_.parseFromString("resname help"),
437                  gmx::InvalidInputError);
438 }
439
440 // TODO: Tests for more parser errors
441
442 TEST_F(SelectionCollectionTest, RecoversFromUnknownGroupReference)
443 {
444     ASSERT_NO_THROW(sc_.parseFromString("group \"foo\""));
445     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
446     EXPECT_THROW(sc_.setIndexGroups(NULL), gmx::InvalidInputError);
447     EXPECT_THROW(sc_.compile(), gmx::APIError);
448 }
449
450 TEST_F(SelectionCollectionTest, RecoversFromMissingMoleculeInfo)
451 {
452     ASSERT_NO_THROW(sc_.parseFromString("molindex 1 to 5"));
453     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
454     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
455 }
456
457 TEST_F(SelectionCollectionTest, RecoversFromMissingAtomTypes)
458 {
459     ASSERT_NO_THROW(sc_.parseFromString("type CA"));
460     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
461     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
462 }
463
464 TEST_F(SelectionCollectionTest, RecoversFromMissingPDBInfo)
465 {
466     ASSERT_NO_THROW(sc_.parseFromString("altloc A"));
467     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
468     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
469 }
470
471 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation)
472 {
473     ASSERT_NO_THROW(sc_.parseFromString("all permute 1 1"));
474     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
475     EXPECT_THROW(sc_.compile(), gmx::InvalidInputError);
476 }
477
478 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation2)
479 {
480     ASSERT_NO_THROW(sc_.parseFromString("all permute 3 2 1"));
481     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
482     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
483 }
484
485 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation3)
486 {
487     ASSERT_NO_THROW(sc_.parseFromString("x < 1.5 permute 3 2 1"));
488     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
489     ASSERT_NO_THROW(sc_.compile());
490     EXPECT_THROW(sc_.evaluate(frame_, NULL), gmx::InconsistentInputError);
491 }
492
493 // TODO: Tests for evaluation errors
494
495
496 /********************************************************************
497  * Tests for selection keywords
498  */
499
500 TEST_F(SelectionCollectionDataTest, HandlesAllNone)
501 {
502     static const char * const selections[] = {
503         "all",
504         "none"
505     };
506     runTest(10, selections);
507 }
508
509 TEST_F(SelectionCollectionDataTest, HandlesAtomnr)
510 {
511     static const char * const selections[] = {
512         "atomnr 1 to 3 6 to 8",
513         "atomnr 4 2 5 to 7",
514         "atomnr <= 5"
515     };
516     runTest(10, selections);
517 }
518
519 TEST_F(SelectionCollectionDataTest, HandlesResnr)
520 {
521     static const char * const selections[] = {
522         "resnr 1 2 5",
523         "resid 4 to 3"
524     };
525     runTest("simple.gro", selections);
526 }
527
528 TEST_F(SelectionCollectionDataTest, HandlesResIndex)
529 {
530     static const char * const selections[] = {
531         "resindex 1 4",
532         "residue 1 3"
533     };
534     runTest("simple.pdb", selections);
535 }
536
537 // TODO: Add test for "molindex"
538
539 TEST_F(SelectionCollectionDataTest, HandlesAtomname)
540 {
541     static const char * const selections[] = {
542         "name CB",
543         "atomname S1 S2"
544     };
545     runTest("simple.gro", selections);
546 }
547
548 TEST_F(SelectionCollectionDataTest, HandlesPdbAtomname)
549 {
550     static const char * const selections[] = {
551         "name HG21",
552         "name 1HG2",
553         "pdbname HG21 CB",
554         "pdbatomname 1HG2"
555     };
556     runTest("simple.pdb", selections);
557 }
558
559 // TODO: Add test for atomtype
560
561 TEST_F(SelectionCollectionDataTest, HandlesChain)
562 {
563     static const char * const selections[] = {
564         "chain A",
565         "chain B"
566     };
567     runTest("simple.pdb", selections);
568 }
569
570 // TODO: Add test for mass
571 // TODO: Add test for charge
572
573 TEST_F(SelectionCollectionDataTest, HandlesAltLoc)
574 {
575     static const char * const selections[] = {
576         "altloc \" \"",
577         "altloc A"
578     };
579     runTest("simple.pdb", selections);
580 }
581
582 TEST_F(SelectionCollectionDataTest, HandlesInsertCode)
583 {
584     static const char * const selections[] = {
585         "insertcode \" \"",
586         "insertcode A"
587     };
588     runTest("simple.pdb", selections);
589 }
590
591 TEST_F(SelectionCollectionDataTest, HandlesOccupancy)
592 {
593     static const char * const selections[] = {
594         "occupancy 1",
595         "occupancy < .5"
596     };
597     runTest("simple.pdb", selections);
598 }
599
600 TEST_F(SelectionCollectionDataTest, HandlesBeta)
601 {
602     static const char * const selections[] = {
603         "beta 0",
604         "beta >= 0.3"
605     };
606     runTest("simple.pdb", selections);
607 }
608
609 TEST_F(SelectionCollectionDataTest, HandlesResname)
610 {
611     static const char * const selections[] = {
612         "resname RA",
613         "resname RB RC"
614     };
615     runTest("simple.gro", selections);
616 }
617
618 TEST_F(SelectionCollectionDataTest, HandlesCoordinateKeywords)
619 {
620     static const char * const selections[] = {
621         "x < 3",
622         "y >= 3",
623         "x {-1 to 2}"
624     };
625     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
626     runTest("simple.gro", selections);
627 }
628
629
630 TEST_F(SelectionCollectionDataTest, HandlesSameResidue)
631 {
632     static const char * const selections[] = {
633         "same residue as atomnr 1 4 12"
634     };
635     runTest("simple.gro", selections);
636 }
637
638
639 TEST_F(SelectionCollectionDataTest, HandlesSameResidueName)
640 {
641     static const char * const selections[] = {
642         "same resname as atomnr 1 14"
643     };
644     runTest("simple.gro", selections);
645 }
646
647
648 TEST_F(SelectionCollectionDataTest, HandlesPositionKeywords)
649 {
650     static const char * const selections[] = {
651         "cog of resnr 1 3",
652         "res_cog of name CB and resnr 1 3",
653         "whole_res_cog of name CB and resnr 1 3",
654         "part_res_cog of x < 3",
655         "dyn_res_cog of x < 3"
656     };
657     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
658              | efTestPositionAtoms);
659     runTest("simple.gro", selections);
660 }
661
662
663 TEST_F(SelectionCollectionDataTest, HandlesDistanceKeyword)
664 {
665     static const char * const selections[] = {
666         "distance from cog of resnr 1 < 2"
667     };
668     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
669     runTest("simple.gro", selections);
670 }
671
672
673 TEST_F(SelectionCollectionDataTest, HandlesMinDistanceKeyword)
674 {
675     static const char * const selections[] = {
676         "mindistance from resnr 1 < 2"
677     };
678     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
679     runTest("simple.gro", selections);
680 }
681
682
683 TEST_F(SelectionCollectionDataTest, HandlesWithinKeyword)
684 {
685     static const char * const selections[] = {
686         "within 1 of resnr 2"
687     };
688     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
689     runTest("simple.gro", selections);
690 }
691
692
693 TEST_F(SelectionCollectionDataTest, HandlesInSolidAngleKeyword)
694 {
695     // Both of these should evaluate to empty on a correct implementation.
696     static const char * const selections[] = {
697         "resname TP and not insolidangle center cog of resname C span resname R cutoff 20",
698         "resname TN and insolidangle center cog of resname C span resname R cutoff 20"
699     };
700     setFlags(TestFlags() | efDontTestCompiledAtoms | efTestEvaluation);
701     runTest("sphere.gro", selections);
702 }
703
704
705 TEST_F(SelectionCollectionDataTest, HandlesPermuteModifier)
706 {
707     static const char * const selections[] = {
708         "all permute 3 1 2",
709         "res_cog of resnr 1 to 4 permute 2 1",
710         "name CB S1 and res_cog x < 3 permute 2 1"
711     };
712     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
713              | efTestPositionAtoms | efTestPositionMapping);
714     runTest("simple.gro", selections);
715 }
716
717
718 TEST_F(SelectionCollectionDataTest, HandlesPlusModifier)
719 {
720     static const char * const selections[] = {
721         "name S2 plus name S1",
722         "res_cog of resnr 2 plus res_cog of resnr 1 plus res_cog of resnr 3",
723         "name S1 and y < 3 plus res_cog of x < 2.5"
724     };
725     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
726              | efTestPositionAtoms | efTestPositionMapping);
727     runTest("simple.gro", selections);
728 }
729
730
731 TEST_F(SelectionCollectionDataTest, HandlesMergeModifier)
732 {
733     static const char * const selections[] = {
734         "name S2 merge name S1",
735         "resnr 1 2 and name S2 merge resnr 1 2 and name S1 merge res_cog of resnr 1 2",
736         "name S1 and x < 2.5 merge res_cog of x < 2.5"
737     };
738     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
739              | efTestPositionAtoms | efTestPositionMapping);
740     runTest("simple.gro", selections);
741 }
742
743
744 /********************************************************************
745  * Tests for selection syntactic constructs
746  */
747
748 TEST_F(SelectionCollectionDataTest, HandlesConstantPositions)
749 {
750     static const char * const selections[] = {
751         "[1, -2, 3.5]"
752     };
753     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
754     runTest("simple.gro", selections);
755 }
756
757
758 TEST_F(SelectionCollectionDataTest, HandlesWithinConstantPositions)
759 {
760     static const char * const selections[] = {
761         "within 1 of [2, 1, 0]"
762     };
763     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
764     runTest("simple.gro", selections);
765 }
766
767
768 TEST_F(SelectionCollectionDataTest, HandlesForcedStringMatchingMode)
769 {
770     static const char * const selections[] = {
771         "name = S1 \"C?\"",
772         "name ? S1 \"C?\""
773     };
774     runTest("simple.gro", selections);
775 }
776
777
778 TEST_F(SelectionCollectionDataTest, HandlesWildcardMatching)
779 {
780     static const char * const selections[] = {
781         "name \"S?\"",
782         "name ? \"S?\""
783     };
784     runTest("simple.gro", selections);
785 }
786
787
788 TEST_F(SelectionCollectionDataTest, HandlesRegexMatching)
789 {
790     static const char * const selections[] = {
791         "resname \"R[BD]\"",
792         "resname ~ \"R[BD]\""
793     };
794     if (gmx::Regex::isSupported())
795     {
796         runTest("simple.gro", selections);
797     }
798 }
799
800
801 TEST_F(SelectionCollectionDataTest, HandlesBasicBoolean)
802 {
803     static const char * const selections[] = {
804         "atomnr 1 to 5 and atomnr 2 to 7",
805         "atomnr 1 to 5 or not atomnr 3 to 8",
806         "not not atomnr 1 to 5 and atomnr 2 to 6 and not not atomnr 3 to 7",
807         "atomnr 1 to 5 and (atomnr 2 to 7 and atomnr 3 to 6)",
808         "x < 5 and atomnr 1 to 5 and y < 3 and atomnr 2 to 4"
809     };
810     runTest(10, selections);
811 }
812
813
814 TEST_F(SelectionCollectionDataTest, HandlesNumericComparisons)
815 {
816     static const char * const selections[] = {
817         "x > 2",
818         "2 < x",
819         "y > resnr",
820         "resnr < 2.5",
821         "2.5 > resnr"
822     };
823     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
824     runTest("simple.gro", selections);
825 }
826
827
828 TEST_F(SelectionCollectionDataTest, HandlesArithmeticExpressions)
829 {
830     static const char * const selections[] = {
831         "x+1 > 3",
832         "(y-1)^2 <= 1",
833         "x+--1 > 3",
834         "-x+-1 < -3"
835     };
836     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
837     runTest("simple.gro", selections);
838 }
839
840
841 TEST_F(SelectionCollectionDataTest, HandlesNumericVariables)
842 {
843     static const char * const selections[] = {
844         "value = x + y",
845         "value <= 4",
846         "index = resnr",
847         "index < 3"
848     };
849     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
850     runTest("simple.gro", selections);
851 }
852
853
854 TEST_F(SelectionCollectionDataTest, HandlesComplexNumericVariables)
855 {
856     static const char * const selections[] = {
857         "value = x + y",
858         "resname RA and value <= 4",
859         "resname RA RB and x < 3 and value <= 4",
860         "index = atomnr",
861         "resname RA and index < 3",
862         "resname RB and y < 3 and index < 6"
863     };
864     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
865     runTest("simple.gro", selections);
866 }
867
868
869 TEST_F(SelectionCollectionDataTest, HandlesPositionVariables)
870 {
871     static const char * const selections[] = {
872         "foo = res_cog of resname RA",
873         "foo",
874         "within 1 of foo",
875         "bar = cog of resname RA",
876         "bar",
877         "within 1 of bar"
878     };
879     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
880     runTest("simple.gro", selections);
881 }
882
883
884 TEST_F(SelectionCollectionDataTest, HandlesConstantPositionInVariable)
885 {
886     static const char * const selections[] = {
887         "constpos = [1.0, 2.5, 0.5]",
888         "constpos",
889         "within 2 of constpos"
890     };
891     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
892              | efTestPositionAtoms);
893     runTest("simple.gro", selections);
894 }
895
896
897 TEST_F(SelectionCollectionDataTest, HandlesNumericConstantsInVariables)
898 {
899     static const char * const selections[] = {
900         "constint = 4",
901         "constreal1 = 0.5",
902         "constreal2 = 2.7",
903         "resnr < constint",
904         "x + constreal1 < constreal2"
905     };
906     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
907     runTest("simple.gro", selections);
908 }
909
910
911 /********************************************************************
912  * Tests for complex boolean syntax
913  */
914
915 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysis)
916 {
917     static const char * const selections[] = {
918         "atomnr 1 to 5 and atomnr 2 to 7 and x < 2",
919         "atomnr 1 to 5 and (atomnr 4 to 7 or x < 2)",
920         "atomnr 1 to 5 and y < 3 and (atomnr 4 to 7 or x < 2)",
921         "atomnr 1 to 5 and not (atomnr 4 to 7 or x < 2)",
922         "atomnr 1 to 5 or (atomnr 4 to 6 and (atomnr 5 to 7 or x < 2))"
923     };
924     runTest(10, selections);
925 }
926
927
928 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysisWithVariables)
929 {
930     static const char * const selections[] = {
931         "foo = atomnr 4 to 7 or x < 2",
932         "atomnr 1 to 4 and foo",
933         "atomnr 2 to 6 and y < 3 and foo",
934         "atomnr 6 to 10 and not foo"
935     };
936     runTest(10, selections);
937 }
938
939
940 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysisWithMoreVariables)
941 {
942     static const char * const selections[] = {
943         "foo = atomnr 4 to 7",
944         "bar = foo and x < 2",
945         "bar2 = foo and y < 2",
946         "atomnr 1 to 4 and bar",
947         "atomnr 2 to 6 and y < 3 and bar2",
948         "atomnr 6 to 10 and not foo"
949     };
950     runTest(10, selections);
951 }
952
953
954 /********************************************************************
955  * Tests for complex subexpression cases
956  *
957  * These tests use some knowledge of the implementation to trigger different
958  * paths in the code.
959  */
960
961 TEST_F(SelectionCollectionDataTest, HandlesUnusedVariables)
962 {
963     static const char * const selections[] = {
964         "unused1 = atomnr 1 to 3",
965         "foo = atomnr 4 to 7",
966         "atomnr 1 to 6 and foo",
967         "unused2 = atomnr 3 to 5"
968     };
969     runTest(10, selections);
970 }
971
972
973 TEST_F(SelectionCollectionDataTest, HandlesVariablesWithStaticEvaluationGroups)
974 {
975     static const char * const selections[] = {
976         "foo = atomnr 4 to 7 and x < 2",
977         "atomnr 1 to 5 and foo",
978         "atomnr 3 to 7 and foo"
979     };
980     runTest(10, selections);
981 }
982
983
984 TEST_F(SelectionCollectionDataTest, HandlesVariablesWithMixedEvaluationGroups)
985 {
986     static const char * const selections[] = {
987         "foo = atomnr 4 to 7 and x < 2",
988         "atomnr 1 to 6 and foo",
989         "within 1 of foo",
990         "foo"
991     };
992     runTest(10, selections);
993 }
994
995
996 } // namespace