#include <library/cpp/testing/unittest/registar.h>
#include <util/generic/map.h>
#include "xml-document.h"
Y_UNIT_TEST_SUITE(TestXmlDocument) {
Y_UNIT_TEST(Iteration) {
NXml::TDocument xml(
"<?xml version=\"1.0\"?>\n"
"<root>qq<a><b></b></a>ww<c></c></root>",
NXml::TDocument::String);
NXml::TConstNode root = xml.Root();
UNIT_ASSERT_EQUAL(root.Name(), "root");
NXml::TConstNode n = root.FirstChild().NextSibling();
UNIT_ASSERT_EQUAL(n.Name(), "a");
n = n.NextSibling().NextSibling();
UNIT_ASSERT_EQUAL(n.Name(), "c");
}
Y_UNIT_TEST(ParseString) {
NXml::TDocument xml(
"<?xml version=\"1.0\"?>\n"
"<root>\n"
"<a><b len=\"15\" correct=\"1\">hello world</b></a>\n"
"<text>Некоторый текст</text>\n"
"</root>",
NXml::TDocument::String);
NXml::TConstNode root = xml.Root();
NXml::TConstNode b = root.Node("a/b");
UNIT_ASSERT_EQUAL(b.Attr<int>("len"), 15);
UNIT_ASSERT_EQUAL(b.Attr<bool>("correct"), true);
NXml::TConstNode text = root.Node("text");
UNIT_ASSERT_EQUAL(text.Value<TString>(), "Некоторый текст");
}
Y_UNIT_TEST(SerializeString) {
NXml::TDocument xml("frob", NXml::TDocument::RootName);
xml.Root().SetAttr("xyzzy", "Frobozz");
xml.Root().SetAttr("kulness", 0.3);
xml.Root().SetAttr("timelimit", 3);
NXml::TNode authors = xml.Root().AddChild("authors");
authors.AddChild("graham").SetAttr("name", "Nelson");
authors.AddChild("zarf").SetValue("Andrew Plotkin");
authors.AddChild("emshort", "Emily Short");
TString data = xml.ToString("utf-8");
UNIT_ASSERT_EQUAL(data, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<frob xyzzy=\"Frobozz\" kulness=\"0.3\" timelimit=\"3\">\n"
" <authors>\n"
" <graham name=\"Nelson\"/>\n"
" <zarf>Andrew Plotkin</zarf>\n"
" <emshort>Emily Short</emshort>\n"
" </authors>\n"
"</frob>\n");
// check default utf8 output with ru
{
NXml::TDocument xml2("frob", NXml::TDocument::RootName);
xml2.Root().SetAttr("xyzzy", "привет =)");
UNIT_ASSERT_VALUES_EQUAL(xml2.ToString(), "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<frob xyzzy=\"привет =)\"/>\n");
}
}
Y_UNIT_TEST(XPathNs) {
using namespace NXml;
TDocument xml(
"<?xml version=\"1.0\"?>\n"
"<root xmlns='http://hello.com/hello'>\n"
"<a><b len=\"15\" correct=\"1\">hello world</b></a>\n"
"<text>Некоторый текст</text>\n"
"</root>",
TDocument::String);
TNamespacesForXPath nss;
TNamespaceForXPath ns = {"h", "http://hello.com/hello"};
nss.push_back(ns);
TConstNode root = xml.Root();
TConstNode b = root.Node("h:a/h:b", false, nss);
UNIT_ASSERT_EQUAL(b.Attr<int>("len"), 15);
UNIT_ASSERT_EQUAL(b.Attr<bool>("correct"), true);
TConstNode text = root.Node("h:text", false, nss);
UNIT_ASSERT_EQUAL(text.Value<TString>(), "Некоторый текст");
// For performance you can create xpath context once using nss and pass it.
TXPathContextPtr ctxt = root.CreateXPathContext(nss);
UNIT_ASSERT(root.Node("text", true, *ctxt).IsNull());
UNIT_ASSERT_EXCEPTION(root.Node("text", false, *ctxt), yexception);
UNIT_ASSERT_EQUAL(root.Node("h:text", false, *ctxt).Value<TString>(), "Некоторый текст");
}
Y_UNIT_TEST(XmlNodes) {
using namespace NXml;
TDocument xml("<?xml version=\"1.0\"?>\n"
"<root>qq<a><b>asdfg</b></a>ww<c></c></root>",
NXml::TDocument::String);
TNode root = xml.Root();
UNIT_ASSERT_EQUAL(root.Value<TString>(), "qqasdfgww");
TConstNode node = root.FirstChild();
UNIT_ASSERT_EQUAL(node.IsText(), true);
UNIT_ASSERT_EQUAL(node.Value<TString>(), "qq");
node = node.NextSibling();
UNIT_ASSERT_EQUAL(node.IsText(), false);
UNIT_ASSERT_EQUAL(node.Name(), "a");
UNIT_ASSERT_EQUAL(node.Value<TString>(), "asdfg");
node = node.NextSibling();
UNIT_ASSERT_EQUAL(node.IsText(), true);
UNIT_ASSERT_EQUAL(node.Value<TString>(), "ww");
node = node.NextSibling();
UNIT_ASSERT_EQUAL(node.IsText(), false);
UNIT_ASSERT_EQUAL(node.Name(), "c");
UNIT_ASSERT_EQUAL(node.Value<TString>(), "");
node = node.NextSibling();
UNIT_ASSERT_EQUAL(node.IsNull(), true);
TStringStream iterLog;
for (const auto& node2 : root.Nodes("/root/*")) {
iterLog << node2.Name() << ';';
}
UNIT_ASSERT_STRINGS_EQUAL(iterLog.Str(), "a;c;");
// get only element nodes, ignore text nodes with empty "name" param
node = root.FirstChild(TString());
UNIT_ASSERT_EQUAL(node.IsText(), false);
UNIT_ASSERT_EQUAL(node.Name(), "a");
node = node.NextSibling(TString());
UNIT_ASSERT_EQUAL(node.IsText(), false);
UNIT_ASSERT_EQUAL(node.Name(), "c");
// use exact "name" to retrieve children and siblings
node = root.FirstChild("a");
UNIT_ASSERT_EQUAL(node.IsNull(), false);
UNIT_ASSERT_EQUAL(node.Name(), "a");
node = node.NextSibling("c");
UNIT_ASSERT_EQUAL(node.IsNull(), false);
UNIT_ASSERT_EQUAL(node.Name(), "c");
node = root.FirstChild("c"); // skip "a"
UNIT_ASSERT_EQUAL(node.IsNull(), false);
UNIT_ASSERT_EQUAL(node.Name(), "c");
// node not found: no exceptions, null nodes are returned
node = root.FirstChild("b"); // b is not direct child of root
UNIT_ASSERT_EQUAL(node.IsNull(), true);
node = root.FirstChild("nosuchnode");
UNIT_ASSERT_EQUAL(node.IsNull(), true);
node = root.FirstChild();
node = root.NextSibling("unknownnode");
UNIT_ASSERT_EQUAL(node.IsNull(), true);
UNIT_ASSERT_EXCEPTION(node.Name(), yexception);
UNIT_ASSERT_EXCEPTION(node.Value<TString>(), yexception);
UNIT_ASSERT_EXCEPTION(node.IsText(), yexception);
}
Y_UNIT_TEST(DefVal) {
using namespace NXml;
TDocument xml("<?xml version=\"1.0\"?>\n"
"<root><a></a></root>",
NXml::TDocument::String);
UNIT_ASSERT_EQUAL(xml.Root().Node("a", true).Node("b", true).Value<int>(3), 3);
}
Y_UNIT_TEST(NodesVsXPath) {
using namespace NXml;
TDocument xml("<?xml version=\"1.0\"?>\n"
"<root><a x=\"y\"></a></root>",
NXml::TDocument::String);
UNIT_ASSERT_EXCEPTION(xml.Root().Nodes("/root/a/@x"), yexception);
UNIT_ASSERT_VALUES_EQUAL(xml.Root().XPath("/root/a/@x").Size(), 1);
}
Y_UNIT_TEST(NodeIsFirst) {
using namespace NXml;
TDocument xml("<?xml version=\"1.0\"?>\n"
"<root><a x=\"y\">first</a>"
"<a>second</a></root>",
NXml::TDocument::String);
UNIT_ASSERT_EXCEPTION(xml.Root().Node("/root/a/@x"), yexception);
UNIT_ASSERT_STRINGS_EQUAL(xml.Root().Node("/root/a").Value<TString>(), "first");
}
Y_UNIT_TEST(CopyNode) {
using namespace NXml;
// default-construct empty node
TNode empty;
// put to container
TMap<int, TNode> nmap;
nmap[2];
// do copy
TDocument xml("<?xml version=\"1.0\"?>\n"
"<root><a></a></root>",
TDocument::String);
TDocument xml2("<?xml version=\"1.0\"?>\n"
"<root><node><b>bold</b><i>ita</i></node></root>",
TDocument::String);
TNode node = xml2.Root().Node("//node");
TNode place = xml.Root().Node("//a");
place.AddChild(node);
TStringStream s;
xml.Save(s, "", false);
UNIT_ASSERT_VALUES_EQUAL(s.Str(),
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<root><a><node><b>bold</b><i>ita</i></node></a></root>\n");
}
Y_UNIT_TEST(RenderNode) {
using namespace NXml;
{
// no namespaces
TDocument xml(
"<?xml version=\"1.0\"?>\n"
"<root>\n"
"<a><b len=\"15\" correct=\"1\">hello world</b></a>\n"
"<text>Некоторый текст</text>\n"
"</root>",
TDocument::String);
TNode n = xml.Root().Node("//a");
UNIT_ASSERT_VALUES_EQUAL(n.ToString(), "<a><b len=\"15\" correct=\"1\">hello world</b></a>");
}
{
// namespaces
TDocument xml(
"<?xml version=\"1.0\"?>\n"
"<root xmlns='http://hello.com/hello'>\n"
"<a><b len=\"15\" correct=\"1\">hello world</b></a>\n"
"<text>Некоторый текст</text>\n"
"</root>",
TDocument::String);
TNamespacesForXPath nss;
TNamespaceForXPath ns = {"h", "http://hello.com/hello"};
nss.push_back(ns);
TNode n = xml.Root().Node("//h:a", false, nss);
UNIT_ASSERT_VALUES_EQUAL(n.ToString(), "<a><b len=\"15\" correct=\"1\">hello world</b></a>");
}
}
Y_UNIT_TEST(ReuseXPathContext) {
using namespace NXml;
TDocument xml(
"<?xml version=\"1.0\"?>\n"
"<root>\n"
"<a><b><c>Hello, world!</c></b></a>\n"
"<text x=\"10\">First</text>\n"
"<text y=\"20\">Second</text>\n"
"</root>",
TDocument::String);
TXPathContextPtr rootCtxt = xml.Root().CreateXPathContext();
// Check Node()
TConstNode b = xml.Root().Node("a/b", false, *rootCtxt);
// We can use root node context for xpath evaluation in any node
TConstNode c1 = b.Node("c", false, *rootCtxt);
UNIT_ASSERT_EQUAL(c1.Value<TString>(), "Hello, world!");
TXPathContextPtr bCtxt = b.CreateXPathContext();
TConstNode c2 = b.Node("c", false, *bCtxt);
UNIT_ASSERT_EQUAL(c2.Value<TString>(), "Hello, world!");
// Mixing contexts from different documents is forbidden
TDocument otherXml("<root></root>", TDocument::String);
TXPathContextPtr otherCtxt = otherXml.Root().CreateXPathContext();
UNIT_ASSERT_EXCEPTION(b.Node("c", false, *otherCtxt), yexception);
// Check Nodes()
TConstNodes texts = xml.Root().Nodes("text", true, *rootCtxt);
UNIT_ASSERT_EQUAL(texts.Size(), 2);
// Nodes() does't work for non-element nodes
UNIT_ASSERT_EXCEPTION(xml.Root().Nodes("text/@x", true, *rootCtxt), yexception);
// Check XPath()
TConstNodes ys = xml.Root().XPath("text/@y", true, *rootCtxt);
UNIT_ASSERT_EQUAL(ys.Size(), 1);
UNIT_ASSERT_EQUAL(ys[0].Value<int>(), 20);
}
Y_UNIT_TEST(Html) {
using namespace NXml;
TDocument htmlChunk("video", TDocument::RootName);
TNode videoNode = htmlChunk.Root();
videoNode.SetAttr("controls");
TStringStream ss;
videoNode.SaveAsHtml(ss);
UNIT_ASSERT_EQUAL(ss.Str(), "<video controls></video>");
}
Y_UNIT_TEST(Move) {
using namespace NXml;
TDocument xml1("foo", TDocument::RootName);
xml1.Root().AddChild("bar");
UNIT_ASSERT_VALUES_EQUAL(xml1.Root().ToString(), "<foo><bar/></foo>");
TDocument xml2 = std::move(xml1);
UNIT_ASSERT_EXCEPTION(xml1.Root(), yexception);
UNIT_ASSERT_VALUES_EQUAL(xml2.Root().ToString(), "<foo><bar/></foo>");
}
Y_UNIT_TEST(StringConversion) {
using namespace NXml;
TDocument xml("foo", TDocument::RootName);
auto root = xml.Root();
const TStringBuf stringBuf = "bar";
root.SetAttr("bar", stringBuf);
const TString tString = "baz";
root.SetAttr("baz", tString);
root.SetAttr("quux", "literal");
root.SetAttr("frob", 500);
}
}