Merge "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             efTestPositionMasses        = 1<<4,
164             efTestPositionCharges       = 1<<5,
165             efDontTestCompiledAtoms     = 1<<8
166         };
167         typedef gmx::FlagsTemplate<TestFlag> TestFlags;
168
169         SelectionCollectionDataTest()
170             : checker_(data_.rootChecker()), count_(0), framenr_(0)
171         {
172         }
173
174         void setFlags(TestFlags flags) { flags_ = flags; }
175
176         template <size_t count>
177         void runTest(int natoms, const char *const (&selections)[count])
178         {
179             runTest(natoms, selections, count);
180         }
181         template <size_t count>
182         void runTest(const char *filename, const char *const (&selections)[count])
183         {
184             runTest(filename, selections, count);
185         }
186
187         template <size_t count>
188         void runParser(const char *const (&selections)[count])
189         {
190             runParser(selections, count);
191         }
192
193         void runCompiler();
194         void runEvaluate();
195         void runEvaluateFinal();
196
197     private:
198         static void checkSelection(gmx::test::TestReferenceChecker *checker,
199                                    const gmx::Selection &sel, TestFlags flags);
200
201         void runTest(int natoms, const char *const *selections, size_t count);
202         void runTest(const char *filename, const char *const *selections,
203                      size_t count);
204         void runParser(const char *const *selections, size_t count);
205
206         void checkCompiled();
207
208         gmx::test::TestReferenceData    data_;
209         gmx::test::TestReferenceChecker checker_;
210         size_t                          count_;
211         int                             framenr_;
212         TestFlags                       flags_;
213 };
214
215
216 void
217 SelectionCollectionDataTest::checkSelection(
218         gmx::test::TestReferenceChecker *checker,
219         const gmx::Selection &sel, TestFlags flags)
220 {
221     using gmx::test::TestReferenceChecker;
222
223     {
224         gmx::ConstArrayRef<int> atoms = sel.atomIndices();
225         checker->checkSequence(atoms.begin(), atoms.end(), "Atoms");
226     }
227     if (flags.test(efTestPositionAtoms)
228         || flags.test(efTestPositionCoordinates)
229         || flags.test(efTestPositionMapping)
230         || flags.test(efTestPositionMasses)
231         || flags.test(efTestPositionCharges))
232     {
233         TestReferenceChecker compound(
234                 checker->checkSequenceCompound("Positions", sel.posCount()));
235         for (int i = 0; i < sel.posCount(); ++i)
236         {
237             TestReferenceChecker          poscompound(compound.checkCompound("Position", NULL));
238             const gmx::SelectionPosition &p = sel.position(i);
239             if (flags.test(efTestPositionAtoms))
240             {
241                 gmx::ConstArrayRef<int> atoms = p.atomIndices();
242                 poscompound.checkSequence(atoms.begin(), atoms.end(), "Atoms");
243             }
244             if (flags.test(efTestPositionCoordinates))
245             {
246                 poscompound.checkVector(p.x(), "Coordinates");
247             }
248             if (flags.test(efTestPositionMapping))
249             {
250                 poscompound.checkInteger(p.refId(), "RefId");
251                 poscompound.checkInteger(p.mappedId(), "MappedId");
252             }
253             if (flags.test(efTestPositionMasses))
254             {
255                 poscompound.checkReal(p.mass(), "Mass");
256             }
257             if (flags.test(efTestPositionCharges))
258             {
259                 poscompound.checkReal(p.charge(), "Charge");
260             }
261         }
262     }
263 }
264
265
266 void
267 SelectionCollectionDataTest::runParser(const char *const *selections,
268                                        size_t             count)
269 {
270     using gmx::test::TestReferenceChecker;
271
272     TestReferenceChecker compound(checker_.checkCompound("ParsedSelections", "Parsed"));
273     size_t               varcount = 0;
274     count_ = 0;
275     for (size_t i = 0; i < count; ++i)
276     {
277         SCOPED_TRACE(std::string("Parsing selection \"")
278                      + selections[i] + "\"");
279         gmx::SelectionList result;
280         ASSERT_NO_THROW(result = sc_.parseFromString(selections[i]));
281         sel_.insert(sel_.end(), result.begin(), result.end());
282         if (sel_.size() == count_)
283         {
284             std::string          id = gmx::formatString("Variable%d", static_cast<int>(varcount + 1));
285             TestReferenceChecker varcompound(
286                     compound.checkCompound("ParsedVariable", id.c_str()));
287             varcompound.checkString(selections[i], "Input");
288             ++varcount;
289         }
290         else
291         {
292             std::string          id = gmx::formatString("Selection%d", static_cast<int>(count_ + 1));
293             TestReferenceChecker selcompound(
294                     compound.checkCompound("ParsedSelection", id.c_str()));
295             selcompound.checkString(selections[i], "Input");
296             selcompound.checkString(sel_[count_].name(), "Name");
297             selcompound.checkString(sel_[count_].selectionText(), "Text");
298             selcompound.checkBoolean(sel_[count_].isDynamic(), "Dynamic");
299             ++count_;
300         }
301     }
302 }
303
304
305 void
306 SelectionCollectionDataTest::runCompiler()
307 {
308     ASSERT_NO_THROW(sc_.compile());
309     ASSERT_EQ(count_, sel_.size());
310     checkCompiled();
311 }
312
313
314 void
315 SelectionCollectionDataTest::checkCompiled()
316 {
317     using gmx::test::TestReferenceChecker;
318     const TestFlags      mask = ~TestFlags(efTestPositionCoordinates);
319
320     TestReferenceChecker compound(checker_.checkCompound("CompiledSelections", "Compiled"));
321     for (size_t i = 0; i < count_; ++i)
322     {
323         SCOPED_TRACE(std::string("Checking selection \"") +
324                      sel_[i].selectionText() + "\"");
325         std::string          id = gmx::formatString("Selection%d", static_cast<int>(i + 1));
326         TestReferenceChecker selcompound(
327                 compound.checkCompound("Selection", id.c_str()));
328         if (!flags_.test(efDontTestCompiledAtoms))
329         {
330             checkSelection(&selcompound, sel_[i], flags_ & mask);
331         }
332     }
333 }
334
335
336 void
337 SelectionCollectionDataTest::runEvaluate()
338 {
339     using gmx::test::TestReferenceChecker;
340
341     ++framenr_;
342     ASSERT_NO_THROW(sc_.evaluate(frame_, NULL));
343     std::string          frame = gmx::formatString("Frame%d", framenr_);
344     TestReferenceChecker compound(
345             checker_.checkCompound("EvaluatedSelections", frame.c_str()));
346     for (size_t i = 0; i < count_; ++i)
347     {
348         SCOPED_TRACE(std::string("Checking selection \"") +
349                      sel_[i].selectionText() + "\"");
350         std::string          id = gmx::formatString("Selection%d", static_cast<int>(i + 1));
351         TestReferenceChecker selcompound(
352                 compound.checkCompound("Selection", id.c_str()));
353         checkSelection(&selcompound, sel_[i], flags_);
354     }
355 }
356
357
358 void
359 SelectionCollectionDataTest::runEvaluateFinal()
360 {
361     ASSERT_NO_THROW(sc_.evaluateFinal(framenr_));
362     if (!checker_.isWriteMode())
363     {
364         checkCompiled();
365     }
366 }
367
368
369 void
370 SelectionCollectionDataTest::runTest(int natoms, const char * const *selections,
371                                      size_t count)
372 {
373     ASSERT_NO_FATAL_FAILURE(runParser(selections, count));
374     ASSERT_NO_FATAL_FAILURE(setAtomCount(natoms));
375     ASSERT_NO_FATAL_FAILURE(runCompiler());
376 }
377
378
379 void
380 SelectionCollectionDataTest::runTest(const char         *filename,
381                                      const char * const *selections,
382                                      size_t              count)
383 {
384     ASSERT_NO_FATAL_FAILURE(runParser(selections, count));
385     ASSERT_NO_FATAL_FAILURE(loadTopology(filename));
386     ASSERT_NO_FATAL_FAILURE(runCompiler());
387     if (flags_.test(efTestEvaluation))
388     {
389         ASSERT_NO_FATAL_FAILURE(runEvaluate());
390         ASSERT_NO_FATAL_FAILURE(runEvaluateFinal());
391     }
392 }
393
394
395 /********************************************************************
396  * Tests for SelectionCollection functionality without reference data
397  */
398
399 TEST_F(SelectionCollectionTest, HandlesNoSelections)
400 {
401     EXPECT_FALSE(sc_.requiresTopology());
402     EXPECT_NO_THROW(sc_.compile());
403 }
404
405 TEST_F(SelectionCollectionTest, HandlesVelocityAndForceRequests)
406 {
407     ASSERT_NO_THROW(sel_ = sc_.parseFromString("atomnr 1 to 10; none"));
408     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
409     ASSERT_EQ(2U, sel_.size());
410     ASSERT_NO_THROW(sel_[0].setEvaluateVelocities(true));
411     ASSERT_NO_THROW(sel_[1].setEvaluateVelocities(true));
412     ASSERT_NO_THROW(sel_[0].setEvaluateForces(true));
413     ASSERT_NO_THROW(sel_[1].setEvaluateForces(true));
414     ASSERT_NO_THROW(sc_.compile());
415     EXPECT_TRUE(sel_[0].hasVelocities());
416     EXPECT_TRUE(sel_[1].hasVelocities());
417     EXPECT_TRUE(sel_[0].hasForces());
418     EXPECT_TRUE(sel_[1].hasForces());
419 }
420
421 TEST_F(SelectionCollectionTest, ParsesSelectionsFromFile)
422 {
423     ASSERT_NO_THROW(sel_ = sc_.parseFromFile(
424                                 gmx::test::TestFileManager::getInputFilePath("selfile.dat")));
425     // These should match the contents of selfile.dat
426     ASSERT_EQ(2U, sel_.size());
427     EXPECT_STREQ("resname RA RB", sel_[0].selectionText());
428     EXPECT_STREQ("resname RB RC", sel_[1].selectionText());
429 }
430
431 TEST_F(SelectionCollectionTest, HandlesInvalidRegularExpressions)
432 {
433     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
434     EXPECT_THROW({
435                      sc_.parseFromString("resname ~ \"R[A\"");
436                      sc_.compile();
437                  }, gmx::InvalidInputError);
438 }
439
440 TEST_F(SelectionCollectionTest, HandlesUnsupportedRegularExpressions)
441 {
442     if (!gmx::Regex::isSupported())
443     {
444         ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
445         EXPECT_THROW({
446                          sc_.parseFromString("resname \"R[AD]\"");
447                          sc_.compile();
448                      }, gmx::InvalidInputError);
449     }
450 }
451
452 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue)
453 {
454     EXPECT_THROW(sc_.parseFromString("mindist from atomnr 1 cutoff"),
455                  gmx::InvalidInputError);
456 }
457
458 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue2)
459 {
460     EXPECT_THROW(sc_.parseFromString("within 1 of"),
461                  gmx::InvalidInputError);
462 }
463
464 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue3)
465 {
466     EXPECT_THROW(sc_.parseFromString("within of atomnr 1"),
467                  gmx::InvalidInputError);
468 }
469
470 TEST_F(SelectionCollectionTest, HandlesHelpKeywordInInvalidContext)
471 {
472     EXPECT_THROW(sc_.parseFromString("resname help"),
473                  gmx::InvalidInputError);
474 }
475
476 // TODO: Tests for more parser errors
477
478 TEST_F(SelectionCollectionTest, RecoversFromUnknownGroupReference)
479 {
480     ASSERT_NO_THROW(sc_.parseFromString("group \"foo\""));
481     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
482     EXPECT_THROW(sc_.setIndexGroups(NULL), gmx::InvalidInputError);
483     EXPECT_THROW(sc_.compile(), gmx::APIError);
484 }
485
486 TEST_F(SelectionCollectionTest, RecoversFromMissingMoleculeInfo)
487 {
488     ASSERT_NO_THROW(sc_.parseFromString("molindex 1 to 5"));
489     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
490     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
491 }
492
493 TEST_F(SelectionCollectionTest, RecoversFromMissingAtomTypes)
494 {
495     ASSERT_NO_THROW(sc_.parseFromString("type CA"));
496     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
497     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
498 }
499
500 TEST_F(SelectionCollectionTest, RecoversFromMissingPDBInfo)
501 {
502     ASSERT_NO_THROW(sc_.parseFromString("altloc A"));
503     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
504     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
505 }
506
507 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation)
508 {
509     ASSERT_NO_THROW(sc_.parseFromString("all permute 1 1"));
510     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
511     EXPECT_THROW(sc_.compile(), gmx::InvalidInputError);
512 }
513
514 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation2)
515 {
516     ASSERT_NO_THROW(sc_.parseFromString("all permute 3 2 1"));
517     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
518     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
519 }
520
521 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation3)
522 {
523     ASSERT_NO_THROW(sc_.parseFromString("x < 1.5 permute 3 2 1"));
524     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
525     ASSERT_NO_THROW(sc_.compile());
526     EXPECT_THROW(sc_.evaluate(frame_, NULL), gmx::InconsistentInputError);
527 }
528
529 // TODO: Tests for evaluation errors
530
531
532 /********************************************************************
533  * Tests for selection keywords
534  */
535
536 TEST_F(SelectionCollectionDataTest, HandlesAllNone)
537 {
538     static const char * const selections[] = {
539         "all",
540         "none"
541     };
542     runTest(10, selections);
543 }
544
545 TEST_F(SelectionCollectionDataTest, HandlesAtomnr)
546 {
547     static const char * const selections[] = {
548         "atomnr 1 to 3 6 to 8",
549         "atomnr 4 2 5 to 7",
550         "atomnr <= 5"
551     };
552     runTest(10, selections);
553 }
554
555 TEST_F(SelectionCollectionDataTest, HandlesResnr)
556 {
557     static const char * const selections[] = {
558         "resnr 1 2 5",
559         "resid 4 to 3"
560     };
561     runTest("simple.gro", selections);
562 }
563
564 TEST_F(SelectionCollectionDataTest, HandlesResIndex)
565 {
566     static const char * const selections[] = {
567         "resindex 1 4",
568         "residue 1 3"
569     };
570     runTest("simple.pdb", selections);
571 }
572
573 // TODO: Add test for "molindex"
574
575 TEST_F(SelectionCollectionDataTest, HandlesAtomname)
576 {
577     static const char * const selections[] = {
578         "name CB",
579         "atomname S1 S2"
580     };
581     runTest("simple.gro", selections);
582 }
583
584 TEST_F(SelectionCollectionDataTest, HandlesPdbAtomname)
585 {
586     static const char * const selections[] = {
587         "name HG21",
588         "name 1HG2",
589         "pdbname HG21 CB",
590         "pdbatomname 1HG2"
591     };
592     runTest("simple.pdb", selections);
593 }
594
595 // TODO: Add test for atomtype
596
597 TEST_F(SelectionCollectionDataTest, HandlesChain)
598 {
599     static const char * const selections[] = {
600         "chain A",
601         "chain B"
602     };
603     runTest("simple.pdb", selections);
604 }
605
606 TEST_F(SelectionCollectionDataTest, HandlesMass)
607 {
608     static const char * const selections[] = {
609         "mass > 5"
610     };
611     ASSERT_NO_FATAL_FAILURE(runParser(selections));
612     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
613     for (int i = 0; i < top_->atoms.nr; ++i)
614     {
615         top_->atoms.atom[i].m = 1.0 + i;
616     }
617     ASSERT_NO_FATAL_FAILURE(runCompiler());
618 }
619
620 TEST_F(SelectionCollectionDataTest, HandlesCharge)
621 {
622     static const char * const selections[] = {
623         "charge < 0.5"
624     };
625     ASSERT_NO_FATAL_FAILURE(runParser(selections));
626     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
627     for (int i = 0; i < top_->atoms.nr; ++i)
628     {
629         top_->atoms.atom[i].q = i / 10.0;
630     }
631     ASSERT_NO_FATAL_FAILURE(runCompiler());
632 }
633
634 TEST_F(SelectionCollectionDataTest, HandlesAltLoc)
635 {
636     static const char * const selections[] = {
637         "altloc \" \"",
638         "altloc A"
639     };
640     runTest("simple.pdb", selections);
641 }
642
643 TEST_F(SelectionCollectionDataTest, HandlesInsertCode)
644 {
645     static const char * const selections[] = {
646         "insertcode \" \"",
647         "insertcode A"
648     };
649     runTest("simple.pdb", selections);
650 }
651
652 TEST_F(SelectionCollectionDataTest, HandlesOccupancy)
653 {
654     static const char * const selections[] = {
655         "occupancy 1",
656         "occupancy < .5"
657     };
658     runTest("simple.pdb", selections);
659 }
660
661 TEST_F(SelectionCollectionDataTest, HandlesBeta)
662 {
663     static const char * const selections[] = {
664         "beta 0",
665         "beta >= 0.3"
666     };
667     runTest("simple.pdb", selections);
668 }
669
670 TEST_F(SelectionCollectionDataTest, HandlesResname)
671 {
672     static const char * const selections[] = {
673         "resname RA",
674         "resname RB RC"
675     };
676     runTest("simple.gro", selections);
677 }
678
679 TEST_F(SelectionCollectionDataTest, HandlesCoordinateKeywords)
680 {
681     static const char * const selections[] = {
682         "x < 3",
683         "y >= 3",
684         "x {-1 to 2}"
685     };
686     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
687     runTest("simple.gro", selections);
688 }
689
690
691 TEST_F(SelectionCollectionDataTest, HandlesSameResidue)
692 {
693     static const char * const selections[] = {
694         "same residue as atomnr 1 4 12"
695     };
696     runTest("simple.gro", selections);
697 }
698
699
700 TEST_F(SelectionCollectionDataTest, HandlesSameResidueName)
701 {
702     static const char * const selections[] = {
703         "same resname as atomnr 1 14"
704     };
705     runTest("simple.gro", selections);
706 }
707
708
709 TEST_F(SelectionCollectionDataTest, HandlesPositionKeywords)
710 {
711     static const char * const selections[] = {
712         "cog of resnr 1 3",
713         "res_cog of name CB and resnr 1 3",
714         "whole_res_cog of name CB and resnr 1 3",
715         "part_res_cog of x < 3",
716         "dyn_res_cog of x < 3"
717     };
718     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
719              | efTestPositionAtoms);
720     runTest("simple.gro", selections);
721 }
722
723
724 TEST_F(SelectionCollectionDataTest, HandlesDistanceKeyword)
725 {
726     static const char * const selections[] = {
727         "distance from cog of resnr 1 < 2"
728     };
729     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
730     runTest("simple.gro", selections);
731 }
732
733
734 TEST_F(SelectionCollectionDataTest, HandlesMinDistanceKeyword)
735 {
736     static const char * const selections[] = {
737         "mindistance from resnr 1 < 2"
738     };
739     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
740     runTest("simple.gro", selections);
741 }
742
743
744 TEST_F(SelectionCollectionDataTest, HandlesWithinKeyword)
745 {
746     static const char * const selections[] = {
747         "within 1 of resnr 2"
748     };
749     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
750     runTest("simple.gro", selections);
751 }
752
753
754 TEST_F(SelectionCollectionDataTest, HandlesInSolidAngleKeyword)
755 {
756     // Both of these should evaluate to empty on a correct implementation.
757     static const char * const selections[] = {
758         "resname TP and not insolidangle center cog of resname C span resname R cutoff 20",
759         "resname TN and insolidangle center cog of resname C span resname R cutoff 20"
760     };
761     setFlags(TestFlags() | efDontTestCompiledAtoms | efTestEvaluation);
762     runTest("sphere.gro", selections);
763 }
764
765
766 TEST_F(SelectionCollectionDataTest, HandlesPermuteModifier)
767 {
768     static const char * const selections[] = {
769         "all permute 3 1 2",
770         "res_cog of resnr 1 to 4 permute 2 1",
771         "name CB S1 and res_cog x < 3 permute 2 1"
772     };
773     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
774              | efTestPositionAtoms | efTestPositionMapping);
775     runTest("simple.gro", selections);
776 }
777
778
779 TEST_F(SelectionCollectionDataTest, HandlesPlusModifier)
780 {
781     static const char * const selections[] = {
782         "name S2 plus name S1",
783         "res_cog of resnr 2 plus res_cog of resnr 1 plus res_cog of resnr 3",
784         "name S1 and y < 3 plus res_cog of x < 2.5"
785     };
786     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
787              | efTestPositionAtoms | efTestPositionMapping);
788     runTest("simple.gro", selections);
789 }
790
791
792 TEST_F(SelectionCollectionDataTest, HandlesMergeModifier)
793 {
794     static const char * const selections[] = {
795         "name S2 merge name S1",
796         "resnr 1 2 and name S2 merge resnr 1 2 and name S1 merge res_cog of resnr 1 2",
797         "name S1 and x < 2.5 merge res_cog of x < 2.5"
798     };
799     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
800              | efTestPositionAtoms | efTestPositionMapping);
801     runTest("simple.gro", selections);
802 }
803
804
805 /********************************************************************
806  * Tests for generic selection evaluation
807  */
808
809 TEST_F(SelectionCollectionDataTest, ComputesMassesAndCharges)
810 {
811     static const char * const selections[] = {
812         "name CB",
813         "y > 2",
814         "res_cog of y > 2"
815     };
816     setFlags(TestFlags() | efTestEvaluation | efTestPositionAtoms
817              | efTestPositionMasses | efTestPositionCharges);
818     ASSERT_NO_FATAL_FAILURE(runParser(selections));
819     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
820     for (int i = 0; i < top_->atoms.nr; ++i)
821     {
822         top_->atoms.atom[i].m =   1.0 + i / 100.0;
823         top_->atoms.atom[i].q = -(1.0 + i / 100.0);
824     }
825     ASSERT_NO_FATAL_FAILURE(runCompiler());
826     ASSERT_NO_FATAL_FAILURE(runEvaluate());
827     ASSERT_NO_FATAL_FAILURE(runEvaluateFinal());
828 }
829
830 TEST_F(SelectionCollectionDataTest, ComputesMassesAndChargesWithoutTopology)
831 {
832     static const char * const selections[] = {
833         "atomnr 1 to 3 8 to 9",
834         "y > 2",
835         "cog of (y > 2)"
836     };
837     setFlags(TestFlags() | efTestPositionAtoms
838              | efTestPositionMasses | efTestPositionCharges);
839     runTest(10, selections);
840 }
841
842
843 /********************************************************************
844  * Tests for selection syntactic constructs
845  */
846
847 TEST_F(SelectionCollectionDataTest, HandlesConstantPositions)
848 {
849     static const char * const selections[] = {
850         "[1, -2, 3.5]"
851     };
852     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
853     runTest("simple.gro", selections);
854 }
855
856
857 TEST_F(SelectionCollectionDataTest, HandlesWithinConstantPositions)
858 {
859     static const char * const selections[] = {
860         "within 1 of [2, 1, 0]"
861     };
862     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
863     runTest("simple.gro", selections);
864 }
865
866
867 TEST_F(SelectionCollectionDataTest, HandlesForcedStringMatchingMode)
868 {
869     static const char * const selections[] = {
870         "name = S1 \"C?\"",
871         "name ? S1 \"C?\""
872     };
873     runTest("simple.gro", selections);
874 }
875
876
877 TEST_F(SelectionCollectionDataTest, HandlesWildcardMatching)
878 {
879     static const char * const selections[] = {
880         "name \"S?\"",
881         "name ? \"S?\""
882     };
883     runTest("simple.gro", selections);
884 }
885
886
887 TEST_F(SelectionCollectionDataTest, HandlesRegexMatching)
888 {
889     static const char * const selections[] = {
890         "resname \"R[BD]\"",
891         "resname ~ \"R[BD]\""
892     };
893     if (gmx::Regex::isSupported())
894     {
895         runTest("simple.gro", selections);
896     }
897 }
898
899
900 TEST_F(SelectionCollectionDataTest, HandlesBasicBoolean)
901 {
902     static const char * const selections[] = {
903         "atomnr 1 to 5 and atomnr 2 to 7",
904         "atomnr 1 to 5 or not atomnr 3 to 8",
905         "not not atomnr 1 to 5 and atomnr 2 to 6 and not not atomnr 3 to 7",
906         "atomnr 1 to 5 and (atomnr 2 to 7 and atomnr 3 to 6)",
907         "x < 5 and atomnr 1 to 5 and y < 3 and atomnr 2 to 4"
908     };
909     runTest(10, selections);
910 }
911
912
913 TEST_F(SelectionCollectionDataTest, HandlesNumericComparisons)
914 {
915     static const char * const selections[] = {
916         "x > 2",
917         "2 < x",
918         "y > resnr",
919         "resnr < 2.5",
920         "2.5 > resnr"
921     };
922     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
923     runTest("simple.gro", selections);
924 }
925
926
927 TEST_F(SelectionCollectionDataTest, HandlesArithmeticExpressions)
928 {
929     static const char * const selections[] = {
930         "x+1 > 3",
931         "(y-1)^2 <= 1",
932         "x+--1 > 3",
933         "-x+-1 < -3"
934     };
935     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
936     runTest("simple.gro", selections);
937 }
938
939
940 TEST_F(SelectionCollectionDataTest, HandlesNumericVariables)
941 {
942     static const char * const selections[] = {
943         "value = x + y",
944         "value <= 4",
945         "index = resnr",
946         "index < 3"
947     };
948     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
949     runTest("simple.gro", selections);
950 }
951
952
953 TEST_F(SelectionCollectionDataTest, HandlesComplexNumericVariables)
954 {
955     static const char * const selections[] = {
956         "value = x + y",
957         "resname RA and value <= 4",
958         "resname RA RB and x < 3 and value <= 4",
959         "index = atomnr",
960         "resname RA and index < 3",
961         "resname RB and y < 3 and index < 6"
962     };
963     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
964     runTest("simple.gro", selections);
965 }
966
967
968 TEST_F(SelectionCollectionDataTest, HandlesPositionVariables)
969 {
970     static const char * const selections[] = {
971         "foo = res_cog of resname RA",
972         "foo",
973         "within 1 of foo",
974         "bar = cog of resname RA",
975         "bar",
976         "within 1 of bar"
977     };
978     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
979     runTest("simple.gro", selections);
980 }
981
982
983 TEST_F(SelectionCollectionDataTest, HandlesConstantPositionInVariable)
984 {
985     static const char * const selections[] = {
986         "constpos = [1.0, 2.5, 0.5]",
987         "constpos",
988         "within 2 of constpos"
989     };
990     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
991              | efTestPositionAtoms);
992     runTest("simple.gro", selections);
993 }
994
995
996 TEST_F(SelectionCollectionDataTest, HandlesNumericConstantsInVariables)
997 {
998     static const char * const selections[] = {
999         "constint = 4",
1000         "constreal1 = 0.5",
1001         "constreal2 = 2.7",
1002         "resnr < constint",
1003         "x + constreal1 < constreal2"
1004     };
1005     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
1006     runTest("simple.gro", selections);
1007 }
1008
1009
1010 /********************************************************************
1011  * Tests for complex boolean syntax
1012  */
1013
1014 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysis)
1015 {
1016     static const char * const selections[] = {
1017         "atomnr 1 to 5 and atomnr 2 to 7 and x < 2",
1018         "atomnr 1 to 5 and (atomnr 4 to 7 or x < 2)",
1019         "atomnr 1 to 5 and y < 3 and (atomnr 4 to 7 or x < 2)",
1020         "atomnr 1 to 5 and not (atomnr 4 to 7 or x < 2)",
1021         "atomnr 1 to 5 or (atomnr 4 to 6 and (atomnr 5 to 7 or x < 2))"
1022     };
1023     runTest(10, selections);
1024 }
1025
1026
1027 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysisWithVariables)
1028 {
1029     static const char * const selections[] = {
1030         "foo = atomnr 4 to 7 or x < 2",
1031         "atomnr 1 to 4 and foo",
1032         "atomnr 2 to 6 and y < 3 and foo",
1033         "atomnr 6 to 10 and not foo"
1034     };
1035     runTest(10, selections);
1036 }
1037
1038
1039 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysisWithMoreVariables)
1040 {
1041     static const char * const selections[] = {
1042         "foo = atomnr 4 to 7",
1043         "bar = foo and x < 2",
1044         "bar2 = foo and y < 2",
1045         "atomnr 1 to 4 and bar",
1046         "atomnr 2 to 6 and y < 3 and bar2",
1047         "atomnr 6 to 10 and not foo"
1048     };
1049     runTest(10, selections);
1050 }
1051
1052
1053 /********************************************************************
1054  * Tests for complex subexpression cases
1055  *
1056  * These tests use some knowledge of the implementation to trigger different
1057  * paths in the code.
1058  */
1059
1060 TEST_F(SelectionCollectionDataTest, HandlesUnusedVariables)
1061 {
1062     static const char * const selections[] = {
1063         "unused1 = atomnr 1 to 3",
1064         "foo = atomnr 4 to 7",
1065         "atomnr 1 to 6 and foo",
1066         "unused2 = atomnr 3 to 5"
1067     };
1068     runTest(10, selections);
1069 }
1070
1071
1072 TEST_F(SelectionCollectionDataTest, HandlesVariablesWithStaticEvaluationGroups)
1073 {
1074     static const char * const selections[] = {
1075         "foo = atomnr 4 to 7 and x < 2",
1076         "atomnr 1 to 5 and foo",
1077         "atomnr 3 to 7 and foo"
1078     };
1079     runTest(10, selections);
1080 }
1081
1082
1083 TEST_F(SelectionCollectionDataTest, HandlesVariablesWithMixedEvaluationGroups)
1084 {
1085     static const char * const selections[] = {
1086         "foo = atomnr 4 to 7 and x < 2",
1087         "atomnr 1 to 6 and foo",
1088         "within 1 of foo",
1089         "foo"
1090     };
1091     runTest(10, selections);
1092 }
1093
1094
1095 } // namespace