{
return true;
}
- _impl->_nalloc = nframes;
+ // nframes previous frames plus the current one
+ _impl->_nalloc = nframes + 1;
return true;
}
-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 ()
* \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
{
* Tests for gmx::AnalysisData.
*/
-TEST(AnalysisDataTest, BasicInitialization)
+/*
+ * Tests that simple initialization works.
+ */
+TEST(AnalysisDataInitializationTest, BasicInitialization)
{
gmx::AnalysisData data;
EXPECT_EQ(0, data.columnCount());
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
--- /dev/null
+/*
+ *
+ * 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
--- /dev/null
+/*
+ *
+ * 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
--- /dev/null
+/*
+ *
+ * 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
--- /dev/null
+/*
+ *
+ * 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
--- /dev/null
+/*
+ *
+ * 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
#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,
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
--- /dev/null
+<?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>
--- /dev/null
+<?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>