Merge branch release-5-1 into release-2016
[alexxy/gromacs.git] / src / testutils / refdata.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements classes and functions from refdata.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_testutils
41  */
42 #include "gmxpre.h"
43
44 #include "refdata.h"
45
46 #include <cctype>
47 #include <cstdlib>
48
49 #include <algorithm>
50 #include <limits>
51 #include <string>
52
53 #include <gtest/gtest.h>
54
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"
62
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"
69
70 namespace gmx
71 {
72 namespace test
73 {
74
75 /********************************************************************
76  * TestReferenceData::Impl declaration
77  */
78
79 namespace internal
80 {
81
82 /*! \internal \brief
83  * Private implementation class for TestReferenceData.
84  *
85  * \ingroup module_testutils
86  */
87 class TestReferenceDataImpl
88 {
89     public:
90         //! Initializes a checker in the given mode.
91         TestReferenceDataImpl(ReferenceDataMode mode, bool bSelfTestMode);
92
93         //! Performs final reference data processing when test ends.
94         void onTestEnd(bool testPassed);
95
96         //! Full path of the reference data file.
97         std::string             fullFilename_;
98         /*! \brief
99          * Root entry for comparing the reference data.
100          *
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.
106          */
107         ReferenceDataEntry::EntryPointer  compareRootEntry_;
108         /*! \brief
109          * Root entry for writing new reference data.
110          *
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.
118          */
119         ReferenceDataEntry::EntryPointer  outputRootEntry_;
120         /*! \brief
121          * Whether updating existing reference data.
122          */
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.
128          */
129         bool                    bInUse_;
130 };
131
132 }       // namespace internal
133
134 /********************************************************************
135  * Internal helpers
136  */
137
138 namespace
139 {
140
141 //! Convenience typedef for a smart pointer to TestReferenceDataImpl.
142 typedef std::shared_ptr<internal::TestReferenceDataImpl>
143     TestReferenceDataImplPointer;
144
145 /*! \brief
146  * Global reference data instance.
147  *
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).
152  */
153 TestReferenceDataImplPointer g_referenceData;
154 //! Global reference data mode set with setReferenceDataMode().
155 ReferenceDataMode            g_referenceDataMode = erefdataCompare;
156
157 //! Returns the global reference data mode.
158 ReferenceDataMode getReferenceDataMode()
159 {
160     return g_referenceDataMode;
161 }
162
163 //! Returns a reference to the global reference data object.
164 TestReferenceDataImplPointer initReferenceDataInstance()
165 {
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;
170 }
171
172 //! Handles reference data creation for self-tests.
173 TestReferenceDataImplPointer initReferenceDataInstanceForSelfTest(ReferenceDataMode mode)
174 {
175     if (g_referenceData)
176     {
177         GMX_RELEASE_ASSERT(g_referenceData.unique(),
178                            "Test cannot create multiple TestReferenceData instances");
179         g_referenceData->onTestEnd(true);
180         g_referenceData.reset();
181     }
182     g_referenceData.reset(new internal::TestReferenceDataImpl(mode, true));
183     return g_referenceData;
184 }
185
186 class ReferenceDataTestEventListener : public ::testing::EmptyTestEventListener
187 {
188     public:
189         virtual void OnTestEnd(const ::testing::TestInfo &test_info)
190         {
191             if (g_referenceData)
192             {
193                 GMX_RELEASE_ASSERT(g_referenceData.unique(),
194                                    "Test leaked TestRefeferenceData objects");
195                 g_referenceData->onTestEnd(test_info.result()->Passed());
196                 g_referenceData.reset();
197             }
198         }
199
200         virtual void OnTestProgramEnd(const ::testing::UnitTest &)
201         {
202             // Could be used e.g. to free internal buffers allocated by an XML parsing library
203         }
204 };
205
206 }       // namespace
207
208 void initReferenceData(IOptionsContainer *options)
209 {
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);
219 }
220
221 /********************************************************************
222  * TestReferenceDataImpl definition
223  */
224
225 namespace internal
226 {
227
228 TestReferenceDataImpl::TestReferenceDataImpl(
229         ReferenceDataMode mode, bool bSelfTestMode)
230     : updateMismatchingEntries_(false), bSelfTestMode_(bSelfTestMode), bInUse_(false)
231 {
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);
238
239     switch (mode)
240     {
241         case erefdataCompare:
242             if (File::exists(fullFilename_, File::throwOnError))
243             {
244                 compareRootEntry_ = readReferenceDataFile(fullFilename_);
245             }
246             break;
247         case erefdataCreateMissing:
248             if (File::exists(fullFilename_, File::throwOnError))
249             {
250                 compareRootEntry_ = readReferenceDataFile(fullFilename_);
251             }
252             else
253             {
254                 compareRootEntry_ = ReferenceDataEntry::createRoot();
255                 outputRootEntry_  = ReferenceDataEntry::createRoot();
256             }
257             break;
258         case erefdataUpdateChanged:
259             if (File::exists(fullFilename_, File::throwOnError))
260             {
261                 compareRootEntry_ = readReferenceDataFile(fullFilename_);
262             }
263             else
264             {
265                 compareRootEntry_ = ReferenceDataEntry::createRoot();
266             }
267             outputRootEntry_          = ReferenceDataEntry::createRoot();
268             updateMismatchingEntries_ = true;
269             break;
270         case erefdataUpdateAll:
271             compareRootEntry_ = ReferenceDataEntry::createRoot();
272             outputRootEntry_  = ReferenceDataEntry::createRoot();
273             break;
274     }
275 }
276
277 void TestReferenceDataImpl::onTestEnd(bool testPassed)
278 {
279     // TODO: Only write the file with update-changed if there were actual changes.
280     if (testPassed && bInUse_ && outputRootEntry_)
281     {
282         std::string dirname = Path::getParentPath(fullFilename_);
283         if (!Directory::exists(dirname))
284         {
285             if (Directory::create(dirname) != 0)
286             {
287                 GMX_THROW(TestException("Creation of reference data directory failed: " + dirname));
288             }
289         }
290         writeReferenceDataFile(fullFilename_, *outputRootEntry_);
291     }
292 }
293
294 }       // namespace internal
295
296
297 /********************************************************************
298  * TestReferenceChecker::Impl
299  */
300
301 /*! \internal \brief
302  * Private implementation class for TestReferenceChecker.
303  *
304  * \ingroup module_testutils
305  */
306 class TestReferenceChecker::Impl
307 {
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;
329
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);
336
337         //! Returns the path of this checker with \p id appended.
338         std::string appendPath(const char *id) const;
339
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
344         {
345             ReferenceDataEntry::EntryPointer entry(new ReferenceDataEntry(type, id));
346             checker.fillEntry(entry.get());
347             return entry;
348         }
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
353         {
354             if (entry.type() != type)
355             {
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();
361             }
362             return checker.checkEntry(entry, fullId);
363         }
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.
368          *
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).
376          */
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.
382          *
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.
392          *
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.
398          */
399         ::testing::AssertionResult
400         processItem(const char *name, const char *id,
401                     const IReferenceDataEntryChecker &checker);
402         /*! \brief
403          * Whether the checker is initialized.
404          */
405         bool initialized() const { return initialized_; }
406         /*! \brief
407          * Whether the checker should ignore all validation calls.
408          *
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.
412          */
413         bool shouldIgnore() const
414         {
415             GMX_RELEASE_ASSERT(initialized(),
416                                "Accessing uninitialized reference data checker.");
417             return compareRootEntry_ == NULL;
418         }
419
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.
426          *
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.
430          */
431         std::string             path_;
432         /*! \brief
433          * Current entry under which reference data is searched for comparison.
434          *
435          * Points to either the TestReferenceDataImpl::compareRootEntry_, or to
436          * a compound entry in the tree rooted at that entry.
437          *
438          * Can be NULL, in which case this checker does nothing (doesn't even
439          * report errors, see shouldIgnore()).
440          */
441         ReferenceDataEntry     *compareRootEntry_;
442         /*! \brief
443          * Current entry under which entries for writing are created.
444          *
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`.
448          */
449         ReferenceDataEntry     *outputRootEntry_;
450         /*! \brief
451          * Iterator to a child of \a compareRootEntry_ that was last found.
452          *
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.
459          */
460         ReferenceDataEntry::ChildIterator lastFoundEntry_;
461         /*! \brief
462          * Whether the reference data is being written (true) or compared
463          * (false).
464          */
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.
470          *
471          * It is the index of the next added unnamed element.
472          */
473         int                     seqIndex_;
474 };
475
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";
486
487
488 TestReferenceChecker::Impl::Impl(bool initialized)
489     : initialized_(initialized), defaultTolerance_(defaultRealTolerance()),
490       compareRootEntry_(NULL), outputRootEntry_(NULL),
491       updateMismatchingEntries_(false), bSelfTestMode_(false), seqIndex_(0)
492 {
493 }
494
495
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)
506 {
507 }
508
509
510 std::string
511 TestReferenceChecker::Impl::appendPath(const char *id) const
512 {
513     std::string printId = (id != NULL) ? id : formatString("[%d]", seqIndex_);
514     return path_ + printId;
515 }
516
517
518 ReferenceDataEntry *TestReferenceChecker::Impl::findEntry(const char *id)
519 {
520     ReferenceDataEntry::ChildIterator entry = compareRootEntry_->findChild(id, lastFoundEntry_);
521     seqIndex_ = (id == NULL) ? seqIndex_+1 : 0;
522     if (compareRootEntry_->isValidChild(entry))
523     {
524         lastFoundEntry_ = entry;
525         return entry->get();
526     }
527     return NULL;
528 }
529
530 ReferenceDataEntry *
531 TestReferenceChecker::Impl::findOrCreateEntry(
532         const char *type, const char *id,
533         const IReferenceDataEntryChecker &checker)
534 {
535     ReferenceDataEntry *entry = findEntry(id);
536     if (entry == NULL && outputRootEntry_ != NULL)
537     {
538         lastFoundEntry_ = compareRootEntry_->addChild(createEntry(type, id, checker));
539         entry           = lastFoundEntry_->get();
540     }
541     return entry;
542 }
543
544 ::testing::AssertionResult
545 TestReferenceChecker::Impl::processItem(const char *type, const char *id,
546                                         const IReferenceDataEntryChecker &checker)
547 {
548     if (shouldIgnore())
549     {
550         return ::testing::AssertionSuccess();
551     }
552     std::string         fullId = appendPath(id);
553     ReferenceDataEntry *entry  = findOrCreateEntry(type, id, checker);
554     if (entry == NULL)
555     {
556         return ::testing::AssertionFailure()
557                << "Reference data item " << fullId << " not found";
558     }
559     ::testing::AssertionResult result(checkEntry(*entry, fullId, type, checker));
560     if (outputRootEntry_ != NULL && entry->correspondingOutputEntry() == NULL)
561     {
562         if (!updateMismatchingEntries_ || result)
563         {
564             outputRootEntry_->addChild(entry->cloneToOutputEntry());
565         }
566         else
567         {
568             ReferenceDataEntry::EntryPointer outputEntry(createEntry(type, id, checker));
569             entry->setCorrespondingOutputEntry(outputEntry.get());
570             outputRootEntry_->addChild(move(outputEntry));
571             return ::testing::AssertionSuccess();
572         }
573     }
574     if (bSelfTestMode_ && !result)
575     {
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() << "'";
581     }
582     return result;
583 }
584
585
586 /********************************************************************
587  * TestReferenceData
588  */
589
590 TestReferenceData::TestReferenceData()
591     : impl_(initReferenceDataInstance())
592 {
593 }
594
595
596 TestReferenceData::TestReferenceData(ReferenceDataMode mode)
597     : impl_(initReferenceDataInstanceForSelfTest(mode))
598 {
599 }
600
601
602 TestReferenceData::~TestReferenceData()
603 {
604 }
605
606
607 TestReferenceChecker TestReferenceData::rootChecker()
608 {
609     if (!impl_->bInUse_ && !impl_->compareRootEntry_)
610     {
611         ADD_FAILURE() << "Reference data file not found: "
612         << impl_->fullFilename_;
613     }
614     impl_->bInUse_ = true;
615     if (!impl_->compareRootEntry_)
616     {
617         return TestReferenceChecker(new TestReferenceChecker::Impl(true));
618     }
619     return TestReferenceChecker(
620             new TestReferenceChecker::Impl("", impl_->compareRootEntry_.get(),
621                                            impl_->outputRootEntry_.get(),
622                                            impl_->updateMismatchingEntries_, impl_->bSelfTestMode_,
623                                            defaultRealTolerance()));
624 }
625
626
627 /********************************************************************
628  * TestReferenceChecker
629  */
630
631 TestReferenceChecker::TestReferenceChecker()
632     : impl_(new Impl(false))
633 {
634 }
635
636 TestReferenceChecker::TestReferenceChecker(Impl *impl)
637     : impl_(impl)
638 {
639 }
640
641 TestReferenceChecker::TestReferenceChecker(const TestReferenceChecker &other)
642     : impl_(new Impl(*other.impl_))
643 {
644 }
645
646 TestReferenceChecker::TestReferenceChecker(TestReferenceChecker &&other)
647     : impl_(std::move(other.impl_))
648 {
649 }
650
651 TestReferenceChecker &
652 TestReferenceChecker::operator=(TestReferenceChecker &&other)
653 {
654     impl_ = std::move(other.impl_);
655     return *this;
656 }
657
658 TestReferenceChecker::~TestReferenceChecker()
659 {
660 }
661
662 bool TestReferenceChecker::isValid() const
663 {
664     return impl_->initialized();
665 }
666
667
668 void TestReferenceChecker::setDefaultTolerance(
669         const FloatingPointTolerance &tolerance)
670 {
671     impl_->defaultTolerance_ = tolerance;
672 }
673
674
675 bool TestReferenceChecker::checkPresent(bool bPresent, const char *id)
676 {
677     if (impl_->shouldIgnore() || impl_->outputRootEntry_ != NULL)
678     {
679         return bPresent;
680     }
681     ReferenceDataEntry::ChildIterator  entry
682         = impl_->compareRootEntry_->findChild(id, impl_->lastFoundEntry_);
683     const bool                         bFound
684         = impl_->compareRootEntry_->isValidChild(entry);
685     if (bFound != bPresent)
686     {
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.");
691     }
692     if (bFound && bPresent)
693     {
694         impl_->lastFoundEntry_ = entry;
695         return true;
696     }
697     return false;
698 }
699
700
701 TestReferenceChecker TestReferenceChecker::checkCompound(const char *type, const char *id)
702 {
703     if (impl_->shouldIgnore())
704     {
705         return TestReferenceChecker(new Impl(true));
706     }
707     std::string         fullId = impl_->appendPath(id);
708     NullChecker         checker;
709     ReferenceDataEntry *entry  = impl_->findOrCreateEntry(type, id, checker);
710     if (entry == NULL)
711     {
712         ADD_FAILURE() << "Reference data item " << fullId << " not found";
713         return TestReferenceChecker(new Impl(true));
714     }
715     if (impl_->updateMismatchingEntries_)
716     {
717         entry->makeCompound(type);
718     }
719     else
720     {
721         ::testing::AssertionResult result(impl_->checkEntry(*entry, fullId, type, checker));
722         EXPECT_PLAIN(result);
723         if (!result)
724         {
725             return TestReferenceChecker(new Impl(true));
726         }
727     }
728     if (impl_->outputRootEntry_ != NULL && entry->correspondingOutputEntry() == NULL)
729     {
730         impl_->outputRootEntry_->addChild(entry->cloneToOutputEntry());
731     }
732     return TestReferenceChecker(
733             new Impl(fullId, entry, entry->correspondingOutputEntry(),
734                      impl_->updateMismatchingEntries_, impl_->bSelfTestMode_,
735                      impl_->defaultTolerance_));
736 }
737
738
739 /*! \brief Throw a TestException if the caller tries to write particular refdata that can't work.
740  *
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.
745  *
746  * \todo Eliminate this limitation of TinyXML2. See
747  * e.g. https://github.com/leethomason/tinyxml2/issues/432
748  */
749 static void
750 throwIfNonEmptyAndOnlyWhitespace(const std::string &s, const char *id)
751 {
752     if (!s.empty() && std::all_of(s.cbegin(), s.cend(), [](const char &c){ return std::isspace(c); }))
753     {
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));
762     }
763 }
764
765 void TestReferenceChecker::checkBoolean(bool value, const char *id)
766 {
767     EXPECT_PLAIN(impl_->processItem(Impl::cBooleanNodeName, id,
768                                     ExactStringChecker(value ? "true" : "false")));
769 }
770
771
772 void TestReferenceChecker::checkString(const char *value, const char *id)
773 {
774     throwIfNonEmptyAndOnlyWhitespace(value, id);
775     EXPECT_PLAIN(impl_->processItem(Impl::cStringNodeName, id,
776                                     ExactStringChecker(value)));
777 }
778
779
780 void TestReferenceChecker::checkString(const std::string &value, const char *id)
781 {
782     throwIfNonEmptyAndOnlyWhitespace(value, id);
783     EXPECT_PLAIN(impl_->processItem(Impl::cStringNodeName, id,
784                                     ExactStringChecker(value)));
785 }
786
787
788 void TestReferenceChecker::checkTextBlock(const std::string &value,
789                                           const char        *id)
790 {
791     EXPECT_PLAIN(impl_->processItem(Impl::cStringNodeName, id,
792                                     ExactStringBlockChecker(value)));
793 }
794
795
796 void TestReferenceChecker::checkInteger(int value, const char *id)
797 {
798     EXPECT_PLAIN(impl_->processItem(Impl::cIntegerNodeName, id,
799                                     ExactStringChecker(formatString("%d", value))));
800 }
801
802 void TestReferenceChecker::checkInt64(gmx_int64_t value, const char *id)
803 {
804     EXPECT_PLAIN(impl_->processItem(Impl::cInt64NodeName, id,
805                                     ExactStringChecker(formatString("%" GMX_PRId64, value))));
806 }
807
808 void TestReferenceChecker::checkUInt64(gmx_uint64_t value, const char *id)
809 {
810     EXPECT_PLAIN(impl_->processItem(Impl::cUInt64NodeName, id,
811                                     ExactStringChecker(formatString("%" GMX_PRIu64, value))));
812 }
813
814 void TestReferenceChecker::checkDouble(double value, const char *id)
815 {
816     FloatingPointChecker<double> checker(value, impl_->defaultTolerance_);
817     EXPECT_PLAIN(impl_->processItem(Impl::cRealNodeName, id, checker));
818 }
819
820
821 void TestReferenceChecker::checkFloat(float value, const char *id)
822 {
823     FloatingPointChecker<float> checker(value, impl_->defaultTolerance_);
824     EXPECT_PLAIN(impl_->processItem(Impl::cRealNodeName, id, checker));
825 }
826
827
828 void TestReferenceChecker::checkReal(float value, const char *id)
829 {
830     checkFloat(value, id);
831 }
832
833
834 void TestReferenceChecker::checkReal(double value, const char *id)
835 {
836     checkDouble(value, id);
837 }
838
839
840 void TestReferenceChecker::checkRealFromString(const std::string &value, const char *id)
841 {
842     FloatingPointFromStringChecker<real> checker(value, impl_->defaultTolerance_);
843     EXPECT_PLAIN(impl_->processItem(Impl::cRealNodeName, id, checker));
844 }
845
846
847 void TestReferenceChecker::checkVector(const int value[3], const char *id)
848 {
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");
853 }
854
855
856 void TestReferenceChecker::checkVector(const float value[3], const char *id)
857 {
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");
862 }
863
864
865 void TestReferenceChecker::checkVector(const double value[3], const char *id)
866 {
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");
871 }
872
873
874 TestReferenceChecker
875 TestReferenceChecker::checkSequenceCompound(const char *id, size_t length)
876 {
877     TestReferenceChecker compound(checkCompound(Impl::cSequenceType, id));
878     compound.checkInteger(static_cast<int>(length), Impl::cSequenceLengthName);
879     return compound;
880 }
881
882 } // namespace test
883 } // namespace gmx