Introduce gmxpre.h for truly global definitions
[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, 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 <cstdio>
47 #include <cstdlib>
48
49 #include <limits>
50 #include <string>
51
52 #include <gtest/gtest.h>
53 #include <libxml/parser.h>
54 #include <libxml/xmlmemory.h>
55
56 #include "gromacs/options/basicoptions.h"
57 #include "gromacs/options/options.h"
58 #include "gromacs/utility/exceptions.h"
59 #include "gromacs/utility/gmxassert.h"
60 #include "gromacs/utility/path.h"
61 #include "gromacs/utility/stringutil.h"
62
63 #include "testutils/testasserts.h"
64 #include "testutils/testexceptions.h"
65 #include "testutils/testfilemanager.h"
66
67 namespace
68 {
69
70 /*! \internal \brief
71  * Global test environment for freeing up libxml2 internal buffers.
72  */
73 class TestReferenceDataEnvironment : public ::testing::Environment
74 {
75     public:
76         //! Frees internal buffers allocated by libxml2.
77         virtual void TearDown()
78         {
79             xmlCleanupParser();
80         }
81 };
82
83 //! Global reference data mode set with gmx::test::setReferenceDataMode().
84 // TODO: Make this a real enum; requires solving a TODO in StringOption.
85 int g_referenceDataMode = gmx::test::erefdataCompare;
86
87 } // namespace
88
89 namespace gmx
90 {
91 namespace test
92 {
93
94 ReferenceDataMode getReferenceDataMode()
95 {
96     return static_cast<ReferenceDataMode>(g_referenceDataMode);
97 }
98
99 void setReferenceDataMode(ReferenceDataMode mode)
100 {
101     g_referenceDataMode = mode;
102 }
103
104 std::string getReferenceDataPath()
105 {
106     return TestFileManager::getInputFilePath("refdata");
107 }
108
109 void initReferenceData(Options *options)
110 {
111     // Needs to correspond to the enum order in refdata.h.
112     const char *const refDataEnum[] = { "check", "create", "update" };
113     options->addOption(
114             StringOption("ref-data").enumValue(refDataEnum)
115                 .defaultEnumIndex(0)
116                 .storeEnumIndex(&g_referenceDataMode)
117                 .description("Operation mode for tests that use reference data"));
118     ::testing::AddGlobalTestEnvironment(new TestReferenceDataEnvironment);
119 }
120
121 /********************************************************************
122  * TestReferenceData::Impl
123  */
124
125 /*! \internal \brief
126  * Private implementation class for TestReferenceData.
127  *
128  * \ingroup module_testutils
129  */
130 class TestReferenceData::Impl
131 {
132     public:
133         //! String constant for output XML version string.
134         static const xmlChar * const cXmlVersion;
135         //! String constant for XML stylesheet processing instruction name.
136         static const xmlChar * const cXmlStyleSheetNodeName;
137         //! String constant for XML stylesheet reference.
138         static const xmlChar * const cXmlStyleSheetContent;
139         //! String constant for naming the root XML element.
140         static const xmlChar * const cRootNodeName;
141
142         //! Initializes a checker in the given mode.
143         Impl(ReferenceDataMode mode, bool bSelfTestMode);
144         ~Impl();
145
146         //! Full path of the reference data file.
147         std::string             fullFilename_;
148         /*! \brief
149          * XML document for the reference data.
150          *
151          * May be NULL if there was an I/O error in initialization.
152          */
153         xmlDocPtr               refDoc_;
154         /*! \brief
155          * Whether the reference data is being written (true) or compared
156          * (false).
157          */
158         bool                    bWrite_;
159         //! `true` if self-testing (enables extra failure messages).
160         bool                    bSelfTestMode_;
161         /*! \brief
162          * Whether any reference checkers have been created for this data.
163          */
164         bool                    bInUse_;
165 };
166
167 const xmlChar * const TestReferenceData::Impl::cXmlVersion =
168     (const xmlChar *)"1.0";
169 const xmlChar * const TestReferenceData::Impl::cXmlStyleSheetNodeName =
170     (const xmlChar *)"xml-stylesheet";
171 const xmlChar * const TestReferenceData::Impl::cXmlStyleSheetContent =
172     (const xmlChar *)"type=\"text/xsl\" href=\"referencedata.xsl\"";
173 const xmlChar * const TestReferenceData::Impl::cRootNodeName =
174     (const xmlChar *)"ReferenceData";
175
176
177 TestReferenceData::Impl::Impl(ReferenceDataMode mode, bool bSelfTestMode)
178     : refDoc_(NULL), bWrite_(false), bSelfTestMode_(bSelfTestMode), bInUse_(false)
179 {
180     std::string dirname  = getReferenceDataPath();
181     std::string filename = TestFileManager::getTestSpecificFileName(".xml");
182     fullFilename_ = Path::join(dirname, filename);
183
184     bWrite_ = true;
185     if (mode != erefdataUpdateAll)
186     {
187         FILE *fp = std::fopen(fullFilename_.c_str(), "r");
188         if (fp != NULL)
189         {
190             bWrite_ = false;
191             fclose(fp);
192         }
193         else if (mode == erefdataCompare)
194         {
195             bWrite_ = false;
196             return;
197         }
198     }
199     if (bWrite_)
200     {
201         // TODO: Error checking
202         refDoc_ = xmlNewDoc(cXmlVersion);
203         xmlNodePtr rootNode = xmlNewDocNode(refDoc_, NULL, cRootNodeName, NULL);
204         xmlDocSetRootElement(refDoc_, rootNode);
205         xmlNodePtr xslNode = xmlNewDocPI(refDoc_, cXmlStyleSheetNodeName,
206                                          cXmlStyleSheetContent);
207         xmlAddPrevSibling(rootNode, xslNode);
208     }
209     else
210     {
211         refDoc_ = xmlParseFile(fullFilename_.c_str());
212         if (refDoc_ == NULL)
213         {
214             GMX_THROW(TestException("Reference data not parsed successfully: " + fullFilename_));
215         }
216         xmlNodePtr rootNode = xmlDocGetRootElement(refDoc_);
217         if (rootNode == NULL)
218         {
219             xmlFreeDoc(refDoc_);
220             GMX_THROW(TestException("Reference data is empty: " + fullFilename_));
221         }
222         if (xmlStrcmp(rootNode->name, cRootNodeName) != 0)
223         {
224             xmlFreeDoc(refDoc_);
225             GMX_THROW(TestException("Invalid root node type in " + fullFilename_));
226         }
227     }
228 }
229
230
231 TestReferenceData::Impl::~Impl()
232 {
233     if (bWrite_ && bInUse_ && refDoc_ != NULL)
234     {
235         std::string dirname = getReferenceDataPath();
236         if (!Directory::exists(dirname))
237         {
238             if (Directory::create(dirname) != 0)
239             {
240                 ADD_FAILURE() << "Creation of reference data directory failed for " << dirname;
241             }
242         }
243         if (xmlSaveFormatFile(fullFilename_.c_str(), refDoc_, 1) == -1)
244         {
245             ADD_FAILURE() << "Saving reference data failed for " + fullFilename_;
246         }
247     }
248     if (refDoc_ != NULL)
249     {
250         xmlFreeDoc(refDoc_);
251     }
252 }
253
254
255 /********************************************************************
256  * TestReferenceChecker::Impl
257  */
258
259 /*! \internal \brief
260  * Private implementation class for TestReferenceChecker.
261  *
262  * \ingroup module_testutils
263  */
264 class TestReferenceChecker::Impl
265 {
266     public:
267         //! String constant for naming XML elements for boolean values.
268         static const xmlChar * const cBooleanNodeName;
269         //! String constant for naming XML elements for string values.
270         static const xmlChar * const cStringNodeName;
271         //! String constant for naming XML elements for integer values.
272         static const xmlChar * const cIntegerNodeName;
273         //! String constant for naming XML elements for int64 values.
274         static const xmlChar * const cInt64NodeName;
275         //! String constant for naming XML elements for unsigned int64 values.
276         static const xmlChar * const cUInt64NodeName;
277         //! String constant for naming XML elements for floating-point values.
278         static const xmlChar * const cRealNodeName;
279         //! String constant for naming XML attribute for value identifiers.
280         static const xmlChar * const cIdAttrName;
281         //! String constant for naming compounds for vectors.
282         static const char * const    cVectorType;
283         //! String constant for naming compounds for sequences.
284         static const char * const    cSequenceType;
285         //! String constant for value identifier for sequence length.
286         static const char * const    cSequenceLengthName;
287
288         //! Creates a checker that does nothing.
289         explicit Impl(bool bWrite);
290         //! Creates a checker with a given root node.
291         Impl(const std::string &path, xmlNodePtr rootNode, bool bWrite,
292              bool bSelfTestMode, const FloatingPointTolerance &defaultTolerance);
293
294         //! Returns a string for SCOPED_TRACE() for checking element \p id.
295         std::string traceString(const char *id) const;
296         //! Returns the path of this checker with \p id appended.
297         std::string appendPath(const char *id) const;
298
299         /*! \brief
300          * Finds a reference data node.
301          *
302          * \param[in]  name   Type of node to find (can be NULL, in which case
303          *      any type is matched).
304          * \param[in]  id     Unique identifier of the node (can be NULL, in
305          *      which case the next node without an id is matched).
306          * \returns    Matching node, or NULL if no matching node found.
307          *
308          * Searches for a node in the reference data that matches the given
309          * \p name and \p id.  Searching starts from the node that follows the
310          * previously matched node (relevant for performance, and if there are
311          * duplicate ids or nodes without ids).  Note that the match pointer is
312          * not updated by this method.
313          */
314         xmlNodePtr findNode(const xmlChar *name, const char *id) const;
315         /*! \brief
316          * Finds/creates a reference data node to match against.
317          *
318          * \param[in]  name   Type of node to find.
319          * \param[in]  id     Unique identifier of the node (can be NULL, in
320          *      which case the next node without an id is matched).
321          * \param[out] bFound Whether the node was found (false if the node was
322          *      created in write mode).
323          * \returns Matching node, or NULL if no matching node found
324          *      (NULL is never returned in write mode).
325          * \throws  TestException if node creation fails in write mode.
326          *
327          * Finds a node using findNode() and updates the match pointer is a
328          * match is found.  If a match is not found, the method returns NULL in
329          * read mode and creates a new node in write mode.  If the creation
330          * fails in write mode, throws.
331          */
332         xmlNodePtr findOrCreateNode(const xmlChar *name, const char *id,
333                                     bool *bFound);
334         /*! \brief
335          * Helper method for checking a reference data value.
336          *
337          * \param[in]  name   Type of node to find.
338          * \param[in]  id     Unique identifier of the node (can be NULL, in
339          *      which case the next node without an id is matched).
340          * \param[in]  value  String value of the value to be compared.
341          * \param[out] bFound true if a matchin value was found.
342          * \returns String value for the reference value.
343          * \throws  TestException if node creation fails in write mode.
344          *
345          * Performs common tasks in checking a reference value:
346          * finding/creating the correct XML node and reading/writing its string
347          * value.  Caller is responsible for converting the value to and from
348          * string where necessary and performing the actual comparison.
349          *
350          * In read mode, if a value is not found, adds a Google Test failure
351          * and returns an empty string.  If the reference value is found,
352          * returns it (\p value is not used in this case).
353          *
354          * In write mode, creates the node if it is not found, sets its value
355          * as \p value and returns \p value.
356          */
357         std::string processItem(const xmlChar *name, const char *id,
358                                 const char *value, bool *bFound);
359         //! Convenience wrapper that takes a std::string.
360         std::string processItem(const xmlChar *name, const char *id,
361                                 const std::string &value, bool *bFound);
362         /*! \brief
363          * Whether the checker should ignore all validation calls.
364          *
365          * This is used to ignore any calls within compounds for which
366          * reference data could not be found, such that only one error is
367          * issued for the missing compound, instead of every individual value.
368          */
369         bool shouldIgnore() const;
370
371         //! Default floating-point comparison tolerance.
372         FloatingPointTolerance  defaultTolerance_;
373         /*! \brief
374          * Human-readable path to the root node of this checker.
375          *
376          * For the root checker, this will be "/", and for each compound, the
377          * id of the compound is added.  Used for reporting comparison
378          * mismatches.
379          */
380         std::string             path_;
381         /*! \brief
382          * Current node under which reference data is searched.
383          *
384          * Points to either the root of TestReferenceData::Impl::refDoc_, or to
385          * a compound node.
386          *
387          * Can be NULL, in which case this checker does nothing (doesn't even
388          * report errors, see shouldIgnore()).
389          */
390         xmlNodePtr              currNode_;
391         /*! \brief
392          * Points to a child of \a currNode_ that was last found.
393          *
394          * On initialization, is initialized to NULL.  After every check, is
395          * updated to point to the node that was used for the check.
396          * Subsequent checks start the search for the matching node on this
397          * node.
398          *
399          * Is NULL if \a currNode_ contains no children or if no checks have
400          * yet been made.
401          * Otherwise, always points to a direct child of \a currNode_.
402          */
403         xmlNodePtr              prevFoundNode_;
404         /*! \brief
405          * Whether the reference data is being written (true) or compared
406          * (false).
407          */
408         bool                    bWrite_;
409         //! `true` if self-testing (enables extra failure messages).
410         bool                    bSelfTestMode_;
411         /*! \brief
412          * Current number of unnamed elements in a sequence.
413          *
414          * It is the index of the next added unnamed element.
415          */
416         int                     seqIndex_;
417 };
418
419 const xmlChar * const TestReferenceChecker::Impl::cBooleanNodeName =
420     (const xmlChar *)"Bool";
421 const xmlChar * const TestReferenceChecker::Impl::cStringNodeName =
422     (const xmlChar *)"String";
423 const xmlChar * const TestReferenceChecker::Impl::cIntegerNodeName  =
424     (const xmlChar *)"Int";
425 const xmlChar * const TestReferenceChecker::Impl::cInt64NodeName  =
426     (const xmlChar *)"Int64";
427 const xmlChar * const TestReferenceChecker::Impl::cUInt64NodeName  =
428     (const xmlChar *)"UInt64";
429 const xmlChar * const TestReferenceChecker::Impl::cRealNodeName =
430     (const xmlChar *)"Real";
431 const xmlChar * const TestReferenceChecker::Impl::cIdAttrName =
432     (const xmlChar *)"Name";
433 const char * const    TestReferenceChecker::Impl::cVectorType =
434     "Vector";
435 const char * const    TestReferenceChecker::Impl::cSequenceType =
436     "Sequence";
437 const char * const    TestReferenceChecker::Impl::cSequenceLengthName =
438     "Length";
439
440
441 TestReferenceChecker::Impl::Impl(bool bWrite)
442     : defaultTolerance_(defaultRealTolerance()),
443       currNode_(NULL), prevFoundNode_(NULL), bWrite_(bWrite),
444       bSelfTestMode_(false), seqIndex_(0)
445 {
446 }
447
448
449 TestReferenceChecker::Impl::Impl(const std::string &path, xmlNodePtr rootNode,
450                                  bool bWrite, bool bSelfTestMode,
451                                  const FloatingPointTolerance &defaultTolerance)
452     : defaultTolerance_(defaultTolerance), path_(path + "/"),
453       currNode_(rootNode), prevFoundNode_(NULL), bWrite_(bWrite),
454       bSelfTestMode_(bSelfTestMode), seqIndex_(0)
455 {
456 }
457
458
459 std::string
460 TestReferenceChecker::Impl::traceString(const char *id) const
461 {
462     return "Checking '" + appendPath(id) + "'";
463 }
464
465
466 std::string
467 TestReferenceChecker::Impl::appendPath(const char *id) const
468 {
469     std::string printId = (id != NULL) ? id : formatString("[%d]", seqIndex_);
470     return path_ + printId;
471 }
472
473
474 xmlNodePtr
475 TestReferenceChecker::Impl::findNode(const xmlChar *name, const char *id) const
476 {
477     if (currNode_ == NULL || currNode_->children == NULL)
478     {
479         return NULL;
480     }
481     const xmlChar *xmlId = reinterpret_cast<const xmlChar *>(id);
482     xmlNodePtr     node  = prevFoundNode_;
483     bool           bWrap = true;
484     if (node != NULL)
485     {
486         if (id == NULL)
487         {
488             xmlChar *refId = xmlGetProp(node, cIdAttrName);
489             if (refId == NULL)
490             {
491                 if (name == NULL || xmlStrcmp(node->name, name) == 0)
492                 {
493                     bWrap = false;
494                     node  = node->next;
495                     if (node == NULL)
496                     {
497                         return NULL;
498                     }
499                 }
500             }
501             else
502             {
503                 xmlFree(refId);
504             }
505         }
506     }
507     else
508     {
509         node  = currNode_->children;
510         bWrap = false;
511     }
512     do
513     {
514         if (name == NULL || xmlStrcmp(node->name, name) == 0)
515         {
516             xmlChar *refId = xmlGetProp(node, cIdAttrName);
517             if (xmlId == NULL && refId == NULL)
518             {
519                 return node;
520             }
521             if (refId != NULL)
522             {
523                 if (xmlId != NULL && xmlStrcmp(refId, xmlId) == 0)
524                 {
525                     xmlFree(refId);
526                     return node;
527                 }
528                 xmlFree(refId);
529             }
530         }
531         node = node->next;
532         if (bWrap && node == NULL)
533         {
534             node = currNode_->children;
535         }
536     }
537     while (node != NULL && node != prevFoundNode_);
538     return NULL;
539 }
540
541
542 xmlNodePtr
543 TestReferenceChecker::Impl::findOrCreateNode(const xmlChar *name,
544                                              const char    *id,
545                                              bool          *bFound)
546 {
547     *bFound = false;
548     xmlNodePtr node = findNode(name, id);
549     if (node != NULL)
550     {
551         *bFound        = true;
552         prevFoundNode_ = node;
553     }
554     if (node == NULL)
555     {
556         if (bWrite_)
557         {
558             node = xmlNewTextChild(currNode_, NULL, name, NULL);
559             if (node != NULL && id != NULL)
560             {
561                 const xmlChar *xmlId = reinterpret_cast<const xmlChar *>(id);
562                 xmlAttrPtr     prop  = xmlNewProp(node, cIdAttrName, xmlId);
563                 if (prop == NULL)
564                 {
565                     xmlFreeNode(node);
566                     node = NULL;
567                 }
568             }
569             if (node == NULL)
570             {
571                 GMX_THROW(TestException("XML node creation failed"));
572             }
573             prevFoundNode_ = node;
574         }
575         else
576         {
577             ADD_FAILURE() << "Reference data item not found";
578         }
579     }
580     seqIndex_ = (id == NULL) ? seqIndex_+1 : 0;
581
582     return node;
583 }
584
585
586 std::string
587 TestReferenceChecker::Impl::processItem(const xmlChar *name, const char *id,
588                                         const char *value, bool *bFound)
589 {
590     xmlNodePtr node = findOrCreateNode(name, id, bFound);
591     if (node == NULL)
592     {
593         return std::string();
594     }
595     if (bWrite_ && !*bFound)
596     {
597         xmlNodeAddContent(node, reinterpret_cast<const xmlChar *>(value));
598         *bFound = true;
599         return std::string(value);
600     }
601     else
602     {
603         xmlChar    *refXmlValue = xmlNodeGetContent(node);
604         std::string refValue(reinterpret_cast<const char *>(refXmlValue));
605         xmlFree(refXmlValue);
606         return refValue;
607     }
608 }
609
610
611 std::string
612 TestReferenceChecker::Impl::processItem(const xmlChar *name, const char *id,
613                                         const std::string &value, bool *bFound)
614 {
615     return processItem(name, id, value.c_str(), bFound);
616 }
617
618
619 bool
620 TestReferenceChecker::Impl::shouldIgnore() const
621 {
622     return currNode_ == NULL;
623 }
624
625
626 /********************************************************************
627  * TestReferenceData
628  */
629
630 TestReferenceData::TestReferenceData()
631     : impl_(new Impl(getReferenceDataMode(), false))
632 {
633 }
634
635
636 TestReferenceData::TestReferenceData(ReferenceDataMode mode)
637     : impl_(new Impl(mode, true))
638 {
639 }
640
641
642 TestReferenceData::~TestReferenceData()
643 {
644 }
645
646
647 bool TestReferenceData::isWriteMode() const
648 {
649     return impl_->bWrite_;
650 }
651
652
653 TestReferenceChecker TestReferenceData::rootChecker()
654 {
655     if (!isWriteMode() && !impl_->bInUse_ && impl_->refDoc_ == NULL)
656     {
657         ADD_FAILURE() << "Reference data file not found: "
658         << impl_->fullFilename_;
659     }
660     impl_->bInUse_ = true;
661     if (impl_->refDoc_ == NULL)
662     {
663         return TestReferenceChecker(new TestReferenceChecker::Impl(isWriteMode()));
664     }
665     xmlNodePtr rootNode = xmlDocGetRootElement(impl_->refDoc_);
666     // TODO: The default tolerance for double-precision builds that explicitly
667     // call checkFloat() may not be ideal.
668     return TestReferenceChecker(
669             new TestReferenceChecker::Impl("", rootNode, isWriteMode(),
670                                            impl_->bSelfTestMode_,
671                                            defaultRealTolerance()));
672 }
673
674
675 /********************************************************************
676  * TestReferenceChecker
677  */
678
679 TestReferenceChecker::TestReferenceChecker(Impl *impl)
680     : impl_(impl)
681 {
682 }
683
684
685 TestReferenceChecker::TestReferenceChecker(const TestReferenceChecker &other)
686     : impl_(new Impl(*other.impl_))
687 {
688 }
689
690
691 TestReferenceChecker &
692 TestReferenceChecker::operator=(const TestReferenceChecker &other)
693 {
694     impl_.reset(new Impl(*other.impl_));
695     return *this;
696 }
697
698
699 TestReferenceChecker::~TestReferenceChecker()
700 {
701 }
702
703
704 bool TestReferenceChecker::isWriteMode() const
705 {
706     return impl_->bWrite_;
707 }
708
709
710 void TestReferenceChecker::setDefaultTolerance(
711         const FloatingPointTolerance &tolerance)
712 {
713     impl_->defaultTolerance_ = tolerance;
714 }
715
716
717 bool TestReferenceChecker::checkPresent(bool bPresent, const char *id)
718 {
719     if (isWriteMode() || impl_->shouldIgnore())
720     {
721         return bPresent;
722     }
723     xmlNodePtr node   = impl_->findNode(NULL, id);
724     bool       bFound = (node != NULL);
725     if (bFound != bPresent)
726     {
727         ADD_FAILURE() << "Mismatch while checking reference data item '"
728         << impl_->appendPath(id) << "'\n"
729         << "Expected: " << (bPresent ? "it is present.\n" : "it is absent.\n")
730         << "  Actual: " << (bFound ? "it is present." : "it is absent.");
731     }
732     if (bFound && bPresent)
733     {
734         impl_->prevFoundNode_ = node;
735         return true;
736     }
737     return false;
738 }
739
740
741 TestReferenceChecker TestReferenceChecker::checkCompound(const char *type, const char *id)
742 {
743     SCOPED_TRACE(impl_->traceString(id));
744     if (impl_->shouldIgnore())
745     {
746         return TestReferenceChecker(new Impl(isWriteMode()));
747     }
748     const xmlChar *xmlNodeName = reinterpret_cast<const xmlChar *>(type);
749     bool           bFound;
750     xmlNodePtr     newNode     = impl_->findOrCreateNode(xmlNodeName, id, &bFound);
751     if (newNode == NULL)
752     {
753         return TestReferenceChecker(new Impl(isWriteMode()));
754     }
755     return TestReferenceChecker(
756             new Impl(impl_->appendPath(id), newNode, isWriteMode(),
757                      impl_->bSelfTestMode_, impl_->defaultTolerance_));
758 }
759
760
761 void TestReferenceChecker::checkBoolean(bool value, const char *id)
762 {
763     if (impl_->shouldIgnore())
764     {
765         return;
766     }
767     SCOPED_TRACE(impl_->traceString(id));
768     bool        bFound      = false;
769     const char *strValue    = value ? "true" : "false";
770     std::string refStrValue =
771         impl_->processItem(Impl::cBooleanNodeName, id, strValue, &bFound);
772     if (bFound)
773     {
774         EXPECT_EQ(refStrValue, strValue);
775     }
776 }
777
778
779 void TestReferenceChecker::checkString(const char *value, const char *id)
780 {
781     if (impl_->shouldIgnore())
782     {
783         return;
784     }
785     SCOPED_TRACE(impl_->traceString(id));
786     bool        bFound      = false;
787     std::string refStrValue =
788         impl_->processItem(Impl::cStringNodeName, id, value, &bFound);
789     if (bFound)
790     {
791         EXPECT_EQ(refStrValue, value);
792     }
793 }
794
795
796 void TestReferenceChecker::checkString(const std::string &value, const char *id)
797 {
798     checkString(value.c_str(), id);
799 }
800
801
802 void TestReferenceChecker::checkStringBlock(const std::string &value,
803                                             const char        *id)
804 {
805     if (impl_->shouldIgnore())
806     {
807         return;
808     }
809     SCOPED_TRACE(impl_->traceString(id));
810     bool       bFound;
811     xmlNodePtr node = impl_->findOrCreateNode(Impl::cStringNodeName, id, &bFound);
812     if (node == NULL)
813     {
814         return;
815     }
816     // An extra newline is written in the beginning to make lines align
817     // in the output xml (otherwise, the first line would be off by the length
818     // of the starting CDATA tag).
819     if (isWriteMode() && !bFound)
820     {
821         std::string    adjustedValue = "\n" + value;
822         const xmlChar *xmlValue
823             = reinterpret_cast<const xmlChar *>(adjustedValue.c_str());
824         // TODO: Figure out if \r and \r\n can be handled without them changing
825         // to \n in the roundtrip
826         xmlNodePtr cdata
827             = xmlNewCDataBlock(node->doc, xmlValue,
828                                static_cast<int>(adjustedValue.length()));
829         xmlAddChild(node, cdata);
830     }
831     else
832     {
833         xmlNodePtr cdata = node->children;
834         while (cdata != NULL && cdata->type != XML_CDATA_SECTION_NODE)
835         {
836             cdata = cdata->next;
837         }
838         if (cdata == NULL)
839         {
840             ADD_FAILURE() << "Invalid string block element";
841             return;
842         }
843         xmlChar *refXmlValue = xmlNodeGetContent(cdata);
844         if (refXmlValue[0] != '\n')
845         {
846             ADD_FAILURE() << "Invalid string block element";
847             xmlFree(refXmlValue);
848             return;
849         }
850         std::string refValue(reinterpret_cast<const char *>(refXmlValue + 1));
851         xmlFree(refXmlValue);
852         EXPECT_EQ(refValue, value);
853     }
854 }
855
856
857 void TestReferenceChecker::checkInteger(int value, const char *id)
858 {
859     if (impl_->shouldIgnore())
860     {
861         return;
862     }
863     SCOPED_TRACE(impl_->traceString(id));
864     bool        bFound      = false;
865     std::string strValue    = formatString("%d", value);
866     std::string refStrValue =
867         impl_->processItem(Impl::cIntegerNodeName, id, strValue, &bFound);
868     if (bFound)
869     {
870         EXPECT_EQ(refStrValue, strValue);
871     }
872 }
873
874 void TestReferenceChecker::checkInt64(gmx_int64_t value, const char *id)
875 {
876     if (impl_->shouldIgnore())
877     {
878         return;
879     }
880     SCOPED_TRACE(impl_->traceString(id));
881     bool        bFound      = false;
882     std::string strValue    = formatString("%" GMX_PRId64, value);
883     std::string refStrValue =
884         impl_->processItem(Impl::cInt64NodeName, id, strValue, &bFound);
885     if (bFound)
886     {
887         EXPECT_EQ(refStrValue, strValue);
888     }
889 }
890
891 void TestReferenceChecker::checkUInt64(gmx_uint64_t value, const char *id)
892 {
893     if (impl_->shouldIgnore())
894     {
895         return;
896     }
897     SCOPED_TRACE(impl_->traceString(id));
898     bool        bFound      = false;
899     std::string strValue    = formatString("%" GMX_PRIu64, value);
900     std::string refStrValue =
901         impl_->processItem(Impl::cUInt64NodeName, id, strValue, &bFound);
902     if (bFound)
903     {
904         EXPECT_EQ(refStrValue, strValue);
905     }
906 }
907
908 void TestReferenceChecker::checkDouble(double value, const char *id)
909 {
910     if (impl_->shouldIgnore())
911     {
912         return;
913     }
914     SCOPED_TRACE(impl_->traceString(id));
915     bool        bFound      = false;
916     const int   prec        = std::numeric_limits<double>::digits10 + 2;
917     std::string strValue    = formatString("%.*g", prec, value);
918     std::string refStrValue =
919         impl_->processItem(Impl::cRealNodeName, id, strValue, &bFound);
920     if (bFound)
921     {
922         char  *endptr;
923         double refValue = std::strtod(refStrValue.c_str(), &endptr);
924         EXPECT_EQ('\0', *endptr);
925         if (impl_->bSelfTestMode_)
926         {
927             EXPECT_DOUBLE_EQ_TOL(refValue, value, impl_->defaultTolerance_)
928             << "String value: " << strValue << std::endl
929             << " Ref. string: " << refStrValue;
930         }
931         else
932         {
933             EXPECT_DOUBLE_EQ_TOL(refValue, value, impl_->defaultTolerance_);
934         }
935     }
936 }
937
938
939 void TestReferenceChecker::checkFloat(float value, const char *id)
940 {
941     if (impl_->shouldIgnore())
942     {
943         return;
944     }
945     SCOPED_TRACE(impl_->traceString(id));
946     bool        bFound      = false;
947     const int   prec        = std::numeric_limits<float>::digits10 + 2;
948     std::string strValue    = formatString("%.*g", prec, value);
949     std::string refStrValue =
950         impl_->processItem(Impl::cRealNodeName, id, strValue, &bFound);
951     if (bFound)
952     {
953         char  *endptr;
954         float  refValue = static_cast<float>(std::strtod(refStrValue.c_str(), &endptr));
955         EXPECT_EQ('\0', *endptr);
956         if (impl_->bSelfTestMode_)
957         {
958             EXPECT_FLOAT_EQ_TOL(refValue, value, impl_->defaultTolerance_)
959             << "String value: " << strValue << std::endl
960             << " Ref. string: " << refStrValue;
961         }
962         else
963         {
964             EXPECT_FLOAT_EQ_TOL(refValue, value, impl_->defaultTolerance_);
965         }
966     }
967 }
968
969
970 void TestReferenceChecker::checkReal(float value, const char *id)
971 {
972     checkFloat(value, id);
973 }
974
975
976 void TestReferenceChecker::checkReal(double value, const char *id)
977 {
978     checkDouble(value, id);
979 }
980
981
982 void TestReferenceChecker::checkVector(const int value[3], const char *id)
983 {
984     TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
985     compound.checkInteger(value[0], "X");
986     compound.checkInteger(value[1], "Y");
987     compound.checkInteger(value[2], "Z");
988 }
989
990
991 void TestReferenceChecker::checkVector(const float value[3], const char *id)
992 {
993     TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
994     compound.checkReal(value[0], "X");
995     compound.checkReal(value[1], "Y");
996     compound.checkReal(value[2], "Z");
997 }
998
999
1000 void TestReferenceChecker::checkVector(const double value[3], const char *id)
1001 {
1002     TestReferenceChecker compound(checkCompound(Impl::cVectorType, id));
1003     compound.checkReal(value[0], "X");
1004     compound.checkReal(value[1], "Y");
1005     compound.checkReal(value[2], "Z");
1006 }
1007
1008
1009 TestReferenceChecker
1010 TestReferenceChecker::checkSequenceCompound(const char *id, size_t length)
1011 {
1012     TestReferenceChecker compound(checkCompound(Impl::cSequenceType, id));
1013     compound.checkInteger(static_cast<int>(length), Impl::cSequenceLengthName);
1014     return compound;
1015 }
1016
1017 } // namespace test
1018 } // namespace gmx