2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
5 * Copyright (c) 2017,2019,2020,2021, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
38 * Implements classes in mock_datamodule.h.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_analysisdata
45 #include "mock_datamodule.h"
47 #include <gmock/gmock.h>
48 #include <gtest/gtest.h>
50 #include "gromacs/analysisdata/analysisdata.h"
51 #include "gromacs/analysisdata/dataframe.h"
52 #include "gromacs/utility/gmxassert.h"
53 #include "gromacs/utility/stringutil.h"
55 #include "gromacs/analysisdata/tests/datatest.h"
56 #include "testutils/refdata.h"
57 #include "testutils/testasserts.h"
64 /********************************************************************
65 * MockAnalysisDataModule::Impl
69 * Private implementation class for gmx::test::MockAnalysisDataModule.
71 * \ingroup module_analysisdata
73 class MockAnalysisDataModule::Impl
76 //! Initializes a mock object with the given flags.
77 explicit Impl(int flags);
80 * Callback used to initialize reference data checks
82 * Called in response to dataStarted().
83 * Records the source data for later use (for access to data properties).
85 void startReferenceData(AbstractAnalysisData* data);
87 * Callback used to check frame start against reference data.
89 * Called to check parameters and order of calls to frameStarted().
90 * In addition to reference data checks, this method checks statically
91 * that the new frame matches \a frameIndex_.
93 void startReferenceFrame(const AnalysisDataFrameHeader& header);
95 * Callback used to check frame points against reference data.
97 * Called to check parameters and order of calls to pointsAdded().
99 void checkReferencePoints(const AnalysisDataPointSetRef& points);
101 * Callback used to check frame finish against reference data.
103 * Called to check parameters and order of calls to frameFinished().
105 void finishReferenceFrame(const AnalysisDataFrameHeader& header);
107 * Callback used to check serial frame finish with reference data.
109 * Called to check parameters and order of calls to
110 * frameFinishedSerial().
111 * \a frameIndex_ is incremented here.
113 void finishReferenceFrameSerial(int frameIndex);
116 * Reference data checker to use for checking frames.
118 * Must be initialized if startReferenceFrame() is called.
120 TestReferenceChecker rootChecker_;
122 * Reference data checker to use to check the current frame.
124 * Initialized between startReferenceFrame() and finishReferenceFrame()
127 TestReferenceChecker frameChecker_;
129 const AbstractAnalysisData* source_;
130 //! Flags that will be returned by the mock module.
132 //! Index of the current/next frame.
140 * Checks a single AnalysisDataValue.
142 * \ingroup module_analysisdata
144 void checkReferenceDataPoint(TestReferenceChecker* checker, const AnalysisDataValue& value)
146 TestReferenceChecker compound(checker->checkCompound("DataValue", nullptr));
147 compound.checkReal(value.value(), "Value");
148 if (compound.checkPresent(value.hasError(), "Error"))
150 compound.checkReal(value.error(), "Error");
152 if (compound.checkPresent(!value.isPresent(), "Present"))
154 compound.checkBoolean(value.isPresent(), "Present");
160 MockAnalysisDataModule::Impl::Impl(int flags) : source_(nullptr), flags_(flags), frameIndex_(0) {}
163 void MockAnalysisDataModule::Impl::startReferenceData(AbstractAnalysisData* data)
169 void MockAnalysisDataModule::Impl::startReferenceFrame(const AnalysisDataFrameHeader& header)
171 GMX_RELEASE_ASSERT(rootChecker_, "Root checker not set, but reference data used");
172 EXPECT_FALSE(frameChecker_.isValid());
173 EXPECT_EQ(frameIndex_, header.index());
175 rootChecker_.checkCompound("DataFrame", formatString("Frame%d", frameIndex_).c_str());
176 frameChecker_.checkReal(header.x(), "X");
180 void MockAnalysisDataModule::Impl::checkReferencePoints(const AnalysisDataPointSetRef& points)
182 EXPECT_TRUE(frameChecker_.isValid());
185 TestReferenceChecker checker(frameChecker_.checkCompound("DataValues", nullptr));
186 checker.checkInteger(points.columnCount(), "Count");
187 if (checker.checkPresent(source_->dataSetCount() > 1, "DataSet"))
189 checker.checkInteger(points.dataSetIndex(), "DataSet");
191 const int sourceColumnCount = source_->columnCount(points.dataSetIndex());
192 const bool bAllColumns = (points.firstColumn() == 0 && points.columnCount() == sourceColumnCount);
193 if (checker.checkPresent(!bAllColumns, "FirstColumn"))
195 checker.checkInteger(points.firstColumn(), "FirstColumn");
196 checker.checkInteger(points.lastColumn(), "LastColumn");
199 AnalysisDataValuesRef::const_iterator value;
200 for (value = points.values().begin(); value != points.values().end(); ++value)
202 checkReferenceDataPoint(&checker, *value);
208 void MockAnalysisDataModule::Impl::finishReferenceFrame(const AnalysisDataFrameHeader& header)
210 EXPECT_TRUE(frameChecker_.isValid());
211 EXPECT_EQ(frameIndex_, header.index());
212 frameChecker_ = TestReferenceChecker();
216 void MockAnalysisDataModule::Impl::finishReferenceFrameSerial(int frameIndex)
218 EXPECT_FALSE(frameChecker_.isValid());
219 EXPECT_EQ(frameIndex_, frameIndex);
224 /********************************************************************
225 * MockAnalysisDataModule
232 * Helper function for checking the data frame header against static data.
234 * \param[in] header Frame header to check.
235 * \param[in] refFrame Data to check against.
237 void checkHeader(const AnalysisDataFrameHeader& header, const AnalysisDataTestInputFrame& refFrame)
239 EXPECT_EQ(refFrame.index(), header.index());
240 EXPECT_FLOAT_EQ(refFrame.x(), header.x());
241 EXPECT_FLOAT_EQ(refFrame.dx(), header.dx());
245 * Helper function for checking a point set against static data.
247 * \param[in] points Point set to check.
248 * \param[in] refPoints Data to check against.
249 * \param[in] columnOffset Offset of first column of \p points in \p refPoints.
251 void checkPoints(const AnalysisDataPointSetRef& points,
252 const AnalysisDataTestInputPointSet& refPoints,
255 for (int i = 0; i < points.columnCount(); ++i)
257 const int column = points.firstColumn() - refPoints.firstColumn() + i + columnOffset;
258 EXPECT_FLOAT_EQ(refPoints.y(column), points.y(i))
259 << " Column: " << i + 1 << " / " << points.columnCount() << " (+"
260 << points.firstColumn() << ")\n"
261 << "Ref. col: " << column + 1 << " / " << refPoints.size() << " (+"
262 << refPoints.firstColumn() << ", offs " << columnOffset << ")";
267 * Helper function for checking a full frame against static data.
269 * \param[in] frame Frame to check.
270 * \param[in] refFrame Data to check against.
272 void checkFrame(const AnalysisDataFrameRef& frame, const AnalysisDataTestInputFrame& refFrame)
274 checkHeader(frame.header(), refFrame);
275 ASSERT_EQ(refFrame.pointSetCount(), frame.pointSetCount());
276 for (int i = 0; i < frame.pointSetCount(); ++i)
278 const AnalysisDataPointSetRef& points = frame.pointSet(i);
279 const AnalysisDataTestInputPointSet& refPoints = refFrame.pointSet(i);
280 EXPECT_EQ(refPoints.firstColumn(), points.firstColumn());
281 checkPoints(points, refPoints, 0);
286 * Functor for checking data frame header against static test input data.
288 * This functor is designed to be invoked as a handled for
289 * IAnalysisDataModule::frameStarted().
291 class StaticDataFrameHeaderChecker
295 * Constructs a checker against a given input data frame.
297 * \param[in] frame Frame to check against.
299 * \p frame must exist for the lifetime of this object.
301 StaticDataFrameHeaderChecker(const AnalysisDataTestInputFrame* frame) : frame_(frame) {}
303 //! Function call operator for the functor.
304 void operator()(const AnalysisDataFrameHeader& header) const
306 SCOPED_TRACE(formatString("Frame %d", frame_->index()));
307 checkHeader(header, *frame_);
311 const AnalysisDataTestInputFrame* frame_;
315 * Functor for checking data frame points against static test input data.
317 * This functor is designed to be invoked as a handled for
318 * IAnalysisDataModule::pointsAdded().
320 class StaticDataPointsChecker
324 * Constructs a checker against a given input data frame and point set.
326 * \param[in] frame Frame to check against.
327 * \param[in] points Point set in \p frame to check against.
328 * \param[in] firstcol Expected first column.
329 * \param[in] n Expected number of columns.
331 * \p firstcol and \p n are used to create a checker that only expects
332 * to be called for a subset of columns.
333 * \p frame and \p points must exist for the lifetime of this object.
335 StaticDataPointsChecker(const AnalysisDataTestInputFrame* frame,
336 const AnalysisDataTestInputPointSet* points,
339 frame_(frame), points_(points), firstcol_(firstcol), n_(n)
343 //! Function call operator for the functor.
344 void operator()(const AnalysisDataPointSetRef& points) const
346 SCOPED_TRACE(formatString("Frame %d, point set %d", frame_->index(), points_->index()));
347 EXPECT_EQ(points_->dataSetIndex(), points.dataSetIndex());
348 const int expectedFirstColumn = std::max(0, points_->firstColumn() - firstcol_);
349 const int expectedLastColumn = std::min(n_ - 1, points_->lastColumn() - firstcol_);
350 EXPECT_EQ(expectedFirstColumn, points.firstColumn());
351 EXPECT_EQ(expectedLastColumn, points.lastColumn());
352 checkHeader(points.header(), *frame_);
353 checkPoints(points, *points_, firstcol_);
357 const AnalysisDataTestInputFrame* frame_;
358 const AnalysisDataTestInputPointSet* points_;
364 * Functor for requesting data storage.
366 * This functor is designed to be invoked as a handled for
367 * IAnalysisDataModule::dataStarted().
369 class DataStorageRequester
373 * Constructs a functor that requests the given amount of storage.
375 * \param[in] count Number of frames of storage to request, or
378 * \see AbstractAnalysisData::requestStorage()
380 explicit DataStorageRequester(int count) : count_(count) {}
382 //! Function call operator for the functor.
383 void operator()(AbstractAnalysisData* data) const { EXPECT_TRUE(data->requestStorage(count_)); }
390 * Functor for checking data frame points and storage against static test input
393 * This functor is designed to be invoked as a handled for
394 * IAnalysisDataModule::pointsAdded().
396 class StaticDataPointsStorageChecker
400 * Constructs a checker for a given frame.
402 * \param[in] source Data object that is being checked.
403 * \param[in] data Test input data to check against.
404 * \param[in] frameIndex Frame index for which this functor expects
406 * \param[in] pointSetIndex Point set for which this functor expects
408 * \param[in] storageCount How many past frames should be checked for
409 * storage (-1 = check all frames).
411 * This checker works as StaticDataPointsChecker, but additionally
412 * checks that previous frames can be accessed using access methods
413 * in AbstractAnalysisData and that correct data is returned.
415 * \p source and \p data must exist for the lifetime of this object.
417 StaticDataPointsStorageChecker(AbstractAnalysisData* source,
418 const AnalysisDataTestInput* data,
424 frameIndex_(frameIndex),
425 pointSetIndex_(pointSetIndex),
426 storageCount_(storageCount)
430 //! Function call operator for the functor.
431 void operator()(const AnalysisDataPointSetRef& points) const
433 SCOPED_TRACE(formatString("Frame %d", frameIndex_));
434 const AnalysisDataTestInputFrame& refFrame = data_->frame(frameIndex_);
435 const AnalysisDataTestInputPointSet& refPoints = refFrame.pointSet(pointSetIndex_);
436 EXPECT_EQ(refPoints.firstColumn(), points.firstColumn());
437 EXPECT_EQ(refPoints.size(), points.columnCount());
438 checkHeader(points.header(), refFrame);
439 checkPoints(points, refPoints, 0);
440 for (int past = 1; (storageCount_ < 0 || past <= storageCount_) && past <= frameIndex_; ++past)
442 int index = frameIndex_ - past;
443 SCOPED_TRACE(formatString("Checking storage of frame %d", index));
444 ASSERT_NO_THROW_GMX({
445 AnalysisDataFrameRef frame = source_->getDataFrame(index);
446 ASSERT_TRUE(frame.isValid());
447 checkFrame(frame, data_->frame(index));
453 AbstractAnalysisData* source_;
454 const AnalysisDataTestInput* data_;
461 * Sets the mock object expectation to mimick AnalysisDataModuleSerial.
463 * Makes MockAnalysisDataModule::parallelDataStarted() behave as if the mock
464 * object was an AnalysisDataModuleSerial object: forward the call to
465 * MockAnalysisDataModule::dataStarted() and return false.
467 void setSerialExpectationForParallelDataStarted(MockAnalysisDataModule* mock)
470 using ::testing::AtMost;
471 using ::testing::DoAll;
472 using ::testing::Invoke;
473 using ::testing::Return;
474 using ::testing::WithArg;
475 EXPECT_CALL(*mock, parallelDataStarted(_, _))
477 .WillOnce(DoAll(WithArg<0>(Invoke(mock, &MockAnalysisDataModule::dataStarted)), Return(false)));
480 } // anonymous namespace
483 MockAnalysisDataModule::MockAnalysisDataModule(int flags) : impl_(new Impl(flags)) {}
486 MockAnalysisDataModule::~MockAnalysisDataModule() {}
489 int MockAnalysisDataModule::flags() const
491 return impl_->flags_;
495 void MockAnalysisDataModule::setupStaticCheck(const AnalysisDataTestInput& data,
496 AbstractAnalysisData* source,
499 impl_->flags_ |= efAllowMulticolumn | efAllowMultipoint | efAllowMultipleDataSets;
502 using ::testing::Invoke;
503 using ::testing::Property;
504 using ::testing::Return;
508 #ifndef __clang_analyzer__
509 ::testing::Expectation init =
510 EXPECT_CALL(*this, parallelDataStarted(source, _)).WillOnce(Return(true));
511 ::testing::ExpectationSet framesFinished;
512 ::testing::Expectation prevFinish;
513 for (int row = 0; row < data.frameCount(); ++row)
515 ::testing::InSequence frameSequence;
516 const AnalysisDataTestInputFrame& frame = data.frame(row);
517 EXPECT_CALL(*this, frameStarted(Property(&AnalysisDataFrameHeader::index, row)))
519 .WillOnce(Invoke(StaticDataFrameHeaderChecker(&frame)));
520 for (int ps = 0; ps < frame.pointSetCount(); ++ps)
522 const AnalysisDataTestInputPointSet& points = frame.pointSet(ps);
523 StaticDataPointsChecker checker(
524 &frame, &points, 0, data.columnCount(points.dataSetIndex()));
525 EXPECT_CALL(*this, pointsAdded(Property(&AnalysisDataPointSetRef::frameIndex, row)))
526 .WillOnce(Invoke(checker));
528 EXPECT_CALL(*this, frameFinished(Property(&AnalysisDataFrameHeader::index, row)))
529 .WillOnce(Invoke(StaticDataFrameHeaderChecker(&frame)));
530 ::testing::Expectation finish;
533 finish = EXPECT_CALL(*this, frameFinishedSerial(row)).After(prevFinish);
537 finish = EXPECT_CALL(*this, frameFinishedSerial(row));
539 framesFinished += finish;
542 EXPECT_CALL(*this, dataFinished()).After(framesFinished);
547 ::testing::InSequence dummy;
548 setSerialExpectationForParallelDataStarted(this);
549 EXPECT_CALL(*this, dataStarted(source));
550 for (int row = 0; row < data.frameCount(); ++row)
552 const AnalysisDataTestInputFrame& frame = data.frame(row);
553 EXPECT_CALL(*this, frameStarted(_)).WillOnce(Invoke(StaticDataFrameHeaderChecker(&frame)));
554 for (int ps = 0; ps < frame.pointSetCount(); ++ps)
556 const AnalysisDataTestInputPointSet& points = frame.pointSet(ps);
557 StaticDataPointsChecker checker(
558 &frame, &points, 0, data.columnCount(points.dataSetIndex()));
559 EXPECT_CALL(*this, pointsAdded(_)).WillOnce(Invoke(checker));
561 EXPECT_CALL(*this, frameFinished(_)).WillOnce(Invoke(StaticDataFrameHeaderChecker(&frame)));
562 EXPECT_CALL(*this, frameFinishedSerial(row));
564 EXPECT_CALL(*this, dataFinished());
569 void MockAnalysisDataModule::setupStaticColumnCheck(const AnalysisDataTestInput& data,
572 AbstractAnalysisData* /*source*/)
574 impl_->flags_ |= efAllowMulticolumn | efAllowMultipoint | efAllowMultipleDataSets;
576 ::testing::InSequence dummy;
578 using ::testing::Invoke;
580 setSerialExpectationForParallelDataStarted(this);
581 EXPECT_CALL(*this, dataStarted(_));
582 for (int row = 0; row < data.frameCount(); ++row)
584 const AnalysisDataTestInputFrame& frame = data.frame(row);
585 EXPECT_CALL(*this, frameStarted(_)).WillOnce(Invoke(StaticDataFrameHeaderChecker(&frame)));
586 for (int ps = 0; ps < frame.pointSetCount(); ++ps)
588 const AnalysisDataTestInputPointSet& points = frame.pointSet(ps);
589 if (points.lastColumn() >= firstcol && points.firstColumn() <= firstcol + n - 1)
591 EXPECT_CALL(*this, pointsAdded(_))
592 .WillOnce(Invoke(StaticDataPointsChecker(&frame, &points, firstcol, n)));
595 EXPECT_CALL(*this, frameFinished(_)).WillOnce(Invoke(StaticDataFrameHeaderChecker(&frame)));
596 EXPECT_CALL(*this, frameFinishedSerial(row));
598 EXPECT_CALL(*this, dataFinished());
602 void MockAnalysisDataModule::setupStaticStorageCheck(const AnalysisDataTestInput& data,
604 AbstractAnalysisData* source)
606 GMX_RELEASE_ASSERT(data.isMultipoint() == source->isMultipoint(),
607 "Mismatching multipoint properties");
608 impl_->flags_ |= efAllowMulticolumn | efAllowMultipoint | efAllowMultipleDataSets;
610 ::testing::InSequence dummy;
612 using ::testing::Invoke;
614 setSerialExpectationForParallelDataStarted(this);
615 EXPECT_CALL(*this, dataStarted(source)).WillOnce(Invoke(DataStorageRequester(storageCount)));
616 for (int row = 0; row < data.frameCount(); ++row)
618 const AnalysisDataTestInputFrame& frame = data.frame(row);
619 EXPECT_CALL(*this, frameStarted(_)).WillOnce(Invoke(StaticDataFrameHeaderChecker(&frame)));
620 for (int pointSet = 0; pointSet < frame.pointSetCount(); ++pointSet)
622 StaticDataPointsStorageChecker checker(source, &data, row, pointSet, storageCount);
623 EXPECT_CALL(*this, pointsAdded(_)).WillOnce(Invoke(checker));
625 EXPECT_CALL(*this, frameFinished(_)).WillOnce(Invoke(StaticDataFrameHeaderChecker(&frame)));
626 EXPECT_CALL(*this, frameFinishedSerial(row));
628 EXPECT_CALL(*this, dataFinished());
632 void MockAnalysisDataModule::setupReferenceCheck(const TestReferenceChecker& checker,
633 AbstractAnalysisData* source)
635 impl_->flags_ |= efAllowMulticolumn | efAllowMultipoint | efAllowMissing | efAllowMultipleDataSets;
637 impl_->rootChecker_ = TestReferenceChecker(checker);
638 // Google Mock does not support checking the order fully, because
639 // the number of frames is not known.
640 // Order of the frameStarted(), pointsAdded() and frameFinished()
641 // calls is checked using Google Test assertions in the invoked methods.
643 using ::testing::AnyNumber;
644 using ::testing::Expectation;
645 using ::testing::Invoke;
647 setSerialExpectationForParallelDataStarted(this);
648 Expectation dataStart =
649 EXPECT_CALL(*this, dataStarted(source)).WillOnce(Invoke(impl_.get(), &Impl::startReferenceData));
650 Expectation frameStart = EXPECT_CALL(*this, frameStarted(_))
652 .WillRepeatedly(Invoke(impl_.get(), &Impl::startReferenceFrame));
653 Expectation pointsAdd = EXPECT_CALL(*this, pointsAdded(_))
655 .WillRepeatedly(Invoke(impl_.get(), &Impl::checkReferencePoints));
656 Expectation frameFinish = EXPECT_CALL(*this, frameFinished(_))
658 .WillRepeatedly(Invoke(impl_.get(), &Impl::finishReferenceFrame));
659 Expectation frameFinishSerial =
660 EXPECT_CALL(*this, frameFinishedSerial(_))
662 .WillRepeatedly(Invoke(impl_.get(), &Impl::finishReferenceFrameSerial));
663 EXPECT_CALL(*this, dataFinished()).After(frameStart, pointsAdd, frameFinish, frameFinishSerial);