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