File: | home/maarten/src/libreoffice/core/test/source/diff/diff.cxx |
Warning: | line 177, column 24 Access to field 'name' results in a dereference of a null pointer (loaded from variable 'root1') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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_CPPUNIT1 1 | |||
11 | ||||
12 | #include <test/xmldiff.hxx> | |||
13 | ||||
14 | #include <libxml/xpath.h> | |||
15 | #include <libxml/parser.h> | |||
16 | ||||
17 | #include <set> | |||
18 | #include <sstream> | |||
19 | #include <cassert> | |||
20 | #include <vector> | |||
21 | ||||
22 | #if USE_CPPUNIT1 | |||
23 | #include <cppunit/TestAssert.h> | |||
24 | #endif | |||
25 | ||||
26 | #include <rtl/math.hxx> | |||
27 | ||||
28 | namespace { | |||
29 | ||||
30 | struct tolerance | |||
31 | { | |||
32 | ~tolerance() | |||
33 | { | |||
34 | xmlFree(elementName); | |||
35 | xmlFree(attribName); | |||
36 | } | |||
37 | ||||
38 | tolerance() | |||
39 | : elementName(nullptr) | |||
40 | , attribName(nullptr) | |||
41 | , relative(false) | |||
42 | , value(0.0) | |||
43 | { | |||
44 | } | |||
45 | ||||
46 | tolerance(const tolerance& tol) | |||
47 | { | |||
48 | elementName = xmlStrdup(tol.elementName); | |||
49 | attribName = xmlStrdup(tol.attribName); | |||
50 | relative = tol.relative; | |||
51 | value = tol.value; | |||
52 | } | |||
53 | ||||
54 | xmlChar* elementName; | |||
55 | xmlChar* attribName; | |||
56 | bool relative; | |||
57 | double value; | |||
58 | bool operator<(const tolerance& rTol) const | |||
59 | { | |||
60 | int cmp = xmlStrcmp(elementName, rTol.elementName); | |||
61 | if(cmp == 0) | |||
62 | { | |||
63 | cmp = xmlStrcmp(attribName, rTol.attribName); | |||
64 | } | |||
65 | ||||
66 | return cmp < 0; | |||
67 | } | |||
68 | }; | |||
69 | ||||
70 | class XMLDiff | |||
71 | { | |||
72 | public: | |||
73 | XMLDiff(const char* pFileName, const char* pContent, int size, const char* pToleranceFileName); | |||
74 | ~XMLDiff(); | |||
75 | ||||
76 | bool compare(); | |||
77 | private: | |||
78 | typedef std::set<tolerance> ToleranceContainer; | |||
79 | ||||
80 | void loadToleranceFile(xmlDocPtr xmlTolerance); | |||
81 | bool compareAttributes(xmlNodePtr node1, xmlNodePtr node2); | |||
82 | bool compareElements(xmlNode* node1, xmlNode* node2); | |||
83 | ||||
84 | /// Error message for cppunit that prints out when expected and found are not equal. | |||
85 | void cppunitAssertEqual(const xmlChar *expected, const xmlChar *found); | |||
86 | ||||
87 | /// Error message for cppunit that prints out when expected and found are not equal - for doubles. | |||
88 | void cppunitAssertEqualDouble(const xmlNodePtr node, const xmlAttrPtr attr, double expected, double found, double delta); | |||
89 | ||||
90 | ToleranceContainer toleranceContainer; | |||
91 | xmlDocPtr xmlFile1; | |||
92 | xmlDocPtr xmlFile2; | |||
93 | std::string fileName; | |||
94 | }; | |||
95 | ||||
96 | } | |||
97 | ||||
98 | XMLDiff::XMLDiff( const char* pFileName, const char* pContent, int size, const char* pToleranceFile) | |||
99 | : xmlFile1(xmlParseFile(pFileName)) | |||
100 | , xmlFile2(xmlParseMemory(pContent, size)) | |||
101 | , fileName(pFileName) | |||
102 | { | |||
103 | if(pToleranceFile) | |||
104 | { | |||
105 | xmlDocPtr xmlToleranceFile = xmlParseFile(pToleranceFile); | |||
106 | loadToleranceFile(xmlToleranceFile); | |||
107 | xmlFreeDoc(xmlToleranceFile); | |||
108 | } | |||
109 | } | |||
110 | ||||
111 | XMLDiff::~XMLDiff() | |||
112 | { | |||
113 | xmlFreeDoc(xmlFile1); | |||
114 | xmlFreeDoc(xmlFile2); | |||
115 | } | |||
116 | ||||
117 | namespace { | |||
118 | ||||
119 | void readAttributesForTolerance(xmlNodePtr node, tolerance& tol) | |||
120 | { | |||
121 | xmlChar* elementName = xmlGetProp(node, BAD_CAST(xmlChar *)("elementName")); | |||
122 | tol.elementName = elementName; | |||
123 | ||||
124 | xmlChar* attribName = xmlGetProp(node, BAD_CAST(xmlChar *)("attribName")); | |||
125 | tol.attribName = attribName; | |||
126 | ||||
127 | xmlChar* value = xmlGetProp(node, BAD_CAST(xmlChar *)("value")); | |||
128 | double val = xmlXPathCastStringToNumber(value); | |||
129 | xmlFree(value); | |||
130 | tol.value = val; | |||
131 | ||||
132 | xmlChar* relative = xmlGetProp(node, BAD_CAST(xmlChar *)("relative")); | |||
133 | bool rel = false; | |||
134 | if(xmlStrEqual(relative, BAD_CAST(xmlChar *)("true"))) | |||
135 | rel = true; | |||
136 | xmlFree(relative); | |||
137 | tol.relative = rel; | |||
138 | } | |||
139 | ||||
140 | } | |||
141 | ||||
142 | void XMLDiff::loadToleranceFile(xmlDocPtr xmlToleranceFile) | |||
143 | { | |||
144 | xmlNodePtr root = xmlDocGetRootElement(xmlToleranceFile); | |||
145 | #if USE_CPPUNIT1 | |||
146 | CPPUNIT_ASSERT_MESSAGE("did not find correct tolerance file", xmlStrEqual( root->name, BAD_CAST("tolerances") ))( CppUnit::Asserter::failIf( !(xmlStrEqual( root->name, (xmlChar *)("tolerances") )), CppUnit::Message( "assertion failed", "Expression: " "xmlStrEqual( root->name, BAD_CAST(\"tolerances\") )", CppUnit ::message_to_string("did not find correct tolerance file") ), CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 146 ) ) ); | |||
147 | #else | |||
148 | if(!xmlStrEqual( root->name, BAD_CAST(xmlChar *)("tolerances") )) | |||
149 | { | |||
150 | assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail ( "false", "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 150, __extension__ __PRETTY_FUNCTION__)); | |||
151 | return; | |||
152 | } | |||
153 | #endif | |||
154 | xmlNodePtr child = nullptr; | |||
155 | for (child = root->children; child != nullptr; child = child->next) | |||
156 | { | |||
157 | // assume a valid xml file | |||
158 | if(child->type != XML_ELEMENT_NODE) | |||
159 | continue; | |||
160 | ||||
161 | assert(xmlStrEqual(child->name, BAD_CAST("tolerance")))(static_cast <bool> (xmlStrEqual(child->name, (xmlChar *)("tolerance"))) ? void (0) : __assert_fail ("xmlStrEqual(child->name, BAD_CAST(\"tolerance\"))" , "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 161, __extension__ __PRETTY_FUNCTION__)); | |||
162 | ||||
163 | tolerance tol; | |||
164 | readAttributesForTolerance(child, tol); | |||
165 | toleranceContainer.insert(tol); | |||
166 | } | |||
167 | } | |||
168 | ||||
169 | bool XMLDiff::compare() | |||
170 | { | |||
171 | xmlNode* root1 = xmlDocGetRootElement(xmlFile1); | |||
172 | xmlNode* root2 = xmlDocGetRootElement(xmlFile2); | |||
173 | ||||
174 | #if USE_CPPUNIT1 | |||
175 | CPPUNIT_ASSERT(root1)( CppUnit::Asserter::failIf( !(root1), CppUnit::Message( "assertion failed" , "Expression: " "root1"), CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 175 ) ) ); | |||
176 | CPPUNIT_ASSERT(root2)( CppUnit::Asserter::failIf( !(root2), CppUnit::Message( "assertion failed" , "Expression: " "root2"), CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 176 ) ) ); | |||
177 | cppunitAssertEqual(root1->name, root2->name); | |||
| ||||
178 | #else | |||
179 | if (!root1 || !root2) | |||
180 | return false; | |||
181 | if(!xmlStrEqual(root1->name, root2->name)) | |||
182 | return false; | |||
183 | #endif | |||
184 | return compareElements(root1, root2); | |||
185 | } | |||
186 | ||||
187 | namespace { | |||
188 | ||||
189 | bool checkForEmptyChildren(xmlNodePtr node) | |||
190 | { | |||
191 | if(!node) | |||
192 | return true; | |||
193 | ||||
194 | for(; node != nullptr; node = node->next) | |||
195 | { | |||
196 | if (node->type == XML_ELEMENT_NODE) | |||
197 | return false; | |||
198 | } | |||
199 | return true; | |||
200 | } | |||
201 | ||||
202 | } | |||
203 | ||||
204 | bool XMLDiff::compareElements(xmlNode* node1, xmlNode* node2) | |||
205 | { | |||
206 | #if USE_CPPUNIT1 | |||
207 | cppunitAssertEqual(node1->name, node2->name); | |||
208 | #else | |||
209 | if (!xmlStrEqual( node1->name, node2->name )) | |||
210 | return false; | |||
211 | #endif | |||
212 | ||||
213 | //compare attributes | |||
214 | bool sameAttribs = compareAttributes(node1, node2); | |||
215 | #if USE_CPPUNIT1 | |||
216 | CPPUNIT_ASSERT(sameAttribs)( CppUnit::Asserter::failIf( !(sameAttribs), CppUnit::Message ( "assertion failed", "Expression: " "sameAttribs"), CppUnit:: SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 216 ) ) ); | |||
217 | #else | |||
218 | if (!sameAttribs) | |||
219 | return false; | |||
220 | #endif | |||
221 | ||||
222 | // compare children | |||
223 | xmlNode* child2 = nullptr; | |||
224 | xmlNode* child1 = nullptr; | |||
225 | for(child1 = node1->children, child2 = node2->children; child1 != nullptr && child2 != nullptr; child1 = child1->next, child2 = child2->next) | |||
226 | { | |||
227 | if (child1->type == XML_ELEMENT_NODE) | |||
228 | { | |||
229 | bool bCompare = compareElements(child1, child2); | |||
230 | if(!bCompare) | |||
231 | { | |||
232 | return false; | |||
233 | } | |||
234 | } | |||
235 | } | |||
236 | ||||
237 | #if USE_CPPUNIT1 | |||
238 | CPPUNIT_ASSERT(checkForEmptyChildren(child1))( CppUnit::Asserter::failIf( !(checkForEmptyChildren(child1)) , CppUnit::Message( "assertion failed", "Expression: " "checkForEmptyChildren(child1)" ), CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 238 ) ) ); | |||
239 | CPPUNIT_ASSERT(checkForEmptyChildren(child2))( CppUnit::Asserter::failIf( !(checkForEmptyChildren(child2)) , CppUnit::Message( "assertion failed", "Expression: " "checkForEmptyChildren(child2)" ), CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 239 ) ) ); | |||
240 | #else | |||
241 | if(!checkForEmptyChildren(child1) || !checkForEmptyChildren(child2)) | |||
242 | return false; | |||
243 | #endif | |||
244 | ||||
245 | return true; | |||
246 | } | |||
247 | ||||
248 | void XMLDiff::cppunitAssertEqual(const xmlChar *expected, const xmlChar *found) | |||
249 | { | |||
250 | #if USE_CPPUNIT1 | |||
251 | std::stringstream stringStream; | |||
252 | stringStream << "Reference: " << fileName << "\n- Expected: " << reinterpret_cast<const char*>(expected) << "\n- Found: " << reinterpret_cast<const char*>(found); | |||
253 | ||||
254 | CPPUNIT_ASSERT_MESSAGE(stringStream.str(), xmlStrEqual(expected, found))( CppUnit::Asserter::failIf( !(xmlStrEqual(expected, found)), CppUnit::Message( "assertion failed", "Expression: " "xmlStrEqual(expected, found)" , CppUnit::message_to_string(stringStream.str()) ), CppUnit:: SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 254 ) ) ); | |||
255 | #endif | |||
256 | } | |||
257 | ||||
258 | void XMLDiff::cppunitAssertEqualDouble(const xmlNodePtr node, const xmlAttrPtr attr, double expected, double found, double delta) | |||
259 | { | |||
260 | #if USE_CPPUNIT1 | |||
261 | xmlChar * path = xmlGetNodePath(node); | |||
262 | std::stringstream stringStream; | |||
263 | stringStream << "Reference: " << fileName << "\n- Node: " << reinterpret_cast<const char*>(path) << "\n- Attr: " << reinterpret_cast<const char*>(attr->name); | |||
264 | xmlFree(path); | |||
265 | ||||
266 | CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(stringStream.str(), expected, found, delta)( CppUnit::assertDoubleEquals( (expected), (found), (delta), CppUnit ::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 266 ), CppUnit::message_to_string(stringStream.str()) ) ); | |||
267 | #endif | |||
268 | } | |||
269 | ||||
270 | namespace { | |||
271 | ||||
272 | bool compareValuesWithTolerance(double val1, double val2, double tolerance, bool relative) | |||
273 | { | |||
274 | if(relative) | |||
275 | { | |||
276 | return (val1/tolerance) <= val2 && val2 <= (val1*tolerance); | |||
277 | } | |||
278 | else | |||
279 | { | |||
280 | return (val1 - tolerance) <= val2 && val2 <= (val1 + tolerance); | |||
281 | } | |||
282 | } | |||
283 | ||||
284 | } | |||
285 | ||||
286 | bool XMLDiff::compareAttributes(xmlNodePtr node1, xmlNodePtr node2) | |||
287 | { | |||
288 | xmlAttrPtr attr1 = nullptr; | |||
289 | xmlAttrPtr attr2 = nullptr; | |||
290 | for(attr1 = node1->properties, attr2 = node2->properties; attr1 != nullptr && attr2 != nullptr; attr1 = attr1->next, attr2 = attr2->next) | |||
291 | { | |||
292 | #if USE_CPPUNIT1 | |||
293 | cppunitAssertEqual(attr1->name, attr2->name); | |||
294 | #else | |||
295 | if (!xmlStrEqual( attr1->name, attr2->name )) | |||
296 | return false; | |||
297 | #endif | |||
298 | ||||
299 | xmlChar* val1 = xmlGetProp(node1, attr1->name); | |||
300 | xmlChar* val2 = xmlGetProp(node2, attr2->name); | |||
301 | ||||
302 | double dVal1 = xmlXPathCastStringToNumber(val1); | |||
303 | double dVal2 = xmlXPathCastStringToNumber(val2); | |||
304 | ||||
305 | if(!std::isnan(dVal1) || !std::isnan(dVal2)) | |||
306 | { | |||
307 | //compare by value and respect tolerance | |||
308 | tolerance tol; | |||
309 | tol.elementName = xmlStrdup(node1->name); | |||
310 | tol.attribName = xmlStrdup(attr1->name); | |||
311 | ToleranceContainer::iterator itr = toleranceContainer.find( tol ); | |||
312 | bool useTolerance = false; | |||
313 | if (itr != toleranceContainer.end()) | |||
314 | { | |||
315 | useTolerance = true; | |||
316 | } | |||
317 | ||||
318 | if (useTolerance) | |||
319 | { | |||
320 | bool valInTolerance = compareValuesWithTolerance(dVal1, dVal2, itr->value, itr->relative); | |||
321 | #if USE_CPPUNIT1 | |||
322 | std::stringstream stringStream("Expected Value: "); | |||
323 | stringStream << dVal1 << "; Found Value: " << dVal2 << "; Tolerance: " << itr->value; | |||
324 | stringStream << "; Relative: " << itr->relative; | |||
325 | CPPUNIT_ASSERT_MESSAGE(stringStream.str(), valInTolerance)( CppUnit::Asserter::failIf( !(valInTolerance), CppUnit::Message ( "assertion failed", "Expression: " "valInTolerance", CppUnit ::message_to_string(stringStream.str()) ), CppUnit::SourceLine ( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 325 ) ) ); | |||
326 | #else | |||
327 | if (!valInTolerance) | |||
328 | return false; | |||
329 | #endif | |||
330 | } | |||
331 | else | |||
332 | { | |||
333 | #if USE_CPPUNIT1 | |||
334 | cppunitAssertEqualDouble(node1, attr1, dVal1, dVal2, 1e-08); | |||
335 | #else | |||
336 | if (dVal1 != dVal2) | |||
337 | return false; | |||
338 | #endif | |||
339 | } | |||
340 | } | |||
341 | else | |||
342 | { | |||
343 | ||||
344 | #if USE_CPPUNIT1 | |||
345 | cppunitAssertEqual(val1, val2); | |||
346 | #else | |||
347 | if(!xmlStrEqual( val1, val2 )) | |||
348 | return false; | |||
349 | #endif | |||
350 | } | |||
351 | ||||
352 | xmlFree(val1); | |||
353 | xmlFree(val2); | |||
354 | } | |||
355 | ||||
356 | // unequal number of attributes | |||
357 | #ifdef CPPUNIT_ASSERT | |||
358 | if (attr1 || attr2) | |||
359 | { | |||
360 | std::stringstream failStream; | |||
361 | failStream << "Unequal number of attributes in "; | |||
362 | // print chain from document root | |||
363 | std::vector<std::string> parents; | |||
364 | auto n = node1; | |||
365 | while (n) | |||
366 | { | |||
367 | if (n->name) | |||
368 | parents.push_back(std::string(reinterpret_cast<const char *>(n->name))); | |||
369 | n = n->parent; | |||
370 | } | |||
371 | bool first = true; | |||
372 | for (auto it = parents.rbegin(); it != parents.rend(); ++it) | |||
373 | { | |||
374 | if (!first) | |||
375 | failStream << "->"; | |||
376 | first = false; | |||
377 | failStream << *it; | |||
378 | } | |||
379 | failStream << " Attr1: "; | |||
380 | attr1 = node1->properties; | |||
381 | while (attr1 != nullptr) | |||
382 | { | |||
383 | xmlChar* val1 = xmlGetProp(node1, attr1->name); | |||
384 | failStream << BAD_CAST(xmlChar *)(attr1->name) << "=" << BAD_CAST(xmlChar *)(val1) << ", "; | |||
385 | xmlFree(val1); | |||
386 | attr1 = attr1->next; | |||
387 | } | |||
388 | ||||
389 | failStream << " Attr2: "; | |||
390 | attr2 = node2->properties; | |||
391 | while (attr2 != nullptr) | |||
392 | { | |||
393 | xmlChar* val2 = xmlGetProp(node2, attr2->name); | |||
394 | failStream << BAD_CAST(xmlChar *)(attr2->name) << "=" << BAD_CAST(xmlChar *)(val2) << ", "; | |||
395 | xmlFree(val2); | |||
396 | attr2 = attr2->next; | |||
397 | } | |||
398 | CPPUNIT_ASSERT_MESSAGE(failStream.str(), false)( CppUnit::Asserter::failIf( !(false), CppUnit::Message( "assertion failed" , "Expression: " "false", CppUnit::message_to_string(failStream .str()) ), CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx" , 398 ) ) ); | |||
399 | } | |||
400 | #else | |||
401 | if (attr1 || attr2) | |||
402 | return false; | |||
403 | #endif | |||
404 | ||||
405 | return true; | |||
406 | } | |||
407 | ||||
408 | ||||
409 | bool | |||
410 | doXMLDiff(char const*const pFileName, char const*const pContent, int const size, | |||
411 | char const*const pToleranceFileName) | |||
412 | { | |||
413 | XMLDiff aDiff(pFileName, pContent, size, pToleranceFileName); | |||
414 | return aDiff.compare(); | |||
| ||||
415 | } | |||
416 | ||||
417 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |