Add tests for selection mass evaluation and fix it.
[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, ParsesSelectionsFromFile)
406 {
407     ASSERT_NO_THROW(sel_ = sc_.parseFromFile(
408                                 gmx::test::TestFileManager::getInputFilePath("selfile.dat")));
409     // These should match the contents of selfile.dat
410     ASSERT_EQ(2U, sel_.size());
411     EXPECT_STREQ("resname RA RB", sel_[0].selectionText());
412     EXPECT_STREQ("resname RB RC", sel_[1].selectionText());
413 }
414
415 TEST_F(SelectionCollectionTest, HandlesInvalidRegularExpressions)
416 {
417     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
418     EXPECT_THROW({
419                      sc_.parseFromString("resname ~ \"R[A\"");
420                      sc_.compile();
421                  }, gmx::InvalidInputError);
422 }
423
424 TEST_F(SelectionCollectionTest, HandlesUnsupportedRegularExpressions)
425 {
426     if (!gmx::Regex::isSupported())
427     {
428         ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
429         EXPECT_THROW({
430                          sc_.parseFromString("resname \"R[AD]\"");
431                          sc_.compile();
432                      }, gmx::InvalidInputError);
433     }
434 }
435
436 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue)
437 {
438     EXPECT_THROW(sc_.parseFromString("mindist from atomnr 1 cutoff"),
439                  gmx::InvalidInputError);
440 }
441
442 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue2)
443 {
444     EXPECT_THROW(sc_.parseFromString("within 1 of"),
445                  gmx::InvalidInputError);
446 }
447
448 TEST_F(SelectionCollectionTest, HandlesMissingMethodParamValue3)
449 {
450     EXPECT_THROW(sc_.parseFromString("within of atomnr 1"),
451                  gmx::InvalidInputError);
452 }
453
454 TEST_F(SelectionCollectionTest, HandlesHelpKeywordInInvalidContext)
455 {
456     EXPECT_THROW(sc_.parseFromString("resname help"),
457                  gmx::InvalidInputError);
458 }
459
460 // TODO: Tests for more parser errors
461
462 TEST_F(SelectionCollectionTest, RecoversFromUnknownGroupReference)
463 {
464     ASSERT_NO_THROW(sc_.parseFromString("group \"foo\""));
465     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
466     EXPECT_THROW(sc_.setIndexGroups(NULL), gmx::InvalidInputError);
467     EXPECT_THROW(sc_.compile(), gmx::APIError);
468 }
469
470 TEST_F(SelectionCollectionTest, RecoversFromMissingMoleculeInfo)
471 {
472     ASSERT_NO_THROW(sc_.parseFromString("molindex 1 to 5"));
473     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
474     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
475 }
476
477 TEST_F(SelectionCollectionTest, RecoversFromMissingAtomTypes)
478 {
479     ASSERT_NO_THROW(sc_.parseFromString("type CA"));
480     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
481     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
482 }
483
484 TEST_F(SelectionCollectionTest, RecoversFromMissingPDBInfo)
485 {
486     ASSERT_NO_THROW(sc_.parseFromString("altloc A"));
487     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
488     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
489 }
490
491 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation)
492 {
493     ASSERT_NO_THROW(sc_.parseFromString("all permute 1 1"));
494     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
495     EXPECT_THROW(sc_.compile(), gmx::InvalidInputError);
496 }
497
498 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation2)
499 {
500     ASSERT_NO_THROW(sc_.parseFromString("all permute 3 2 1"));
501     ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
502     EXPECT_THROW(sc_.compile(), gmx::InconsistentInputError);
503 }
504
505 TEST_F(SelectionCollectionTest, RecoversFromInvalidPermutation3)
506 {
507     ASSERT_NO_THROW(sc_.parseFromString("x < 1.5 permute 3 2 1"));
508     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
509     ASSERT_NO_THROW(sc_.compile());
510     EXPECT_THROW(sc_.evaluate(frame_, NULL), gmx::InconsistentInputError);
511 }
512
513 // TODO: Tests for evaluation errors
514
515
516 /********************************************************************
517  * Tests for selection keywords
518  */
519
520 TEST_F(SelectionCollectionDataTest, HandlesAllNone)
521 {
522     static const char * const selections[] = {
523         "all",
524         "none"
525     };
526     runTest(10, selections);
527 }
528
529 TEST_F(SelectionCollectionDataTest, HandlesAtomnr)
530 {
531     static const char * const selections[] = {
532         "atomnr 1 to 3 6 to 8",
533         "atomnr 4 2 5 to 7",
534         "atomnr <= 5"
535     };
536     runTest(10, selections);
537 }
538
539 TEST_F(SelectionCollectionDataTest, HandlesResnr)
540 {
541     static const char * const selections[] = {
542         "resnr 1 2 5",
543         "resid 4 to 3"
544     };
545     runTest("simple.gro", selections);
546 }
547
548 TEST_F(SelectionCollectionDataTest, HandlesResIndex)
549 {
550     static const char * const selections[] = {
551         "resindex 1 4",
552         "residue 1 3"
553     };
554     runTest("simple.pdb", selections);
555 }
556
557 // TODO: Add test for "molindex"
558
559 TEST_F(SelectionCollectionDataTest, HandlesAtomname)
560 {
561     static const char * const selections[] = {
562         "name CB",
563         "atomname S1 S2"
564     };
565     runTest("simple.gro", selections);
566 }
567
568 TEST_F(SelectionCollectionDataTest, HandlesPdbAtomname)
569 {
570     static const char * const selections[] = {
571         "name HG21",
572         "name 1HG2",
573         "pdbname HG21 CB",
574         "pdbatomname 1HG2"
575     };
576     runTest("simple.pdb", selections);
577 }
578
579 // TODO: Add test for atomtype
580
581 TEST_F(SelectionCollectionDataTest, HandlesChain)
582 {
583     static const char * const selections[] = {
584         "chain A",
585         "chain B"
586     };
587     runTest("simple.pdb", selections);
588 }
589
590 TEST_F(SelectionCollectionDataTest, HandlesMass)
591 {
592     static const char * const selections[] = {
593         "mass > 5"
594     };
595     ASSERT_NO_FATAL_FAILURE(runParser(selections));
596     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
597     for (int i = 0; i < top_->atoms.nr; ++i)
598     {
599         top_->atoms.atom[i].m = 1.0 + i;
600     }
601     ASSERT_NO_FATAL_FAILURE(runCompiler());
602 }
603
604 TEST_F(SelectionCollectionDataTest, HandlesCharge)
605 {
606     static const char * const selections[] = {
607         "charge < 0.5"
608     };
609     ASSERT_NO_FATAL_FAILURE(runParser(selections));
610     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
611     for (int i = 0; i < top_->atoms.nr; ++i)
612     {
613         top_->atoms.atom[i].q = i / 10.0;
614     }
615     ASSERT_NO_FATAL_FAILURE(runCompiler());
616 }
617
618 TEST_F(SelectionCollectionDataTest, HandlesAltLoc)
619 {
620     static const char * const selections[] = {
621         "altloc \" \"",
622         "altloc A"
623     };
624     runTest("simple.pdb", selections);
625 }
626
627 TEST_F(SelectionCollectionDataTest, HandlesInsertCode)
628 {
629     static const char * const selections[] = {
630         "insertcode \" \"",
631         "insertcode A"
632     };
633     runTest("simple.pdb", selections);
634 }
635
636 TEST_F(SelectionCollectionDataTest, HandlesOccupancy)
637 {
638     static const char * const selections[] = {
639         "occupancy 1",
640         "occupancy < .5"
641     };
642     runTest("simple.pdb", selections);
643 }
644
645 TEST_F(SelectionCollectionDataTest, HandlesBeta)
646 {
647     static const char * const selections[] = {
648         "beta 0",
649         "beta >= 0.3"
650     };
651     runTest("simple.pdb", selections);
652 }
653
654 TEST_F(SelectionCollectionDataTest, HandlesResname)
655 {
656     static const char * const selections[] = {
657         "resname RA",
658         "resname RB RC"
659     };
660     runTest("simple.gro", selections);
661 }
662
663 TEST_F(SelectionCollectionDataTest, HandlesCoordinateKeywords)
664 {
665     static const char * const selections[] = {
666         "x < 3",
667         "y >= 3",
668         "x {-1 to 2}"
669     };
670     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
671     runTest("simple.gro", selections);
672 }
673
674
675 TEST_F(SelectionCollectionDataTest, HandlesSameResidue)
676 {
677     static const char * const selections[] = {
678         "same residue as atomnr 1 4 12"
679     };
680     runTest("simple.gro", selections);
681 }
682
683
684 TEST_F(SelectionCollectionDataTest, HandlesSameResidueName)
685 {
686     static const char * const selections[] = {
687         "same resname as atomnr 1 14"
688     };
689     runTest("simple.gro", selections);
690 }
691
692
693 TEST_F(SelectionCollectionDataTest, HandlesPositionKeywords)
694 {
695     static const char * const selections[] = {
696         "cog of resnr 1 3",
697         "res_cog of name CB and resnr 1 3",
698         "whole_res_cog of name CB and resnr 1 3",
699         "part_res_cog of x < 3",
700         "dyn_res_cog of x < 3"
701     };
702     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
703              | efTestPositionAtoms);
704     runTest("simple.gro", selections);
705 }
706
707
708 TEST_F(SelectionCollectionDataTest, HandlesDistanceKeyword)
709 {
710     static const char * const selections[] = {
711         "distance from cog of resnr 1 < 2"
712     };
713     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
714     runTest("simple.gro", selections);
715 }
716
717
718 TEST_F(SelectionCollectionDataTest, HandlesMinDistanceKeyword)
719 {
720     static const char * const selections[] = {
721         "mindistance from resnr 1 < 2"
722     };
723     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
724     runTest("simple.gro", selections);
725 }
726
727
728 TEST_F(SelectionCollectionDataTest, HandlesWithinKeyword)
729 {
730     static const char * const selections[] = {
731         "within 1 of resnr 2"
732     };
733     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
734     runTest("simple.gro", selections);
735 }
736
737
738 TEST_F(SelectionCollectionDataTest, HandlesInSolidAngleKeyword)
739 {
740     // Both of these should evaluate to empty on a correct implementation.
741     static const char * const selections[] = {
742         "resname TP and not insolidangle center cog of resname C span resname R cutoff 20",
743         "resname TN and insolidangle center cog of resname C span resname R cutoff 20"
744     };
745     setFlags(TestFlags() | efDontTestCompiledAtoms | efTestEvaluation);
746     runTest("sphere.gro", selections);
747 }
748
749
750 TEST_F(SelectionCollectionDataTest, HandlesPermuteModifier)
751 {
752     static const char * const selections[] = {
753         "all permute 3 1 2",
754         "res_cog of resnr 1 to 4 permute 2 1",
755         "name CB S1 and res_cog x < 3 permute 2 1"
756     };
757     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
758              | efTestPositionAtoms | efTestPositionMapping);
759     runTest("simple.gro", selections);
760 }
761
762
763 TEST_F(SelectionCollectionDataTest, HandlesPlusModifier)
764 {
765     static const char * const selections[] = {
766         "name S2 plus name S1",
767         "res_cog of resnr 2 plus res_cog of resnr 1 plus res_cog of resnr 3",
768         "name S1 and y < 3 plus res_cog of x < 2.5"
769     };
770     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
771              | efTestPositionAtoms | efTestPositionMapping);
772     runTest("simple.gro", selections);
773 }
774
775
776 TEST_F(SelectionCollectionDataTest, HandlesMergeModifier)
777 {
778     static const char * const selections[] = {
779         "name S2 merge name S1",
780         "resnr 1 2 and name S2 merge resnr 1 2 and name S1 merge res_cog of resnr 1 2",
781         "name S1 and x < 2.5 merge res_cog of x < 2.5"
782     };
783     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
784              | efTestPositionAtoms | efTestPositionMapping);
785     runTest("simple.gro", selections);
786 }
787
788
789 /********************************************************************
790  * Tests for generic selection evaluation
791  */
792
793 TEST_F(SelectionCollectionDataTest, ComputesMassesAndCharges)
794 {
795     static const char * const selections[] = {
796         "name CB",
797         "y > 2",
798         "res_cog of y > 2"
799     };
800     setFlags(TestFlags() | efTestEvaluation | efTestPositionAtoms
801              | efTestPositionMasses | efTestPositionCharges);
802     ASSERT_NO_FATAL_FAILURE(runParser(selections));
803     ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
804     for (int i = 0; i < top_->atoms.nr; ++i)
805     {
806         top_->atoms.atom[i].m =   1.0 + i / 100.0;
807         top_->atoms.atom[i].q = -(1.0 + i / 100.0);
808     }
809     ASSERT_NO_FATAL_FAILURE(runCompiler());
810     ASSERT_NO_FATAL_FAILURE(runEvaluate());
811     ASSERT_NO_FATAL_FAILURE(runEvaluateFinal());
812 }
813
814 TEST_F(SelectionCollectionDataTest, ComputesMassesAndChargesWithoutTopology)
815 {
816     static const char * const selections[] = {
817         "atomnr 1 to 3 8 to 9",
818         "y > 2",
819         "cog of (y > 2)"
820     };
821     setFlags(TestFlags() | efTestPositionAtoms
822              | efTestPositionMasses | efTestPositionCharges);
823     runTest(10, selections);
824 }
825
826
827 /********************************************************************
828  * Tests for selection syntactic constructs
829  */
830
831 TEST_F(SelectionCollectionDataTest, HandlesConstantPositions)
832 {
833     static const char * const selections[] = {
834         "[1, -2, 3.5]"
835     };
836     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
837     runTest("simple.gro", selections);
838 }
839
840
841 TEST_F(SelectionCollectionDataTest, HandlesWithinConstantPositions)
842 {
843     static const char * const selections[] = {
844         "within 1 of [2, 1, 0]"
845     };
846     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
847     runTest("simple.gro", selections);
848 }
849
850
851 TEST_F(SelectionCollectionDataTest, HandlesForcedStringMatchingMode)
852 {
853     static const char * const selections[] = {
854         "name = S1 \"C?\"",
855         "name ? S1 \"C?\""
856     };
857     runTest("simple.gro", selections);
858 }
859
860
861 TEST_F(SelectionCollectionDataTest, HandlesWildcardMatching)
862 {
863     static const char * const selections[] = {
864         "name \"S?\"",
865         "name ? \"S?\""
866     };
867     runTest("simple.gro", selections);
868 }
869
870
871 TEST_F(SelectionCollectionDataTest, HandlesRegexMatching)
872 {
873     static const char * const selections[] = {
874         "resname \"R[BD]\"",
875         "resname ~ \"R[BD]\""
876     };
877     if (gmx::Regex::isSupported())
878     {
879         runTest("simple.gro", selections);
880     }
881 }
882
883
884 TEST_F(SelectionCollectionDataTest, HandlesBasicBoolean)
885 {
886     static const char * const selections[] = {
887         "atomnr 1 to 5 and atomnr 2 to 7",
888         "atomnr 1 to 5 or not atomnr 3 to 8",
889         "not not atomnr 1 to 5 and atomnr 2 to 6 and not not atomnr 3 to 7",
890         "atomnr 1 to 5 and (atomnr 2 to 7 and atomnr 3 to 6)",
891         "x < 5 and atomnr 1 to 5 and y < 3 and atomnr 2 to 4"
892     };
893     runTest(10, selections);
894 }
895
896
897 TEST_F(SelectionCollectionDataTest, HandlesNumericComparisons)
898 {
899     static const char * const selections[] = {
900         "x > 2",
901         "2 < x",
902         "y > resnr",
903         "resnr < 2.5",
904         "2.5 > resnr"
905     };
906     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
907     runTest("simple.gro", selections);
908 }
909
910
911 TEST_F(SelectionCollectionDataTest, HandlesArithmeticExpressions)
912 {
913     static const char * const selections[] = {
914         "x+1 > 3",
915         "(y-1)^2 <= 1",
916         "x+--1 > 3",
917         "-x+-1 < -3"
918     };
919     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
920     runTest("simple.gro", selections);
921 }
922
923
924 TEST_F(SelectionCollectionDataTest, HandlesNumericVariables)
925 {
926     static const char * const selections[] = {
927         "value = x + y",
928         "value <= 4",
929         "index = resnr",
930         "index < 3"
931     };
932     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
933     runTest("simple.gro", selections);
934 }
935
936
937 TEST_F(SelectionCollectionDataTest, HandlesComplexNumericVariables)
938 {
939     static const char * const selections[] = {
940         "value = x + y",
941         "resname RA and value <= 4",
942         "resname RA RB and x < 3 and value <= 4",
943         "index = atomnr",
944         "resname RA and index < 3",
945         "resname RB and y < 3 and index < 6"
946     };
947     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
948     runTest("simple.gro", selections);
949 }
950
951
952 TEST_F(SelectionCollectionDataTest, HandlesPositionVariables)
953 {
954     static const char * const selections[] = {
955         "foo = res_cog of resname RA",
956         "foo",
957         "within 1 of foo",
958         "bar = cog of resname RA",
959         "bar",
960         "within 1 of bar"
961     };
962     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
963     runTest("simple.gro", selections);
964 }
965
966
967 TEST_F(SelectionCollectionDataTest, HandlesConstantPositionInVariable)
968 {
969     static const char * const selections[] = {
970         "constpos = [1.0, 2.5, 0.5]",
971         "constpos",
972         "within 2 of constpos"
973     };
974     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates
975              | efTestPositionAtoms);
976     runTest("simple.gro", selections);
977 }
978
979
980 TEST_F(SelectionCollectionDataTest, HandlesNumericConstantsInVariables)
981 {
982     static const char * const selections[] = {
983         "constint = 4",
984         "constreal1 = 0.5",
985         "constreal2 = 2.7",
986         "resnr < constint",
987         "x + constreal1 < constreal2"
988     };
989     setFlags(TestFlags() | efTestEvaluation | efTestPositionCoordinates);
990     runTest("simple.gro", selections);
991 }
992
993
994 /********************************************************************
995  * Tests for complex boolean syntax
996  */
997
998 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysis)
999 {
1000     static const char * const selections[] = {
1001         "atomnr 1 to 5 and atomnr 2 to 7 and x < 2",
1002         "atomnr 1 to 5 and (atomnr 4 to 7 or x < 2)",
1003         "atomnr 1 to 5 and y < 3 and (atomnr 4 to 7 or x < 2)",
1004         "atomnr 1 to 5 and not (atomnr 4 to 7 or x < 2)",
1005         "atomnr 1 to 5 or (atomnr 4 to 6 and (atomnr 5 to 7 or x < 2))"
1006     };
1007     runTest(10, selections);
1008 }
1009
1010
1011 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysisWithVariables)
1012 {
1013     static const char * const selections[] = {
1014         "foo = atomnr 4 to 7 or x < 2",
1015         "atomnr 1 to 4 and foo",
1016         "atomnr 2 to 6 and y < 3 and foo",
1017         "atomnr 6 to 10 and not foo"
1018     };
1019     runTest(10, selections);
1020 }
1021
1022
1023 TEST_F(SelectionCollectionDataTest, HandlesBooleanStaticAnalysisWithMoreVariables)
1024 {
1025     static const char * const selections[] = {
1026         "foo = atomnr 4 to 7",
1027         "bar = foo and x < 2",
1028         "bar2 = foo and y < 2",
1029         "atomnr 1 to 4 and bar",
1030         "atomnr 2 to 6 and y < 3 and bar2",
1031         "atomnr 6 to 10 and not foo"
1032     };
1033     runTest(10, selections);
1034 }
1035
1036
1037 /********************************************************************
1038  * Tests for complex subexpression cases
1039  *
1040  * These tests use some knowledge of the implementation to trigger different
1041  * paths in the code.
1042  */
1043
1044 TEST_F(SelectionCollectionDataTest, HandlesUnusedVariables)
1045 {
1046     static const char * const selections[] = {
1047         "unused1 = atomnr 1 to 3",
1048         "foo = atomnr 4 to 7",
1049         "atomnr 1 to 6 and foo",
1050         "unused2 = atomnr 3 to 5"
1051     };
1052     runTest(10, selections);
1053 }
1054
1055
1056 TEST_F(SelectionCollectionDataTest, HandlesVariablesWithStaticEvaluationGroups)
1057 {
1058     static const char * const selections[] = {
1059         "foo = atomnr 4 to 7 and x < 2",
1060         "atomnr 1 to 5 and foo",
1061         "atomnr 3 to 7 and foo"
1062     };
1063     runTest(10, selections);
1064 }
1065
1066
1067 TEST_F(SelectionCollectionDataTest, HandlesVariablesWithMixedEvaluationGroups)
1068 {
1069     static const char * const selections[] = {
1070         "foo = atomnr 4 to 7 and x < 2",
1071         "atomnr 1 to 6 and foo",
1072         "within 1 of foo",
1073         "foo"
1074     };
1075     runTest(10, selections);
1076 }
1077
1078
1079 } // namespace