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