2014-10-01 22:10:21 +00:00
|
|
|
#include "packager/mpd/test/xml_compare.h"
|
2014-04-12 01:23:20 +00:00
|
|
|
|
2014-08-28 18:35:15 +00:00
|
|
|
#include <libxml/tree.h>
|
|
|
|
|
2014-04-12 01:23:20 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <map>
|
|
|
|
#include <string>
|
2014-06-26 01:40:40 +00:00
|
|
|
#include <utility>
|
2014-04-12 01:23:20 +00:00
|
|
|
|
2014-10-01 22:10:21 +00:00
|
|
|
#include "packager/base/logging.h"
|
2015-07-15 21:57:47 +00:00
|
|
|
#include "packager/base/strings/string_util.h"
|
2014-10-01 22:10:21 +00:00
|
|
|
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
2015-07-15 21:57:47 +00:00
|
|
|
#include "packager/third_party/libxml/src/include/libxml/parser.h"
|
2014-04-12 01:23:20 +00:00
|
|
|
|
2014-09-19 20:41:13 +00:00
|
|
|
namespace edash_packager {
|
2014-04-12 01:23:20 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
xml::ScopedXmlPtr<xmlDoc>::type GetDocFromString(const std::string& xml_str) {
|
2015-07-15 21:57:47 +00:00
|
|
|
xml::ScopedXmlPtr<xmlDoc>::type schema_as_doc(xmlReadMemory(
|
|
|
|
xml_str.data(), xml_str.size(), NULL, NULL, 0));
|
2014-04-12 01:23:20 +00:00
|
|
|
return schema_as_doc.Pass();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make a map from attributes of the node.
|
|
|
|
std::map<std::string, std::string> GetMapOfAttributes(xmlNodePtr node) {
|
2014-06-26 01:40:40 +00:00
|
|
|
DVLOG(2) << "Getting attributes for node "
|
|
|
|
<< reinterpret_cast<const char*>(node->name);
|
2014-04-12 01:23:20 +00:00
|
|
|
std::map<std::string, std::string> attribute_map;
|
|
|
|
for (xmlAttr* attribute = node->properties;
|
|
|
|
attribute && attribute->name && attribute->children;
|
|
|
|
attribute = attribute->next) {
|
|
|
|
const char* name = reinterpret_cast<const char*>(attribute->name);
|
|
|
|
xml::ScopedXmlPtr<xmlChar>::type value(
|
|
|
|
xmlNodeListGetString(node->doc, attribute->children, 1));
|
|
|
|
|
|
|
|
attribute_map[name] = reinterpret_cast<const char*>(value.get());
|
2014-06-26 01:40:40 +00:00
|
|
|
DVLOG(3) << "In node " << reinterpret_cast<const char*>(node->name)
|
|
|
|
<< " found attribute " << name << " with value "
|
|
|
|
<< reinterpret_cast<const char*>(value.get());
|
2014-04-12 01:23:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return attribute_map;
|
|
|
|
}
|
|
|
|
|
2014-06-26 01:40:40 +00:00
|
|
|
bool MapCompareFunc(std::pair<std::string, std::string> a,
|
|
|
|
std::pair<std::string, std::string> b) {
|
|
|
|
if (a.first != b.first) {
|
|
|
|
DLOG(INFO) << "Attribute mismatch " << a.first << " and " << b.first;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a.second != b.second) {
|
|
|
|
DLOG(INFO) << "Value mismatch for " << a.first << std::endl << "Values are "
|
|
|
|
<< a.second << " and " << b.second;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-04-12 01:23:20 +00:00
|
|
|
bool MapsEqual(const std::map<std::string, std::string>& map1,
|
|
|
|
const std::map<std::string, std::string>& map2) {
|
|
|
|
return map1.size() == map2.size() &&
|
2014-06-26 01:40:40 +00:00
|
|
|
std::equal(map1.begin(), map1.end(), map2.begin(), MapCompareFunc);
|
2014-04-12 01:23:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return true if the nodes have the same attributes.
|
|
|
|
bool CompareAttributes(xmlNodePtr node1, xmlNodePtr node2) {
|
|
|
|
return MapsEqual(GetMapOfAttributes(node1), GetMapOfAttributes(node2));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return true if the name of the nodes match.
|
|
|
|
bool CompareNames(xmlNodePtr node1, xmlNodePtr node2) {
|
2014-06-26 01:40:40 +00:00
|
|
|
DVLOG(2) << "Comparing " << reinterpret_cast<const char*>(node1->name)
|
|
|
|
<< " and " << reinterpret_cast<const char*>(node2->name);
|
2014-04-12 01:23:20 +00:00
|
|
|
return xmlStrcmp(node1->name, node2->name) == 0;
|
|
|
|
}
|
|
|
|
|
2015-07-15 21:57:47 +00:00
|
|
|
bool CompareContents(xmlNodePtr node1, xmlNodePtr node2) {
|
|
|
|
std::string node1_content =
|
|
|
|
reinterpret_cast<const char*>(xmlNodeGetContent(node1));
|
|
|
|
std::string node2_content =
|
|
|
|
reinterpret_cast<const char*>(xmlNodeGetContent(node2));
|
|
|
|
base::ReplaceChars(node1_content, "\n", "", &node1_content);
|
|
|
|
base::TrimString(node1_content, " ", &node1_content);
|
|
|
|
base::ReplaceChars(node2_content, "\n", "", &node2_content);
|
|
|
|
base::TrimString(node2_content, " ", &node2_content);
|
|
|
|
DVLOG(2) << "Comparing contents of "
|
|
|
|
<< reinterpret_cast<const char*>(node1->name) << "\n"
|
|
|
|
<< "First node's content:\n" << node1_content << "\n"
|
|
|
|
<< "Second node's content:\n" << node2_content;
|
|
|
|
const bool same_content = node1_content == node2_content;
|
|
|
|
LOG_IF(ERROR, !same_content)
|
|
|
|
<< "Contents of " << reinterpret_cast<const char*>(node1->name)
|
|
|
|
<< " do not match.\n"
|
|
|
|
<< "First node's content:\n" << node1_content << "\n"
|
|
|
|
<< "Second node's content:\n" << node2_content;
|
|
|
|
return same_content;
|
|
|
|
}
|
|
|
|
|
2014-04-12 01:23:20 +00:00
|
|
|
// Recursively check the elements.
|
|
|
|
// Note that the terminating condition of the recursion is when the children do
|
|
|
|
// not match (inside the loop).
|
|
|
|
bool CompareNodes(xmlNodePtr node1, xmlNodePtr node2) {
|
|
|
|
DCHECK(node1 && node2);
|
|
|
|
if (!CompareNames(node1, node2)) {
|
|
|
|
LOG(ERROR) << "Names of the nodes do not match: "
|
|
|
|
<< reinterpret_cast<const char*>(node1->name) << " "
|
|
|
|
<< reinterpret_cast<const char*>(node2->name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!CompareAttributes(node1, node2)) {
|
|
|
|
LOG(ERROR) << "Attributes of element "
|
|
|
|
<< reinterpret_cast<const char*>(node1->name)
|
|
|
|
<< " do not match.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
xmlNodePtr node1_child = xmlFirstElementChild(node1);
|
|
|
|
xmlNodePtr node2_child = xmlFirstElementChild(node2);
|
2015-07-15 21:57:47 +00:00
|
|
|
if (!node1_child && !node2_child) {
|
|
|
|
// Note that xmlFirstElementChild() returns NULL if there are only
|
|
|
|
// text type children.
|
|
|
|
return CompareContents(node1, node2);
|
|
|
|
}
|
|
|
|
|
2014-04-12 01:23:20 +00:00
|
|
|
do {
|
|
|
|
if (!node1_child || !node2_child)
|
|
|
|
return node1_child == node2_child;
|
|
|
|
|
|
|
|
DCHECK(node1_child && node2_child);
|
|
|
|
if (!CompareNodes(node1_child, node2_child))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
node1_child = xmlNextElementSibling(node1_child);
|
|
|
|
node2_child = xmlNextElementSibling(node2_child);
|
|
|
|
} while (node1_child || node2_child);
|
|
|
|
|
|
|
|
// Both nodes have equivalent descendents.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
bool XmlEqual(const std::string& xml1, const std::string& xml2) {
|
|
|
|
xml::ScopedXmlPtr<xmlDoc>::type xml1_doc(GetDocFromString(xml1));
|
|
|
|
xml::ScopedXmlPtr<xmlDoc>::type xml2_doc(GetDocFromString(xml2));
|
|
|
|
return XmlEqual(xml1_doc.get(), xml2_doc.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XmlEqual(const std::string& xml1, xmlDocPtr xml2) {
|
|
|
|
xml::ScopedXmlPtr<xmlDoc>::type xml1_doc(GetDocFromString(xml1));
|
|
|
|
return XmlEqual(xml1_doc.get(), xml2);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XmlEqual(xmlDocPtr xml1, xmlDocPtr xml2) {
|
|
|
|
if (!xml1|| !xml2) {
|
|
|
|
LOG(ERROR) << "xml1 and/or xml2 are not valid XML.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
xmlNodePtr xml1_root_element = xmlDocGetRootElement(xml1);
|
|
|
|
xmlNodePtr xml2_root_element = xmlDocGetRootElement(xml2);
|
|
|
|
if (!xml1_root_element || !xml2_root_element)
|
|
|
|
return xml1_root_element == xml2_root_element;
|
|
|
|
|
|
|
|
return CompareNodes(xml1_root_element, xml2_root_element);
|
|
|
|
}
|
|
|
|
|
2014-09-19 20:41:13 +00:00
|
|
|
} // namespace edash_packager
|