LCOV - code coverage report
Current view: top level - usr/local/src/libreoffice/test/source/diff - diff.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 90 138 65.2 %
Date: 2013-07-09 Functions: 14 18 77.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  */
       9             : 
      10             : #define USE_CPPUNIT 1
      11             : 
      12             : #include "test/xmldiff.hxx"
      13             : 
      14             : #include <libxml/xpath.h>
      15             : #include <libxml/parser.h>
      16             : #include <libxml/tree.h>
      17             : #include <libxml/xmlmemory.h>
      18             : 
      19             : #include <set>
      20             : #include <cstring>
      21             : #include <sstream>
      22             : #include <cmath>
      23             : #include <cassert>
      24             : 
      25             : #if USE_CPPUNIT
      26             : #include <cppunit/extensions/HelperMacros.h>
      27             : #endif
      28             : 
      29             : #include <rtl/math.hxx>
      30             : 
      31             : 
      32             : struct tolerance
      33             : {
      34       18120 :     ~tolerance()
      35             :     {
      36       18120 :         xmlFree(elementName);
      37       18120 :         xmlFree(attribName);
      38       18120 :     }
      39             : 
      40       18120 :     tolerance()
      41             :     {
      42       18120 :         elementName = NULL;
      43       18120 :         attribName = NULL;
      44       18120 :     }
      45             : 
      46           0 :     tolerance(const tolerance& tol)
      47             :     {
      48           0 :         elementName = xmlStrdup(tol.elementName);
      49           0 :         attribName = xmlStrdup(tol.attribName);
      50           0 :         relative = tol.relative;
      51           0 :         value = tol.value;
      52           0 :     }
      53             : 
      54             :     xmlChar* elementName;
      55             :     xmlChar* attribName;
      56             :     bool relative;
      57             :     double value;
      58             :     bool operator==(const tolerance& rTol) const { return xmlStrEqual(elementName, rTol.elementName) && xmlStrEqual(attribName, rTol.attribName); }
      59           0 :     bool operator<(const tolerance& rTol) const
      60             :     {
      61           0 :         int cmp = xmlStrcmp(elementName, rTol.elementName);
      62           0 :         if(cmp == 0)
      63             :         {
      64           0 :             cmp = xmlStrcmp(attribName, rTol.attribName);
      65             :         }
      66             : 
      67           0 :         if(cmp>=0)
      68           0 :             return false;
      69             :         else
      70           0 :             return true;
      71             :     }
      72             : };
      73             : 
      74             : class XMLDiff
      75             : {
      76             : public:
      77             :     XMLDiff(const char* pFileName, const char* pContent, int size, const char* pToleranceFileName);
      78             :     ~XMLDiff();
      79             : 
      80             :     bool compare();
      81             : private:
      82             :     typedef std::set<tolerance> ToleranceContainer;
      83             : 
      84             :     void loadToleranceFile(xmlDocPtr xmlTolerance);
      85             :     bool compareAttributes(xmlNodePtr node1, xmlNodePtr node2);
      86             :     bool compareElements(xmlNodePtr node1, xmlNodePtr node2);
      87             : 
      88             :     /// Error message for cppunit that prints out when expected and found are not equal.
      89             :     void cppunitAssertEqual(const xmlChar *expected, const xmlChar *found);
      90             : 
      91             :     /// Error message for cppunit that prints out when expected and found are not equal - for doubles.
      92             :     void cppunitAssertEqualDouble(const xmlChar *node, double expected, double found, double delta);
      93             : 
      94             :     ToleranceContainer toleranceContainer;
      95             :     xmlDocPtr xmlFile1;
      96             :     xmlDocPtr xmlFile2;
      97             :     std::string fileName;
      98             : };
      99             : 
     100             : 
     101             : 
     102             : 
     103          16 : XMLDiff::XMLDiff( const char* pFileName, const char* pContent, int size, const char* pToleranceFile)
     104          16 :     : fileName(pFileName)
     105             : {
     106          16 :     xmlFile1 = xmlParseFile(pFileName);
     107          16 :     xmlFile2 = xmlParseMemory(pContent, size);
     108             : 
     109          16 :     xmlDocPtr xmlToleranceFile = xmlParseFile(pToleranceFile);
     110          16 :     loadToleranceFile(xmlToleranceFile);
     111          16 :     xmlFreeDoc(xmlToleranceFile);
     112          16 : }
     113             : 
     114          32 : XMLDiff::~XMLDiff()
     115             : {
     116          16 :     xmlFreeDoc(xmlFile1);
     117          16 :     xmlFreeDoc(xmlFile2);
     118          16 : }
     119             : 
     120             : namespace {
     121             : 
     122           0 : void readAttributesForTolerance(xmlNodePtr node, tolerance& tol)
     123             : {
     124           0 :     xmlChar* elementName = xmlGetProp(node, BAD_CAST("elementName"));
     125           0 :     tol.elementName = elementName;
     126             : 
     127           0 :     xmlChar* attribName = xmlGetProp(node, BAD_CAST("attribName"));
     128           0 :     tol.attribName = attribName;
     129             : 
     130           0 :     xmlChar* value = xmlGetProp(node, BAD_CAST("value"));
     131           0 :     double val = xmlXPathCastStringToNumber(value);
     132           0 :     xmlFree(value);
     133           0 :     tol.value = val;
     134             : 
     135           0 :     xmlChar* relative = xmlGetProp(node, BAD_CAST("relative"));
     136           0 :     bool rel = false;
     137           0 :     if(xmlStrEqual(relative, BAD_CAST("true")))
     138           0 :         rel = true;
     139           0 :     xmlFree(relative);
     140           0 :     tol.relative = rel;
     141           0 : }
     142             : 
     143             : }
     144             : 
     145          16 : void XMLDiff::loadToleranceFile(xmlDocPtr xmlToleranceFile)
     146             : {
     147          16 :     xmlNodePtr root = xmlDocGetRootElement(xmlToleranceFile);
     148             : #if USE_CPPUNIT
     149          16 :     CPPUNIT_ASSERT_MESSAGE("did not find correct tolerance file", xmlStrEqual( root->name, BAD_CAST("tolerances") ));
     150             : #else
     151             :     if(!xmlStrEqual( root->name, BAD_CAST("tolerances") ))
     152             :     {
     153             :         assert(false);
     154             :         return;
     155             :     }
     156             : #endif
     157          16 :     xmlNodePtr child = NULL;
     158          32 :     for (child = root->children; child != NULL; child = child->next)
     159             :     {
     160             :         // assume a valid xml file
     161          16 :         if(child->type != XML_ELEMENT_NODE)
     162          16 :             continue;
     163             : 
     164             :         assert(xmlStrEqual(child->name, BAD_CAST("tolerance")));
     165             : 
     166           0 :         tolerance tol;
     167           0 :         readAttributesForTolerance(child, tol);
     168           0 :         toleranceContainer.insert(tol);
     169           0 :     }
     170          16 : }
     171             : 
     172          16 : bool XMLDiff::compare()
     173             : {
     174          16 :     xmlNode* root1 = xmlDocGetRootElement(xmlFile1);
     175          16 :     xmlNode* root2 = xmlDocGetRootElement(xmlFile2);
     176             : 
     177             : #if USE_CPPUNIT
     178          16 :     CPPUNIT_ASSERT(root1);
     179          16 :     CPPUNIT_ASSERT(root2);
     180          16 :     cppunitAssertEqual(root1->name, root2->name);
     181             : #else
     182             :     if (!root1 || !root2)
     183             :         return false;
     184             :     if(!xmlStrEqual(root1->name, root2->name))
     185             :         return false;
     186             : #endif
     187          16 :     return compareElements(root1, root2);
     188             : }
     189             : 
     190             : namespace {
     191             : 
     192       21032 : bool checkForEmptyChildren(xmlNodePtr node)
     193             : {
     194       21032 :     if(!node)
     195       21032 :         return true;
     196             : 
     197           0 :     for(; node != NULL; node = node->next)
     198             :     {
     199           0 :         if (node->type == XML_ELEMENT_NODE)
     200           0 :             return false;
     201             :     }
     202           0 :     return true;
     203             : }
     204             : 
     205             : }
     206             : 
     207       10516 : bool XMLDiff::compareElements(xmlNode* node1, xmlNode* node2)
     208             : {
     209             : #if USE_CPPUNIT
     210       10516 :     cppunitAssertEqual(node1->name, node2->name);
     211             : #else
     212             :     if (!xmlStrEqual( node1->name, node2->name ))
     213             :         return false;
     214             : #endif
     215             : 
     216             :     //compare attributes
     217       10516 :     bool sameAttribs = compareAttributes(node1, node2);
     218             : #if USE_CPPUNIT
     219       10516 :     CPPUNIT_ASSERT(sameAttribs);
     220             : #else
     221             :     if (!sameAttribs)
     222             :         return false;
     223             : #endif
     224             : 
     225             :     // compare children
     226       10516 :     xmlNode* child2 = NULL;
     227       10516 :     xmlNode* child1 = NULL;
     228       34779 :     for(child1 = node1->children, child2 = node2->children; child1 != NULL && child2 != NULL; child1 = child1->next, child2 = child2->next)
     229             :     {
     230       24263 :         if (child1->type == XML_ELEMENT_NODE)
     231             :         {
     232       10500 :             bool bCompare = compareElements(child1, child2);
     233       10500 :             if(!bCompare)
     234             :             {
     235           0 :                 return false;
     236             :             }
     237             :         }
     238             :     }
     239             : 
     240             : #if USE_CPPUNIT
     241       10516 :     CPPUNIT_ASSERT(checkForEmptyChildren(child1));
     242       10516 :     CPPUNIT_ASSERT(checkForEmptyChildren(child2));
     243             : #else
     244             :     if(!checkForEmptyChildren(child1) || !checkForEmptyChildren(child2))
     245             :         return false;
     246             : #endif
     247             : 
     248       10516 :     return true;
     249             : }
     250             : 
     251       41296 : void XMLDiff::cppunitAssertEqual(const xmlChar *expected, const xmlChar *found)
     252             : {
     253             : #if USE_CPPUNIT
     254       41296 :     std::stringstream stringStream;
     255       41296 :     stringStream << "Reference: " << fileName << "\n- Expected: " << (const char*) expected << "\n- Found: " << (const char*) found;
     256             : 
     257       41296 :     CPPUNIT_ASSERT_MESSAGE(stringStream.str(), xmlStrEqual(expected, found));
     258             : #endif
     259       41296 : }
     260             : 
     261       18120 : void XMLDiff::cppunitAssertEqualDouble(const xmlChar *node, double expected, double found, double delta)
     262             : {
     263             : #if USE_CPPUNIT
     264       18120 :     std::stringstream stringStream;
     265       18120 :     stringStream << "Reference: " << fileName << "\n- Node: " << (const char*) node;
     266             : 
     267       18120 :     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(stringStream.str(), expected, found, delta);
     268             : #endif
     269       18120 : }
     270             : 
     271             : namespace {
     272             : 
     273           0 : bool compareValuesWithTolerance(double val1, double val2, double tolerance, bool relative)
     274             : {
     275           0 :     if(relative)
     276             :     {
     277           0 :         return (val1/tolerance) <= val2 && val2 <= (val1*tolerance);
     278             :     }
     279             :     else
     280             :     {
     281           0 :         return (val1 - tolerance) <= val2 && val2 <= (val1 + tolerance);
     282             :     }
     283             : }
     284             : 
     285             : }
     286             : 
     287       10516 : bool XMLDiff::compareAttributes(xmlNodePtr node1, xmlNodePtr node2)
     288             : {
     289       10516 :     xmlAttrPtr attr1 = NULL;
     290       10516 :     xmlAttrPtr attr2 = NULL;
     291       34958 :     for(attr1 = node1->properties, attr2 = node2->properties; attr1 != NULL && attr2 != NULL; attr1 = attr1->next, attr2 = attr2->next)
     292             :     {
     293             : #if USE_CPPUNIT
     294       24442 :         cppunitAssertEqual(attr1->name, attr2->name);
     295             : #else
     296             :         if (!xmlStrEqual( attr1->name, attr2->name ))
     297             :             return false;
     298             : #endif
     299             : 
     300       24442 :         xmlChar* val1 = xmlGetProp(node1, attr1->name);
     301       24442 :         xmlChar* val2 = xmlGetProp(node2, attr2->name);
     302             : 
     303       24442 :         double dVal1 = xmlXPathCastStringToNumber(val1);
     304       24442 :         double dVal2 = xmlXPathCastStringToNumber(val2);
     305             : 
     306       24442 :         if(!rtl::math::isNan(dVal1) || !rtl::math::isNan(dVal2))
     307             :         {
     308             :             //compare by value and respect tolerance
     309       18120 :             tolerance tol;
     310       18120 :             tol.elementName = xmlStrdup(node1->name);
     311       18120 :             tol.attribName = xmlStrdup(attr1->name);
     312       18120 :             ToleranceContainer::iterator itr = toleranceContainer.find( tol );
     313       18120 :             bool useTolerance = false;
     314       18120 :             if (itr != toleranceContainer.end())
     315             :             {
     316           0 :                 useTolerance = true;
     317             :             }
     318             : 
     319       18120 :             if (useTolerance)
     320             :             {
     321           0 :                 bool valInTolerance = compareValuesWithTolerance(dVal1, dVal2, itr->value, itr->relative);
     322             : #if USE_CPPUNIT
     323           0 :                 std::stringstream stringStream("Expected Value: ");
     324           0 :                 stringStream << dVal1 << "; Found Value: " << dVal2 << "; Tolerance: " << itr->value;
     325           0 :                 stringStream << "; Relative: " << itr->relative;
     326           0 :                 CPPUNIT_ASSERT_MESSAGE(stringStream.str(), valInTolerance);
     327             : #else
     328             :                 if (!valInTolerance)
     329             :                     return false;
     330             : #endif
     331             :             }
     332             :             else
     333             :             {
     334             : #if USE_CPPUNIT
     335       18120 :                 cppunitAssertEqualDouble(attr1->name, dVal1, dVal2, 1e-08);
     336             : #else
     337             :                 if (dVal1 != dVal2)
     338             :                     return false;
     339             : #endif
     340       18120 :             }
     341             :         }
     342             :         else
     343             :         {
     344             : 
     345             : #if USE_CPPUNIT
     346        6322 :             cppunitAssertEqual(val1, val2);
     347             : #else
     348             :             if(!xmlStrEqual( val1, val2 ))
     349             :                 return false;
     350             : #endif
     351             :         }
     352             : 
     353       24442 :         xmlFree(val1);
     354       24442 :         xmlFree(val2);
     355             :     }
     356             : 
     357             :     // unequal number of attributes
     358             : #ifdef CPPUNIT_ASSERT
     359       10516 :     CPPUNIT_ASSERT(!attr1);
     360       10516 :     CPPUNIT_ASSERT(!attr2);
     361             : #else
     362             :     if (attr1 || attr2)
     363             :         return false;
     364             : #endif
     365             : 
     366       10516 :     return true;
     367             : }
     368             : 
     369             : 
     370             : bool
     371          16 : doXMLDiff(char const*const pFileName, char const*const pContent, int const size,
     372             :           char const*const pToleranceFileName)
     373             : {
     374          16 :     XMLDiff aDiff(pFileName, pContent, size, pToleranceFileName);
     375          16 :     return aDiff.compare();
     376         171 : }
     377             : 
     378             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10