Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/test/source/diff/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 10 kB image not shown  

Quelle  diff.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */


#define USE_CPPUNIT 1

#include <test/xmldiff.hxx>

#include <libxml/xpath.h>
#include <libxml/parser.h>

#include <set>
#include <sstream>
#include <cassert>
#include <cmath>
#include <vector>

#if USE_CPPUNIT
#include <cppunit/TestAssert.h>
#endif

namespace {

struct tolerance
{
    ~tolerance()
    {
        xmlFree(elementName);
        xmlFree(attribName);
    }

    tolerance()
        : elementName(nullptr)
        , attribName(nullptr)
        , relative(false)
        , value(0.0)
    {
    }

    tolerance(const tolerance& tol)
    {
        elementName = xmlStrdup(tol.elementName);
        attribName = xmlStrdup(tol.attribName);
        relative = tol.relative;
        value = tol.value;
    }

    xmlChar* elementName;
    xmlChar* attribName;
    bool relative;
    double value;
    bool operator<(const tolerance& rTol) const
    {
        int cmp = xmlStrcmp(elementName, rTol.elementName);
        if(cmp == 0)
        {
            cmp = xmlStrcmp(attribName, rTol.attribName);
        }

        return cmp < 0;
    }
};

class XMLDiff
{
public:
    XMLDiff(const char* pFileName, const char* pContent, int size, const char* pToleranceFileName);
    ~XMLDiff();

    bool compare();
private:
    typedef std::set<tolerance> ToleranceContainer;

    void loadToleranceFile(xmlDocPtr xmlTolerance);
    bool compareAttributes(xmlNodePtr node1, xmlNodePtr node2);
    bool compareElements(xmlNode* node1, xmlNode* node2);

    /// Error message for cppunit that prints out when expected and found are not equal.
    void cppunitAssertEqual(const xmlChar *expected, const xmlChar *found);

    /// Error message for cppunit that prints out when expected and found are not equal - for doubles.
    void cppunitAssertEqualDouble(const xmlNodePtr node, const xmlAttrPtr attr, double expected, double found, double delta);

    ToleranceContainer toleranceContainer;
    xmlDocPtr xmlFile1;
    xmlDocPtr xmlFile2;
    std::string fileName;
};

}

XMLDiff::XMLDiff( const char* pFileName, const char* pContent, int size, const char* pToleranceFile)
    : xmlFile1(xmlParseFile(pFileName))
    , xmlFile2(xmlParseMemory(pContent, size))
    , fileName(pFileName)
{
    if(pToleranceFile)
    {
        xmlDocPtr xmlToleranceFile = xmlParseFile(pToleranceFile);
        loadToleranceFile(xmlToleranceFile);
        xmlFreeDoc(xmlToleranceFile);
    }
}

XMLDiff::~XMLDiff()
{
    xmlFreeDoc(xmlFile1);
    xmlFreeDoc(xmlFile2);
}

namespace {

void readAttributesForTolerance(xmlNodePtr node, tolerance& tol)
{
    xmlChar* elementName = xmlGetProp(node, BAD_CAST("elementName"));
    tol.elementName = elementName;

    xmlChar* attribName = xmlGetProp(node, BAD_CAST("attribName"));
    tol.attribName = attribName;

    xmlChar* value = xmlGetProp(node, BAD_CAST("value"));
    double val = xmlXPathCastStringToNumber(value);
    xmlFree(value);
    tol.value = val;

    xmlChar* relative = xmlGetProp(node, BAD_CAST("relative"));
    bool rel = false;
    if(xmlStrEqual(relative, BAD_CAST("true")))
        rel = true;
    xmlFree(relative);
    tol.relative = rel;
}

}

void XMLDiff::loadToleranceFile(xmlDocPtr xmlToleranceFile)
{
    xmlNodePtr root = xmlDocGetRootElement(xmlToleranceFile);
#if USE_CPPUNIT
    CPPUNIT_ASSERT_MESSAGE("did not find correct tolerance file", xmlStrEqual( root->name, BAD_CAST("tolerances") ));
#else
    if(!xmlStrEqual( root->name, BAD_CAST("tolerances") ))
    {
        assert(false);
        return;
    }
#endif
    xmlNodePtr child = nullptr;
    for (child = root->children; child != nullptr; child = child->next)
    {
        // assume a valid xml file
        if(child->type != XML_ELEMENT_NODE)
            continue;

        assert(xmlStrEqual(child->name, BAD_CAST("tolerance")));

        tolerance tol;
        readAttributesForTolerance(child, tol);
        toleranceContainer.insert(tol);
    }
}

bool XMLDiff::compare()
{
    xmlNode* root1 = xmlDocGetRootElement(xmlFile1);
    xmlNode* root2 = xmlDocGetRootElement(xmlFile2);

#if USE_CPPUNIT
    CPPUNIT_ASSERT(root1);
    CPPUNIT_ASSERT(root2);
    cppunitAssertEqual(root1->name, root2->name);
#else
    if (!root1 || !root2)
        return false;
    if(!xmlStrEqual(root1->name, root2->name))
        return false;
#endif
    return compareElements(root1, root2);
}

namespace {

bool checkForEmptyChildren(xmlNodePtr node)
{
    if(!node)
        return true;

    for(; node != nullptr; node = node->next)
    {
        if (node->type == XML_ELEMENT_NODE)
            return false;
    }
    return true;
}

}

bool XMLDiff::compareElements(xmlNode* node1, xmlNode* node2)
{
#if USE_CPPUNIT
    cppunitAssertEqual(node1->name, node2->name);
#else
    if (!xmlStrEqual( node1->name, node2->name ))
        return false;
#endif

    //compare attributes
    bool sameAttribs = compareAttributes(node1, node2);
#if USE_CPPUNIT
    CPPUNIT_ASSERT(sameAttribs);
#else
    if (!sameAttribs)
        return false;
#endif

    // compare children
    xmlNode* child2 = nullptr;
    xmlNode* child1 = nullptr;
    for(child1 = node1->children, child2 = node2->children; child1 != nullptr && child2 != nullptr; child1 = child1->next, child2 = child2->next)
    {
        if (child1->type == XML_ELEMENT_NODE)
        {
            bool bCompare = compareElements(child1, child2);
            if(!bCompare)
            {
                return false;
            }
        }
    }

#if USE_CPPUNIT
    CPPUNIT_ASSERT(checkForEmptyChildren(child1));
    CPPUNIT_ASSERT(checkForEmptyChildren(child2));
#else
    if(!checkForEmptyChildren(child1) || !checkForEmptyChildren(child2))
        return false;
#endif

    return true;
}

void XMLDiff::cppunitAssertEqual(const xmlChar *expected, const xmlChar *found)
{
#if USE_CPPUNIT
    std::stringstream stringStream;
    stringStream << "Reference: " << fileName << "\n- Expected: " << reinterpret_cast<const char*>(expected) << "\n- Found: " << reinterpret_cast<const char*>(found);

    CPPUNIT_ASSERT_MESSAGE(stringStream.str(), xmlStrEqual(expected, found));
#endif
}

void XMLDiff::cppunitAssertEqualDouble(const xmlNodePtr node, const xmlAttrPtr attr, double expected, double found, double delta)
{
#if USE_CPPUNIT
    xmlChar * path = xmlGetNodePath(node);
    std::stringstream stringStream;
    stringStream << "Reference: " << fileName << "\n- Node: " << reinterpret_cast<const char*>(path) << "\n- Attr: " << reinterpret_cast<const char*>(attr->name);
    xmlFree(path);

    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(stringStream.str(), expected, found, delta);
#endif
}

namespace {

bool compareValuesWithTolerance(double val1, double val2, double tolerance, bool relative)
{
    if(relative)
    {
        return (val1/tolerance) <= val2 && val2 <= (val1*tolerance);
    }
    else
    {
        return (val1 - tolerance) <= val2 && val2 <= (val1 + tolerance);
    }
}

}

bool XMLDiff::compareAttributes(xmlNodePtr node1, xmlNodePtr node2)
{
    CPPUNIT_ASSERT(node1);
    CPPUNIT_ASSERT(node2);
    xmlAttrPtr attr1 = nullptr;
    xmlAttrPtr attr2 = nullptr;
    for(attr1 = node1->properties, attr2 = node2->properties; attr1 != nullptr && attr2 != nullptr; attr1 = attr1->next, attr2 = attr2->next)
    {
#if USE_CPPUNIT
        cppunitAssertEqual(attr1->name, attr2->name);
#else
        if (!xmlStrEqual( attr1->name, attr2->name ))
            return false;
#endif

        xmlChar* val1 = xmlGetProp(node1, attr1->name);
        xmlChar* val2 = xmlGetProp(node2, attr2->name);

        double dVal1 = xmlXPathCastStringToNumber(val1);
        double dVal2 = xmlXPathCastStringToNumber(val2);

        if(!std::isnan(dVal1) || !std::isnan(dVal2))
        {
            //compare by value and respect tolerance
            tolerance tol;
            tol.elementName = xmlStrdup(node1->name);
            tol.attribName = xmlStrdup(attr1->name);
            ToleranceContainer::iterator itr = toleranceContainer.find( tol );
            bool useTolerance = false;
            if (itr != toleranceContainer.end())
            {
                useTolerance = true;
            }

            if (useTolerance)
            {
                bool valInTolerance = compareValuesWithTolerance(dVal1, dVal2, itr->value, itr->relative);
#if USE_CPPUNIT
                std::stringstream stringStream("Expected Value: ");
                stringStream << dVal1 << "; Found Value: " << dVal2 << "; Tolerance: " << itr->value;
                stringStream << "; Relative: " << itr->relative;
                CPPUNIT_ASSERT_MESSAGE(stringStream.str(), valInTolerance);
#else
                if (!valInTolerance)
                    return false;
#endif
            }
            else
            {
#if USE_CPPUNIT
                cppunitAssertEqualDouble(node1, attr1, dVal1, dVal2, 1e-08);
#else
                if (dVal1 != dVal2)
                    return false;
#endif
            }
        }
        else
        {

#if USE_CPPUNIT
            cppunitAssertEqual(val1, val2);
#else
            if(!xmlStrEqual( val1, val2 ))
                return false;
#endif
        }

        xmlFree(val1);
        xmlFree(val2);
    }

    // unequal number of attributes
#ifdef CPPUNIT_ASSERT
    if (attr1 || attr2)
    {
        std::stringstream failStream;
        failStream << "Unequal number of attributes in ";
        // print chain from document root
        std::vector<std::string> parents;
        auto n = node1;
        while (n)
        {
            if (n->name)
                parents.push_back(std::string(reinterpret_cast<const char *>(n->name)));
            n = n->parent;
        }
        bool first = true;
        for (auto it = parents.rbegin(); it != parents.rend(); ++it)
        {
            if (!first)
                failStream << "->";
            first = false;
            failStream << *it;
        }
        failStream << " Attr1: ";
        attr1 = node1->properties;
        while (attr1 != nullptr)
        {
            xmlChar* val1 = xmlGetProp(node1, attr1->name);
            failStream << BAD_CAST(attr1->name) << "=" << BAD_CAST(val1) << ", ";
            xmlFree(val1);
            attr1 = attr1->next;
        }

        failStream << " Attr2: ";
        attr2 = node2->properties;
        while (attr2 != nullptr)
        {
            xmlChar* val2 = xmlGetProp(node2, attr2->name);
            failStream << BAD_CAST(attr2->name) << "=" << BAD_CAST(val2) << ", ";
            xmlFree(val2);
            attr2 = attr2->next;
        }
        CPPUNIT_ASSERT_MESSAGE(failStream.str(), false);
    }
#else
    if (attr1 || attr2)
        return false;
#endif

    return true;
}


bool
doXMLDiff(char const*const pFileName, char const*const pContent, int const size,
          char const*const pToleranceFileName)
{
    XMLDiff aDiff(pFileName, pContent, size, pToleranceFileName);
    return aDiff.compare();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

96%


¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.