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