Added more analysisdata unit tests.
authorTeemu Murtola <teemu.murtola@cbr.su.se>
Tue, 25 Oct 2011 17:48:51 +0000 (20:48 +0300)
committerTeemu Murtola <teemu.murtola@gmail.com>
Tue, 21 Feb 2012 20:03:32 +0000 (22:03 +0200)
There is now a framework that makes it quite easy to write tests for
classes that implement the AbstractAnalysisData interface.
Used the framework to implement a more thorough set of tests for the
AnalysisData class, and some simple tests for AnalysisDataAverageModule.

Part of issue #823.

Change-Id: I3bea1f722848e6ebb0bd6fb8f5983ea09f1aeb9e

src/gromacs/analysisdata/abstractdata.cpp
src/gromacs/analysisdata/tests/CMakeLists.txt
src/gromacs/analysisdata/tests/analysisdata.cpp
src/gromacs/analysisdata/tests/average.cpp [new file with mode: 0644]
src/gromacs/analysisdata/tests/datatest.cpp [new file with mode: 0644]
src/gromacs/analysisdata/tests/datatest.h [new file with mode: 0644]
src/gromacs/analysisdata/tests/mock_module-impl.h [new file with mode: 0644]
src/gromacs/analysisdata/tests/mock_module.cpp [new file with mode: 0644]
src/gromacs/analysisdata/tests/mock_module.h
src/gromacs/analysisdata/tests/refdata/AverageModuleTest_BasicTest.xml [new file with mode: 0644]
src/gromacs/analysisdata/tests/refdata/AverageModuleTest_CanCustomizeXAxis.xml [new file with mode: 0644]

index 3b00edeeca6e223b9eec6052439a3c4e407fafcf..4c09c91dfe63bf1b754715efcb19cfefc688f96f 100644 (file)
@@ -481,7 +481,8 @@ AbstractAnalysisDataStored::requestStorage(int nframes)
     {
         return true;
     }
-    _impl->_nalloc = nframes;
+    // nframes previous frames plus the current one
+    _impl->_nalloc = nframes + 1;
     return true;
 }
 
index cb52398f520ef8121da3f1e74539e9a85b1f4f31..f595c1c50a2acffb96bd0e8522cfd7238b19850c 100644 (file)
@@ -1,2 +1,4 @@
-add_gmock_test(AnalysisDataUnitTests analysisdata-test
-               analysisdata.cpp)
+if (TESTUTILS_HAVE_REFDATA)
+    add_gmock_test(AnalysisDataUnitTests analysisdata-test
+                   analysisdata.cpp average.cpp datatest.cpp mock_module.cpp)
+endif ()
index aeb294a6169cbd65556487c8306bb8bef798396c..f7c66fb4d7ccb13b3e709a3d0cc3a9edb590faf9 100644 (file)
  * \brief
  * Tests for analysis data functionality.
  *
+ * These tests check the functionality of gmx::AnalysisData, as well as its
+ * base classes gmx::AbstractAnalysisData and gmx::AbstractAnalysisDataStored.
+ * Most checking is done using gmx::test::AnalysisDataTestFixture and mock
+ * modules that implement gmx::AnalysisDataModuleInterface.
+ *
  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
  * \ingroup module_analysisdata
  */
 #include "gromacs/analysisdata/analysisdata.h"
 #include "gromacs/fatalerror/exceptions.h"
 
+#include "datatest.h"
 #include "mock_module.h"
 
+using gmx::test::MockAnalysisModule;
+
 namespace
 {
 
@@ -52,7 +60,10 @@ namespace
  * Tests for gmx::AnalysisData.
  */
 
-TEST(AnalysisDataTest, BasicInitialization)
+/*
+ * Tests that simple initialization works.
+ */
+TEST(AnalysisDataInitializationTest, BasicInitialization)
 {
     gmx::AnalysisData data;
     EXPECT_EQ(0, data.columnCount());
@@ -72,76 +83,174 @@ TEST(AnalysisDataTest, BasicInitialization)
     EXPECT_FALSE(data.isMultipoint());
 }
 
-
-TEST(AnalysisDataTest, ChecksMultiColumnModules)
+/*
+ * Tests that checking for compatibility of modules with multicolumn data
+ * works.
+ */
+TEST(AnalysisDataInitializationTest, ChecksMultiColumnModules)
 {
     gmx::AnalysisData data;
     data.setColumns(2);
 
-    std::auto_ptr<MockModule> mod(new MockModule(0));
+    std::auto_ptr<MockAnalysisModule> mod(new MockAnalysisModule(0));
     EXPECT_THROW(data.addModule(mod.release()), gmx::APIError);
 
-    mod.reset(new MockModule(gmx::AnalysisDataModuleInterface::efAllowMulticolumn));
+    mod.reset(new MockAnalysisModule(gmx::AnalysisDataModuleInterface::efAllowMulticolumn));
     EXPECT_NO_THROW(data.addModule(mod.release()));
 }
 
-
-TEST(AnalysisDataTest, ChecksMultiPointModules)
+/*
+ * Tests that checking for compatibility of modules with multipoint data
+ * works.
+ */
+TEST(AnalysisDataInitializationTest, ChecksMultiPointModules)
 {
     gmx::AnalysisData data;
     data.setColumns(1, true);
 
-    std::auto_ptr<MockModule> mod(new MockModule(0));
+    std::auto_ptr<MockAnalysisModule> mod(new MockAnalysisModule(0));
     EXPECT_THROW(data.addModule(mod.release()), gmx::APIError);
 
-    mod.reset(new MockModule(gmx::AnalysisDataModuleInterface::efAllowMultipoint));
+    mod.reset(new MockAnalysisModule(gmx::AnalysisDataModuleInterface::efAllowMultipoint));
     EXPECT_NO_THROW(data.addModule(mod.release()));
 }
 
 
-TEST(AnalysisDataTest, CallsModuleCorrectly)
+typedef gmx::test::AnalysisDataTestFixture AnalysisDataTest;
+
+// Input data for the tests below.
+using gmx::test::END_OF_DATA;
+using gmx::test::END_OF_FRAME;
+static const real inputdata[] = {
+    1.0,  0.0, 1.0, 2.0, END_OF_FRAME,
+    2.0,  1.0, 1.0, 1.0, END_OF_FRAME,
+    3.0,  2.0, 0.0, 0.0, END_OF_FRAME,
+    END_OF_DATA
+};
+
+/*
+ * Tests that data is forwarded correctly to modules using two independent
+ * modules.
+ */
+TEST_F(AnalysisDataTest, CallsModuleCorrectly)
+{
+    gmx::test::AnalysisDataTestInput input(inputdata);
+    gmx::AnalysisData data;
+    data.setColumns(input.columnCount());
+
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+    ASSERT_NO_THROW(presentAllData(input, &data));
+}
+
+/*
+ * Tests that data is forwarded correctly to modules that are added using
+ * addColumnModule().
+ * Uses two independent modules.
+ */
+TEST_F(AnalysisDataTest, CallsColumnModuleCorrectly)
 {
+    gmx::test::AnalysisDataTestInput input(inputdata);
     gmx::AnalysisData data;
-    data.setColumns(1);
+    data.setColumns(input.columnCount());
+
+    ASSERT_NO_THROW(addStaticColumnCheckerModule(input, 0, 2, &data));
+    ASSERT_NO_THROW(addStaticColumnCheckerModule(input, 2, 1, &data));
+    ASSERT_NO_THROW(presentAllData(input, &data));
+}
+
+/*
+ * Tests that data is forwarded correctly to modules when the data is added as
+ * individual points and not as full frames.
+ */
+TEST_F(AnalysisDataTest, CallsModuleCorrectlyWithIndividualPoints)
+{
+    gmx::test::AnalysisDataTestInput input(inputdata);
+    gmx::AnalysisData data;
+    data.setColumns(input.columnCount());
 
-    std::auto_ptr<MockModule> mod(new MockModule(0));
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+    ASSERT_NO_THROW(addStaticColumnCheckerModule(input, 1, 2, &data));
+    gmx::AnalysisDataHandle *handle = NULL;
+    ASSERT_NO_THROW(handle = data.startData(NULL));
+    for (int row = 0; row < input.frameCount(); ++row)
     {
-        ::testing::InSequence dummy;
-        using ::testing::_;
-
-        EXPECT_CALL(*mod, dataStarted(&data));
-        EXPECT_CALL(*mod, frameStarted(1.0, 0.0));
-        EXPECT_CALL(*mod, pointsAdded(1.0, 0.0, 0, 1, _, _, _));
-        EXPECT_CALL(*mod, frameFinished());
-        EXPECT_CALL(*mod, frameStarted(2.0, 0.0));
-        EXPECT_CALL(*mod, pointsAdded(2.0, 0.0, 0, 1, _, _, _));
-        EXPECT_CALL(*mod, frameFinished());
-        EXPECT_CALL(*mod, frameStarted(3.0, 0.0));
-        EXPECT_CALL(*mod, pointsAdded(3.0, 0.0, 0, 1, _, _, _));
-        EXPECT_CALL(*mod, frameFinished());
-        EXPECT_CALL(*mod, dataFinished());
+        const gmx::test::AnalysisDataTestInputFrame &frame = input.frame(row);
+        ASSERT_NO_THROW(handle->startFrame(row, frame.x(), frame.dx()));
+        for (int column = 0; column < input.columnCount(); ++column)
+        {
+            ASSERT_NO_THROW(handle->addPoint(column, frame.yptr()[column]));
+        }
+        ASSERT_NO_THROW(handle->finishFrame());
+        EXPECT_EQ(row + 1, data.frameCount());
     }
-    ASSERT_NO_THROW(data.addModule(mod.release()));
+    ASSERT_NO_THROW(handle->finishData());
+}
+
+/*
+ * Tests that data is forwarded correctly (in frame order) to modules when the
+ * data is added through multiple handles in non-increasing order.
+ */
+TEST_F(AnalysisDataTest, CallsModuleCorrectlyWithOutOfOrderFrames)
+{
+    gmx::test::AnalysisDataTestInput input(inputdata);
+    gmx::AnalysisData data;
+    data.setColumns(input.columnCount());
+
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+    ASSERT_NO_THROW(addStaticColumnCheckerModule(input, 1, 2, &data));
+    gmx::AnalysisDataHandle *handle1 = NULL;
+    gmx::AnalysisDataHandle *handle2 = NULL;
+    ASSERT_NO_THROW(handle1 = data.startData(NULL));
+    ASSERT_NO_THROW(handle2 = data.startData(NULL));
+    ASSERT_NO_THROW(presentDataFrame(input, 1, handle1));
+    ASSERT_NO_THROW(presentDataFrame(input, 0, handle2));
+    ASSERT_NO_THROW(presentDataFrame(input, 2, handle1));
+    ASSERT_NO_THROW(handle1->finishData());
+    ASSERT_NO_THROW(handle2->finishData());
+}
+
+/*
+ * Tests that data can be accessed correctly from a module that requests
+ * storage using AbstractAnalysisData::requestStorage() with parameter -1.
+ */
+TEST_F(AnalysisDataTest, FullStorageWorks)
+{
+    gmx::test::AnalysisDataTestInput input(inputdata);
+    gmx::AnalysisData data;
+    data.setColumns(input.columnCount());
 
-    gmx::AnalysisDataHandle *dh = NULL;
-    ASSERT_NO_THROW(dh = data.startData(NULL));
+    ASSERT_NO_THROW(addStaticStorageCheckerModule(input, -1, &data));
+    ASSERT_NO_THROW(presentAllData(input, &data));
+}
 
-    ASSERT_NO_THROW(dh->startFrame(0, 1.0));
-    EXPECT_NO_THROW(dh->addPoint(0, 1.5));
-    EXPECT_NO_THROW(dh->finishFrame());
-    EXPECT_EQ(1, data.frameCount());
+/*
+ * Tests that a data module can be added to an AnalysisData object after data
+ * has been added if all data is still available in storage.
+ */
+TEST_F(AnalysisDataTest, CanAddModuleAfterStoredData)
+{
+    gmx::test::AnalysisDataTestInput input(inputdata);
+    gmx::AnalysisData data;
+    data.setColumns(input.columnCount());
+    data.requestStorage(-1);
 
-    ASSERT_NO_THROW(dh->startFrame(1, 2.0));
-    EXPECT_NO_THROW(dh->addPoint(0, 2.5));
-    EXPECT_NO_THROW(dh->finishFrame());
-    EXPECT_EQ(2, data.frameCount());
+    ASSERT_NO_THROW(presentAllData(input, &data));
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+}
 
-    ASSERT_NO_THROW(dh->startFrame(2, 3.0));
-    EXPECT_NO_THROW(dh->addPoint(0, 3.5));
-    EXPECT_NO_THROW(dh->finishFrame());
-    EXPECT_EQ(3, data.frameCount());
+/*
+ * Tests that data can be accessed correctly from a module that requests
+ * storage using AbstractAnalysisData::requestStorage() only for one frame.
+ */
+TEST_F(AnalysisDataTest, LimitedStorageWorks)
+{
+    gmx::test::AnalysisDataTestInput input(inputdata);
+    gmx::AnalysisData data;
+    data.setColumns(input.columnCount());
 
-    EXPECT_NO_THROW(data.finishData(dh));
+    ASSERT_NO_THROW(addStaticStorageCheckerModule(input, 1, &data));
+    ASSERT_NO_THROW(presentAllData(input, &data));
 }
 
 } // namespace
diff --git a/src/gromacs/analysisdata/tests/average.cpp b/src/gromacs/analysisdata/tests/average.cpp
new file mode 100644 (file)
index 0000000..4bd3bc1
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *
+ *                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 gmx::AnalysisDataAverageModule
+ *
+ * These tests check that gmx::AnalysisDataAverageModule computes averages
+ * correctly with simple input data.
+ * Checking is done using gmx::test::AnalysisDataTestFixture and reference
+ * data.  Also the input data is written to the reference data to catch
+ * out-of-date reference.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include <gtest/gtest.h>
+
+#include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/analysisdata/modules/average.h"
+
+#include "datatest.h"
+
+namespace
+{
+
+/********************************************************************
+ * Tests for gmx::AnalysisDataAverageModule.
+ */
+
+typedef gmx::test::AnalysisDataTestFixture AverageModuleTest;
+
+// Input data for the tests below.
+using gmx::test::END_OF_DATA;
+using gmx::test::END_OF_FRAME;
+static const real inputdata[] = {
+    1.0,  0.0, 1.0, 2.0, END_OF_FRAME,
+    2.0,  1.0, 1.0, 1.0, END_OF_FRAME,
+    3.0,  2.0, 0.0, 0.0, END_OF_FRAME,
+    END_OF_DATA
+};
+
+TEST_F(AverageModuleTest, BasicTest)
+{
+    gmx::test::AnalysisDataTestInput input(inputdata);
+    gmx::AnalysisData data;
+    data.setColumns(input.columnCount());
+    gmx::AnalysisDataAverageModule *module =
+        new gmx::AnalysisDataAverageModule();
+    data.addModule(module);
+
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+    ASSERT_NO_THROW(addReferenceCheckerModule("InputData", &data));
+    ASSERT_NO_THROW(addReferenceCheckerModule("Average", module));
+    ASSERT_NO_THROW(presentAllData(input, &data));
+}
+
+TEST_F(AverageModuleTest, CanCustomizeXAxis)
+{
+    gmx::test::AnalysisDataTestInput input(inputdata);
+    gmx::AnalysisData data;
+    data.setColumns(input.columnCount());
+    gmx::AnalysisDataAverageModule *module =
+        new gmx::AnalysisDataAverageModule();
+    data.addModule(module);
+    module->setXAxis(0.5, 0.5);
+
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+    ASSERT_NO_THROW(addReferenceCheckerModule("InputData", &data));
+    ASSERT_NO_THROW(addReferenceCheckerModule("Average", module));
+    ASSERT_NO_THROW(presentAllData(input, &data));
+}
+
+} // namespace
diff --git a/src/gromacs/analysisdata/tests/datatest.cpp b/src/gromacs/analysisdata/tests/datatest.cpp
new file mode 100644 (file)
index 0000000..8a20282
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ *
+ *                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 datatest.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include "datatest.h"
+
+#include <memory>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/fatalerror/gmxassert.h"
+#include "gromacs/utility/format.h"
+
+#include "testutils/refdata.h"
+
+#include "mock_module.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+/********************************************************************
+ * AnalysisDataTestInputFrame
+ */
+
+AnalysisDataTestInputFrame::AnalysisDataTestInputFrame()
+{
+}
+
+
+/********************************************************************
+ * AnalysisDataTestInput
+ */
+
+namespace
+{
+    void checkValidDataItem(real data)
+    {
+        GMX_RELEASE_ASSERT(data != END_OF_DATA && data != END_OF_FRAME,
+                           "Inconsistent data array");
+    }
+}
+
+AnalysisDataTestInput::AnalysisDataTestInput(const real *data)
+    : columnCount_(0)
+{
+    // Count rows and columns.
+    int rows = 0, columns = -1;
+    const real *dataptr = data;
+    for (int i = 0; dataptr[i] != END_OF_DATA; ++i)
+    {
+        if (dataptr[i] == END_OF_FRAME)
+        {
+            GMX_RELEASE_ASSERT(i > 0, "Empty data frame");
+            if (columns == -1)
+            {
+                columns = i - 1;
+            }
+            GMX_RELEASE_ASSERT(columns == i - 1,
+                               "Different frames have different number of columns");
+            ++rows;
+            dataptr += i + 1;
+            i = -1;
+        }
+    }
+    GMX_RELEASE_ASSERT(rows > 0, "Empty data");
+    columnCount_ = columns;
+
+    // Store the data.
+    frames_.resize(rows);
+    dataptr = data;
+    for (int r = 0; r < rows; ++r)
+    {
+        AnalysisDataTestInputFrame &frame = frames_[r];
+        checkValidDataItem(*dataptr);
+        frame.index_ = r;
+        frame.x_ = *dataptr;
+        ++dataptr;
+        frame.y_.resize(columns);
+        for (int c = 0; c < columns; ++c)
+        {
+            checkValidDataItem(dataptr[c]);
+            frame.y_[c] = dataptr[c];
+        }
+        GMX_RELEASE_ASSERT(dataptr[columns] == END_OF_FRAME,
+                           "Inconsistent data array");
+        dataptr += columns + 1;
+    }
+    GMX_RELEASE_ASSERT(*dataptr == END_OF_DATA, "Inconsistent data array");
+}
+
+
+AnalysisDataTestInput::~AnalysisDataTestInput()
+{
+}
+
+
+const AnalysisDataTestInputFrame &AnalysisDataTestInput::frame(int index) const
+{
+    GMX_RELEASE_ASSERT(index >= 0 && index < frameCount(),
+                       "Out-of-range frame index");
+    return frames_[index];
+}
+
+
+/********************************************************************
+ * AnalysisDataTest
+ */
+
+AnalysisDataTestFixture::AnalysisDataTestFixture()
+{
+}
+
+
+void AnalysisDataTestFixture::presentAllData(const AnalysisDataTestInput &input,
+                                             AnalysisData *data)
+{
+    gmx::AnalysisDataHandle *handle = NULL;
+    handle = data->startData(NULL);
+    for (int row = 0; row < input.frameCount(); ++row)
+    {
+        presentDataFrame(input, row, handle);
+        EXPECT_EQ(row + 1, data->frameCount());
+    }
+    handle->finishData();
+}
+
+
+void AnalysisDataTestFixture::presentDataFrame(const AnalysisDataTestInput &input,
+                                               int row, AnalysisDataHandle *handle)
+{
+    const AnalysisDataTestInputFrame &frame = input.frame(row);
+    handle->startFrame(row, frame.x(), frame.dx());
+    handle->addPoints(0, input.columnCount(), frame.yptr(), frame.dyptr(),
+                      frame.presentptr());
+    handle->finishFrame();
+}
+
+
+void
+AnalysisDataTestFixture::addStaticCheckerModule(const AnalysisDataTestInput &data,
+                                                AbstractAnalysisData *source)
+{
+    std::auto_ptr<MockAnalysisModule> module(
+            new MockAnalysisModule(gmx::AnalysisDataModuleInterface::efAllowMulticolumn));
+    module->setupStaticCheck(data, source);
+    source->addModule(module.release());
+}
+
+
+void
+AnalysisDataTestFixture::addStaticColumnCheckerModule(const AnalysisDataTestInput &data,
+                                                      int firstcol, int n,
+                                                      AbstractAnalysisData *source)
+{
+    std::auto_ptr<MockAnalysisModule> module(
+            new MockAnalysisModule(gmx::AnalysisDataModuleInterface::efAllowMulticolumn));
+    module->setupStaticColumnCheck(data, firstcol, n, source);
+    source->addColumnModule(firstcol, n, module.release());
+}
+
+
+void
+AnalysisDataTestFixture::addStaticStorageCheckerModule(const AnalysisDataTestInput &data,
+                                                       int storageCount,
+                                                       AbstractAnalysisData *source)
+{
+    std::auto_ptr<MockAnalysisModule> module(
+            new MockAnalysisModule(gmx::AnalysisDataModuleInterface::efAllowMulticolumn));
+    module->setupStaticStorageCheck(data, storageCount, source);
+    source->addModule(module.release());
+}
+
+
+void
+AnalysisDataTestFixture::addReferenceCheckerModule(const TestReferenceChecker &checker,
+                                                   AbstractAnalysisData *source)
+{
+    std::auto_ptr<MockAnalysisModule> module(
+            new MockAnalysisModule(gmx::AnalysisDataModuleInterface::efAllowMulticolumn));
+    module->setupReferenceCheck(checker, source);
+    source->addModule(module.release());
+}
+
+
+void
+AnalysisDataTestFixture::addReferenceCheckerModule(const char *id,
+                                                   AbstractAnalysisData *source)
+{
+    TestReferenceChecker checker(data_.rootChecker().checkCompound("AnalysisData", id));
+    addReferenceCheckerModule(checker, source);
+}
+
+} // namespace test
+} // namespace gmx
diff --git a/src/gromacs/analysisdata/tests/datatest.h b/src/gromacs/analysisdata/tests/datatest.h
new file mode 100644 (file)
index 0000000..f0daad6
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ *
+ *                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
+ */
+/*! \libinternal \file
+ * \brief
+ * Helper classes for testing classes that derive from AbstractAnalysisData.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_TESTS_DATATEST_H
+#define GMX_ANALYSISDATA_TESTS_DATATEST_H
+
+#include <limits>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/legacyheaders/types/simple.h"
+
+#include "testutils/refdata.h"
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+class AnalysisData;
+class AnalysisDataHandle;
+
+namespace test
+{
+
+class MockAnalysisModule;
+
+//! Constant to use to signify end of data for AnalysisDataTestInput.
+const real END_OF_DATA = std::numeric_limits<real>::max();
+//! Constant to use to signify end of one data frame for AnalysisDataTestInput.
+const real END_OF_FRAME = std::numeric_limits<real>::min();
+
+/*! \libinternal \brief
+ * Represents a single frame in AnalysisDataTestInput structure.
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataTestInputFrame
+{
+    public:
+        AnalysisDataTestInputFrame();
+
+        int index() const { return index_; }
+        real x() const { return x_; }
+        real dx() const { return 0.0; }
+        real y(int i) const { return y_[i]; }
+        const std::vector<real> &yvector() const { return y_; }
+        const real *yptr() const { return &y_[0]; }
+        const real *dyptr() const { return NULL; }
+        const bool *presentptr() const { return NULL; }
+
+    private:
+        int                     index_;
+        real                    x_;
+        std::vector<real>       y_;
+
+        friend class AnalysisDataTestInput;
+};
+
+/*! \libinternal \brief
+ * Represents static input data for AbstractAnalysisData tests.
+ *
+ * Used to construct structured test input data from a static array of reals,
+ * and then typically used as input to methods in AnalysisDataTestFixture.
+ *
+ * \see AnalysisDataTestFixture
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataTestInput
+{
+    public:
+        /*! \brief
+         * Constructs data representation from a simple array.
+         *
+         * \param[in] data  Array to construct data from.
+         *
+         * The input array should consist of a set of frames, separated by a
+         * END_OF_FRAME marker.  The first value for a frame is the X value,
+         * all following values are Y values.
+         * The final element in the array (after the last END_OF_FRAME) should
+         * be END_OF_DATA.
+         */
+        explicit AnalysisDataTestInput(const real *data);
+        ~AnalysisDataTestInput();
+
+        int frameCount() const { return frames_.size(); }
+        int columnCount() const { return columnCount_; }
+        const AnalysisDataTestInputFrame &frame(int index) const;
+
+    private:
+        int                     columnCount_;
+        std::vector<AnalysisDataTestInputFrame> frames_;
+};
+
+/*! \libinternal \brief
+ * Test fixture for AbstractAnalysisData testing.
+ *
+ * This test fixture is designed to help writing tests for objects that
+ * derive from the AbstractAnalysisData class.  Typical flow in such tests is
+ * that first the test creates the required data objects, then call static
+ * methods in this class to add mock modules (using
+ * AbstractAnalysisData::addModule()) to the data objects to check that they
+ * produce the correct data, and then invokes methods in the data object to
+ * produce the data to be checked.  Static methods are also provided for
+ * pushing data from an AnalysisDataTestInput object to some generic types
+ * derived from AbstractAnalysisData.
+ *
+ * Methods addStaticCheckerModule(), addStaticColumnCheckerModule() and
+ * addStaticStorageCheckerModule() create and add mock modules that check the
+ * data against a given AnalysisDataTestInput instance.
+ *
+ * Method addReferenceCheckerModule() creates and adds a mock module that
+ * checks the output against reference data produced by a previous test
+ * execution (see TestReferenceData).  Two versions are provided, a static
+ * method to be used with any TestReferenceChecker, and a non-static method
+ * that uses the protected \p data_ member.
+ *
+ * presentAllData() and presentDataFrame() are provided to push data from an
+ * AnalysisDataTestInput into an AnalysisData object.  In typical tests, most
+ * checks are done during the these methods, by the added mock modules.
+ *
+ * \todo
+ * Support for errors and for multipoint data.
+ *
+ * \see AnalysisDataTestInput
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataTestFixture : public ::testing::Test
+{
+    public:
+        AnalysisDataTestFixture();
+
+        /*! \brief
+         * Adds all data from AnalysisDataTestInput into an AnalysisData.
+         */
+        static void presentAllData(const AnalysisDataTestInput &input,
+                                   AnalysisData *data);
+        /*! \brief
+         * Adds a single frame from AnalysisDataTestInput into an AnalysisData.
+         */
+        static void presentDataFrame(const AnalysisDataTestInput &input, int row,
+                                     AnalysisDataHandle *handle);
+
+        /*! \brief
+         * Adds a mock module that verifies output against
+         * AnalysisDataTestInput.
+         *
+         * \param[in]  data     Data to compare against.
+         * \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 \p data.
+         * Adds the created module to \p source using \p data->addModule().
+         * Any exceptions from the called functions should be caught by the
+         * caller.
+         *
+         * \see AbstractAnalysisData::addModule()
+         */
+        static void addStaticCheckerModule(const AnalysisDataTestInput &data,
+                                           AbstractAnalysisData *source);
+        /*! \brief
+         * Adds a column mock module that verifies output against
+         * AnalysisDataTestInput.
+         *
+         * \param[in]  data     Data to compare against.
+         * \param[in]  firstcol First column to check.
+         * \param[in]  n        Number of columns to check.
+         * \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 \p data.
+         * Adds the created module to \p source using
+         * \p data->addColumnModule().
+         * Any exceptions from the called functions should be caught by the
+         * caller.
+         *
+         * \see AbstractAnalysisData::addColumnModule()
+         */
+        static void addStaticColumnCheckerModule(const AnalysisDataTestInput &data,
+                                                 int firstcol, int n,
+                                                 AbstractAnalysisData *source);
+        /*! \brief
+         * Adds a mock module that verifies output and storage against
+         * AnalysisDataTestInput.
+         *
+         * \param[in]  data     Data to compare against.
+         * \param[in]  storageCount  Number of previous frames to check
+         *                      (-1 = all).
+         * \param      source   Data object to verify.
+         *
+         * Works like addStaticCheckerModule(), except that in addition, for
+         * each frame, the mock module also checks that previous frames can be
+         * accessed using AbstractAnalysisData::getDataWErr().  In the
+         * AnalysisDataModuleInterface::dataStarted() callback, the mock module
+         * calls AbstractAnalysisData::requestStorage() with \p storageCount as
+         * the parameter.
+         */
+        static void addStaticStorageCheckerModule(const AnalysisDataTestInput &data,
+                                                  int storageCount,
+                                                  AbstractAnalysisData *source);
+        /*! \brief
+         * Adds a mock module that verifies output against reference data.
+         *
+         * \param[in]  checker  Reference data checker to use for comparison.
+         * \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.
+         * 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,
+                                              AbstractAnalysisData *source);
+
+        /*! \brief
+         * Adds a mock module that verifies output against reference data.
+         *
+         * \param[in]  id       Identifier for reference data compound to use.
+         * \param      source   Data object to verify.
+         *
+         * Creates a reference checker module using a compound checker with id
+         * \p id at the root level of \p data_.
+         *
+         * See the static overload for other details.
+         */
+        void addReferenceCheckerModule(const char *id,
+                                       AbstractAnalysisData *source);
+
+    protected:
+        gmx::test::TestReferenceData  data_;
+};
+
+} // namespace test
+
+} // namespace gmx
+
+#endif
diff --git a/src/gromacs/analysisdata/tests/mock_module-impl.h b/src/gromacs/analysisdata/tests/mock_module-impl.h
new file mode 100644 (file)
index 0000000..e5aa92f
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *
+ *                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 private implementation class for gmx::test::MockAnalysisModule.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_TESTS_MOCK_MODULE_IMPL_H
+#define GMX_ANALYSISDATA_TESTS_MOCK_MODULE_IMPL_H
+
+#include "mock_module.h"
+
+#include <memory>
+
+namespace gmx
+{
+namespace test
+{
+
+/*! \internal \brief
+ * Private implementation class for gmx::test::MockAnalysisModule.
+ *
+ * \ingroup module_analysisdata
+ */
+class MockAnalysisModule::Impl
+{
+    public:
+        explicit Impl(int flags);
+
+        void checkReferencePoints(real x, real dx, int firstcol, int n,
+                                  const real *y, const real *dy,
+                                  const bool *present);
+
+        // Could be scoped_ptr
+        std::auto_ptr<TestReferenceChecker>  checker_;
+        int                     flags_;
+        int                     frameIndex_;
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/gromacs/analysisdata/tests/mock_module.cpp b/src/gromacs/analysisdata/tests/mock_module.cpp
new file mode 100644 (file)
index 0000000..5e8c6c8
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ *
+ *                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 mock_module.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include "mock_module.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "gromacs/analysisdata/analysisdata.h"
+#include "gromacs/fatalerror/gmxassert.h"
+#include "gromacs/utility/format.h"
+
+#include "testutils/refdata.h"
+
+#include "datatest.h"
+#include "mock_module-impl.h"
+
+namespace gmx
+{
+namespace test
+{
+
+/********************************************************************
+ * MockAnalysisModule::Impl
+ */
+
+MockAnalysisModule::Impl::Impl(int flags)
+    : flags_(flags), frameIndex_(0)
+{
+}
+
+
+void
+MockAnalysisModule::Impl::checkReferencePoints(real x, real dx, int firstcol, int n,
+                                               const real *y, const real *dy,
+                                               const bool *present)
+{
+    TestReferenceChecker frame(
+        checker_->checkCompound("DataFrame", formatString("Frame%d", frameIndex_).c_str()));
+    ++frameIndex_;
+    frame.checkReal(x, "X");
+    frame.checkSequenceArray(n, y, "Y");
+}
+
+
+/********************************************************************
+ * MockAnalysisModule
+ */
+
+namespace
+{
+
+void checkFrame(real x, real dx, int firstcol, int n, const real *y,
+                const AnalysisDataTestInputFrame &frame)
+{
+    EXPECT_FLOAT_EQ(frame.x(), x);
+    EXPECT_FLOAT_EQ(frame.dx(), dx);
+    for (int i = 0; i < n; ++i)
+    {
+        EXPECT_FLOAT_EQ(frame.y(firstcol + i), y[i]);
+    }
+}
+
+class StaticDataPointsChecker
+{
+    public:
+        StaticDataPointsChecker(const AnalysisDataTestInputFrame *frame,
+                                int firstcol, int n)
+            : frame_(frame), firstcol_(firstcol), n_(n)
+        {
+        }
+
+        void operator()(real x, real dx, int firstcol, int n,
+                        const real *y, const real *dy,
+                        const bool *present) const
+        {
+            SCOPED_TRACE(formatString("Frame %d", frame_->index()));
+            EXPECT_EQ(0, firstcol);
+            EXPECT_EQ(n_, n);
+            checkFrame(x, dx, firstcol_ + firstcol, n, y, *frame_);
+        }
+
+    private:
+        const AnalysisDataTestInputFrame *frame_;
+        int                     firstcol_;
+        int                     n_;
+};
+
+
+class DataStorageRequester
+{
+    public:
+        explicit DataStorageRequester(int count) : count_(count) {}
+
+        void operator()(AbstractAnalysisData *data) const
+        {
+            data->requestStorage(count_);
+        }
+
+    private:
+        int                     count_;
+};
+
+
+class StaticDataPointsStorageChecker
+{
+    public:
+        StaticDataPointsStorageChecker(AbstractAnalysisData *source,
+                                       const AnalysisDataTestInput *data,
+                                       int frameIndex, int storageCount)
+            : source_(source), data_(data),
+              frameIndex_(frameIndex), storageCount_(storageCount)
+        {
+        }
+
+        void operator()(real x, real dx, int firstcol, int n,
+                        const real *y, const real *dy,
+                        const bool *present) const
+        {
+            SCOPED_TRACE(formatString("Frame %d", frameIndex_));
+            EXPECT_EQ(0, firstcol);
+            EXPECT_EQ(data_->columnCount(), n);
+            checkFrame(x, dx, firstcol, n, y, data_->frame(frameIndex_));
+            for (int past = 0;
+                 (storageCount_ < 0 || past <= storageCount_) && past <= frameIndex_;
+                 ++past)
+            {
+                int   index = frameIndex_ - past;
+                real  pastx, pastdx;
+                const real *pasty;
+                SCOPED_TRACE(formatString("Checking storage of frame %d", index));
+                ASSERT_TRUE(source_->getDataWErr(index,
+                                                 &pastx, &pastdx, &pasty, NULL));
+                checkFrame(pastx, pastdx, 0, data_->columnCount(), pasty,
+                           data_->frame(index));
+                if (past > 0)
+                {
+                    ASSERT_TRUE(source_->getDataWErr(-past,
+                                                     &pastx, &pastdx, &pasty, NULL));
+                    checkFrame(pastx, pastdx, 0, data_->columnCount(), pasty,
+                               data_->frame(index));
+                }
+            }
+        }
+
+    private:
+        AbstractAnalysisData   *source_;
+        const AnalysisDataTestInput *data_;
+        int                     frameIndex_;
+        int                     storageCount_;
+};
+
+} // anonymous namespace
+
+
+MockAnalysisModule::MockAnalysisModule(int flags)
+    : impl_(new Impl(flags))
+{
+}
+
+
+MockAnalysisModule::~MockAnalysisModule()
+{
+    delete impl_;
+}
+
+
+int MockAnalysisModule::flags() const
+{
+    return impl_->flags_;
+}
+
+
+void
+MockAnalysisModule::setupStaticCheck(const AnalysisDataTestInput &data,
+                                     AbstractAnalysisData *source)
+{
+    GMX_RELEASE_ASSERT(data.columnCount() == source->columnCount(),
+                       "Mismatching data column count");
+
+    ::testing::InSequence dummy;
+    using ::testing::_;
+    using ::testing::Invoke;
+
+    EXPECT_CALL(*this, dataStarted(source));
+    for (int row = 0; row < data.frameCount(); ++row)
+    {
+        const AnalysisDataTestInputFrame &frame = data.frame(row);
+        EXPECT_CALL(*this, frameStarted(frame.x(), frame.dx()));
+        EXPECT_CALL(*this, pointsAdded(frame.x(), frame.dx(), 0,
+                                       data.columnCount(), _, _, _))
+            .WillOnce(Invoke(StaticDataPointsChecker(&frame, 0, data.columnCount())));
+        EXPECT_CALL(*this, frameFinished());
+    }
+    EXPECT_CALL(*this, dataFinished());
+}
+
+
+void
+MockAnalysisModule::setupStaticColumnCheck(const AnalysisDataTestInput &data,
+                                           int firstcol, int n,
+                                           AbstractAnalysisData *source)
+{
+    GMX_RELEASE_ASSERT(data.columnCount() == source->columnCount(),
+                       "Mismatching data column count");
+    GMX_RELEASE_ASSERT(firstcol >= 0 && n > 0 && firstcol + n <= data.columnCount(),
+                       "Out-of-range columns");
+
+    ::testing::InSequence dummy;
+    using ::testing::_;
+    using ::testing::Invoke;
+
+    EXPECT_CALL(*this, dataStarted(_));
+    for (int row = 0; row < data.frameCount(); ++row)
+    {
+        const AnalysisDataTestInputFrame &frame = data.frame(row);
+        EXPECT_CALL(*this, frameStarted(frame.x(), frame.dx()));
+        EXPECT_CALL(*this, pointsAdded(frame.x(), frame.dx(), 0, n, _, _, _))
+            .WillOnce(Invoke(StaticDataPointsChecker(&frame, firstcol, n)));
+        EXPECT_CALL(*this, frameFinished());
+    }
+    EXPECT_CALL(*this, dataFinished());
+}
+
+
+void
+MockAnalysisModule::setupStaticStorageCheck(const AnalysisDataTestInput &data,
+                                            int storageCount,
+                                            AbstractAnalysisData *source)
+{
+    GMX_RELEASE_ASSERT(data.columnCount() == source->columnCount(),
+                       "Mismatching data column count");
+
+    ::testing::InSequence dummy;
+    using ::testing::_;
+    using ::testing::Invoke;
+
+    EXPECT_CALL(*this, dataStarted(source))
+        .WillOnce(Invoke(DataStorageRequester(storageCount)));
+    for (int row = 0; row < data.frameCount(); ++row)
+    {
+        const AnalysisDataTestInputFrame &frame = data.frame(row);
+        EXPECT_CALL(*this, frameStarted(frame.x(), frame.dx()));
+        EXPECT_CALL(*this, pointsAdded(frame.x(), frame.dx(), 0,
+                                       data.columnCount(), _, _, _))
+            .WillOnce(Invoke(StaticDataPointsStorageChecker(source, &data, row,
+                                                            storageCount)));
+        EXPECT_CALL(*this, frameFinished());
+    }
+    EXPECT_CALL(*this, dataFinished());
+}
+
+
+void
+MockAnalysisModule::setupReferenceCheck(const TestReferenceChecker &checker,
+                                        AbstractAnalysisData *source)
+{
+    impl_->checker_.reset(new TestReferenceChecker(checker));
+    // Google Mock does not support checking the order fully, because
+    // the number of frames is not known.
+    using ::testing::_;
+    using ::testing::AnyNumber;
+    using ::testing::Expectation;
+    using ::testing::Invoke;
+
+    Expectation dataStart = EXPECT_CALL(*this, dataStarted(source));
+    Expectation frameStart = EXPECT_CALL(*this, frameStarted(_, _))
+        .Times(AnyNumber())
+        .After(dataStart);
+    Expectation pointsAdd = EXPECT_CALL(*this, pointsAdded(_, _, _, _, _, _, _))
+        .After(dataStart)
+        .WillRepeatedly(Invoke(impl_, &Impl::checkReferencePoints));
+    Expectation frameFinish = EXPECT_CALL(*this, frameFinished())
+        .Times(AnyNumber())
+        .After(dataStart);
+    EXPECT_CALL(*this, dataFinished())
+        .After(frameStart, pointsAdd, frameFinish);
+}
+
+} // namespace test
+} // namespace gmx
index 7d5062e755503af73c43f1af0ed53c8ee6eb3c86..ec5afe7b163ee859563ba304d479c48b3ed31802 100644 (file)
 
 #include "gromacs/analysisdata/datamodule.h"
 
-class MockModule : public gmx::AnalysisDataModuleInterface
+namespace gmx
+{
+namespace test
+{
+
+class AnalysisDataTestInput;
+class TestReferenceChecker;
+
+class MockAnalysisModule : public AnalysisDataModuleInterface
 {
     public:
-        explicit MockModule(int flags) : _flags(flags) {}
+        explicit MockAnalysisModule(int flags);
+        virtual ~MockAnalysisModule();
 
-        int flags() const { return _flags; }
+        virtual int flags() const;
 
-        MOCK_METHOD1(dataStarted, void(gmx::AbstractAnalysisData *data));
+        MOCK_METHOD1(dataStarted, void(AbstractAnalysisData *data));
         MOCK_METHOD2(frameStarted, void(real x, real dx));
         MOCK_METHOD7(pointsAdded, void(real x, real dx, int firstcol, int n,
                                        const real *y, const real *dy,
@@ -59,8 +68,24 @@ class MockModule : public gmx::AnalysisDataModuleInterface
         MOCK_METHOD0(frameFinished, void());
         MOCK_METHOD0(dataFinished, void());
 
+        void setupStaticCheck(const AnalysisDataTestInput &data,
+                              AbstractAnalysisData *source);
+        void setupStaticColumnCheck(const AnalysisDataTestInput &data,
+                                    int firstcol, int n,
+                                    AbstractAnalysisData *source);
+        void setupStaticStorageCheck(const AnalysisDataTestInput &data,
+                                     int storageCount,
+                                     AbstractAnalysisData *source);
+        void setupReferenceCheck(const TestReferenceChecker &checker,
+                                 AbstractAnalysisData *source);
+
     private:
-        int         _flags;
+        class Impl;
+
+        Impl                   *impl_;
 };
 
+} // namespace test
+} // namespace gmx
+
 #endif
diff --git a/src/gromacs/analysisdata/tests/refdata/AverageModuleTest_BasicTest.xml b/src/gromacs/analysisdata/tests/refdata/AverageModuleTest_BasicTest.xml
new file mode 100644 (file)
index 0000000..aea230c
--- /dev/null
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<ReferenceData>
+  <Compound Name="InputData" Subtype="AnalysisData">
+    <Compound Name="Frame0" Subtype="DataFrame">
+      <Real Name="X">1.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">3</Int>
+        <Real>0.000000</Real>
+        <Real>1.000000</Real>
+        <Real>2.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame1" Subtype="DataFrame">
+      <Real Name="X">2.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">3</Int>
+        <Real>1.000000</Real>
+        <Real>1.000000</Real>
+        <Real>1.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame2" Subtype="DataFrame">
+      <Real Name="X">3.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">3</Int>
+        <Real>2.000000</Real>
+        <Real>0.000000</Real>
+        <Real>0.000000</Real>
+      </Compound>
+    </Compound>
+  </Compound>
+  <Compound Name="Average" Subtype="AnalysisData">
+    <Compound Name="Frame0" Subtype="DataFrame">
+      <Real Name="X">0.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>1.000000</Real>
+        <Real>0.816497</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame1" Subtype="DataFrame">
+      <Real Name="X">1.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>0.666667</Real>
+        <Real>0.471404</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame2" Subtype="DataFrame">
+      <Real Name="X">2.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>1.000000</Real>
+        <Real>0.816497</Real>
+      </Compound>
+    </Compound>
+  </Compound>
+</ReferenceData>
diff --git a/src/gromacs/analysisdata/tests/refdata/AverageModuleTest_CanCustomizeXAxis.xml b/src/gromacs/analysisdata/tests/refdata/AverageModuleTest_CanCustomizeXAxis.xml
new file mode 100644 (file)
index 0000000..46260f9
--- /dev/null
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<ReferenceData>
+  <Compound Name="InputData" Subtype="AnalysisData">
+    <Compound Name="Frame0" Subtype="DataFrame">
+      <Real Name="X">1.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">3</Int>
+        <Real>0.000000</Real>
+        <Real>1.000000</Real>
+        <Real>2.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame1" Subtype="DataFrame">
+      <Real Name="X">2.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">3</Int>
+        <Real>1.000000</Real>
+        <Real>1.000000</Real>
+        <Real>1.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame2" Subtype="DataFrame">
+      <Real Name="X">3.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">3</Int>
+        <Real>2.000000</Real>
+        <Real>0.000000</Real>
+        <Real>0.000000</Real>
+      </Compound>
+    </Compound>
+  </Compound>
+  <Compound Name="Average" Subtype="AnalysisData">
+    <Compound Name="Frame0" Subtype="DataFrame">
+      <Real Name="X">0.500000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>1.000000</Real>
+        <Real>0.816497</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame1" Subtype="DataFrame">
+      <Real Name="X">1.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>0.666667</Real>
+        <Real>0.471404</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame2" Subtype="DataFrame">
+      <Real Name="X">1.500000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>1.000000</Real>
+        <Real>0.816497</Real>
+      </Compound>
+    </Compound>
+  </Compound>
+</ReferenceData>