xml-document_ut.cpp 12 KB

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