Test framework for TrajectoryAnalysisModules.
authorTeemu Murtola <teemu.murtola@gmail.com>
Wed, 23 May 2012 07:14:50 +0000 (10:14 +0300)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Fri, 6 Jul 2012 03:59:01 +0000 (05:59 +0200)
- Add a test fixture that makes it simple to test analysis tools that
  are implemented as TrajectoryAnalysisModule subclasses (moduletest.*).
- Add tests using the new fixture for most of the functionality of the
  Select module.
- Minor adjustment to reference data test fixture to make it more usable
  in this context.
- Move all dataset registration calls to trajectory analysis module
  constructors from initAnalysis().  Also moved static initialization of
  the datasets, in particular calls to setMultipoint().
  In addition to enabling the tests to access the datasets before any
  initialization of the module is done, this also makes the modules
  easier to use for other interfacing than the command-line runner.

Main part of #920, may need some additional features.

Change-Id: I67e123f8361fe7710e936d9b4a880e65a3dc89c5

20 files changed:
share/template/template.cpp
src/gromacs/trajectoryanalysis/modules/angle.cpp
src/gromacs/trajectoryanalysis/modules/distance.cpp
src/gromacs/trajectoryanalysis/modules/select.cpp
src/gromacs/trajectoryanalysis/tests/CMakeLists.txt
src/gromacs/trajectoryanalysis/tests/moduletest.cpp [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/moduletest.h [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_BasicTest.xml [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_HandlesDumpOption.xml [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_NormalizesSizes.xml [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_WritesResidueIndices.xml [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_WritesResidueNumbers.xml [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/refdata/analysisdata-referencedata.xsl [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/refdata/common-referencedata.xsl [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/refdata/referencedata.xsl [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/select.cpp [new file with mode: 0644]
src/gromacs/trajectoryanalysis/tests/simple.gro [new file with mode: 0644]
src/testutils/copy_xsl.sh
src/testutils/datatest.cpp
src/testutils/datatest.h

index 0a414814584e0aa1dd1e36e267bebef94e70dabe..e2035598ea4b9aceec3bc8107f01c21ded37b60e 100644 (file)
@@ -108,6 +108,7 @@ class AnalysisTemplate::ModuleData : public TrajectoryAnalysisModuleData
 AnalysisTemplate::AnalysisTemplate()
     : options_("template", "Template options"), cutoff_(0.0)
 {
+    registerAnalysisDataset(&data_, "avedist");
 }
 
 
@@ -158,7 +159,6 @@ AnalysisTemplate::initAnalysis(const TrajectoryAnalysisSettings &settings,
                                const TopologyInformation & /*top*/)
 {
     data_.setColumnCount(sel_.size());
-    registerAnalysisDataset(&data_, "avedist");
 
     avem_.reset(new AnalysisDataAverageModule());
     data_.addModule(avem_);
index ddda31a17f159ead31dc192614affae211226082..4c558d55d8f0d89e990400da03e05c0b9959149a 100644 (file)
@@ -73,6 +73,7 @@ Angle::Angle()
       _bSplit1(false), _bSplit2(false), _bMulti(false), _bAll(false),
       _bDumpDist(false), _natoms1(0), _natoms2(0), _vt0(NULL)
 {
+    registerAnalysisDataset(&_data, "angle");
 }
 
 
@@ -360,8 +361,6 @@ Angle::initAnalysis(const TrajectoryAnalysisSettings &settings,
         _vt0 = new rvec[na];
     }
 
-    registerAnalysisDataset(&_data, "angle");
-
     AnalysisDataPlotModulePointer plotm(
         new AnalysisDataPlotModule(settings.plotSettings()));
     plotm->setFileName(_fnAngle);
index 02edf7e8f2ade52f3768f1a15568085bec9d10ff..9cf0029477986fae56a1c887dc98ef104fe01095 100644 (file)
@@ -68,6 +68,8 @@ const char Distance::shortDescription[] =
 Distance::Distance()
     : _options(name, shortDescription), _avem(new AnalysisDataAverageModule())
 {
+    _data.setColumnCount(4);
+    registerAnalysisDataset(&_data, "distance");
 }
 
 
@@ -107,8 +109,6 @@ Distance::initAnalysis(const TrajectoryAnalysisSettings &settings,
     {
         GMX_THROW(InvalidInputError("The second selection does not define a single position"));
     }
-    _data.setColumnCount(4);
-    registerAnalysisDataset(&_data, "distance");
 
     _data.addModule(_avem);
     AnalysisDataPlotModulePointer _plotm(new AnalysisDataPlotModule());
index 26de3079c9c566c2de36611c463eb24da083c05b..792074eadc0c5b193cf061b91e765d80ad6cb628 100644 (file)
@@ -248,6 +248,12 @@ Select::Select()
       _bDump(false), _bTotNorm(false), _bFracNorm(false), _bResInd(false),
       _top(NULL)
 {
+    registerAnalysisDataset(&_sdata, "size");
+    registerAnalysisDataset(&_cdata, "cfrac");
+    _idata.setColumnCount(2);
+    _idata.setMultipoint(true);
+    registerAnalysisDataset(&_idata, "index");
+    registerAnalysisDataset(&_mdata, "mask");
 }
 
 
@@ -356,7 +362,6 @@ Select::initAnalysis(const TrajectoryAnalysisSettings &settings,
 
     // TODO: For large systems, a float may not have enough precision
     _sdata.setColumnCount(_sel.size());
-    registerAnalysisDataset(&_sdata, "size");
     _totsize.reserve(_sel.size());
     for (size_t g = 0; g < _sel.size(); ++g)
     {
@@ -374,7 +379,6 @@ Select::initAnalysis(const TrajectoryAnalysisSettings &settings,
     }
 
     _cdata.setColumnCount(_sel.size());
-    registerAnalysisDataset(&_cdata, "cfrac");
     if (!_fnFrac.empty())
     {
         AnalysisDataPlotModulePointer plot(
@@ -388,9 +392,6 @@ Select::initAnalysis(const TrajectoryAnalysisSettings &settings,
     }
 
     // TODO: For large systems, a float may not have enough precision
-    _idata.setColumnCount(2);
-    _idata.setMultipoint(true);
-    registerAnalysisDataset(&_idata, "index");
     if (!_fnIndex.empty())
     {
         AnalysisDataPlotModulePointer plot(
@@ -420,7 +421,6 @@ Select::initAnalysis(const TrajectoryAnalysisSettings &settings,
     }
 
     _mdata.setColumnCount(_sel[0].posCount());
-    registerAnalysisDataset(&_mdata, "mask");
     if (!_fnMask.empty())
     {
         if (_sel.size() > 1U)
index 6e6a858be7b1ab0fc2b74e42f446cee6355cb757..dc96c435de30b5dbc237b9c0408b7d1643b5db90 100644 (file)
@@ -1,2 +1,6 @@
+gmx_add_unit_test(TrajectoryAnalysisUnitTests trajectoryanalysis-test
+                  moduletest.cpp
+                  select.cpp)
+
 add_executable(test_selection test_selection.cpp)
 target_link_libraries(test_selection libgromacs)
diff --git a/src/gromacs/trajectoryanalysis/tests/moduletest.cpp b/src/gromacs/trajectoryanalysis/tests/moduletest.cpp
new file mode 100644 (file)
index 0000000..bdbce26
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements classes in moduletest.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_trajectoryanalysis
+ */
+#include "moduletest.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+#include "gromacs/trajectoryanalysis/cmdlinerunner.h"
+#include "gromacs/utility/file.h"
+
+#include "testutils/cmdlinetest.h"
+#include "testutils/datapath.h"
+#include "testutils/datatest.h"
+#include "testutils/refdata.h"
+
+namespace gmx
+{
+namespace test
+{
+
+/********************************************************************
+ * AbstractTrajectoryAnalysisModuleTestFixture::Impl
+ */
+
+class AbstractTrajectoryAnalysisModuleTestFixture::Impl
+{
+    public:
+        struct OutputFileInfo
+        {
+            OutputFileInfo(const char *option, const std::string &path)
+                : option(option), path(path)
+            {
+            }
+
+            std::string         option;
+            std::string         path;
+        };
+
+        typedef std::set<std::string>       DatasetNames;
+        typedef std::vector<OutputFileInfo> OutputFileList;
+
+        explicit Impl(AbstractTrajectoryAnalysisModuleTestFixture *parent);
+
+        TrajectoryAnalysisModule &module();
+        void ensureModuleCreated();
+
+        AbstractTrajectoryAnalysisModuleTestFixture    &parent_;
+        TrajectoryAnalysisModulePointer                 module_;
+        TestReferenceData               data_;
+        CommandLine                     cmdline_;
+        TestFileManager                 tempFiles_;
+        DatasetNames                    moduleDatasets_;
+        DatasetNames                    outputDatasets_;
+        OutputFileList                  outputFiles_;
+        bool                            bDatasetsIncluded_;
+};
+
+AbstractTrajectoryAnalysisModuleTestFixture::Impl::Impl(
+        AbstractTrajectoryAnalysisModuleTestFixture *parent)
+    : parent_(*parent), bDatasetsIncluded_(false)
+{
+    cmdline_.append("module");
+    cmdline_.append("-quiet");
+}
+
+TrajectoryAnalysisModule &
+AbstractTrajectoryAnalysisModuleTestFixture::Impl::module()
+{
+    ensureModuleCreated();
+    return *module_;
+}
+
+void
+AbstractTrajectoryAnalysisModuleTestFixture::Impl::ensureModuleCreated()
+{
+    if (module_.get() == NULL)
+    {
+        module_ = parent_.createModule();
+        const std::vector<std::string> &datasetNames(module_->datasetNames());
+        moduleDatasets_.clear();
+        moduleDatasets_.insert(datasetNames.begin(), datasetNames.end());
+        outputDatasets_ = moduleDatasets_;
+    }
+}
+
+/********************************************************************
+ * AbstractTrajectoryAnalysisModuleTestFixture
+ */
+
+AbstractTrajectoryAnalysisModuleTestFixture::AbstractTrajectoryAnalysisModuleTestFixture()
+    : impl_(new Impl(this))
+{
+}
+
+AbstractTrajectoryAnalysisModuleTestFixture::~AbstractTrajectoryAnalysisModuleTestFixture()
+{
+}
+
+void
+AbstractTrajectoryAnalysisModuleTestFixture::setTopology(const char *filename)
+{
+    impl_->cmdline_.append("-s");
+    impl_->cmdline_.append(TestFileManager::getTestFilePath(filename));
+}
+
+void
+AbstractTrajectoryAnalysisModuleTestFixture::setTrajectory(const char *filename)
+{
+    impl_->cmdline_.append("-f");
+    impl_->cmdline_.append(TestFileManager::getTestFilePath(filename));
+}
+
+void
+AbstractTrajectoryAnalysisModuleTestFixture::setOutputFile(const char *option,
+                                                           const char *filename)
+{
+    std::string fullFilename = impl_->tempFiles_.getTemporaryFilePath(filename);
+    impl_->cmdline_.append(option);
+    impl_->cmdline_.append(fullFilename);
+    impl_->outputFiles_.push_back(Impl::OutputFileInfo(option, fullFilename));
+}
+
+void
+AbstractTrajectoryAnalysisModuleTestFixture::includeDataset(const char *name)
+{
+    impl_->ensureModuleCreated();
+    if (!impl_->bDatasetsIncluded_)
+    {
+        impl_->outputDatasets_.clear();
+    }
+    bool bFound = (impl_->moduleDatasets_.find(name) != impl_->moduleDatasets_.end());
+    GMX_RELEASE_ASSERT(bFound, "Attempted to include a non-existent dataset");
+    impl_->outputDatasets_.insert(name);
+}
+
+void
+AbstractTrajectoryAnalysisModuleTestFixture::excludeDataset(const char *name)
+{
+    impl_->ensureModuleCreated();
+    bool bFound = (impl_->outputDatasets_.erase(name) > 0);
+    GMX_RELEASE_ASSERT(bFound, "Attempted to exclude a non-existent dataset");
+}
+
+void
+AbstractTrajectoryAnalysisModuleTestFixture::runTest(const CommandLine &args)
+{
+    TrajectoryAnalysisModule &module = impl_->module();
+    // Skip first argument if it is the module name.
+    int firstArg = (args.arg(0)[0] == '-' ? 0 : 1);
+    for (int i = firstArg; i < args.argc(); ++i)
+    {
+        impl_->cmdline_.append(args.arg(i));
+    }
+
+    TestReferenceChecker rootChecker(impl_->data_.rootChecker());
+
+    rootChecker.checkString(args.toString(), "CommandLine");
+
+    if (!impl_->outputDatasets_.empty())
+    {
+        TestReferenceChecker dataChecker(
+                rootChecker.checkCompound("OutputData", "Data"));
+        Impl::DatasetNames::const_iterator dataset;
+        for (dataset = impl_->outputDatasets_.begin();
+             dataset != impl_->outputDatasets_.end();
+             ++dataset)
+        {
+            const char *name = dataset->c_str();
+            AbstractAnalysisData &dataset = module.datasetFromName(name);
+            AnalysisDataTestFixture::addReferenceCheckerModule(
+                    dataChecker, name, &dataset);
+        }
+    }
+
+    TrajectoryAnalysisCommandLineRunner runner(&module);
+    runner.setPrintCopyright(false);
+    int rc = 0;
+    EXPECT_NO_THROW(rc = runner.run(impl_->cmdline_.argc(), impl_->cmdline_.argv()));
+    EXPECT_EQ(0, rc);
+
+    if (!impl_->outputFiles_.empty())
+    {
+        TestReferenceChecker outputChecker(
+                rootChecker.checkCompound("OutputFiles", "Files"));
+        Impl::OutputFileList::const_iterator outfile;
+        for (outfile = impl_->outputFiles_.begin();
+             outfile != impl_->outputFiles_.end();
+             ++outfile)
+        {
+            std::string output = File::readToString(outfile->path);
+            outputChecker.checkStringBlock(output, outfile->option.c_str());
+        }
+    }
+}
+
+} // namespace test
+} // namespace gmx
diff --git a/src/gromacs/trajectoryanalysis/tests/moduletest.h b/src/gromacs/trajectoryanalysis/tests/moduletest.h
new file mode 100644 (file)
index 0000000..aeb81bf
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Declares test fixture for TrajectoryAnalysisModule subclasses.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_TESTS_MODULETEST_H
+#define GMX_TRAJECTORYANALYSIS_TESTS_MODULETEST_H
+
+#include <gtest/gtest.h>
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+#include "gromacs/utility/common.h"
+
+namespace gmx
+{
+
+class TrajectoryAnalysisModule;
+
+namespace test
+{
+
+class CommandLine;
+
+/*! \internal \brief
+ * Test fixture for trajectory analysis modules.
+ *
+ * This class implements common logic for all tests for trajectory analysis
+ * modules.  The tests simply need to specify the type of the module to test,
+ * input files and any additional options, names of datasets to test (if not
+ * all), and possible output files to explicitly test by calling the different
+ * methods in this class.  runTest() then runs the specified module with the
+ * given options and performs all the requested tests against reference data.
+ *
+ * The actual module to be tested is constructed in the pure virtual
+ * createModule() method, which should be implemented in a subclass.
+ * Typically, the TrajectoryAnalysisModuleTestFixture template can be used.
+ *
+ * Any method in this class may throw std::bad_alloc if out of memory.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+class AbstractTrajectoryAnalysisModuleTestFixture : public ::testing::Test
+{
+    public:
+        AbstractTrajectoryAnalysisModuleTestFixture();
+        virtual ~AbstractTrajectoryAnalysisModuleTestFixture();
+
+        /*! \brief
+         * Sets the topology file to use for the test.
+         *
+         * \param[in] filename  Name of input topology file.
+         *
+         * \p filename is interpreted relative to the test input data
+         * directory, see getTestDataPath().
+         *
+         * Must be called at most once.  Either this method or setTrajectory()
+         * must be called before runTest().
+         */
+        void setTopology(const char *filename);
+        /*! \brief
+         * Sets the trajectory file to use for the test.
+         *
+         * \param[in] filename  Name of input trajectory file.
+         *
+         * \p filename is interpreted relative to the test input data
+         * directory, see getTestDataPath().
+         *
+         * Must be called at most once.  Either this method or setTopology()
+         * must be called before runTest().
+         */
+        void setTrajectory(const char *filename);
+        /*! \brief
+         * Sets an output file to use for the test.
+         *
+         * \param[in] option    Option to set.
+         * \param[in] filename  Name of the output file.
+         *
+         * This method:
+         *  - Sets \p option in the tested module to a temporary file name
+         *    constructed from \p filename.
+         *  - Makes runTest() to check the contents of the file against
+         *    reference data after running the module.
+         *  - Marks the temporary file for removal at test teardown.
+         *
+         * \p filename is given to TestTemporaryFileManager to make a unique
+         * filename for the temporary file, but is not otherwise used.
+         *
+         * Currently, this method should not be called for an XVG file, because
+         * the comments in the beginning of the file contain timestamps and
+         * other variable information, causing the test to fail.  Best used
+         * only for custom data formats.  For numeric data, testing the
+         * underlying dataset is typically sufficient.
+         */
+        void setOutputFile(const char *option, const char *filename);
+        /*! \brief
+         * Includes only specified dataset for the test.
+         *
+         * \param[in] name  Name of dataset to include.
+         *
+         * If this method is not called, all datasets are tested by default.
+         * If called once, only the specified dataset is tested.
+         * If called more than once, also the additional datasets are tested.
+         *
+         * \p name should be one of the names registered for the tested module
+         * using TrajectoryAnalysisModule::registerBasicDataset() or
+         * TrajectoryAnalysisModule::registerAnalysisDataset().
+         */
+        void includeDataset(const char *name);
+        /*! \brief
+         * Excludes specified dataset from the test.
+         *
+         * \param[in] name  Name of dataset to exclude.
+         *
+         * If includeDataset() has been called, \p name must be one of the
+         * included datasets.
+         *
+         * \p name should be one of the names registered for the tested module
+         * using TrajectoryAnalysisModule::registerBasicDataset() or
+         * TrajectoryAnalysisModule::registerAnalysisDataset().
+         */
+        void excludeDataset(const char *name);
+
+        /*! \brief
+         * Runs the analysis module with the given additional options.
+         *
+         * \param[in] args  Options to provide to the module.
+         *
+         * \p args should be formatted as command-line options, and contain the
+         * name of the module as the first argument (the latter requirement is
+         * for clarity only).  They are passed to the module in addition to
+         * those specified using other methods in this class.
+         *
+         * All other methods should be called before calling this method.
+         *
+         * Exceptions thrown by the module are caught by this method.
+         */
+        void runTest(const CommandLine &args);
+
+    protected:
+        /*! \brief
+         * Constructs the analysis module to be tested.
+         */
+        virtual TrajectoryAnalysisModulePointer createModule() = 0;
+
+    private:
+        class Impl;
+
+        PrivateImplPointer<Impl> impl_;
+};
+
+/*! \internal \brief
+ * Test fixture for a trajectory analysis module.
+ *
+ * \tparam ModuleType  Type of the analysis module to test.
+ *
+ * \p ModuleType should derive from TrajectoryAnalysisModule and be
+ * default-constructible.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+template <class ModuleType>
+class TrajectoryAnalysisModuleTestFixture
+    : public AbstractTrajectoryAnalysisModuleTestFixture
+{
+    protected:
+        virtual TrajectoryAnalysisModulePointer createModule()
+        {
+            return TrajectoryAnalysisModulePointer(new ModuleType);
+        }
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_BasicTest.xml b/src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_BasicTest.xml
new file mode 100644 (file)
index 0000000..8349ca5
--- /dev/null
@@ -0,0 +1,219 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="CommandLine">select -select 'y &lt; 2.5' 'resname RA'</String>
+  <OutputData Name="Data">
+    <AnalysisData Name="index">
+      <DataFrame Name="Frame0">
+        <Real Name="X">0.000000</Real>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">0</Int>
+          <Int Name="LastColumn">0</Int>
+          <DataValue>
+            <Real Name="Value">8.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">2.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">5.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">6.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">9.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">10.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">13.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">14.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">0</Int>
+          <Int Name="LastColumn">0</Int>
+          <DataValue>
+            <Real Name="Value">6.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">2.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">3.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">7.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">8.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">9.000000</Real>
+          </DataValue>
+        </Sequence>
+      </DataFrame>
+    </AnalysisData>
+    <AnalysisData Name="mask">
+      <DataFrame Name="Frame0">
+        <Real Name="X">0.000000</Real>
+        <Sequence Name="Y">
+          <Int Name="Length">15</Int>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">0.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">0.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">0.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">0.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">0.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">0.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">0.000000</Real>
+          </DataValue>
+        </Sequence>
+      </DataFrame>
+    </AnalysisData>
+    <AnalysisData Name="size">
+      <DataFrame Name="Frame0">
+        <Real Name="X">0.000000</Real>
+        <Sequence Name="Y">
+          <Int Name="Length">2</Int>
+          <DataValue>
+            <Real Name="Value">8.000000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">6.000000</Real>
+          </DataValue>
+        </Sequence>
+      </DataFrame>
+    </AnalysisData>
+  </OutputData>
+  <OutputFiles Name="Files">
+    <String Name="-oi"><![CDATA[
+      0.000    8    1    2    5    6    9   10   13   14    6    1    2    3    7    8    9
+]]></String>
+    <String Name="-on"><![CDATA[
+[ y_<_2.5_f0_t0.000 ]
+   1    2    5    6    9   10   13   14 
+
+[ resname_RA ]
+   1    2    3    7    8    9 
+]]></String>
+  </OutputFiles>
+</ReferenceData>
diff --git a/src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_HandlesDumpOption.xml b/src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_HandlesDumpOption.xml
new file mode 100644 (file)
index 0000000..8f2e0bc
--- /dev/null
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="CommandLine">select -select 'y &lt; 2.5' -dump</String>
+  <OutputData Name="Data">
+    <AnalysisData Name="index">
+      <DataFrame Name="Frame0">
+        <Real Name="X">0.000000</Real>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">0</Int>
+          <Int Name="LastColumn">0</Int>
+          <DataValue>
+            <Real Name="Value">8.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">2.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">5.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">6.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">9.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">10.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">13.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">14.000000</Real>
+          </DataValue>
+        </Sequence>
+      </DataFrame>
+    </AnalysisData>
+  </OutputData>
+  <OutputFiles Name="Files">
+    <String Name="-oi"><![CDATA[
+    1    2    5    6    9   10   13   14
+]]></String>
+  </OutputFiles>
+</ReferenceData>
diff --git a/src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_NormalizesSizes.xml b/src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_NormalizesSizes.xml
new file mode 100644 (file)
index 0000000..204ee84
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="CommandLine">select -select 'y &lt; 2.5' 'resname RA and y &lt; 2.5' 'resname RA' -norm</String>
+  <OutputData Name="Data">
+    <AnalysisData Name="size">
+      <DataFrame Name="Frame0">
+        <Real Name="X">0.000000</Real>
+        <Sequence Name="Y">
+          <Int Name="Length">3</Int>
+          <DataValue>
+            <Real Name="Value">0.533333</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">0.500000</Real>
+          </DataValue>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+        </Sequence>
+      </DataFrame>
+    </AnalysisData>
+  </OutputData>
+</ReferenceData>
diff --git a/src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_WritesResidueIndices.xml b/src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_WritesResidueIndices.xml
new file mode 100644 (file)
index 0000000..9b9db73
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="CommandLine">select -select 'res_com of resname RA RD' -resnr index</String>
+  <OutputData Name="Data">
+    <AnalysisData Name="index">
+      <DataFrame Name="Frame0">
+        <Real Name="X">0.000000</Real>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">0</Int>
+          <Int Name="LastColumn">0</Int>
+          <DataValue>
+            <Real Name="Value">3.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">3.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">5.000000</Real>
+          </DataValue>
+        </Sequence>
+      </DataFrame>
+    </AnalysisData>
+  </OutputData>
+</ReferenceData>
diff --git a/src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_WritesResidueNumbers.xml b/src/gromacs/trajectoryanalysis/tests/refdata/SelectModuleTest_WritesResidueNumbers.xml
new file mode 100644 (file)
index 0000000..4807e08
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="CommandLine">select -select 'res_com of resname RA RD'</String>
+  <OutputData Name="Data">
+    <AnalysisData Name="index">
+      <DataFrame Name="Frame0">
+        <Real Name="X">0.000000</Real>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">0</Int>
+          <Int Name="LastColumn">0</Int>
+          <DataValue>
+            <Real Name="Value">3.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">2.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">4.000000</Real>
+          </DataValue>
+        </Sequence>
+        <Sequence Name="Y">
+          <Int Name="Length">1</Int>
+          <Int Name="FirstColumn">1</Int>
+          <Int Name="LastColumn">1</Int>
+          <DataValue>
+            <Real Name="Value">1.000000</Real>
+          </DataValue>
+        </Sequence>
+      </DataFrame>
+    </AnalysisData>
+  </OutputData>
+</ReferenceData>
diff --git a/src/gromacs/trajectoryanalysis/tests/refdata/analysisdata-referencedata.xsl b/src/gromacs/trajectoryanalysis/tests/refdata/analysisdata-referencedata.xsl
new file mode 100644 (file)
index 0000000..099384f
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+
+<!--
+This file is currently duplicated to each directory containing reference data
+XML files. This is to make it compatible with more browsers.
+To keep these files in sync, please only modify the version in
+  src/testutils/
+and use the copy_xsl.sh script to copy it to relevant locations.
+-->
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:import href="common-referencedata.xsl"/>
+
+<xsl:template match="AnalysisData">
+    <xsl:variable name="has-columnspec"
+                  select="DataFrame/Sequence[@Name='Y']/Int[@Name='FirstColumn']"/>
+    <table border="1">
+        <tr>
+            <th>Frame</th>
+            <th>X</th>
+            <xsl:if test="$has-columnspec">
+                <th>Columns</th>
+            </xsl:if>
+            <th>Values</th>
+        </tr>
+        <xsl:for-each select="DataFrame/Sequence[@Name='Y']">
+        <tr>
+            <td><xsl:value-of select="../@Name"/></td>
+            <td><xsl:value-of select="../Real[@Name='X']"/></td>
+            <xsl:if test="$has-columnspec">
+                <td>
+                    <xsl:choose>
+                        <xsl:when test="Int[@Name='FirstColumn']">
+                            <xsl:value-of select="Int[@Name='FirstColumn']"/>
+                            <xsl:text>-</xsl:text>
+                            <xsl:value-of select="Int[@Name='LastColumn']"/>
+                        </xsl:when>
+                        <xsl:otherwise>all</xsl:otherwise>
+                    </xsl:choose>
+                </td>
+            </xsl:if>
+            <td><xsl:call-template name="SequenceAsCSV"/></td>
+        </tr>
+        </xsl:for-each>
+    </table>
+</xsl:template>
+
+<xsl:template match="DataValue">
+    <xsl:value-of select="Real[@Name='Value']"/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/gromacs/trajectoryanalysis/tests/refdata/common-referencedata.xsl b/src/gromacs/trajectoryanalysis/tests/refdata/common-referencedata.xsl
new file mode 100644 (file)
index 0000000..1ae38d8
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+
+<!--
+This file is currently duplicated to each directory containing reference data
+XML files. This is to make it compatible with more browsers.
+To keep these files in sync, please only modify the version in
+  src/testutils/
+and use the copy_xsl.sh script to copy it to relevant locations.
+-->
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="/">
+    <html><body>
+        <xsl:apply-templates/>
+    </body></html>
+</xsl:template>
+
+<xsl:template match="/ReferenceData">
+    <h1>Test Reference Data</h1>
+    <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="Vector">
+    (<xsl:value-of select="*[@Name='X']"/>;
+     <xsl:value-of select="*[@Name='Y']"/>;
+     <xsl:value-of select="*[@Name='Z']"/>)
+</xsl:template>
+
+<xsl:template name="SequenceAsHorizontalTable">
+    <xsl:param name="root" select="."/>
+    <table border="1">
+        <tr><th>Count</th><th>Items</th></tr>
+        <tr>
+            <td><xsl:value-of select="$root/Int[@Name='Length']"/></td>
+            <td>
+                <xsl:call-template name="SequenceAsCSV">
+                    <xsl:with-param name="root" select="$root"/>
+                </xsl:call-template>
+            </td>
+        </tr>
+    </table>
+</xsl:template>
+
+<xsl:template name="SequenceAsCSV">
+    <xsl:param name="root" select="."/>
+    <xsl:for-each select="$root/*">
+        <xsl:if test="not(.[@Name])">
+            <xsl:apply-templates select="."/>
+            <xsl:if test="position() &lt; last()">, </xsl:if>
+        </xsl:if>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="Bool">
+    <xsl:value-of select="."/>
+</xsl:template>
+
+<xsl:template name="String">
+    <xsl:value-of select="."/>
+</xsl:template>
+
+<xsl:template name="Int">
+    <xsl:value-of select="."/>
+</xsl:template>
+
+<xsl:template name="Real">
+    <xsl:value-of select="."/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/gromacs/trajectoryanalysis/tests/refdata/referencedata.xsl b/src/gromacs/trajectoryanalysis/tests/refdata/referencedata.xsl
new file mode 100644 (file)
index 0000000..971d540
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:import href="common-referencedata.xsl"/>
+<xsl:import href="analysisdata-referencedata.xsl"/>
+
+<xsl:template match="String[@Name='CommandLine']">
+    <xsl:value-of select="."/>
+</xsl:template>
+
+<xsl:template match="OutputData">
+    <h2>Raw Output Data</h2>
+    <xsl:apply-templates />
+</xsl:template>
+
+<xsl:template match="AnalysisData">
+    <h3><xsl:value-of select="@Name"/></h3>
+    <xsl:apply-imports />
+</xsl:template>
+
+<xsl:template match="OutputFiles">
+    <h2>Output Files</h2>
+    <xsl:for-each select="*">
+        <h3><xsl:value-of select="@Name"/></h3>
+        <pre>
+            <xsl:value-of select="."/>
+        </pre>
+    </xsl:for-each>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/gromacs/trajectoryanalysis/tests/select.cpp b/src/gromacs/trajectoryanalysis/tests/select.cpp
new file mode 100644 (file)
index 0000000..0d44ee8
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Tests for functionality of the "select" trajectory analysis module.
+ *
+ * These tests test most of the functionality of the module, but currently
+ * missing are:
+ *  - Tests related to -oc output.  This would require a more complex input
+ *    structure for reasonable testing (the same structure could also be used
+ *    in selection unit tests for 'insolidangle' keyword).
+ *  - Tests for XVG labels.  This is a limitation of the current testing
+ *    framework.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_trajectoryanalysis
+ */
+#include <gtest/gtest.h>
+
+#include "gromacs/trajectoryanalysis/modules/select.h"
+
+#include "testutils/cmdlinetest.h"
+
+#include "moduletest.h"
+
+namespace
+{
+
+using gmx::test::CommandLine;
+
+/********************************************************************
+ * Tests for gmx::analysismodules::Select.
+ */
+
+typedef gmx::test::TrajectoryAnalysisModuleTestFixture<gmx::analysismodules::Select>
+        SelectModuleTest;
+
+TEST_F(SelectModuleTest, BasicTest)
+{
+    const char *const cmdline[] = {
+        "select",
+        "-select", "y < 2.5", "resname RA"
+    };
+    setTopology("simple.gro");
+    setOutputFile("-oi", "index.dat");
+    setOutputFile("-on", "index.ndx");
+    excludeDataset("cfrac");
+    runTest(CommandLine::create(cmdline));
+}
+
+TEST_F(SelectModuleTest, HandlesDumpOption)
+{
+    const char *const cmdline[] = {
+        "select",
+        "-select", "y < 2.5",
+        "-dump"
+    };
+    setTopology("simple.gro");
+    setOutputFile("-oi", "index.dat");
+    includeDataset("index");
+    runTest(CommandLine::create(cmdline));
+}
+
+TEST_F(SelectModuleTest, NormalizesSizes)
+{
+    const char *const cmdline[] = {
+        "select",
+        "-select", "y < 2.5", "resname RA and y < 2.5", "resname RA",
+        "-norm"
+    };
+    setTopology("simple.gro");
+    includeDataset("size");
+    runTest(CommandLine::create(cmdline));
+}
+
+TEST_F(SelectModuleTest, WritesResidueNumbers)
+{
+    const char *const cmdline[] = {
+        "select",
+        "-select", "res_com of resname RA RD"
+    };
+    setTopology("simple.gro");
+    includeDataset("index");
+    runTest(CommandLine::create(cmdline));
+}
+
+TEST_F(SelectModuleTest, WritesResidueIndices)
+{
+    const char *const cmdline[] = {
+        "select",
+        "-select", "res_com of resname RA RD",
+        "-resnr", "index"
+    };
+    setTopology("simple.gro");
+    includeDataset("index");
+    runTest(CommandLine::create(cmdline));
+}
+
+} // namespace
diff --git a/src/gromacs/trajectoryanalysis/tests/simple.gro b/src/gromacs/trajectoryanalysis/tests/simple.gro
new file mode 100644 (file)
index 0000000..13e93cd
--- /dev/null
@@ -0,0 +1,18 @@
+Test system
+ 15
+    2RA      CB    1   1.000   1.000   0.000
+    2RA      S1    2   1.000   2.000   0.000
+    2RA      S2    3   1.000   3.000   0.000
+    3RB      CB    4   1.000   4.000   0.000
+    3RB      S1    5   2.000   1.000   0.000
+    3RB      S2    6   2.000   2.000   0.000
+    4RA      CB    7   2.000   3.000   0.000
+    4RA      S1    8   2.000   4.000   0.000
+    4RA      S2    9   3.000   1.000   0.000
+    5RC      CB   10   3.000   2.000   0.000
+    5RC      S1   11   3.000   3.000   0.000
+    5RC      S2   12   3.000   4.000   0.000
+    1RD      CB   13   4.000   1.000   0.000
+    1RD      S1   14   4.000   2.000   0.000
+    1RD      S2   15   4.000   3.000   0.000
+  10.00000  10.00000  10.00000
index d4c943043e83f83cc13def48d62a662d384eb412..6f8b510b474dfb460e9346d926fb1af09d3fd44c 100755 (executable)
@@ -5,12 +5,12 @@
 cd `dirname $0`
 cd ../../
 
-for destdir in analysisdata selection ; do
+for destdir in analysisdata selection trajectoryanalysis ; do
     cp -f src/testutils/common-referencedata.xsl \
           src/gromacs/$destdir/tests/refdata/
 done
 
-for destdir in analysisdata ; do
+for destdir in analysisdata trajectoryanalysis ; do
     cp -f src/testutils/analysisdata-referencedata.xsl \
           src/gromacs/$destdir/tests/refdata/
 done
index 39d1f7cca02cb2aa246b1c9799d1fdda081fe3b7..4f0d4654c07ef6f0881f986b5187d4f1577ba38a 100644 (file)
@@ -211,11 +211,12 @@ AnalysisDataTestFixture::addStaticStorageCheckerModule(const AnalysisDataTestInp
 
 
 void
-AnalysisDataTestFixture::addReferenceCheckerModule(const TestReferenceChecker &checker,
+AnalysisDataTestFixture::addReferenceCheckerModule(TestReferenceChecker checker,
+                                                   const char *id,
                                                    AbstractAnalysisData *source)
 {
     MockAnalysisDataModulePointer module(new MockAnalysisDataModule(0));
-    module->setupReferenceCheck(checker, source);
+    module->setupReferenceCheck(checker.checkCompound("AnalysisData", id), source);
     source->addModule(module);
 }
 
@@ -224,8 +225,7 @@ void
 AnalysisDataTestFixture::addReferenceCheckerModule(const char *id,
                                                    AbstractAnalysisData *source)
 {
-    TestReferenceChecker checker(data_.rootChecker().checkCompound("AnalysisData", id));
-    addReferenceCheckerModule(checker, source);
+    addReferenceCheckerModule(data_.rootChecker(), id, source);
 }
 
 } // namespace test
index 44fc48c7d4bdfdbe9b4008b7f7e8b04bc27e0487..a41e808630671733137bef13d316e1676de0310c 100644 (file)
@@ -322,19 +322,21 @@ class AnalysisDataTestFixture : public ::testing::Test
          * Adds a mock module that verifies output against reference data.
          *
          * \param[in]  checker  Reference data checker to use for comparison.
+         * \param[in]  id       Identifier for reference data compound to use.
          * \param      source   Data object to verify.
          *
          * Creates a mock module that verifies that the
          * AnalysisDataModuleInterface methods are called correctly by
          * \p source.  Parameters for the calls are verified against reference
-         * data using \p checker.
+         * data using a child compound \p id of \p checker.
          * Adds the created module to \p source using \p data->addModule().
          * Any exceptions from the called functions should be caught by the
          * caller.
          *
          * \see TestReferenceData
          */
-        static void addReferenceCheckerModule(const TestReferenceChecker &checker,
+        static void addReferenceCheckerModule(TestReferenceChecker checker,
+                                              const char *id,
                                               AbstractAnalysisData *source);
 
         /*! \brief