Split up compare.*
[gromacs.git] / src / testutils / refdata.cpp
blob2afd1ec41ce81d46c591da5175b16f8d23ccbefe
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2011,2012,2013,2014,2015,2016, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
35 /*! \internal \file
36 * \brief
37 * Implements classes and functions from refdata.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_testutils
42 #include "gmxpre.h"
44 #include "refdata.h"
46 #include <cctype>
47 #include <cstdlib>
49 #include <algorithm>
50 #include <limits>
51 #include <string>
53 #include <gtest/gtest.h>
55 #include "gromacs/options/basicoptions.h"
56 #include "gromacs/options/ioptionscontainer.h"
57 #include "gromacs/utility/exceptions.h"
58 #include "gromacs/utility/gmxassert.h"
59 #include "gromacs/utility/path.h"
60 #include "gromacs/utility/real.h"
61 #include "gromacs/utility/stringutil.h"
63 #include "testutils/refdata-checkers.h"
64 #include "testutils/refdata-impl.h"
65 #include "testutils/refdata-xml.h"
66 #include "testutils/testasserts.h"
67 #include "testutils/testexceptions.h"
68 #include "testutils/testfilemanager.h"
70 namespace gmx
72 namespace test
75 /********************************************************************
76 * TestReferenceData::Impl declaration
79 namespace internal
82 /*! \internal \brief
83 * Private implementation class for TestReferenceData.
85 * \ingroup module_testutils
87 class TestReferenceDataImpl
89 public:
90 //! Initializes a checker in the given mode.
91 TestReferenceDataImpl(ReferenceDataMode mode, bool bSelfTestMode);
93 //! Performs final reference data processing when test ends.
94 void onTestEnd(bool testPassed);
96 //! Full path of the reference data file.
97 std::string fullFilename_;
98 /*! \brief
99 * Root entry for comparing the reference data.
101 * Null after construction iff in compare mode and reference data was
102 * not loaded successfully.
103 * In all write modes, copies are present for nodes added to
104 * \a outputRootEntry_, and ReferenceDataEntry::correspondingOutputEntry()
105 * points to the copy in the output tree.
107 ReferenceDataEntry::EntryPointer compareRootEntry_;
108 /*! \brief
109 * Root entry for writing new reference data.
111 * Null if only comparing against existing data. Otherwise, starts
112 * always as empty.
113 * When creating new reference data, this is maintained as a copy of
114 * \a compareRootEntry_.
115 * When updating existing data, entries are added either by copying
116 * from \a compareRootEntry_ (if they exist and comparison passes), or
117 * by creating new ones.
119 ReferenceDataEntry::EntryPointer outputRootEntry_;
120 /*! \brief
121 * Whether updating existing reference data.
123 bool updateMismatchingEntries_;
124 //! `true` if self-testing (enables extra failure messages).
125 bool bSelfTestMode_;
126 /*! \brief
127 * Whether any reference checkers have been created for this data.
129 bool bInUse_;
132 } // namespace internal
134 /********************************************************************
135 * Internal helpers
138 namespace
141 //! Convenience typedef for a smart pointer to TestReferenceDataImpl.
142 typedef std::shared_ptr<internal::TestReferenceDataImpl>
143 TestReferenceDataImplPointer;
145 /*! \brief
146 * Global reference data instance.
148 * The object is created when the test creates a TestReferenceData, and the
149 * object is destructed (and other post-processing is done) at the end of each
150 * test by ReferenceDataTestEventListener (which is installed as a Google Test
151 * test listener).
153 TestReferenceDataImplPointer g_referenceData;
154 //! Global reference data mode set with setReferenceDataMode().
155 ReferenceDataMode g_referenceDataMode = erefdataCompare;
157 //! Returns the global reference data mode.
158 ReferenceDataMode getReferenceDataMode()
160 return g_referenceDataMode;
163 //! Returns a reference to the global reference data object.
164 TestReferenceDataImplPointer initReferenceDataInstance()
166 GMX_RELEASE_ASSERT(!g_referenceData,
167 "Test cannot create multiple TestReferenceData instances");
168 g_referenceData.reset(new internal::TestReferenceDataImpl(getReferenceDataMode(), false));
169 return g_referenceData;
172 //! Handles reference data creation for self-tests.
173 TestReferenceDataImplPointer initReferenceDataInstanceForSelfTest(ReferenceDataMode mode)
175 if (g_referenceData)
177 GMX_RELEASE_ASSERT(g_referenceData.unique(),
178 "Test cannot create multiple TestReferenceData instances");
179 g_referenceData->onTestEnd(true);
180 g_referenceData.reset();
182 g_referenceData.reset(new internal::TestReferenceDataImpl(mode, true));
183 return g_referenceData;
186 class ReferenceDataTestEventListener : public ::testing::EmptyTestEventListener
188 public:
189 virtual void OnTestEnd(const ::testing::TestInfo &test_info)
191 if (g_referenceData)
193 GMX_RELEASE_ASSERT(g_referenceData.unique(),
194 "Test leaked TestRefeferenceData objects");
195 g_referenceData->onTestEnd(test_info.result()->Passed());
196 g_referenceData.reset();
200 virtual void OnTestProgramEnd(const ::testing::UnitTest &)
202 // Could be used e.g. to free internal buffers allocated by an XML parsing library
206 } // namespace
208 void initReferenceData(IOptionsContainer *options)
210 // Needs to correspond to the enum order in refdata.h.
211 const char *const refDataEnum[] =
212 { "check", "create", "update-changed", "update-all" };
213 options->addOption(
214 EnumOption<ReferenceDataMode>("ref-data")
215 .enumValue(refDataEnum).store(&g_referenceDataMode)
216 .description("Operation mode for tests that use reference data"));
217 ::testing::UnitTest::GetInstance()->listeners().Append(
218 new ReferenceDataTestEventListener);
221 /********************************************************************
222 * TestReferenceDataImpl definition
225 namespace internal
228 TestReferenceDataImpl::TestReferenceDataImpl(
229 ReferenceDataMode mode, bool bSelfTestMode)
230 : updateMismatchingEntries_(false), bSelfTestMode_(bSelfTestMode), bInUse_(false)
232 const std::string dirname =
233 bSelfTestMode
234 ? TestFileManager::getGlobalOutputTempDirectory()
235 : TestFileManager::getInputDataDirectory();
236 const std::string filename = TestFileManager::getTestSpecificFileName(".xml");
237 fullFilename_ = Path::join(dirname, "refdata", filename);
239 switch (mode)
241 case erefdataCompare:
242 if (File::exists(fullFilename_, File::throwOnError))
244 compareRootEntry_ = readReferenceDataFile(fullFilename_);
246 break;
247 case erefdataCreateMissing:
248 if (File::exists(fullFilename_, File::throwOnError))
250 compareRootEntry_ = readReferenceDataFile(fullFilename_);
252 else
254 compareRootEntry_ = ReferenceDataEntry::createRoot();
255 outputRootEntry_ = ReferenceDataEntry::createRoot();
257 break;
258 case erefdataUpdateChanged:
259 if (File::exists(fullFilename_, File::throwOnError))
261 compareRootEntry_ = readReferenceDataFile(fullFilename_);
263 else
265 compareRootEntry_ = ReferenceDataEntry::createRoot();
267 outputRootEntry_ = ReferenceDataEntry::createRoot();
268 updateMismatchingEntries_ = true;
269 break;
270 case erefdataUpdateAll:
271 compareRootEntry_ = ReferenceDataEntry::createRoot();
272 outputRootEntry_ = ReferenceDataEntry::createRoot();
273 break;
277 void TestReferenceDataImpl::onTestEnd(bool testPassed)
279 // TODO: Only write the file with update-changed if there were actual changes.
280 if (testPassed && bInUse_ && outputRootEntry_)
282 std::string dirname = Path::getParentPath(fullFilename_);
283 if (!Directory::exists(dirname))
285 if (Directory::create(dirname) != 0)
287 GMX_THROW(TestException("Creation of reference data directory failed: " + dirname));
290 writeReferenceDataFile(fullFilename_, *outputRootEntry_);
294 } // namespace internal
297 /********************************************************************
298 * TestReferenceChecker::Impl
301 /*! \internal \brief
302 * Private implementation class for TestReferenceChecker.
304 * \ingroup module_testutils
306 class TestReferenceChecker::Impl
308 public:
309 //! String constant for naming XML elements for boolean values.
310 static const char * const cBooleanNodeName;
311 //! String constant for naming XML elements for string values.
312 static const char * const cStringNodeName;
313 //! String constant for naming XML elements for integer values.
314 static const char * const cIntegerNodeName;
315 //! String constant for naming XML elements for int64 values.
316 static const char * const cInt64NodeName;
317 //! String constant for naming XML elements for unsigned int64 values.
318 static const char * const cUInt64NodeName;
319 //! String constant for naming XML elements for floating-point values.
320 static const char * const cRealNodeName;
321 //! String constant for naming XML attribute for value identifiers.
322 static const char * const cIdAttrName;
323 //! String constant for naming compounds for vectors.
324 static const char * const cVectorType;
325 //! String constant for naming compounds for sequences.
326 static const char * const cSequenceType;
327 //! String constant for value identifier for sequence length.
328 static const char * const cSequenceLengthName;
330 //! Creates a checker that does nothing.
331 explicit Impl(bool initialized);
332 //! Creates a checker with a given root entry.
333 Impl(const std::string &path, ReferenceDataEntry *compareRootEntry,
334 ReferenceDataEntry *outputRootEntry, bool updateMismatchingEntries,
335 bool bSelfTestMode, const FloatingPointTolerance &defaultTolerance);
337 //! Returns the path of this checker with \p id appended.
338 std::string appendPath(const char *id) const;
340 //! Creates an entry with given parameters and fills it with \p checker.
341 ReferenceDataEntry::EntryPointer
342 createEntry(const char *type, const char *id,
343 const IReferenceDataEntryChecker &checker) const
345 ReferenceDataEntry::EntryPointer entry(new ReferenceDataEntry(type, id));
346 checker.fillEntry(entry.get());
347 return entry;
349 //! Checks an entry for correct type and using \p checker.
350 ::testing::AssertionResult
351 checkEntry(const ReferenceDataEntry &entry, const std::string &fullId,
352 const char *type, const IReferenceDataEntryChecker &checker) const
354 if (entry.type() != type)
356 return ::testing::AssertionFailure()
357 << "Mismatching reference data item type" << std::endl
358 << " In item: " << fullId << std::endl
359 << " Actual: " << type << std::endl
360 << "Reference: " << entry.type();
362 return checker.checkEntry(entry, fullId);
364 //! Finds an entry by id and updates the last found entry pointer.
365 ReferenceDataEntry *findEntry(const char *id);
366 /*! \brief
367 * Finds/creates a reference data entry to match against.
369 * \param[in] type Type of entry to create.
370 * \param[in] id Unique identifier of the entry (can be NULL, in
371 * which case the next entry without an id is matched).
372 * \param[out] checker Checker to use for filling out created entries.
373 * \returns Matching entry, or NULL if no matching entry found
374 * (NULL is never returned in write mode; new entries are created
375 * instead).
377 ReferenceDataEntry *
378 findOrCreateEntry(const char *type, const char *id,
379 const IReferenceDataEntryChecker &checker);
380 /*! \brief
381 * Helper method for checking a reference data value.
383 * \param[in] name Type of entry to find.
384 * \param[in] id Unique identifier of the entry (can be NULL, in
385 * which case the next entry without an id is matched).
386 * \param[in] checker Checker that provides logic specific to the
387 * type of the entry.
388 * \returns Whether the reference data matched, including details
389 * of the mismatch if the comparison failed.
390 * \throws TestException if there is a problem parsing the
391 * reference data.
393 * Performs common tasks in checking a reference value, such as
394 * finding or creating the correct entry.
395 * Caller needs to provide a checker object that provides the string
396 * value for a newly created entry and performs the actual comparison
397 * against a found entry.
399 ::testing::AssertionResult
400 processItem(const char *name, const char *id,
401 const IReferenceDataEntryChecker &checker);
402 /*! \brief
403 * Whether the checker is initialized.
405 bool initialized() const { return initialized_; }
406 /*! \brief
407 * Whether the checker should ignore all validation calls.
409 * This is used to ignore any calls within compounds for which
410 * reference data could not be found, such that only one error is
411 * issued for the missing compound, instead of every individual value.
413 bool shouldIgnore() const
415 GMX_RELEASE_ASSERT(initialized(),
416 "Accessing uninitialized reference data checker.");
417 return compareRootEntry_ == NULL;
420 //! Whether initialized with other means than the default constructor.
421 bool initialized_;
422 //! Default floating-point comparison tolerance.
423 FloatingPointTolerance defaultTolerance_;
424 /*! \brief
425 * Human-readable path to the root node of this checker.
427 * For the root checker, this will be "/", and for each compound, the
428 * id of the compound is added. Used for reporting comparison
429 * mismatches.
431 std::string path_;
432 /*! \brief
433 * Current entry under which reference data is searched for comparison.
435 * Points to either the TestReferenceDataImpl::compareRootEntry_, or to
436 * a compound entry in the tree rooted at that entry.
438 * Can be NULL, in which case this checker does nothing (doesn't even
439 * report errors, see shouldIgnore()).
441 ReferenceDataEntry *compareRootEntry_;
442 /*! \brief
443 * Current entry under which entries for writing are created.
445 * Points to either the TestReferenceDataImpl::outputRootEntry_, or to
446 * a compound entry in the tree rooted at that entry. NULL if only
447 * comparing, or if shouldIgnore() returns `false`.
449 ReferenceDataEntry *outputRootEntry_;
450 /*! \brief
451 * Iterator to a child of \a compareRootEntry_ that was last found.
453 * If `compareRootEntry_->isValidChild()` returns false, no entry has
454 * been found yet.
455 * After every check, is updated to point to the entry that was used
456 * for the check.
457 * Subsequent checks start the search for the matching node on this
458 * node.
460 ReferenceDataEntry::ChildIterator lastFoundEntry_;
461 /*! \brief
462 * Whether the reference data is being written (true) or compared
463 * (false).
465 bool updateMismatchingEntries_;
466 //! `true` if self-testing (enables extra failure messages).
467 bool bSelfTestMode_;
468 /*! \brief
469 * Current number of unnamed elements in a sequence.
471 * It is the index of the next added unnamed element.
473 int seqIndex_;
476 const char *const TestReferenceChecker::Impl::cBooleanNodeName = "Bool";
477 const char *const TestReferenceChecker::Impl::cStringNodeName = "String";
478 const char *const TestReferenceChecker::Impl::cIntegerNodeName = "Int";
479 const char *const TestReferenceChecker::Impl::cInt64NodeName = "Int64";
480 const char *const TestReferenceChecker::Impl::cUInt64NodeName = "UInt64";
481 const char *const TestReferenceChecker::Impl::cRealNodeName = "Real";
482 const char *const TestReferenceChecker::Impl::cIdAttrName = "Name";
483 const char *const TestReferenceChecker::Impl::cVectorType = "Vector";
484 const char *const TestReferenceChecker::Impl::cSequenceType = "Sequence";
485 const char *const TestReferenceChecker::Impl::cSequenceLengthName = "Length";
488 TestReferenceChecker::Impl::Impl(bool initialized)
489 : initialized_(initialized), defaultTolerance_(defaultRealTolerance()),
490 compareRootEntry_(NULL), outputRootEntry_(NULL),
491 updateMismatchingEntries_(false), bSelfTestMode_(false), seqIndex_(0)
496 TestReferenceChecker::Impl::Impl(const std::string &path,
497 ReferenceDataEntry *compareRootEntry,
498 ReferenceDataEntry *outputRootEntry,
499 bool updateMismatchingEntries, bool bSelfTestMode,
500 const FloatingPointTolerance &defaultTolerance)
501 : initialized_(true), defaultTolerance_(defaultTolerance), path_(path + "/"),
502 compareRootEntry_(compareRootEntry), outputRootEntry_(outputRootEntry),
503 lastFoundEntry_(compareRootEntry->children().end()),
504 updateMismatchingEntries_(updateMismatchingEntries),
505 bSelfTestMode_(bSelfTestMode), seqIndex_(0)
510 std::string
511 TestReferenceChecker::Impl::appendPath(const char *id) const
513 std::string printId = (id != NULL) ? id : formatString("[%d]", seqIndex_);
514 return path_ + printId;
518 ReferenceDataEntry *TestReferenceChecker::Impl::findEntry(const char *id)
520 ReferenceDataEntry::ChildIterator entry = compareRootEntry_->findChild(id, lastFoundEntry_);
521 seqIndex_ = (id == NULL) ? seqIndex_+1 : 0;
522 if (compareRootEntry_->isValidChild(entry))
524 lastFoundEntry_ = entry;
525 return entry->get();
527 return NULL;
530 ReferenceDataEntry *
531 TestReferenceChecker::Impl::findOrCreateEntry(
532 const char *type, const char *id,
533 const IReferenceDataEntryChecker &checker)
535 ReferenceDataEntry *entry = findEntry(id);
536 if (entry == NULL && outputRootEntry_ != NULL)
538 lastFoundEntry_ = compareRootEntry_->addChild(createEntry(type, id, checker));
539 entry = lastFoundEntry_->get();
541 return entry;
544 ::testing::AssertionResult
545 TestReferenceChecker::Impl::processItem(const char *type, const char *id,
546 const IReferenceDataEntryChecker &checker)
548 if (shouldIgnore())
550 return ::testing::AssertionSuccess();
552 std::string fullId = appendPath(id);
553 ReferenceDataEntry *entry = findOrCreateEntry(type, id, checker);
554 if (entry == NULL)
556 return ::testing::AssertionFailure()
557 << "Reference data item " << fullId << " not found";
559 ::testing::AssertionResult result(checkEntry(*entry, fullId, type, checker));
560 if (outputRootEntry_ != NULL && entry->correspondingOutputEntry() == NULL)
562 if (!updateMismatchingEntries_ || result)
564 outputRootEntry_->addChild(entry->cloneToOutputEntry());
566 else
568 ReferenceDataEntry::EntryPointer outputEntry(createEntry(type, id, checker));
569 entry->setCorrespondingOutputEntry(outputEntry.get());
570 outputRootEntry_->addChild(move(outputEntry));
571 return ::testing::AssertionSuccess();
574 if (bSelfTestMode_ && !result)
576 ReferenceDataEntry expected(type, id);
577 checker.fillEntry(&expected);
578 result << std::endl
579 << "String value: '" << expected.value() << "'" << std::endl
580 << " Ref. string: '" << entry->value() << "'";
582 return result;
586 /********************************************************************
587 * TestReferenceData
590 TestReferenceData::TestReferenceData()
591 : impl_(initReferenceDataInstance())
596 TestReferenceData::TestReferenceData(ReferenceDataMode mode)
597 : impl_(initReferenceDataInstanceForSelfTest(mode))
602 TestReferenceData::~TestReferenceData()
607 TestReferenceChecker TestReferenceData::rootChecker()
609 if (!impl_->bInUse_ && !impl_->compareRootEntry_)
611 ADD_FAILURE() << "Reference data file not found: "
612 << impl_->fullFilename_;
614 impl_->bInUse_ = true;
615 if (!impl_->compareRootEntry_)
617 return TestReferenceChecker(new TestReferenceChecker::Impl(true));
619 return TestReferenceChecker(
620 new TestReferenceChecker::Impl("", impl_->compareRootEntry_.get(),
621 impl_->outputRootEntry_.get(),
622 impl_->updateMismatchingEntries_, impl_->bSelfTestMode_,
623 defaultRealTolerance()));
627 /********************************************************************
628 * TestReferenceChecker
631 TestReferenceChecker::TestReferenceChecker()
632 : impl_(new Impl(false))
636 TestReferenceChecker::TestReferenceChecker(Impl *impl)
637 : impl_(impl)
641 TestReferenceChecker::TestReferenceChecker(const TestReferenceChecker &other)
642 : impl_(new Impl(*other.impl_))
646 TestReferenceChecker::TestReferenceChecker(TestReferenceChecker &&other)
647 : impl_(std::move(other.impl_))
651 TestReferenceChecker &
652 TestReferenceChecker::operator=(TestReferenceChecker &&other)
654 impl_ = std::move(other.impl_);
655 return *this;
658 TestReferenceChecker::~TestReferenceChecker()
662 bool TestReferenceChecker::isValid() const
664 return impl_->initialized();
668 void TestReferenceChecker::setDefaultTolerance(
669 const FloatingPointTolerance &tolerance)
671 impl_->defaultTolerance_ = tolerance;
675 bool TestReferenceChecker::checkPresent(bool bPresent, const char *id)
677 if (impl_->shouldIgnore() || impl_->outputRootEntry_ != NULL)
679 return bPresent;
681 ReferenceDataEntry::ChildIterator entry
682 = impl_->compareRootEntry_->findChild(id, impl_->lastFoundEntry_);
683 const bool bFound
684 = impl_->compareRootEntry_->isValidChild(entry);
685 if (bFound != bPresent)
687 ADD_FAILURE() << "Mismatch while checking reference data item '"
688 << impl_->appendPath(id) << "'\n"
689 << "Expected: " << (bPresent ? "it is present.\n" : "it is absent.\n")
690 << " Actual: " << (bFound ? "it is present." : "it is absent.");
692 if (bFound && bPresent)
694 impl_->lastFoundEntry_ = entry;
695 return true;
697 return false;
701 TestReferenceChecker TestReferenceChecker::checkCompound(const char *type, const char *id)
703 if (impl_->shouldIgnore())
705 return TestReferenceChecker(new Impl(true));
707 std::string fullId = impl_->appendPath(id);
708 NullChecker checker;
709 ReferenceDataEntry *entry = impl_->findOrCreateEntry(type, id, checker);
710 if (entry == NULL)
712 ADD_FAILURE() << "Reference data item " << fullId << " not found";
713 return TestReferenceChecker(new Impl(true));
715 if (impl_->updateMismatchingEntries_)
717 entry->makeCompound(type);
719 else
721 ::testing::AssertionResult result(impl_->checkEntry(*entry, fullId, type, checker));
722 EXPECT_PLAIN(result);
723 if (!result)
725 return TestReferenceChecker(new Impl(true));
728 if (impl_->outputRootEntry_ != NULL && entry->correspondingOutputEntry() == NULL)
730 impl_->outputRootEntry_->addChild(entry->cloneToOutputEntry());
732 return TestReferenceChecker(
733 new Impl(fullId, entry, entry->correspondingOutputEntry(),
734 impl_->updateMismatchingEntries_, impl_->bSelfTestMode_,
735 impl_->defaultTolerance_));
739 /*! \brief Throw a TestException if the caller tries to write particular refdata that can't work.
741 * If the string to write is non-empty and has only whitespace,
742 * TinyXML2 can't read it correctly, so throw an exception for this
743 * case, so that we can't accidentally use it and run into mysterious
744 * problems.
746 * \todo Eliminate this limitation of TinyXML2. See
747 * e.g. https://github.com/leethomason/tinyxml2/issues/432
749 static void
750 throwIfNonEmptyAndOnlyWhitespace(const std::string &s, const char *id)
752 if (!s.empty() && std::all_of(s.cbegin(), s.cend(), [](const char &c){ return std::isspace(c); }))
754 std::string message("String '" + s + "' with ");
755 message += (id != nullptr) ? "null " : "";
756 message += "ID ";
757 message += (id != nullptr) ? "" : id;
758 message += " cannot be handled. We must refuse to write a refdata String"
759 "field for a non-empty string that contains only whitespace, "
760 "because it will not be read correctly by TinyXML2.";
761 GMX_THROW(TestException(message));
765 void TestReferenceChecker::checkBoolean(bool value, const char *id)
767 EXPECT_PLAIN(impl_->processItem(Impl::cBooleanNodeName, id,
768 ExactStringChecker(value ? "true" : "false")));
772 void TestReferenceChecker::checkString(const char *value, const char *id)
774 throwIfNonEmptyAndOnlyWhitespace(value, id);
775 EXPECT_PLAIN(impl_->processItem(Impl::cStringNodeName, id,
776 ExactStringChecker(value)));
780 void TestReferenceChecker::checkString(const std::string &value, const char *id)
782 throwIfNonEmptyAndOnlyWhitespace(value, id);
783 EXPECT_PLAIN(impl_->processItem(Impl::cStringNodeName, id,
784 ExactStringChecker(value)));
788 void TestReferenceChecker::checkTextBlock(const std::string &value,
789 const char *id)
791 EXPECT_PLAIN(impl_->processItem(Impl::cStringNodeName, id,
792 ExactStringBlockChecker(value)));
796 void TestReferenceChecker::checkInteger(int value, const char *id)
798 EXPECT_PLAIN(impl_->processItem(Impl::cIntegerNodeName, id,
799 ExactStringChecker(formatString("%d", value))));
802 void TestReferenceChecker::checkInt64(gmx_int64_t value, const char *id)
804 EXPECT_PLAIN(impl_->processItem(Impl::cInt64NodeName, id,
805 ExactStringChecker(formatString("%" GMX_PRId64, value))));
808 void TestReferenceChecker::checkUInt64(gmx_uint64_t value, const char *id)
810 EXPECT_PLAIN(impl_->processItem(Impl::cUInt64NodeName, id,
811 ExactStringChecker(formatString("%" GMX_PRIu64, value))));
814 void TestReferenceChecker::checkDouble(double value, const char *id)
816 FloatingPointChecker<double> checker(value, impl_->defaultTolerance_);
817 EXPECT_PLAIN(impl_->processItem(Impl::cRealNodeName, id, checker));
821 void TestReferenceChecker::checkFloat(float value, const char *id)
823 FloatingPointChecker<float> checker(value, impl_->defaultTolerance_);
824 EXPECT_PLAIN(impl_->processItem(Impl::cRealNodeName, id, checker));
828 void TestReferenceChecker::checkReal(float value, const char *id)
830 checkFloat(value, id);
834 void TestReferenceChecker::checkReal(double value, const char *id)
836 checkDouble(value, id);
840 void TestReferenceChecker::checkRealFromString(const std::string &value, const char *id)
842 FloatingPointFromStringChecker<real> checker(value, impl_->defaultTolerance_);
843 EXPECT_PLAIN(impl_->processItem(Impl::cRealNodeName, id, checker));
847 void TestReferenceChecker::checkVector(const int value[3], const char *id)
849 TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
850 compound.checkInteger(value[0], "X");
851 compound.checkInteger(value[1], "Y");
852 compound.checkInteger(value[2], "Z");
856 void TestReferenceChecker::checkVector(const float value[3], const char *id)
858 TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
859 compound.checkReal(value[0], "X");
860 compound.checkReal(value[1], "Y");
861 compound.checkReal(value[2], "Z");
865 void TestReferenceChecker::checkVector(const double value[3], const char *id)
867 TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
868 compound.checkReal(value[0], "X");
869 compound.checkReal(value[1], "Y");
870 compound.checkReal(value[2], "Z");
874 TestReferenceChecker
875 TestReferenceChecker::checkSequenceCompound(const char *id, size_t length)
877 TestReferenceChecker compound(checkCompound(Impl::cSequenceType, id));
878 compound.checkInteger(static_cast<int>(length), Impl::cSequenceLengthName);
879 return compound;
882 } // namespace test
883 } // namespace gmx