ui-gxmlcpp  1.4.4
UnitTests.cpp

CPPUnit Tests for this library. Binary should be installed as ui-gxmlcpp-test.

// Local configuration
#include "config.h"
// STDC++
#include <sstream>
#include <fstream>
#include <iostream>
// C++ Libraries
#include <ui-utilcpp/Text.hpp>
#ifdef WIN32
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#else
#define BOOST_AUTO_TEST_MAIN
#define BOOST_TEST_DYN_LINK
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#endif
namespace UI {
namespace GXML {
// Generate pseudo-random string with given size from characters [a-z]
std::string randomString(std::string::size_type size)
{
time_t t;
::srand((unsigned) time(&t));
std::string result("");
for (std::string::size_type i(0); i < size; ++i)
{
char const c('a' + rand() % 26);
result += c;
}
return result;
}
// Derive a custom Configuration class from Conf for your project
// (here: UnitTests).
class UnitTestsConf: public Conf
{
public:
UnitTestsConf()
{
setCustomLogging(true);
setEXSLT();
}
};
// Exactly _one_ object of this class should be living while your are
// using libxml2/libxslt. Typically, you would put one object in
// main() or a Main() class. A singleton would be perfect to avoid
// multiple instances. For simplicity we use only a global variable
// here.
UnitTestsConf conf;
// Tree: Constructor tests
BOOST_AUTO_TEST_CASE(test_Tree_Constructors)
{
// Build temp folder path
std::string tmpFolderPath;
#ifdef WIN32
char path[MAX_PATH];
::GetTempPath(MAX_PATH, path);
tmpFolderPath = path;
#else
tmpFolderPath = "/tmp/";
#endif
// Prepare
std::string fileName(tmpFolderPath + "__xyz__ui-gxmlcpp_XML-unit-test.xml");
{
std::ofstream file(fileName.c_str());
file << "<root/>";
}
std::stringstream stringStream("<root/>");
// Constructors
Tree buffer((char *) "<root/>garbage", 7);
Tree cstr((char *) "<root/>");
Tree str(std::string("<root/>"));
Tree sstream(stringStream);
Tree file(Tree::File_, fileName);
Tree tree(file);
// = + == oparators
tree = file;
BOOST_CHECK(tree == file);
// Tear down (this may not work on non-POSIX environments)
::unlink(fileName.c_str());
}
// Tree: Build trees from scratch
BOOST_AUTO_TEST_CASE(test_Tree_Build)
{
std::string goal(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<root>"
"<tag attribute=\"aval\">tval</tag>"
"<after>afterVal</after>"
"<after>foo</after>"
"</root>\n");
UI::GXML::Tree tree("<root/>");
UI::GXML::Tree::Node tag(root.addChild("tag", "tval"));
tag.setAttribute("attribute", "aval");
UI::GXML::Tree::Node after(tag.addSiblingAfter("after", "afterVal"));
UI::GXML::Tree::Node foo(after.addSiblingAfter(after));
foo.setContent("foo");
BOOST_CHECK(tree.dump() == goal);
}
// Tree: Read content of nodes
BOOST_AUTO_TEST_CASE(test_Tree_Node_Read)
{
// Run this twice, once with fixed and once with multi contexts
for (int c(0); c < 2; ++c)
{
std::string xml(
"<root>"
" <tag attr='attrcontent'>tagcontent</tag>"
"</root>");
Tree tree(xml);
tree.setContext(c == 0);
{
// DOCUMENT NODE
Tree::Node n(tree.getNode("/"));
BOOST_CHECK(n.getType() == XML_DOCUMENT_NODE);
BOOST_CHECK(n.getName() == "");
BOOST_CHECK(n.getContent() == "");
// Tree's simplification
BOOST_CHECK(tree.getContent("/") == "");
}
{
// ELEMENT NODE
Tree::Node n(tree.getNode("/root/tag"));
BOOST_CHECK(n.getType() == XML_ELEMENT_NODE);
BOOST_CHECK(n.getName() == "tag");
BOOST_CHECK(n.getContent() == "tagcontent");
BOOST_CHECK(n.getAttribute("attr") == "attrcontent");
// Tree's simplification
BOOST_CHECK(tree.getContent("/root/tag") == "tagcontent");
BOOST_CHECK(tree.getContent("/root/tag/@attr") == "attrcontent");
}
{
// ATTRIBUTE NODE
Tree::Node n(tree.getNode("/root/tag/@attr"));
BOOST_CHECK(n.getType() == XML_ATTRIBUTE_NODE);
BOOST_CHECK(n.getName() == "attr");
BOOST_CHECK(n.getContent() == "attrcontent");
// Tree's simplification
BOOST_CHECK(tree.getContent("/root/tag/@attr") == "attrcontent");
}
}
}
// Tree: Read content from multple nodes (NodeSet).
BOOST_AUTO_TEST_CASE(test_Tree_NodeSet_Read)
{
// Run this twice, once with fixed and once with multi contexts
for (int c(0); c < 2; ++c)
{
std::string xml(
"<root>"
" <tag1 attr='attrcontent1'>tagcontent1</tag1>"
" <tag2 attr='attrcontent2'>tagcontent2</tag2>"
" <tag3 attr='attrcontent3'>tagcontent3</tag3>"
"</root>");
Tree tree(xml);
tree.setContext(c == 0);
{
BOOST_CHECK(tree.getNodeSet("/*").size() == 1);
BOOST_CHECK(tree.getNodeSet("/root/*").size() == 3);
BOOST_CHECK(tree.getNodeSet("/root/tag1").size() == 1);
}
{
Tree::NodeSet ns(tree.getNodeSet("/root/*"));
std::string cumContent("");
std::string cumAttr("");
for (Tree::NodeSet::const_iterator i(ns.begin()); i != ns.end(); ++i)
{
cumContent += (*i).getContent();
cumAttr += (*i).getAttribute("attr");
}
BOOST_CHECK(cumContent == "tagcontent1tagcontent2tagcontent3");
BOOST_CHECK(cumAttr == "attrcontent1attrcontent2attrcontent3");
}
}
}
// Tree: Add nodes+attributes and change contents
BOOST_AUTO_TEST_CASE(test_Tree_Node_Write)
{
// Run this twice, once with fixed and once with multi contexts
for (int c(0); c < 2; ++c)
{
std::string xml("<root/>");
Tree tree(xml);
tree.setContext(c == 0);
{
// Add a child element node
tree.addChild("/root", "tag", "tagcontent");
Tree::Node n1(tree.getNode("/root/tag"));
BOOST_CHECK(n1.getContent() == "tagcontent");
BOOST_CHECK(n1.getName() == "tag");
// Add an attribute
tree.setAttribute("/root/tag", "attr", "attrcontent");
BOOST_CHECK(tree.getContent("/root/tag/@attr") == "attrcontent");
BOOST_CHECK(tree.getAttribute("/root/tag", "attr") == "attrcontent");
// Rename an element node
tree.setName("/root/tag", "tux");
BOOST_CHECK(tree.getContent("/root/tux") == "tagcontent");
// Change content of a node
tree.setContent("/root/tux", "tuxcontent");
BOOST_CHECK(tree.getContent("/root/tux") == "tuxcontent");
// Change content of an attribute
tree.setAttribute("/root/tux", "attr", "tuxcontent");
BOOST_CHECK(tree.getContent("/root/tux/@attr") == "tuxcontent");
BOOST_CHECK(tree.getAttribute("/root/tux", "attr") == "tuxcontent");
}
}
}
// Tree::Dump (serializing)
BOOST_AUTO_TEST_CASE(test_Tree_Dump)
{
std::string xmlUTF8(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<root>"
"<tag attr=\"attrcontent1\">tagcontent1</tag>"
"<umlaute attr=\"öäü\">öäü</umlaute>"
"</root>\n");
std::string xmlLatin(
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
"<root>"
"<tag attr=\"attrcontent1\">tagcontent1</tag>"
"<umlaute attr=\"öäü\">öäü</umlaute>"
"</root>\n");
{
// XML(UTF-8) -> Tree(UTF-8) -> XML(UTF-8)
Tree t(xmlUTF8);
BOOST_CHECK(Tree::Dump(t, false, "UTF-8").get() == xmlUTF8);
}
{
// XML(UTF-8) -> Tree(UTF-8) -> XML(ISO-8859-1)
Tree t(xmlUTF8);
BOOST_CHECK(Tree::Dump(t, false, "ISO-8859-1").get() == xmlLatin);
}
{
// XML(ISO-8859-1) -> Tree(UTF-8) -> XML(UTF-8)
Tree t(xmlLatin);
BOOST_CHECK(Tree::Dump(t, false, "UTF-8").get() == xmlUTF8);
}
}
// Tree::Node::Dump (serializing)
BOOST_AUTO_TEST_CASE(Tree_Node_Dump)
{
std::string header("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
std::string tag1("<tag attr=\"attr1\">pos1</tag>");
std::string tag2("<tag attr=\"attr2\">pos2</tag>");
std::string tag3("<tag>öäü</tag>");
// OutputBuffer bug: This started to fail with sizes about >= 4000
std::string tag4("<tag>" + randomString(5000) + "</tag>");
std::string xml("<root>" + tag1 + tag2 + tag3 + tag4 + "</root>");
std::string xmlDoc(header + xml);
{
Tree const tree(xmlDoc);
{
// Dump root node
Tree::Node n(tree.getNode("/root"));
BOOST_CHECK(n.dump() == xml);
}
{
// Dump tag no 2
Tree::Node n(tree.getNode("/root/tag[position()=2]"));
BOOST_CHECK(n.dump() == tag2);
}
{
// Dump all tags below /root
Tree::NodeSet ns(tree.getNodeSet("/root/*"));
BOOST_CHECK(ns.dump() == tag1 + tag2 + tag3 + tag4);
}
{
// Dump attribute node 1
Tree::Node n(tree.getNode("/root/tag[position()=1]/@attr"));
BOOST_CHECK(n.dump() == " attr=\"attr1\"");
}
{
// Dump all attribute nodes
Tree::NodeSet ns(tree.getNodeSet("/root/tag/@attr"));
BOOST_CHECK(ns.dump() == " attr=\"attr1\" attr=\"attr2\"");
}
{
// Dump tag no 3
Tree::Node n(tree.getNode("/root/tag[position()=3]"));
BOOST_CHECK(n.dump() == tag3);
}
{
// Dump tag no 4 (long blob)
Tree::Node n(tree.getNode("/root/tag[position()=4]"));
BOOST_CHECK(n.dump() == tag4);
}
}
}
BOOST_AUTO_TEST_CASE(test_xsl_translation)
{
std::string xmlUTF8NoHeader(
"<root>"
"<tag attr=\"attrcontent\">tagcontent</tag>"
"<umlaute attr=\"öäü\">öäü</umlaute>"
"</root>\n");
std::string xmlUTF8(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ xmlUTF8NoHeader);
std::string xmlLatin(
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
"<root>"
"<tag attr=\"attrcontent\">tagcontent</tag>"
"<umlaute attr=\"öäü\">öäü</umlaute>"
"</root>\n");
std::string xslIDSnippet(
"<xsl:template match=\"@*|node()\">"
" <xsl:copy>"
" <xsl:apply-templates select=\"@*|node()\"/>"
" </xsl:copy>"
"</xsl:template>");
std::string xsl_to_XML_UTF8(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
""
"<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">"
""
"<xsl:output method=\"xml\" encoding=\"UTF-8\" />"
""
+ xslIDSnippet +
""
"</xsl:stylesheet>");
std::string xsl_to_XML_LATIN(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
""
"<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">"
""
"<xsl:output method=\"xml\" encoding=\"ISO-8859-1\" />"
""
+ xslIDSnippet +
""
"</xsl:stylesheet>");
std::string xsl_to_HTML_UTF8(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
""
"<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">"
""
"<xsl:output method=\"html\" encoding=\"UTF-8\" />"
""
+ xslIDSnippet +
""
"</xsl:stylesheet>");
conf.setKeepBlanks(true);
{
// XML: UTF-8 --> UTF-8
Tree xmlTree(xmlUTF8);
XSLTree xslTree(xsl_to_XML_UTF8);
XSLTransTree xslTransTree(xslTree, xmlTree);
XSLTransTree::Dump dump(xslTransTree);
BOOST_CHECK(dump.getEncoding() == "UTF-8");
BOOST_CHECK(dump.get() == xmlUTF8);
}
{
// XML: UTF-8 -> ISO-8859-1
Tree xmlTree(xmlUTF8);
XSLTree xslTree(xsl_to_XML_LATIN);
XSLTransTree xslTransTree(xslTree, xmlTree);
XSLTransTree::Dump dump(xslTransTree);
BOOST_CHECK(dump.getEncoding() == "ISO-8859-1");
BOOST_CHECK(dump.get() == xmlLatin);
}
{
// XML: ISO-8859-1 -> UTF-8
Tree xmlTree(xmlLatin);
XSLTree xslTree(xsl_to_XML_UTF8);
XSLTransTree xslTransTree(xslTree, xmlTree);
XSLTransTree::Dump dump(xslTransTree);
BOOST_CHECK(dump.getEncoding() == "UTF-8");
BOOST_CHECK(dump.get() == xmlUTF8);
}
{
// HTML: UTF-8 -> UTF-8
Tree xmlTree(xmlLatin);
XSLTree xslTree(xsl_to_HTML_UTF8);
XSLTransTree xslTransTree(xslTree, xmlTree);
XSLTransTree::Dump dump(xslTransTree);
BOOST_CHECK(dump.getEncoding() == "UTF-8");
BOOST_CHECK(dump.get() == xmlUTF8NoHeader);
}
conf.setKeepBlanks(false);
}
BOOST_AUTO_TEST_CASE(test_xmlschema_validation)
{
std::string schema(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
""
"<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
" <xsd:element name=\"root\" type=\"xsd:string\"/>"
"</xsd:schema>");
std::string validXml(
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
"<root>text</root>\n");
std::string invalidXml(
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
"<root><subtag/></root>\n");
SchemaTree schemaTree(schema);
Tree validTree(validXml);
Tree invalidTree(invalidXml);
BOOST_CHECK(schemaTree.isValid(validTree));
conf.setCustomLoggingEnable(false);
BOOST_CHECK(!schemaTree.isValid(invalidTree));
conf.setCustomLoggingEnable(true);
// libxml2 <= 2.6.11 had bug: Once a non-validating XML was checked
// against the Schema, all following checks would not validate.
// So, we check this here.
BOOST_CHECK(schemaTree.isValid(validTree));
}
BOOST_AUTO_TEST_CASE(test_relaxng_validation)
{
std::string relaxNG(
"<element name='root' xmlns='http://relaxng.org/ns/structure/1.0'>"
" <zeroOrMore>"
" <element name='item'>"
" <element name='id'>"
" <text/>"
" </element>"
" </element>"
" </zeroOrMore>"
"</element>");
std::string validXml(
"<?xml version='1.0' encoding='UTF-8'?>\n"
"<root>"
" <item>"
" <id>1234</id>"
" </item>"
" <item>"
" <id>5678</id>"
" </item>"
"</root>");
std::string invalidXml(
"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
"<root>"
" <item>"
" <ERROR>error</ERROR>"
" </item>"
"</root>");
RelaxNGTree relaxNGTree(relaxNG);
Tree validTree(validXml);
Tree invalidTree(invalidXml);
BOOST_CHECK(relaxNGTree.isValid(validTree));
conf.setCustomLoggingEnable(false);
BOOST_CHECK(!relaxNGTree.isValid(invalidTree));
conf.setCustomLoggingEnable(true);
}
BOOST_AUTO_TEST_CASE(test_internal_encoding)
{
std::string none(
"<?xml version='1.0'?>"
"<root/>");
std::string utf8(
"<?xml version='1.0' encoding='UTF-8'?>"
"<root/>");
std::string iso(
"<?xml version='1.0' encoding='ISO-8859-1'?>"
"<root/>");
Tree noneTree(none);
Tree utf8Tree(utf8);
Tree isoTree(iso);
BOOST_CHECK(noneTree.getOrigEncoding() == "");
BOOST_CHECK(utf8Tree.getOrigEncoding() == "UTF-8");
BOOST_CHECK(isoTree.getOrigEncoding() == "ISO-8859-1");
}
BOOST_AUTO_TEST_CASE(test_context)
{
Tree tree(
"<?xml version='1.0'?>"
"<root>"
"<sub name='x'>x</sub>"
"<sub name='X'>X</sub>"
"</root>");
// xpath on tree
Tree::Node node(tree.getNode("/root"));
BOOST_CHECK(node.getName() == "root");
// xpath on node (relative)
Tree::Node sub1(node.getNode("sub[@name='x']"));
BOOST_CHECK(sub1.getName() == "sub" && sub1.getAttribute("name") == "x" && sub1.getContent() == "x");
Tree::Node sub2(node.getNode("sub[@name='X']"));
BOOST_CHECK(sub2.getName() == "sub" && sub2.getAttribute("name") == "X" && sub2.getContent() == "X");
}
}}