2014-06-05 06:42:17 +08:00
|
|
|
#pragma once
|
|
|
|
|
2016-07-04 07:22:08 +08:00
|
|
|
#include <pugixml.hpp>
|
2014-06-05 06:42:17 +08:00
|
|
|
#include <sstream>
|
2015-10-30 11:16:31 +08:00
|
|
|
|
2014-06-06 04:19:31 +08:00
|
|
|
#include "path_helper.hpp"
|
2014-06-05 06:42:17 +08:00
|
|
|
|
2016-07-21 07:04:44 +08:00
|
|
|
class xml_helper
|
2014-06-05 06:42:17 +08:00
|
|
|
{
|
|
|
|
public:
|
2014-07-20 02:43:48 +08:00
|
|
|
enum class difference_type
|
2014-06-05 06:42:17 +08:00
|
|
|
{
|
2014-07-20 02:43:48 +08:00
|
|
|
names_differ,
|
|
|
|
missing_attribute,
|
|
|
|
attribute_values_differ,
|
|
|
|
missing_text,
|
|
|
|
text_values_differ,
|
|
|
|
missing_child,
|
|
|
|
child_order_differs,
|
|
|
|
equivalent,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct comparison_result
|
|
|
|
{
|
|
|
|
difference_type difference;
|
2015-11-11 08:46:57 +08:00
|
|
|
std::string value_left;
|
|
|
|
std::string value_right;
|
2016-07-21 07:04:44 +08:00
|
|
|
|
|
|
|
operator bool() const
|
|
|
|
{
|
|
|
|
return difference == difference_type::equivalent;
|
|
|
|
}
|
2014-07-20 02:43:48 +08:00
|
|
|
};
|
|
|
|
|
2016-08-03 12:12:18 +08:00
|
|
|
static bool documents_match(const pugi::xml_document &expected,
|
|
|
|
const pugi::xml_document &observed)
|
2015-10-31 06:54:04 +08:00
|
|
|
{
|
2016-08-03 12:12:18 +08:00
|
|
|
auto result = compare_xml_nodes(expected.root(), observed.root());
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
{
|
|
|
|
std::cout << "documents don't match" << std::endl;
|
|
|
|
|
|
|
|
std::cout << "expected:" << std::endl;
|
|
|
|
expected.save(std::cout);
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
|
|
|
std::cout << "observed:" << std::endl;
|
|
|
|
observed.save(std::cout);
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2015-10-31 06:54:04 +08:00
|
|
|
}
|
2016-07-04 07:22:08 +08:00
|
|
|
|
2016-08-05 13:52:05 +08:00
|
|
|
static bool string_matches_workbook_part(const std::string &expected,
|
|
|
|
xlnt::workbook &wb, const xlnt::path &part)
|
|
|
|
{
|
|
|
|
std::vector<std::uint8_t> bytes;
|
|
|
|
wb.save(bytes);
|
|
|
|
xlnt::zip_file archive;
|
|
|
|
archive.load(bytes);
|
|
|
|
|
|
|
|
return string_matches_archive_member(expected, archive, part);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool file_matches_workbook_part(const xlnt::path &expected,
|
|
|
|
xlnt::workbook &wb, const xlnt::path &part)
|
|
|
|
{
|
|
|
|
std::vector<std::uint8_t> bytes;
|
|
|
|
wb.save(bytes);
|
|
|
|
xlnt::zip_file archive;
|
|
|
|
archive.load(bytes);
|
|
|
|
|
|
|
|
return file_matches_archive_member(expected, archive, part);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool string_matches_archive_member(const std::string &expected,
|
|
|
|
xlnt::zip_file &archive,
|
|
|
|
const xlnt::path &member)
|
|
|
|
{
|
|
|
|
pugi::xml_document expected_document;
|
|
|
|
expected_document.load(expected.c_str());
|
|
|
|
|
|
|
|
pugi::xml_document observed_document;
|
|
|
|
expected_document.load(archive.read(member).c_str());
|
|
|
|
|
|
|
|
return documents_match(expected_document, observed_document);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool file_matches_archive_member(const xlnt::path &file,
|
|
|
|
xlnt::zip_file &archive,
|
|
|
|
const xlnt::path &member)
|
|
|
|
{
|
|
|
|
pugi::xml_document expected_document;
|
|
|
|
expected_document.load(file.read_contents().c_str());
|
|
|
|
|
|
|
|
pugi::xml_document observed_document;
|
|
|
|
if (!archive.has_file(member)) return false;
|
|
|
|
expected_document.load(archive.read(member).c_str());
|
|
|
|
|
|
|
|
return documents_match(expected_document, observed_document);
|
|
|
|
}
|
|
|
|
|
2016-08-03 12:12:18 +08:00
|
|
|
static bool file_matches_document(const xlnt::path &expected,
|
|
|
|
const pugi::xml_document &observed)
|
|
|
|
{
|
|
|
|
return string_matches_document(expected.read_contents(), observed);
|
|
|
|
}
|
2015-11-03 03:22:13 +08:00
|
|
|
|
2016-08-03 12:12:18 +08:00
|
|
|
static bool string_matches_document(const std::string &expected_string,
|
|
|
|
const pugi::xml_document &observed_document)
|
|
|
|
{
|
|
|
|
pugi::xml_document expected_document;
|
|
|
|
expected_document.load(expected_string.c_str());
|
2016-05-12 07:24:53 +08:00
|
|
|
|
2016-08-03 12:12:18 +08:00
|
|
|
return documents_match(expected_document, observed_document);
|
2015-10-30 11:16:31 +08:00
|
|
|
}
|
|
|
|
|
2016-08-03 12:12:18 +08:00
|
|
|
static bool strings_match(const std::string &expected_string, const std::string &observed_string)
|
2015-10-30 11:16:31 +08:00
|
|
|
{
|
2016-07-04 07:22:08 +08:00
|
|
|
pugi::xml_document left_xml;
|
2016-08-03 12:12:18 +08:00
|
|
|
left_xml.load(expected_string.c_str());
|
2015-11-03 03:22:13 +08:00
|
|
|
|
2016-07-04 07:22:08 +08:00
|
|
|
pugi::xml_document right_xml;
|
2016-08-03 12:12:18 +08:00
|
|
|
right_xml.load(observed_string.c_str());
|
2015-11-03 03:22:13 +08:00
|
|
|
|
2016-08-03 12:12:18 +08:00
|
|
|
return documents_match(left_xml, right_xml);
|
2015-10-14 12:03:48 +08:00
|
|
|
}
|
2016-08-06 22:40:17 +08:00
|
|
|
|
|
|
|
static bool archives_match(xlnt::zip_file &left_archive, xlnt::zip_file &right_archive)
|
|
|
|
{
|
|
|
|
auto left_info = left_archive.infolist();
|
|
|
|
auto right_info = right_archive.infolist();
|
|
|
|
|
2016-08-12 12:22:14 +08:00
|
|
|
if (left_info.size() != right_info.size())
|
|
|
|
{
|
|
|
|
std::cout << "left has a different number of files than right" << std::endl;
|
|
|
|
|
|
|
|
std::cout << "left has: ";
|
|
|
|
for (auto &info : left_info)
|
|
|
|
{
|
|
|
|
std::cout << info.filename.string() << ", ";
|
|
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
|
|
|
std::cout << "right has: ";
|
2016-08-16 12:23:49 +08:00
|
|
|
for (auto &info : right_info)
|
2016-08-12 12:22:14 +08:00
|
|
|
{
|
|
|
|
std::cout << info.filename.string() << ", ";
|
|
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool match = true;
|
2016-08-06 22:40:17 +08:00
|
|
|
|
|
|
|
for (auto left_member : left_info)
|
|
|
|
{
|
2016-08-12 12:22:14 +08:00
|
|
|
if (!right_archive.has_file(left_member))
|
|
|
|
{
|
|
|
|
match = false;
|
|
|
|
std::cout << "right is missing file: " << left_member.filename.string() << std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
2016-08-06 22:40:17 +08:00
|
|
|
|
|
|
|
auto left_member_contents = left_archive.read(left_member);
|
|
|
|
auto right_member_contents = right_archive.read(left_member.filename);
|
|
|
|
|
|
|
|
if (!strings_match(left_member_contents, right_member_contents))
|
|
|
|
{
|
2016-08-12 12:22:14 +08:00
|
|
|
std::cout << left_member.filename.string() << std::endl;
|
|
|
|
match = false;
|
2016-08-06 22:40:17 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-12 12:22:14 +08:00
|
|
|
return match;
|
2016-08-06 22:40:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool workbooks_match(const xlnt::workbook &left, const xlnt::workbook &right)
|
|
|
|
{
|
|
|
|
std::vector<std::uint8_t> buffer;
|
|
|
|
|
|
|
|
left.save(buffer);
|
|
|
|
xlnt::zip_file left_archive(buffer);
|
|
|
|
|
|
|
|
buffer.clear();
|
|
|
|
|
|
|
|
right.save(buffer);
|
|
|
|
xlnt::zip_file right_archive(buffer);
|
|
|
|
|
|
|
|
return archives_match(left_archive, right_archive);
|
|
|
|
}
|
2015-10-14 12:03:48 +08:00
|
|
|
|
2016-08-03 12:12:18 +08:00
|
|
|
static comparison_result compare_xml_nodes(const pugi::xml_node &left, const pugi::xml_node &right)
|
2014-07-20 02:43:48 +08:00
|
|
|
{
|
2016-07-04 07:22:08 +08:00
|
|
|
std::string left_temp = left.name();
|
|
|
|
std::string right_temp = right.name();
|
2014-07-20 02:43:48 +08:00
|
|
|
|
|
|
|
if(left_temp != right_temp)
|
2014-07-17 07:53:45 +08:00
|
|
|
{
|
2014-07-20 02:43:48 +08:00
|
|
|
return {difference_type::names_differ, left_temp, right_temp};
|
2014-07-17 07:53:45 +08:00
|
|
|
}
|
2014-07-20 02:43:48 +08:00
|
|
|
|
2016-07-04 07:22:08 +08:00
|
|
|
for(auto &left_attribute : left.attributes())
|
2014-07-17 07:53:45 +08:00
|
|
|
{
|
2016-07-04 07:22:08 +08:00
|
|
|
left_temp = left_attribute.name();
|
|
|
|
|
|
|
|
if(!right.attribute(left_attribute.name()))
|
2014-07-20 02:43:48 +08:00
|
|
|
{
|
|
|
|
return {difference_type::missing_attribute, left_temp, "((empty))"};
|
|
|
|
}
|
|
|
|
|
2016-07-04 07:22:08 +08:00
|
|
|
left_temp = left_attribute.name();
|
|
|
|
right_temp = right.attribute(left_attribute.name()).name();
|
2014-07-20 02:43:48 +08:00
|
|
|
|
|
|
|
if(left_temp != right_temp)
|
|
|
|
{
|
|
|
|
return {difference_type::attribute_values_differ, left_temp, right_temp};
|
|
|
|
}
|
2014-07-17 07:53:45 +08:00
|
|
|
}
|
2016-08-14 07:44:45 +08:00
|
|
|
|
|
|
|
for (auto &right_attribute : right.attributes())
|
|
|
|
{
|
|
|
|
right_temp = right_attribute.name();
|
|
|
|
|
|
|
|
if (!left.attribute(right_attribute.name()))
|
|
|
|
{
|
|
|
|
return{ difference_type::missing_attribute, "((empty))", right_temp };
|
|
|
|
}
|
|
|
|
|
|
|
|
right_temp = right_attribute.name();
|
|
|
|
left_temp = left.attribute(right_attribute.name()).name();
|
|
|
|
|
|
|
|
if (left_temp != right_temp)
|
|
|
|
{
|
|
|
|
return{ difference_type::attribute_values_differ, left_temp, right_temp };
|
|
|
|
}
|
|
|
|
}
|
2014-07-20 02:43:48 +08:00
|
|
|
|
2016-07-04 07:22:08 +08:00
|
|
|
if(left.text())
|
2014-07-20 02:43:48 +08:00
|
|
|
{
|
2016-07-04 07:22:08 +08:00
|
|
|
left_temp = left.text().get();
|
2014-07-20 02:43:48 +08:00
|
|
|
|
2016-07-04 07:22:08 +08:00
|
|
|
if(!right.text())
|
2014-07-20 02:43:48 +08:00
|
|
|
{
|
|
|
|
return {difference_type::missing_text, left_temp, "((empty))"};
|
|
|
|
}
|
|
|
|
|
2016-07-04 07:22:08 +08:00
|
|
|
right_temp = right.text().get();
|
2014-07-20 02:43:48 +08:00
|
|
|
|
|
|
|
if(left_temp != right_temp)
|
|
|
|
{
|
|
|
|
return {difference_type::text_values_differ, left_temp, right_temp};
|
|
|
|
}
|
|
|
|
}
|
2016-07-04 07:22:08 +08:00
|
|
|
else if(right.text())
|
2014-07-20 02:43:48 +08:00
|
|
|
{
|
2016-07-04 07:22:08 +08:00
|
|
|
right_temp = right.text().get();
|
2014-07-20 02:43:48 +08:00
|
|
|
return {difference_type::text_values_differ, "((empty))", right_temp};
|
|
|
|
}
|
|
|
|
|
2016-07-04 07:22:08 +08:00
|
|
|
auto right_children = right.children();
|
2015-10-31 06:54:04 +08:00
|
|
|
auto right_child_iter = right_children.begin();
|
2015-10-30 11:16:31 +08:00
|
|
|
|
2016-07-04 07:22:08 +08:00
|
|
|
for(auto left_child : left.children())
|
2014-07-20 02:43:48 +08:00
|
|
|
{
|
2016-07-04 07:22:08 +08:00
|
|
|
left_temp = left_child.name();
|
2014-07-20 02:43:48 +08:00
|
|
|
|
2015-10-31 06:54:04 +08:00
|
|
|
if(right_child_iter == right_children.end())
|
2014-07-20 02:43:48 +08:00
|
|
|
{
|
|
|
|
return {difference_type::child_order_differs, left_temp, "((end))"};
|
|
|
|
}
|
|
|
|
|
|
|
|
auto right_child = *right_child_iter;
|
|
|
|
right_child_iter++;
|
|
|
|
|
2016-08-03 12:12:18 +08:00
|
|
|
auto child_comparison_result = compare_xml_nodes(left_child, right_child);
|
2014-07-20 02:43:48 +08:00
|
|
|
|
|
|
|
if(!child_comparison_result)
|
|
|
|
{
|
|
|
|
return child_comparison_result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-31 06:54:04 +08:00
|
|
|
if(right_child_iter != right_children.end())
|
2014-07-20 02:43:48 +08:00
|
|
|
{
|
2016-07-04 07:22:08 +08:00
|
|
|
right_temp = right_child_iter->name();
|
2014-07-20 02:43:48 +08:00
|
|
|
return {difference_type::child_order_differs, "((end))", right_temp};
|
|
|
|
}
|
|
|
|
|
|
|
|
return {difference_type::equivalent, "", ""};
|
|
|
|
}
|
2014-06-05 06:42:17 +08:00
|
|
|
};
|