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: */
|