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