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