Add support for serializing 32-bit integers
[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,2017,2018,2019, 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/any.h"
58 #include "gromacs/utility/exceptions.h"
59 #include "gromacs/utility/gmxassert.h"
60 #include "gromacs/utility/keyvaluetree.h"
61 #include "gromacs/utility/path.h"
62 #include "gromacs/utility/real.h"
63 #include "gromacs/utility/stringutil.h"
64
65 #include "testutils/refdata_checkers.h"
66 #include "testutils/refdata_impl.h"
67 #include "testutils/refdata_xml.h"
68 #include "testutils/testasserts.h"
69 #include "testutils/testexceptions.h"
70 #include "testutils/testfilemanager.h"
71
72 namespace gmx
73 {
74 namespace test
75 {
76
77 /********************************************************************
78  * TestReferenceData::Impl declaration
79  */
80
81 namespace internal
82 {
83
84 /*! \internal \brief
85  * Private implementation class for TestReferenceData.
86  *
87  * \ingroup module_testutils
88  */
89 class TestReferenceDataImpl
90 {
91     public:
92         //! Initializes a checker in the given mode.
93         TestReferenceDataImpl(ReferenceDataMode mode, bool bSelfTestMode);
94
95         //! Performs final reference data processing when test ends.
96         void onTestEnd(bool testPassed);
97
98         //! Full path of the reference data file.
99         std::string             fullFilename_;
100         /*! \brief
101          * Root entry for comparing the reference data.
102          *
103          * Null after construction iff in compare mode and reference data was
104          * not loaded successfully.
105          * In all write modes, copies are present for nodes added to
106          * \a outputRootEntry_, and ReferenceDataEntry::correspondingOutputEntry()
107          * points to the copy in the output tree.
108          */
109         ReferenceDataEntry::EntryPointer  compareRootEntry_;
110         /*! \brief
111          * Root entry for writing new reference data.
112          *
113          * Null if only comparing against existing data.  Otherwise, starts
114          * always as empty.
115          * When creating new reference data, this is maintained as a copy of
116          * \a compareRootEntry_.
117          * When updating existing data, entries are added either by copying
118          * from \a compareRootEntry_ (if they exist and comparison passes), or
119          * by creating new ones.
120          */
121         ReferenceDataEntry::EntryPointer  outputRootEntry_;
122         /*! \brief
123          * Whether updating existing reference data.
124          */
125         bool                    updateMismatchingEntries_;
126         //! `true` if self-testing (enables extra failure messages).
127         bool                    bSelfTestMode_;
128         /*! \brief
129          * Whether any reference checkers have been created for this data.
130          */
131         bool                    bInUse_;
132 };
133
134 }       // namespace internal
135
136 /********************************************************************
137  * Internal helpers
138  */
139
140 namespace
141 {
142
143 //! Convenience typedef for a smart pointer to TestReferenceDataImpl.
144 typedef std::shared_ptr<internal::TestReferenceDataImpl>
145     TestReferenceDataImplPointer;
146
147 /*! \brief
148  * Global reference data instance.
149  *
150  * The object is created when the test creates a TestReferenceData, and the
151  * object is destructed (and other post-processing is done) at the end of each
152  * test by ReferenceDataTestEventListener (which is installed as a Google Test
153  * test listener).
154  */
155 TestReferenceDataImplPointer g_referenceData;
156 //! Global reference data mode set with setReferenceDataMode().
157 ReferenceDataMode            g_referenceDataMode = erefdataCompare;
158
159 //! Returns the global reference data mode.
160 ReferenceDataMode getReferenceDataMode()
161 {
162     return g_referenceDataMode;
163 }
164
165 //! Returns a reference to the global reference data object.
166 TestReferenceDataImplPointer initReferenceDataInstance()
167 {
168     GMX_RELEASE_ASSERT(!g_referenceData,
169                        "Test cannot create multiple TestReferenceData instances");
170     g_referenceData.reset(new internal::TestReferenceDataImpl(getReferenceDataMode(), false));
171     return g_referenceData;
172 }
173
174 //! Handles reference data creation for self-tests.
175 TestReferenceDataImplPointer initReferenceDataInstanceForSelfTest(ReferenceDataMode mode)
176 {
177     if (g_referenceData)
178     {
179         GMX_RELEASE_ASSERT(g_referenceData.unique(),
180                            "Test cannot create multiple TestReferenceData instances");
181         g_referenceData->onTestEnd(true);
182         g_referenceData.reset();
183     }
184     g_referenceData.reset(new internal::TestReferenceDataImpl(mode, true));
185     return g_referenceData;
186 }
187
188 class ReferenceDataTestEventListener : public ::testing::EmptyTestEventListener
189 {
190     public:
191         void OnTestEnd(const ::testing::TestInfo &test_info) override
192         {
193             if (g_referenceData)
194             {
195                 GMX_RELEASE_ASSERT(g_referenceData.unique(),
196                                    "Test leaked TestRefeferenceData objects");
197                 g_referenceData->onTestEnd(test_info.result()->Passed());
198                 g_referenceData.reset();
199             }
200         }
201
202         void OnTestProgramEnd(const ::testing::UnitTest & /*unused*/) override
203         {
204             // Could be used e.g. to free internal buffers allocated by an XML parsing library
205         }
206 };
207
208 //! Formats a path to a reference data entry with a non-null id.
209 std::string formatEntryPath(const std::string &prefix, const std::string &id)
210 {
211     return prefix + "/" + id;
212 }
213
214 //! Formats a path to a reference data entry with a null id.
215 std::string formatSequenceEntryPath(const std::string &prefix, int seqIndex)
216 {
217     return formatString("%s/[%d]", prefix.c_str(), seqIndex+1);
218 }
219
220 //! Finds all entries that have not been checked under a given root.
221 void gatherUnusedEntries(const ReferenceDataEntry &root,
222                          const std::string        &rootPath,
223                          std::vector<std::string> *unusedPaths)
224 {
225     if (!root.hasBeenChecked())
226     {
227         unusedPaths->push_back(rootPath);
228         return;
229     }
230     int seqIndex = 0;
231     for (const auto &child : root.children())
232     {
233         std::string path;
234         if (child->id().empty())
235         {
236             path = formatSequenceEntryPath(rootPath, seqIndex);
237             ++seqIndex;
238         }
239         else
240         {
241             path = formatEntryPath(rootPath, child->id());
242         }
243         gatherUnusedEntries(*child, path, unusedPaths);
244     }
245 }
246
247 //! Produces a GTest assertion of any entries under given root have not been checked.
248 void checkUnusedEntries(const ReferenceDataEntry &root, const std::string &rootPath)
249 {
250     std::vector<std::string> unusedPaths;
251     gatherUnusedEntries(root, rootPath, &unusedPaths);
252     if (!unusedPaths.empty())
253     {
254         std::string paths;
255         if (unusedPaths.size() > 5)
256         {
257             paths = joinStrings(unusedPaths.begin(), unusedPaths.begin() + 5, "\n  ");
258             paths = "  " + paths + "\n  ...";
259         }
260         else
261         {
262             paths = joinStrings(unusedPaths.begin(), unusedPaths.end(), "\n  ");
263             paths = "  " + paths;
264         }
265         ADD_FAILURE() << "Reference data items not used in test:" << std::endl << paths;
266     }
267 }
268
269 }       // namespace
270
271 void initReferenceData(IOptionsContainer *options)
272 {
273     // Needs to correspond to the enum order in refdata.h.
274     const char *const refDataEnum[] =
275     { "check", "create", "update-changed", "update-all" };
276     options->addOption(
277             EnumOption<ReferenceDataMode>("ref-data")
278                 .enumValue(refDataEnum).store(&g_referenceDataMode)
279                 .description("Operation mode for tests that use reference data"));
280     ::testing::UnitTest::GetInstance()->listeners().Append(
281             new ReferenceDataTestEventListener);
282 }
283
284 /********************************************************************
285  * TestReferenceDataImpl definition
286  */
287
288 namespace internal
289 {
290
291 TestReferenceDataImpl::TestReferenceDataImpl(
292         ReferenceDataMode mode, bool bSelfTestMode)
293     : updateMismatchingEntries_(false), bSelfTestMode_(bSelfTestMode), bInUse_(false)
294 {
295     const std::string dirname =
296         bSelfTestMode
297         ? TestFileManager::getGlobalOutputTempDirectory()
298         : TestFileManager::getInputDataDirectory();
299     const std::string filename = TestFileManager::getTestSpecificFileName(".xml");
300     fullFilename_ = Path::join(dirname, "refdata", filename);
301
302     switch (mode)
303     {
304         case erefdataCompare:
305             if (File::exists(fullFilename_, File::throwOnError))
306             {
307                 compareRootEntry_ = readReferenceDataFile(fullFilename_);
308             }
309             break;
310         case erefdataCreateMissing:
311             if (File::exists(fullFilename_, File::throwOnError))
312             {
313                 compareRootEntry_ = readReferenceDataFile(fullFilename_);
314             }
315             else
316             {
317                 compareRootEntry_ = ReferenceDataEntry::createRoot();
318                 outputRootEntry_  = ReferenceDataEntry::createRoot();
319             }
320             break;
321         case erefdataUpdateChanged:
322             if (File::exists(fullFilename_, File::throwOnError))
323             {
324                 compareRootEntry_ = readReferenceDataFile(fullFilename_);
325             }
326             else
327             {
328                 compareRootEntry_ = ReferenceDataEntry::createRoot();
329             }
330             outputRootEntry_          = ReferenceDataEntry::createRoot();
331             updateMismatchingEntries_ = true;
332             break;
333         case erefdataUpdateAll:
334             compareRootEntry_ = ReferenceDataEntry::createRoot();
335             outputRootEntry_  = ReferenceDataEntry::createRoot();
336             break;
337     }
338 }
339
340 void TestReferenceDataImpl::onTestEnd(bool testPassed)
341 {
342     if (!bInUse_)
343     {
344         return;
345     }
346     // TODO: Only write the file with update-changed if there were actual changes.
347     if (outputRootEntry_)
348     {
349         if (testPassed)
350         {
351             std::string dirname = Path::getParentPath(fullFilename_);
352             if (!Directory::exists(dirname))
353             {
354                 if (Directory::create(dirname) != 0)
355                 {
356                     GMX_THROW(TestException("Creation of reference data directory failed: " + dirname));
357                 }
358             }
359             writeReferenceDataFile(fullFilename_, *outputRootEntry_);
360         }
361     }
362     else if (compareRootEntry_)
363     {
364         checkUnusedEntries(*compareRootEntry_, "");
365     }
366 }
367
368 }       // namespace internal
369
370
371 /********************************************************************
372  * TestReferenceChecker::Impl
373  */
374
375 /*! \internal \brief
376  * Private implementation class for TestReferenceChecker.
377  *
378  * \ingroup module_testutils
379  */
380 class TestReferenceChecker::Impl
381 {
382     public:
383         //! String constant for naming XML elements for boolean values.
384         static const char * const    cBooleanNodeName;
385         //! String constant for naming XML elements for string values.
386         static const char * const    cStringNodeName;
387         //! String constant for naming XML elements for unsigned char values.
388         static const char * const    cUCharNodeName;
389         //! String constant for naming XML elements for integer values.
390         static const char * const    cIntegerNodeName;
391         //! String constant for naming XML elements for int32 values.
392         static const char * const    cInt32NodeName;
393         //! String constant for naming XML elements for unsigned int32 values.
394         static const char * const    cUInt32NodeName;
395         //! String constant for naming XML elements for int32 values.
396         static const char * const    cInt64NodeName;
397         //! String constant for naming XML elements for unsigned int64 values.
398         static const char * const    cUInt64NodeName;
399         //! String constant for naming XML elements for floating-point values.
400         static const char * const    cRealNodeName;
401         //! String constant for naming XML attribute for value identifiers.
402         static const char * const    cIdAttrName;
403         //! String constant for naming compounds for vectors.
404         static const char * const    cVectorType;
405         //! String constant for naming compounds for key-value tree objects.
406         static const char * const    cObjectType;
407         //! String constant for naming compounds for sequences.
408         static const char * const    cSequenceType;
409         //! String constant for value identifier for sequence length.
410         static const char * const    cSequenceLengthName;
411
412         //! Creates a checker that does nothing.
413         explicit Impl(bool initialized);
414         //! Creates a checker with a given root entry.
415         Impl(const std::string &path, ReferenceDataEntry *compareRootEntry,
416              ReferenceDataEntry *outputRootEntry, bool updateMismatchingEntries,
417              bool bSelfTestMode, const FloatingPointTolerance &defaultTolerance);
418
419         //! Returns the path of this checker with \p id appended.
420         std::string appendPath(const char *id) const;
421
422         //! Creates an entry with given parameters and fills it with \p checker.
423         ReferenceDataEntry::EntryPointer
424         createEntry(const char *type, const char *id,
425                     const IReferenceDataEntryChecker &checker) const
426         {
427             ReferenceDataEntry::EntryPointer entry(new ReferenceDataEntry(type, id));
428             checker.fillEntry(entry.get());
429             return entry;
430         }
431         //! Checks an entry for correct type and using \p checker.
432         ::testing::AssertionResult
433         checkEntry(const ReferenceDataEntry &entry, const std::string &fullId,
434                    const char *type, const IReferenceDataEntryChecker &checker) const
435         {
436             if (entry.type() != type)
437             {
438                 return ::testing::AssertionFailure()
439                        << "Mismatching reference data item type" << std::endl
440                        << "  In item: " << fullId << std::endl
441                        << "   Actual: " << type << std::endl
442                        << "Reference: " << entry.type();
443             }
444             return checker.checkEntry(entry, fullId);
445         }
446         //! Finds an entry by id and updates the last found entry pointer.
447         ReferenceDataEntry *findEntry(const char *id);
448         /*! \brief
449          * Finds/creates a reference data entry to match against.
450          *
451          * \param[in]  type   Type of entry to create.
452          * \param[in]  id     Unique identifier of the entry (can be NULL, in
453          *      which case the next entry without an id is matched).
454          * \param[out] checker  Checker to use for filling out created entries.
455          * \returns    Matching entry, or NULL if no matching entry found
456          *      (NULL is never returned in write mode; new entries are created
457          *      instead).
458          */
459         ReferenceDataEntry *
460         findOrCreateEntry(const char *type, const char *id,
461                           const IReferenceDataEntryChecker &checker);
462         /*! \brief
463          * Helper method for checking a reference data value.
464          *
465          * \param[in]  name   Type of entry to find.
466          * \param[in]  id     Unique identifier of the entry (can be NULL, in
467          *     which case the next entry without an id is matched).
468          * \param[in]  checker  Checker that provides logic specific to the
469          *     type of the entry.
470          * \returns    Whether the reference data matched, including details
471          *     of the mismatch if the comparison failed.
472          * \throws     TestException if there is a problem parsing the
473          *     reference data.
474          *
475          * Performs common tasks in checking a reference value, such as
476          * finding or creating the correct entry.
477          * Caller needs to provide a checker object that provides the string
478          * value for a newly created entry and performs the actual comparison
479          * against a found entry.
480          */
481         ::testing::AssertionResult
482         processItem(const char *name, const char *id,
483                     const IReferenceDataEntryChecker &checker);
484         /*! \brief
485          * Whether the checker is initialized.
486          */
487         bool initialized() const { return initialized_; }
488         /*! \brief
489          * Whether the checker should ignore all validation calls.
490          *
491          * This is used to ignore any calls within compounds for which
492          * reference data could not be found, such that only one error is
493          * issued for the missing compound, instead of every individual value.
494          */
495         bool shouldIgnore() const
496         {
497             GMX_RELEASE_ASSERT(initialized(),
498                                "Accessing uninitialized reference data checker.");
499             return compareRootEntry_ == nullptr;
500         }
501
502         //! Whether initialized with other means than the default constructor.
503         bool                    initialized_;
504         //! Default floating-point comparison tolerance.
505         FloatingPointTolerance  defaultTolerance_;
506         /*! \brief
507          * Human-readable path to the root node of this checker.
508          *
509          * For the root checker, this will be "/", and for each compound, the
510          * id of the compound is added.  Used for reporting comparison
511          * mismatches.
512          */
513         std::string             path_;
514         /*! \brief
515          * Current entry under which reference data is searched for comparison.
516          *
517          * Points to either the TestReferenceDataImpl::compareRootEntry_, or to
518          * a compound entry in the tree rooted at that entry.
519          *
520          * Can be NULL, in which case this checker does nothing (doesn't even
521          * report errors, see shouldIgnore()).
522          */
523         ReferenceDataEntry     *compareRootEntry_;
524         /*! \brief
525          * Current entry under which entries for writing are created.
526          *
527          * Points to either the TestReferenceDataImpl::outputRootEntry_, or to
528          * a compound entry in the tree rooted at that entry.  NULL if only
529          * comparing, or if shouldIgnore() returns `false`.
530          */
531         ReferenceDataEntry     *outputRootEntry_;
532         /*! \brief
533          * Iterator to a child of \a compareRootEntry_ that was last found.
534          *
535          * If `compareRootEntry_->isValidChild()` returns false, no entry has
536          * been found yet.
537          * After every check, is updated to point to the entry that was used
538          * for the check.
539          * Subsequent checks start the search for the matching node on this
540          * node.
541          */
542         ReferenceDataEntry::ChildIterator lastFoundEntry_;
543         /*! \brief
544          * Whether the reference data is being written (true) or compared
545          * (false).
546          */
547         bool                    updateMismatchingEntries_;
548         //! `true` if self-testing (enables extra failure messages).
549         bool                    bSelfTestMode_;
550         /*! \brief
551          * Current number of unnamed elements in a sequence.
552          *
553          * It is the index of the current unnamed element.
554          */
555         int                     seqIndex_;
556 };
557
558 const char *const TestReferenceChecker::Impl::cBooleanNodeName    = "Bool";
559 const char *const TestReferenceChecker::Impl::cStringNodeName     = "String";
560 const char *const TestReferenceChecker::Impl::cUCharNodeName      = "UChar";
561 const char *const TestReferenceChecker::Impl::cIntegerNodeName    = "Int";
562 const char *const TestReferenceChecker::Impl::cInt32NodeName      = "Int32";
563 const char *const TestReferenceChecker::Impl::cUInt32NodeName     = "UInt32";
564 const char *const TestReferenceChecker::Impl::cInt64NodeName      = "Int64";
565 const char *const TestReferenceChecker::Impl::cUInt64NodeName     = "UInt64";
566 const char *const TestReferenceChecker::Impl::cRealNodeName       = "Real";
567 const char *const TestReferenceChecker::Impl::cIdAttrName         = "Name";
568 const char *const TestReferenceChecker::Impl::cVectorType         = "Vector";
569 const char *const TestReferenceChecker::Impl::cObjectType         = "Object";
570 const char *const TestReferenceChecker::Impl::cSequenceType       = "Sequence";
571 const char *const TestReferenceChecker::Impl::cSequenceLengthName = "Length";
572
573
574 TestReferenceChecker::Impl::Impl(bool initialized)
575     : initialized_(initialized), defaultTolerance_(defaultRealTolerance()),
576       compareRootEntry_(nullptr), outputRootEntry_(nullptr),
577       updateMismatchingEntries_(false), bSelfTestMode_(false), seqIndex_(-1)
578 {
579 }
580
581
582 TestReferenceChecker::Impl::Impl(const std::string &path,
583                                  ReferenceDataEntry *compareRootEntry,
584                                  ReferenceDataEntry *outputRootEntry,
585                                  bool updateMismatchingEntries, bool bSelfTestMode,
586                                  const FloatingPointTolerance &defaultTolerance)
587     : initialized_(true), defaultTolerance_(defaultTolerance), path_(path),
588       compareRootEntry_(compareRootEntry), outputRootEntry_(outputRootEntry),
589       lastFoundEntry_(compareRootEntry->children().end()),
590       updateMismatchingEntries_(updateMismatchingEntries),
591       bSelfTestMode_(bSelfTestMode), seqIndex_(-1)
592 {
593 }
594
595
596 std::string
597 TestReferenceChecker::Impl::appendPath(const char *id) const
598 {
599     return id != nullptr
600            ? formatEntryPath(path_, id)
601            : formatSequenceEntryPath(path_, seqIndex_);
602 }
603
604
605 ReferenceDataEntry *TestReferenceChecker::Impl::findEntry(const char *id)
606 {
607     ReferenceDataEntry::ChildIterator entry = compareRootEntry_->findChild(id, lastFoundEntry_);
608     seqIndex_ = (id == nullptr) ? seqIndex_+1 : -1;
609     if (compareRootEntry_->isValidChild(entry))
610     {
611         lastFoundEntry_ = entry;
612         return entry->get();
613     }
614     return nullptr;
615 }
616
617 ReferenceDataEntry *
618 TestReferenceChecker::Impl::findOrCreateEntry(
619         const char *type, const char *id,
620         const IReferenceDataEntryChecker &checker)
621 {
622     ReferenceDataEntry *entry = findEntry(id);
623     if (entry == nullptr && outputRootEntry_ != nullptr)
624     {
625         lastFoundEntry_ = compareRootEntry_->addChild(createEntry(type, id, checker));
626         entry           = lastFoundEntry_->get();
627     }
628     return entry;
629 }
630
631 ::testing::AssertionResult
632 TestReferenceChecker::Impl::processItem(const char *type, const char *id,
633                                         const IReferenceDataEntryChecker &checker)
634 {
635     if (shouldIgnore())
636     {
637         return ::testing::AssertionSuccess();
638     }
639     std::string         fullId = appendPath(id);
640     ReferenceDataEntry *entry  = findOrCreateEntry(type, id, checker);
641     if (entry == nullptr)
642     {
643         return ::testing::AssertionFailure()
644                << "Reference data item " << fullId << " not found";
645     }
646     entry->setChecked();
647     ::testing::AssertionResult result(checkEntry(*entry, fullId, type, checker));
648     if (outputRootEntry_ != nullptr && entry->correspondingOutputEntry() == nullptr)
649     {
650         if (!updateMismatchingEntries_ || result)
651         {
652             outputRootEntry_->addChild(entry->cloneToOutputEntry());
653         }
654         else
655         {
656             ReferenceDataEntry::EntryPointer outputEntry(createEntry(type, id, checker));
657             entry->setCorrespondingOutputEntry(outputEntry.get());
658             outputRootEntry_->addChild(move(outputEntry));
659             return ::testing::AssertionSuccess();
660         }
661     }
662     if (bSelfTestMode_ && !result)
663     {
664         ReferenceDataEntry expected(type, id);
665         checker.fillEntry(&expected);
666         result << std::endl
667         << "String value: '" << expected.value() << "'" << std::endl
668         << " Ref. string: '" << entry->value() << "'";
669     }
670     return result;
671 }
672
673
674 /********************************************************************
675  * TestReferenceData
676  */
677
678 TestReferenceData::TestReferenceData()
679     : impl_(initReferenceDataInstance())
680 {
681 }
682
683
684 TestReferenceData::TestReferenceData(ReferenceDataMode mode)
685     : impl_(initReferenceDataInstanceForSelfTest(mode))
686 {
687 }
688
689
690 TestReferenceData::~TestReferenceData()
691 {
692 }
693
694
695 TestReferenceChecker TestReferenceData::rootChecker()
696 {
697     if (!impl_->bInUse_ && !impl_->compareRootEntry_)
698     {
699         ADD_FAILURE() << "Reference data file not found: "
700         << impl_->fullFilename_;
701     }
702     impl_->bInUse_ = true;
703     if (!impl_->compareRootEntry_)
704     {
705         return TestReferenceChecker(new TestReferenceChecker::Impl(true));
706     }
707     impl_->compareRootEntry_->setChecked();
708     return TestReferenceChecker(
709             new TestReferenceChecker::Impl("", impl_->compareRootEntry_.get(),
710                                            impl_->outputRootEntry_.get(),
711                                            impl_->updateMismatchingEntries_, impl_->bSelfTestMode_,
712                                            defaultRealTolerance()));
713 }
714
715
716 /********************************************************************
717  * TestReferenceChecker
718  */
719
720 TestReferenceChecker::TestReferenceChecker()
721     : impl_(new Impl(false))
722 {
723 }
724
725 TestReferenceChecker::TestReferenceChecker(Impl *impl)
726     : impl_(impl)
727 {
728 }
729
730 TestReferenceChecker::TestReferenceChecker(const TestReferenceChecker &other)
731     : impl_(new Impl(*other.impl_))
732 {
733 }
734
735 TestReferenceChecker::TestReferenceChecker(TestReferenceChecker &&other) noexcept
736     : impl_(std::move(other.impl_))
737 {
738 }
739
740 TestReferenceChecker &
741 TestReferenceChecker::operator=(TestReferenceChecker &&other) noexcept
742 {
743     impl_ = std::move(other.impl_);
744     return *this;
745 }
746
747 TestReferenceChecker::~TestReferenceChecker()
748 {
749 }
750
751 bool TestReferenceChecker::isValid() const
752 {
753     return impl_->initialized();
754 }
755
756
757 void TestReferenceChecker::setDefaultTolerance(
758         const FloatingPointTolerance &tolerance)
759 {
760     impl_->defaultTolerance_ = tolerance;
761 }
762
763
764 void TestReferenceChecker::checkUnusedEntries()
765 {
766     if (impl_->compareRootEntry_)
767     {
768         gmx::test::checkUnusedEntries(*impl_->compareRootEntry_, impl_->path_);
769         // Mark them checked so that they are reported only once.
770         impl_->compareRootEntry_->setCheckedIncludingChildren();
771     }
772 }
773
774
775 bool TestReferenceChecker::checkPresent(bool bPresent, const char *id)
776 {
777     if (impl_->shouldIgnore() || impl_->outputRootEntry_ != nullptr)
778     {
779         return bPresent;
780     }
781     ReferenceDataEntry::ChildIterator  entry
782         = impl_->compareRootEntry_->findChild(id, impl_->lastFoundEntry_);
783     const bool                         bFound
784         = impl_->compareRootEntry_->isValidChild(entry);
785     if (bFound != bPresent)
786     {
787         ADD_FAILURE() << "Mismatch while checking reference data item '"
788         << impl_->appendPath(id) << "'\n"
789         << "Expected: " << (bPresent ? "it is present.\n" : "it is absent.\n")
790         << "  Actual: " << (bFound ? "it is present." : "it is absent.");
791     }
792     if (bFound && bPresent)
793     {
794         impl_->lastFoundEntry_ = entry;
795         return true;
796     }
797     return false;
798 }
799
800
801 TestReferenceChecker TestReferenceChecker::checkCompound(const char *type, const char *id)
802 {
803     if (impl_->shouldIgnore())
804     {
805         return TestReferenceChecker(new Impl(true));
806     }
807     std::string         fullId = impl_->appendPath(id);
808     NullChecker         checker;
809     ReferenceDataEntry *entry  = impl_->findOrCreateEntry(type, id, checker);
810     if (entry == nullptr)
811     {
812         ADD_FAILURE() << "Reference data item " << fullId << " not found";
813         return TestReferenceChecker(new Impl(true));
814     }
815     entry->setChecked();
816     if (impl_->updateMismatchingEntries_)
817     {
818         entry->makeCompound(type);
819     }
820     else
821     {
822         ::testing::AssertionResult result(impl_->checkEntry(*entry, fullId, type, checker));
823         EXPECT_PLAIN(result);
824         if (!result)
825         {
826             return TestReferenceChecker(new Impl(true));
827         }
828     }
829     if (impl_->outputRootEntry_ != nullptr && entry->correspondingOutputEntry() == nullptr)
830     {
831         impl_->outputRootEntry_->addChild(entry->cloneToOutputEntry());
832     }
833     return TestReferenceChecker(
834             new Impl(fullId, entry, entry->correspondingOutputEntry(),
835                      impl_->updateMismatchingEntries_, impl_->bSelfTestMode_,
836                      impl_->defaultTolerance_));
837 }
838
839 TestReferenceChecker TestReferenceChecker::checkCompound(const char *type, const std::string &id)
840 {
841     return checkCompound(type, id.c_str());
842 }
843
844 /*! \brief Throw a TestException if the caller tries to write particular refdata that can't work.
845  *
846  * If the string to write is non-empty and has only whitespace,
847  * TinyXML2 can't read it correctly, so throw an exception for this
848  * case, so that we can't accidentally use it and run into mysterious
849  * problems.
850  *
851  * \todo Eliminate this limitation of TinyXML2. See
852  * e.g. https://github.com/leethomason/tinyxml2/issues/432
853  */
854 static void
855 throwIfNonEmptyAndOnlyWhitespace(const std::string &s, const char *id)
856 {
857     if (!s.empty() && std::all_of(s.cbegin(), s.cend(), [](const char &c){ return std::isspace(c); }))
858     {
859         std::string message("String '" + s + "' with ");
860         message += (id != nullptr) ? "null " : "";
861         message += "ID ";
862         message += (id != nullptr) ? "" : id;
863         message += " cannot be handled. We must refuse to write a refdata String"
864             "field for a non-empty string that contains only whitespace, "
865             "because it will not be read correctly by TinyXML2.";
866         GMX_THROW(TestException(message));
867     }
868 }
869
870 void TestReferenceChecker::checkBoolean(bool value, const char *id)
871 {
872     EXPECT_PLAIN(impl_->processItem(Impl::cBooleanNodeName, id,
873                                     ExactStringChecker(value ? "true" : "false")));
874 }
875
876
877 void TestReferenceChecker::checkString(const char *value, const char *id)
878 {
879     throwIfNonEmptyAndOnlyWhitespace(value, id);
880     EXPECT_PLAIN(impl_->processItem(Impl::cStringNodeName, id,
881                                     ExactStringChecker(value)));
882 }
883
884
885 void TestReferenceChecker::checkString(const std::string &value, const char *id)
886 {
887     throwIfNonEmptyAndOnlyWhitespace(value, id);
888     EXPECT_PLAIN(impl_->processItem(Impl::cStringNodeName, id,
889                                     ExactStringChecker(value)));
890 }
891
892
893 void TestReferenceChecker::checkTextBlock(const std::string &value,
894                                           const char        *id)
895 {
896     EXPECT_PLAIN(impl_->processItem(Impl::cStringNodeName, id,
897                                     ExactStringBlockChecker(value)));
898 }
899
900
901 void TestReferenceChecker::checkUChar(unsigned char value, const char *id)
902 {
903     EXPECT_PLAIN(impl_->processItem(Impl::cUCharNodeName, id,
904                                     ExactStringChecker(formatString("%d", value))));
905 }
906
907 void TestReferenceChecker::checkInteger(int value, const char *id)
908 {
909     EXPECT_PLAIN(impl_->processItem(Impl::cIntegerNodeName, id,
910                                     ExactStringChecker(formatString("%d", value))));
911 }
912
913 void TestReferenceChecker::checkInt32(int32_t value, const char *id)
914 {
915     EXPECT_PLAIN(impl_->processItem(Impl::cInt32NodeName, id,
916                                     ExactStringChecker(formatString("%" PRId32, value))));
917 }
918
919 void TestReferenceChecker::checkUInt32(uint32_t value, const char *id)
920 {
921     EXPECT_PLAIN(impl_->processItem(Impl::cUInt32NodeName, id,
922                                     ExactStringChecker(formatString("%" PRIu32, value))));
923 }
924
925 void TestReferenceChecker::checkInt64(int64_t value, const char *id)
926 {
927     EXPECT_PLAIN(impl_->processItem(Impl::cInt64NodeName, id,
928                                     ExactStringChecker(formatString("%" PRId64, value))));
929 }
930
931 void TestReferenceChecker::checkUInt64(uint64_t value, const char *id)
932 {
933     EXPECT_PLAIN(impl_->processItem(Impl::cUInt64NodeName, id,
934                                     ExactStringChecker(formatString("%" PRIu64, value))));
935 }
936
937 void TestReferenceChecker::checkDouble(double value, const char *id)
938 {
939     FloatingPointChecker<double> checker(value, impl_->defaultTolerance_);
940     EXPECT_PLAIN(impl_->processItem(Impl::cRealNodeName, id, checker));
941 }
942
943
944 void TestReferenceChecker::checkFloat(float value, const char *id)
945 {
946     FloatingPointChecker<float> checker(value, impl_->defaultTolerance_);
947     EXPECT_PLAIN(impl_->processItem(Impl::cRealNodeName, id, checker));
948 }
949
950
951 void TestReferenceChecker::checkReal(float value, const char *id)
952 {
953     checkFloat(value, id);
954 }
955
956
957 void TestReferenceChecker::checkReal(double value, const char *id)
958 {
959     checkDouble(value, id);
960 }
961
962
963 void TestReferenceChecker::checkRealFromString(const std::string &value, const char *id)
964 {
965     FloatingPointFromStringChecker<real> checker(value, impl_->defaultTolerance_);
966     EXPECT_PLAIN(impl_->processItem(Impl::cRealNodeName, id, checker));
967 }
968
969
970 void TestReferenceChecker::checkVector(const int value[3], const char *id)
971 {
972     TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
973     compound.checkInteger(value[0], "X");
974     compound.checkInteger(value[1], "Y");
975     compound.checkInteger(value[2], "Z");
976 }
977
978
979 void TestReferenceChecker::checkVector(const float value[3], const char *id)
980 {
981     TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
982     compound.checkReal(value[0], "X");
983     compound.checkReal(value[1], "Y");
984     compound.checkReal(value[2], "Z");
985 }
986
987
988 void TestReferenceChecker::checkVector(const double value[3], const char *id)
989 {
990     TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
991     compound.checkReal(value[0], "X");
992     compound.checkReal(value[1], "Y");
993     compound.checkReal(value[2], "Z");
994 }
995
996
997 void TestReferenceChecker::checkAny(const Any &any, const char *id)
998 {
999     if (any.isType<bool>())
1000     {
1001         checkBoolean(any.cast<bool>(), id);
1002     }
1003     else if (any.isType<int>())
1004     {
1005         checkInteger(any.cast<int>(), id);
1006     }
1007     else if (any.isType<int32_t>())
1008     {
1009         checkInt32(any.cast<int32_t>(), id);
1010     }
1011     else if (any.isType<uint32_t>())
1012     {
1013         checkInt32(any.cast<uint32_t>(), id);
1014     }
1015     else if (any.isType<int64_t>())
1016     {
1017         checkInt64(any.cast<int64_t>(), id);
1018     }
1019     else if (any.isType<uint64_t>())
1020     {
1021         checkInt64(any.cast<uint64_t>(), id);
1022     }
1023     else if (any.isType<float>())
1024     {
1025         checkFloat(any.cast<float>(), id);
1026     }
1027     else if (any.isType<double>())
1028     {
1029         checkDouble(any.cast<double>(), id);
1030     }
1031     else if (any.isType<std::string>())
1032     {
1033         checkString(any.cast<std::string>(), id);
1034     }
1035     else
1036     {
1037         GMX_THROW(TestException("Unsupported any type"));
1038     }
1039 }
1040
1041
1042 void TestReferenceChecker::checkKeyValueTreeObject(const KeyValueTreeObject &tree, const char *id)
1043 {
1044     TestReferenceChecker compound(checkCompound(Impl::cObjectType, id));
1045     for (const auto &prop : tree.properties())
1046     {
1047         compound.checkKeyValueTreeValue(prop.value(), prop.key().c_str());
1048     }
1049     compound.checkUnusedEntries();
1050 }
1051
1052
1053 void TestReferenceChecker::checkKeyValueTreeValue(const KeyValueTreeValue &value, const char *id)
1054 {
1055     if (value.isObject())
1056     {
1057         checkKeyValueTreeObject(value.asObject(), id);
1058     }
1059     else if (value.isArray())
1060     {
1061         const auto &values = value.asArray().values();
1062         checkSequence(values.begin(), values.end(), id);
1063     }
1064     else
1065     {
1066         checkAny(value.asAny(), id);
1067     }
1068 }
1069
1070
1071 TestReferenceChecker
1072 TestReferenceChecker::checkSequenceCompound(const char *id, size_t length)
1073 {
1074     TestReferenceChecker compound(checkCompound(Impl::cSequenceType, id));
1075     compound.checkInteger(static_cast<int>(length), Impl::cSequenceLengthName);
1076     return compound;
1077 }
1078
1079
1080 unsigned char TestReferenceChecker::readUChar(const char *id)
1081 {
1082     if (impl_->shouldIgnore())
1083     {
1084         GMX_THROW(TestException("Trying to read from non-existent reference data value"));
1085     }
1086     int value = 0;
1087     EXPECT_PLAIN(impl_->processItem(Impl::cUCharNodeName, id,
1088                                     ValueExtractor<int>(&value)));
1089     return value;
1090 }
1091
1092
1093 int TestReferenceChecker::readInteger(const char *id)
1094 {
1095     if (impl_->shouldIgnore())
1096     {
1097         GMX_THROW(TestException("Trying to read from non-existent reference data value"));
1098     }
1099     int value = 0;
1100     EXPECT_PLAIN(impl_->processItem(Impl::cIntegerNodeName, id,
1101                                     ValueExtractor<int>(&value)));
1102     return value;
1103 }
1104
1105
1106 int32_t TestReferenceChecker::readInt32(const char *id)
1107 {
1108     if (impl_->shouldIgnore())
1109     {
1110         GMX_THROW(TestException("Trying to read from non-existent reference data value"));
1111     }
1112     int32_t value = 0;
1113     EXPECT_PLAIN(impl_->processItem(Impl::cInt32NodeName, id,
1114                                     ValueExtractor<int32_t>(&value)));
1115     return value;
1116 }
1117
1118
1119 int64_t TestReferenceChecker::readInt64(const char *id)
1120 {
1121     if (impl_->shouldIgnore())
1122     {
1123         GMX_THROW(TestException("Trying to read from non-existent reference data value"));
1124     }
1125     int64_t value = 0;
1126     EXPECT_PLAIN(impl_->processItem(Impl::cInt64NodeName, id,
1127                                     ValueExtractor<int64_t>(&value)));
1128     return value;
1129 }
1130
1131
1132 float TestReferenceChecker::readFloat(const char *id)
1133 {
1134     if (impl_->shouldIgnore())
1135     {
1136         GMX_THROW(TestException("Trying to read from non-existent reference data value"));
1137     }
1138     float value = 0;
1139     EXPECT_PLAIN(impl_->processItem(Impl::cRealNodeName, id,
1140                                     ValueExtractor<float>(&value)));
1141     return value;
1142 }
1143
1144
1145 double TestReferenceChecker::readDouble(const char *id)
1146 {
1147     if (impl_->shouldIgnore())
1148     {
1149         GMX_THROW(TestException("Trying to read from non-existent reference data value"));
1150     }
1151     double value = 0;
1152     EXPECT_PLAIN(impl_->processItem(Impl::cRealNodeName, id,
1153                                     ValueExtractor<double>(&value)));
1154     return value;
1155 }
1156
1157
1158 std::string TestReferenceChecker::readString(const char *id)
1159 {
1160     if (impl_->shouldIgnore())
1161     {
1162         GMX_THROW(TestException("Trying to read from non-existent reference data value"));
1163     }
1164     std::string value;
1165     EXPECT_PLAIN(impl_->processItem(Impl::cStringNodeName, id,
1166                                     ValueExtractor<std::string>(&value)));
1167     return value;
1168 }
1169
1170 } // namespace test
1171 } // namespace gmx