xml-document_ut.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. #include <library/cpp/testing/unittest/registar.h>
  2. #include <util/generic/map.h>
  3. #include <util/generic/yexception.h>
  4. #include "xml-document.h"
  5. Y_UNIT_TEST_SUITE(TestXmlDocument) {
  6. Y_UNIT_TEST(Iteration) {
  7. NXml::TDocument xml(
  8. "<?xml version=\"1.0\"?>\n"
  9. "<root>qq<a><b></b></a>ww<c></c></root>",
  10. NXml::TDocument::String);
  11. NXml::TConstNode root = xml.Root();
  12. UNIT_ASSERT_EQUAL(root.Name(), "root");
  13. NXml::TConstNode n = root.FirstChild().NextSibling();
  14. UNIT_ASSERT_EQUAL(n.Name(), "a");
  15. n = n.NextSibling().NextSibling();
  16. UNIT_ASSERT_EQUAL(n.Name(), "c");
  17. }
  18. Y_UNIT_TEST(ParseString) {
  19. NXml::TDocument xml(
  20. "<?xml version=\"1.0\"?>\n"
  21. "<root>\n"
  22. "<a><b len=\"15\" correct=\"1\">hello world</b></a>\n"
  23. "<text>Некоторый текст</text>\n"
  24. "</root>",
  25. NXml::TDocument::String);
  26. NXml::TConstNode root = xml.Root();
  27. NXml::TConstNode b = root.Node("a/b");
  28. UNIT_ASSERT_EQUAL(b.Attr<int>("len"), 15);
  29. UNIT_ASSERT_EQUAL(b.Attr<bool>("correct"), true);
  30. NXml::TConstNode text = root.Node("text");
  31. UNIT_ASSERT_EQUAL(text.Value<TString>(), "Некоторый текст");
  32. }
  33. Y_UNIT_TEST(GetAttributes) {
  34. NXml::TDocument xml(R"(<?xml version="1.0"?>
  35. <root>
  36. <a><b len="15" correct="1">hello world</b></a>
  37. <text>Некоторый текст</text>
  38. </root>
  39. )",
  40. NXml::TDocument::String);
  41. NXml::TConstNode root = xml.Root();
  42. NXml::TConstNode b = root.Node("a/b");
  43. UNIT_ASSERT_EXCEPTION_CONTAINS(b.Attr<int>("unknown attrib"), yexception, "@unknown attrib");
  44. const auto unknownAttr = b.TryAttr<int>("unknown attrib");
  45. UNIT_ASSERT(unknownAttr.Empty());
  46. const auto knownAttr = b.TryAttr<int>("len");
  47. UNIT_ASSERT(knownAttr.Defined());
  48. UNIT_ASSERT_EQUAL(knownAttr.GetRef(), 15);
  49. }
  50. Y_UNIT_TEST(SerializeString) {
  51. NXml::TDocument xml("frob", NXml::TDocument::RootName);
  52. xml.Root().SetAttr("xyzzy", "Frobozz");
  53. xml.Root().SetAttr("kulness", 0.3);
  54. xml.Root().SetAttr("timelimit", 3);
  55. NXml::TNode authors = xml.Root().AddChild("authors");
  56. authors.AddChild("graham").SetAttr("name", "Nelson");
  57. authors.AddChild("zarf").SetValue("Andrew Plotkin");
  58. authors.AddChild("emshort", "Emily Short");
  59. TString data = xml.ToString("utf-8");
  60. UNIT_ASSERT_EQUAL(data, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
  61. "<frob xyzzy=\"Frobozz\" kulness=\"0.3\" timelimit=\"3\">\n"
  62. " <authors>\n"
  63. " <graham name=\"Nelson\"/>\n"
  64. " <zarf>Andrew Plotkin</zarf>\n"
  65. " <emshort>Emily Short</emshort>\n"
  66. " </authors>\n"
  67. "</frob>\n");
  68. // check default utf8 output with ru
  69. {
  70. NXml::TDocument xml2("frob", NXml::TDocument::RootName);
  71. xml2.Root().SetAttr("xyzzy", "привет =)");
  72. UNIT_ASSERT_VALUES_EQUAL(xml2.ToString(), "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
  73. "<frob xyzzy=\"привет =)\"/>\n");
  74. }
  75. }
  76. Y_UNIT_TEST(XPathNs) {
  77. using namespace NXml;
  78. TDocument xml(
  79. "<?xml version=\"1.0\"?>\n"
  80. "<root xmlns='http://hello.com/hello'>\n"
  81. "<a><b len=\"15\" correct=\"1\">hello world</b></a>\n"
  82. "<text>Некоторый текст</text>\n"
  83. "</root>",
  84. TDocument::String);
  85. TNamespacesForXPath nss;
  86. TNamespaceForXPath ns = {"h", "http://hello.com/hello"};
  87. nss.push_back(ns);
  88. TConstNode root = xml.Root();
  89. TConstNode b = root.Node("h:a/h:b", false, nss);
  90. UNIT_ASSERT_EQUAL(b.Attr<int>("len"), 15);
  91. UNIT_ASSERT_EQUAL(b.Attr<bool>("correct"), true);
  92. TConstNode text = root.Node("h:text", false, nss);
  93. UNIT_ASSERT_EQUAL(text.Value<TString>(), "Некоторый текст");
  94. // For performance you can create xpath context once using nss and pass it.
  95. TXPathContextPtr ctxt = root.CreateXPathContext(nss);
  96. UNIT_ASSERT(root.Node("text", true, *ctxt).IsNull());
  97. UNIT_ASSERT_EXCEPTION(root.Node("text", false, *ctxt), yexception);
  98. UNIT_ASSERT_EQUAL(root.Node("h:text", false, *ctxt).Value<TString>(), "Некоторый текст");
  99. }
  100. Y_UNIT_TEST(XmlNodes) {
  101. using namespace NXml;
  102. TDocument xml("<?xml version=\"1.0\"?>\n"
  103. "<root>qq<a><b>asdfg</b></a>ww<c></c></root>",
  104. NXml::TDocument::String);
  105. TNode root = xml.Root();
  106. UNIT_ASSERT_EQUAL(root.Value<TString>(), "qqasdfgww");
  107. TConstNode node = root.FirstChild();
  108. UNIT_ASSERT_EQUAL(node.IsText(), true);
  109. UNIT_ASSERT_EQUAL(node.Value<TString>(), "qq");
  110. node = node.NextSibling();
  111. UNIT_ASSERT_EQUAL(node.IsText(), false);
  112. UNIT_ASSERT_EQUAL(node.Name(), "a");
  113. UNIT_ASSERT_EQUAL(node.Value<TString>(), "asdfg");
  114. node = node.NextSibling();
  115. UNIT_ASSERT_EQUAL(node.IsText(), true);
  116. UNIT_ASSERT_EQUAL(node.Value<TString>(), "ww");
  117. node = node.NextSibling();
  118. UNIT_ASSERT_EQUAL(node.IsText(), false);
  119. UNIT_ASSERT_EQUAL(node.Name(), "c");
  120. UNIT_ASSERT_EQUAL(node.Value<TString>(), "");
  121. node = node.NextSibling();
  122. UNIT_ASSERT_EQUAL(node.IsNull(), true);
  123. TStringStream iterLog;
  124. for (const auto& node2 : root.Nodes("/root/*")) {
  125. iterLog << node2.Name() << ';';
  126. }
  127. UNIT_ASSERT_STRINGS_EQUAL(iterLog.Str(), "a;c;");
  128. // get only element nodes, ignore text nodes with empty "name" param
  129. node = root.FirstChild(TString());
  130. UNIT_ASSERT_EQUAL(node.IsText(), false);
  131. UNIT_ASSERT_EQUAL(node.Name(), "a");
  132. node = node.NextSibling(TString());
  133. UNIT_ASSERT_EQUAL(node.IsText(), false);
  134. UNIT_ASSERT_EQUAL(node.Name(), "c");
  135. // use exact "name" to retrieve children and siblings
  136. node = root.FirstChild("a");
  137. UNIT_ASSERT_EQUAL(node.IsNull(), false);
  138. UNIT_ASSERT_EQUAL(node.Name(), "a");
  139. node = node.NextSibling("c");
  140. UNIT_ASSERT_EQUAL(node.IsNull(), false);
  141. UNIT_ASSERT_EQUAL(node.Name(), "c");
  142. node = root.FirstChild("c"); // skip "a"
  143. UNIT_ASSERT_EQUAL(node.IsNull(), false);
  144. UNIT_ASSERT_EQUAL(node.Name(), "c");
  145. // node not found: no exceptions, null nodes are returned
  146. node = root.FirstChild("b"); // b is not direct child of root
  147. UNIT_ASSERT_EQUAL(node.IsNull(), true);
  148. node = root.FirstChild("nosuchnode");
  149. UNIT_ASSERT_EQUAL(node.IsNull(), true);
  150. node = root.FirstChild();
  151. node = root.NextSibling("unknownnode");
  152. UNIT_ASSERT_EQUAL(node.IsNull(), true);
  153. UNIT_ASSERT_EXCEPTION(node.Name(), yexception);
  154. UNIT_ASSERT_EXCEPTION(node.Value<TString>(), yexception);
  155. UNIT_ASSERT_EXCEPTION(node.IsText(), yexception);
  156. }
  157. Y_UNIT_TEST(DefVal) {
  158. using namespace NXml;
  159. TDocument xml("<?xml version=\"1.0\"?>\n"
  160. "<root><a></a></root>",
  161. NXml::TDocument::String);
  162. UNIT_ASSERT_EQUAL(xml.Root().Node("a", true).Node("b", true).Value<int>(3), 3);
  163. }
  164. Y_UNIT_TEST(NodesVsXPath) {
  165. using namespace NXml;
  166. TDocument xml("<?xml version=\"1.0\"?>\n"
  167. "<root><a x=\"y\"></a></root>",
  168. NXml::TDocument::String);
  169. UNIT_ASSERT_EXCEPTION(xml.Root().Nodes("/root/a/@x"), yexception);
  170. UNIT_ASSERT_VALUES_EQUAL(xml.Root().XPath("/root/a/@x").Size(), 1);
  171. }
  172. Y_UNIT_TEST(NodeIsFirst) {
  173. using namespace NXml;
  174. TDocument xml("<?xml version=\"1.0\"?>\n"
  175. "<root><a x=\"y\">first</a>"
  176. "<a>second</a></root>",
  177. NXml::TDocument::String);
  178. UNIT_ASSERT_EXCEPTION(xml.Root().Node("/root/a/@x"), yexception);
  179. UNIT_ASSERT_STRINGS_EQUAL(xml.Root().Node("/root/a").Value<TString>(), "first");
  180. }
  181. Y_UNIT_TEST(CopyNode) {
  182. using namespace NXml;
  183. // default-construct empty node
  184. TNode empty;
  185. // put to container
  186. TMap<int, TNode> nmap;
  187. nmap[2];
  188. // do copy
  189. TDocument xml("<?xml version=\"1.0\"?>\n"
  190. "<root><a></a></root>",
  191. TDocument::String);
  192. TDocument xml2("<?xml version=\"1.0\"?>\n"
  193. "<root><node><b>bold</b><i>ita</i></node></root>",
  194. TDocument::String);
  195. TNode node = xml2.Root().Node("//node");
  196. TNode place = xml.Root().Node("//a");
  197. place.AddChild(node);
  198. TStringStream s;
  199. xml.Save(s, "", false);
  200. UNIT_ASSERT_VALUES_EQUAL(s.Str(),
  201. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
  202. "<root><a><node><b>bold</b><i>ita</i></node></a></root>\n");
  203. }
  204. Y_UNIT_TEST(RenderNode) {
  205. using namespace NXml;
  206. {
  207. // no namespaces
  208. TDocument xml(
  209. "<?xml version=\"1.0\"?>\n"
  210. "<root>\n"
  211. "<a><b len=\"15\" correct=\"1\">hello world</b></a>\n"
  212. "<text>Некоторый текст</text>\n"
  213. "</root>",
  214. TDocument::String);
  215. TNode n = xml.Root().Node("//a");
  216. UNIT_ASSERT_VALUES_EQUAL(n.ToString(), "<a><b len=\"15\" correct=\"1\">hello world</b></a>");
  217. }
  218. {
  219. // namespaces
  220. TDocument xml(
  221. "<?xml version=\"1.0\"?>\n"
  222. "<root xmlns='http://hello.com/hello'>\n"
  223. "<a><b len=\"15\" correct=\"1\">hello world</b></a>\n"
  224. "<text>Некоторый текст</text>\n"
  225. "</root>",
  226. TDocument::String);
  227. TNamespacesForXPath nss;
  228. TNamespaceForXPath ns = {"h", "http://hello.com/hello"};
  229. nss.push_back(ns);
  230. TNode n = xml.Root().Node("//h:a", false, nss);
  231. UNIT_ASSERT_VALUES_EQUAL(n.ToString(), "<a><b len=\"15\" correct=\"1\">hello world</b></a>");
  232. }
  233. }
  234. Y_UNIT_TEST(ReuseXPathContext) {
  235. using namespace NXml;
  236. TDocument xml(
  237. "<?xml version=\"1.0\"?>\n"
  238. "<root>\n"
  239. "<a><b><c>Hello, world!</c></b></a>\n"
  240. "<text x=\"10\">First</text>\n"
  241. "<text y=\"20\">Second</text>\n"
  242. "</root>",
  243. TDocument::String);
  244. TXPathContextPtr rootCtxt = xml.Root().CreateXPathContext();
  245. // Check Node()
  246. TConstNode b = xml.Root().Node("a/b", false, *rootCtxt);
  247. // We can use root node context for xpath evaluation in any node
  248. TConstNode c1 = b.Node("c", false, *rootCtxt);
  249. UNIT_ASSERT_EQUAL(c1.Value<TString>(), "Hello, world!");
  250. TXPathContextPtr bCtxt = b.CreateXPathContext();
  251. TConstNode c2 = b.Node("c", false, *bCtxt);
  252. UNIT_ASSERT_EQUAL(c2.Value<TString>(), "Hello, world!");
  253. // Mixing contexts from different documents is forbidden
  254. TDocument otherXml("<root></root>", TDocument::String);
  255. TXPathContextPtr otherCtxt = otherXml.Root().CreateXPathContext();
  256. UNIT_ASSERT_EXCEPTION(b.Node("c", false, *otherCtxt), yexception);
  257. // Check Nodes()
  258. TConstNodes texts = xml.Root().Nodes("text", true, *rootCtxt);
  259. UNIT_ASSERT_EQUAL(texts.Size(), 2);
  260. // Nodes() does't work for non-element nodes
  261. UNIT_ASSERT_EXCEPTION(xml.Root().Nodes("text/@x", true, *rootCtxt), yexception);
  262. // Check XPath()
  263. TConstNodes ys = xml.Root().XPath("text/@y", true, *rootCtxt);
  264. UNIT_ASSERT_EQUAL(ys.Size(), 1);
  265. UNIT_ASSERT_EQUAL(ys[0].Value<int>(), 20);
  266. }
  267. Y_UNIT_TEST(Html) {
  268. using namespace NXml;
  269. TDocument htmlChunk("video", TDocument::RootName);
  270. TNode videoNode = htmlChunk.Root();
  271. videoNode.SetAttr("controls");
  272. TStringStream ss;
  273. videoNode.SaveAsHtml(ss);
  274. UNIT_ASSERT_EQUAL(ss.Str(), "<video controls></video>");
  275. }
  276. Y_UNIT_TEST(Move) {
  277. using namespace NXml;
  278. TDocument xml1("foo", TDocument::RootName);
  279. xml1.Root().AddChild("bar");
  280. UNIT_ASSERT_VALUES_EQUAL(xml1.Root().ToString(), "<foo><bar/></foo>");
  281. TDocument xml2 = std::move(xml1);
  282. UNIT_ASSERT_EXCEPTION(xml1.Root(), yexception);
  283. UNIT_ASSERT_VALUES_EQUAL(xml2.Root().ToString(), "<foo><bar/></foo>");
  284. }
  285. Y_UNIT_TEST(StringConversion) {
  286. using namespace NXml;
  287. TDocument xml("foo", TDocument::RootName);
  288. auto root = xml.Root();
  289. const TStringBuf stringBuf = "bar";
  290. root.SetAttr("bar", stringBuf);
  291. const TString tString = "baz";
  292. root.SetAttr("baz", tString);
  293. root.SetAttr("quux", "literal");
  294. root.SetAttr("frob", 500);
  295. }
  296. }