3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
9 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11 * Copyright (c) 2001-2009, The GROMACS development team,
12 * check out http://www.gromacs.org for more information.
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * If you want to redistribute modifications, please consider that
20 * scientific software is very special. Version control is crucial -
21 * bugs must be traceable. We will be happy to consider code for
22 * inclusion in the official distribution, but derived work must not
23 * be called official GROMACS. Details are found in the README & COPYING
24 * files - if they are missing, get the official version at www.gromacs.org.
26 * To help us fund GROMACS development, we humbly ask that you cite
27 * the papers on the package - you can find them in the top README file.
29 * For more info, check our website at http://www.gromacs.org
31 /*! \libinternal \file
33 * Helper classes for testing classes that derive from AbstractAnalysisData.
35 * \author Teemu Murtola <teemu.murtola@cbr.su.se>
37 * \ingroup module_analysisdata
39 #ifndef GMX_ANALYSISDATA_TESTS_DATATEST_H
40 #define GMX_ANALYSISDATA_TESTS_DATATEST_H
45 #include <gtest/gtest.h>
47 #include "gromacs/legacyheaders/types/simple.h"
49 #include "gromacs/utility/gmxassert.h"
51 #include "testutils/refdata.h"
56 class AbstractAnalysisData;
58 class AnalysisDataHandle;
63 class MockAnalysisModule;
65 //! Constant to use to signify end of data for AnalysisDataTestInput.
66 const real END_OF_DATA = std::numeric_limits<real>::max();
67 //! Constant to use to signify end of one data frame for AnalysisDataTestInput.
68 const real END_OF_FRAME = std::numeric_limits<real>::min();
69 //! Constant to use to signify end of one multipoint set for AnalysisDataTestInput.
70 const real MPSTOP = -std::numeric_limits<real>::max();
72 /*! \libinternal \brief
73 * Represents a single set of points in AnalysisDataTestInputFrame structure.
75 * If the data is not multipoint, each frame contains exactly one set of
76 * points. If there is more than one set of points, each of these sets is
77 * passed separately and AnalysisDataHandle::finishPointSet() called in
80 * \ingroup module_analysisdata
82 class AnalysisDataTestInputPointSet
85 //! Returns the number of columns in the point set.
86 int size() const { return y_.size(); }
87 //! Returns the value in column \p i.
88 real y(int i) const { return y_[i]; }
89 //! Returns the error in column \p i.
90 real dy(int i) const { return 0.0; }
91 //! Returns whether the value in column \p i is present.
92 real present(int i) const { return true; }
93 //! Returns a vector of values for all columns.
94 const std::vector<real> &yvector() const { return y_; }
97 //! Creates an empty point set.
98 AnalysisDataTestInputPointSet();
100 std::vector<real> y_;
102 friend class AnalysisDataTestInput;
105 /*! \libinternal \brief
106 * Represents a single frame in AnalysisDataTestInput structure.
108 * \ingroup module_analysisdata
110 class AnalysisDataTestInputFrame
113 //! Returns zero-based index for the frame.
114 int index() const { return index_; }
115 //! Returns x coordinate for the frame.
116 real x() const { return x_; }
117 //! Returns error in the x coordinate for the frame.
118 real dx() const { return 0.0; }
120 //! Number of individual point sets in the frame.
121 int pointSetCount() const { return points_.size(); }
122 //! Returns a point set object for a given point set.
123 const AnalysisDataTestInputPointSet &points(int index = 0) const
125 GMX_ASSERT(index >= 0 && static_cast<size_t>(index) < points_.size(),
126 "Point set index out of range");
127 return points_[index];
131 //! Constructs a new frame object with the given values.
132 AnalysisDataTestInputFrame(int index, real x);
136 std::vector<AnalysisDataTestInputPointSet> points_;
138 friend class AnalysisDataTestInput;
141 /*! \libinternal \brief
142 * Represents static input data for AbstractAnalysisData tests.
144 * Used to construct structured test input data from a static array of reals,
145 * and then typically used as input to methods in AnalysisDataTestFixture.
147 * \see AnalysisDataTestFixture
149 * \ingroup module_analysisdata
151 class AnalysisDataTestInput
155 * Constructs data representation from a simple array.
157 * \param[in] data Array to construct data from.
159 * The input array should consist of a set of frames, separated by a
160 * END_OF_FRAME marker. The first value for a frame is the X value,
161 * all following values are Y values.
162 * For multipoint data, one frame can contain several point sets,
163 * separated by MPSTOP markers. There should be no MPSTOP marker after
164 * the last point set, only an END_OF_FRAME marker. All point sets are
165 * assumed to start from column zero, but the sets may contain
166 * different number of columns. For non-multipoint data, all frames
167 * must containt the same number of columns.
168 * The final element in the array (after the last END_OF_FRAME) should
171 explicit AnalysisDataTestInput(const real *data);
172 ~AnalysisDataTestInput();
174 //! Returns the number of frames in the input data.
175 int frameCount() const { return frames_.size(); }
176 //! Returns the number of columns in the input data.
177 int columnCount() const { return columnCount_; }
178 //! Whether the input data is multipoint.
179 bool isMultipoint() const { return bMultipoint_; }
180 //! Returns a frame object for the given input frame.
181 const AnalysisDataTestInputFrame &frame(int index) const;
186 std::vector<AnalysisDataTestInputFrame> frames_;
189 /*! \libinternal \brief
190 * Test fixture for AbstractAnalysisData testing.
192 * This test fixture is designed to help writing tests for objects that
193 * derive from the AbstractAnalysisData class. Typical flow in such tests is
194 * that first the test creates the required data objects, then call static
195 * methods in this class to add mock modules (using
196 * AbstractAnalysisData::addModule()) to the data objects to check that they
197 * produce the correct data, and then invokes methods in the data object to
198 * produce the data to be checked. Static methods are also provided for
199 * pushing data from an AnalysisDataTestInput object to some generic types
200 * derived from AbstractAnalysisData.
202 * Methods addStaticCheckerModule(), addStaticColumnCheckerModule() and
203 * addStaticStorageCheckerModule() create and add mock modules that check the
204 * data against a given AnalysisDataTestInput instance.
206 * Method addReferenceCheckerModule() creates and adds a mock module that
207 * checks the output against reference data produced by a previous test
208 * execution (see TestReferenceData). Two versions are provided, a static
209 * method to be used with any TestReferenceChecker, and a non-static method
210 * that uses the protected \p data_ member.
212 * presentAllData() and presentDataFrame() are provided to push data from an
213 * AnalysisDataTestInput into an AnalysisData object. In typical tests, most
214 * checks are done during the these methods, by the added mock modules.
215 * setupArrayData() performs the same function for classes derived from
216 * AbstractAnalysisArrayData. In that case, the test should separately ensure
217 * that AbstractAnalysisArrayData::valuesReady() gets called.
220 * Support for errors and for arbitrary multipoint data.
222 * \see AnalysisDataTestInput
224 * \ingroup module_analysisdata
226 class AnalysisDataTestFixture : public ::testing::Test
229 AnalysisDataTestFixture();
232 * Adds all data from AnalysisDataTestInput into an AnalysisData.
234 static void presentAllData(const AnalysisDataTestInput &input,
237 * Adds a single frame from AnalysisDataTestInput into an AnalysisData.
239 static void presentDataFrame(const AnalysisDataTestInput &input, int row,
240 AnalysisDataHandle handle);
242 * Initializes an array data object from AnalysisDataTestInput.
244 * \tparam ArrayData Class derived from AbstractAnalysisArrayData.
246 * The ArrayData class should expose the setter methods
247 * (setColumnCount(), setRowCount(), allocateValues(), setValue())
248 * publicly or declare the fixture class as a friend.
249 * The X axis in \p data must be configured to match \p input before
250 * calling this method.
252 * Does not call AbstractAnalysisArrayData::valuesReady().
253 * The test must ensure that this method gets called, otherwise the
254 * mock modules never get called.
256 template <class ArrayData>
257 static void setupArrayData(const AnalysisDataTestInput &input,
261 * Adds a mock module that verifies output against
262 * AnalysisDataTestInput.
264 * \param[in] data Data to compare against.
265 * \param source Data object to verify.
267 * Creates a mock module that verifies that the
268 * AnalysisDataModuleInterface methods are called correctly by
269 * \p source. Parameters for the calls are verified against \p data.
270 * Adds the created module to \p source using \p data->addModule().
271 * Any exceptions from the called functions should be caught by the
274 * \see AbstractAnalysisData::addModule()
276 static void addStaticCheckerModule(const AnalysisDataTestInput &data,
277 AbstractAnalysisData *source);
279 * Adds a column mock module that verifies output against
280 * AnalysisDataTestInput.
282 * \param[in] data Data to compare against.
283 * \param[in] firstcol First column to check.
284 * \param[in] n Number of columns to check.
285 * \param source Data object to verify.
287 * Creates a mock module that verifies that the
288 * AnalysisDataModuleInterface methods are called correctly by
289 * \p source. Parameters for the calls are verified against \p data.
290 * Adds the created module to \p source using
291 * \p data->addColumnModule().
292 * Any exceptions from the called functions should be caught by the
295 * \see AbstractAnalysisData::addColumnModule()
297 static void addStaticColumnCheckerModule(const AnalysisDataTestInput &data,
299 AbstractAnalysisData *source);
301 * Adds a mock module that verifies output and storage against
302 * AnalysisDataTestInput.
304 * \param[in] data Data to compare against.
305 * \param[in] storageCount Number of previous frames to check
307 * \param source Data object to verify.
309 * Works like addStaticCheckerModule(), except that in addition, for
310 * each frame, the mock module also checks that previous frames can be
311 * accessed using AbstractAnalysisData::getDataFrame(). In the
312 * AnalysisDataModuleInterface::dataStarted() callback, the mock module
313 * calls AbstractAnalysisData::requestStorage() with \p storageCount as
316 static void addStaticStorageCheckerModule(const AnalysisDataTestInput &data,
318 AbstractAnalysisData *source);
320 * Adds a mock module that verifies output against reference data.
322 * \param[in] checker Reference data checker to use for comparison.
323 * \param source Data object to verify.
325 * Creates a mock module that verifies that the
326 * AnalysisDataModuleInterface methods are called correctly by
327 * \p source. Parameters for the calls are verified against reference
328 * data using \p checker.
329 * Adds the created module to \p source using \p data->addModule().
330 * Any exceptions from the called functions should be caught by the
333 * \see TestReferenceData
335 static void addReferenceCheckerModule(const TestReferenceChecker &checker,
336 AbstractAnalysisData *source);
339 * Adds a mock module that verifies output against reference data.
341 * \param[in] id Identifier for reference data compound to use.
342 * \param source Data object to verify.
344 * Creates a reference checker module using a compound checker with id
345 * \p id at the root level of \p data_.
347 * See the static overload for other details.
349 void addReferenceCheckerModule(const char *id,
350 AbstractAnalysisData *source);
354 * Reference data object used for the reference checker modules.
356 * Tests can use the data object also for their own purposes if needed.
358 gmx::test::TestReferenceData data_;
362 template <class ArrayData>
363 void AnalysisDataTestFixture::setupArrayData(const AnalysisDataTestInput &input,
366 GMX_RELEASE_ASSERT(!input.isMultipoint(),
367 "Array data cannot be initialized from multipoint data");
368 GMX_RELEASE_ASSERT(data->columnCount() == 0 || data->columnCount() == input.columnCount(),
369 "Mismatching input and target data");
370 GMX_RELEASE_ASSERT(data->rowCount() == 0 || data->rowCount() == input.frameCount(),
371 "Mismatching input and target data");
372 data->setColumnCount(input.columnCount());
373 data->setRowCount(input.frameCount());
374 data->allocateValues();
375 for (int row = 0; row < input.frameCount(); ++row)
377 const AnalysisDataTestInputFrame &frame = input.frame(row);
378 EXPECT_FLOAT_EQ(frame.x(), data->xvalue(row));
379 const AnalysisDataTestInputPointSet &points = frame.points();
380 for (int column = 0; column < input.columnCount(); ++column)
382 data->setValue(row, column, points.y(column));