1 #include "test.hpp"
2 
3 #include "writer_string.hpp"
4 
5 #include <string>
6 #include <sstream>
7 #include <stdexcept>
8 
9 using namespace pugi;
10 
11 TEST_XML(write_simple, "<node attr='1'><child>text</child></node>")
12 {
13 	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n<child>text</child>\n</node>\n"), STR(""), 0);
14 }
15 
16 TEST_XML(write_raw, "<node attr='1'><child>text</child></node>")
17 {
18 	CHECK_NODE_EX(doc, STR("<node attr=\"1\"><child>text</child></node>"), STR(""), format_raw);
19 }
20 
21 TEST_XML(write_indent, "<node attr='1'><child><sub>text</sub></child></node>")
22 {
23 	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n\t<child>\n\t\t<sub>text</sub>\n\t</child>\n</node>\n"), STR("\t"), format_indent);
24 }
25 
26 TEST_XML(write_indent_attributes, "<node attr='1' other='2'><child><sub>text</sub></child></node>")
27 {
28 	CHECK_NODE_EX(doc, STR("<node\n\tattr=\"1\"\n\tother=\"2\">\n\t<child>\n\t\t<sub>text</sub>\n\t</child>\n</node>\n"), STR("\t"), format_indent_attributes);
29 }
30 
31 TEST_XML(write_indent_attributes_empty_element, "<node attr='1' other='2' />")
32 {
33 	CHECK_NODE_EX(doc, STR("<node\n\tattr=\"1\"\n\tother=\"2\" />\n"), STR("\t"), format_indent_attributes);
34 }
35 
36 TEST_XML_FLAGS(write_indent_attributes_declaration, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><node attr='1' other='2' />", parse_full)
37 {
38 	CHECK_NODE_EX(doc, STR("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<node\n\tattr=\"1\"\n\tother=\"2\" />\n"), STR("\t"), format_indent_attributes);
39 }
40 
41 TEST_XML(write_indent_attributes_raw, "<node attr='1' other='2'><child><sub>text</sub></child></node>")
42 {
43 	CHECK_NODE_EX(doc, STR("<node attr=\"1\" other=\"2\"><child><sub>text</sub></child></node>"), STR("\t"), format_indent_attributes | format_raw);
44 }
45 
46 TEST_XML(write_indent_attributes_empty_indent, "<node attr='1' other='2'><child><sub>text</sub></child></node>")
47 {
48 	CHECK_NODE_EX(doc, STR("<node\nattr=\"1\"\nother=\"2\">\n<child>\n<sub>text</sub>\n</child>\n</node>\n"), STR(""), format_indent_attributes);
49 }
50 
51 TEST_XML(write_pcdata, "<node attr='1'><child><sub/>text</child></node>")
52 {
53 	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n\t<child>\n\t\t<sub />text</child>\n</node>\n"), STR("\t"), format_indent);
54 }
55 
56 TEST_XML_FLAGS(write_cdata, "<![CDATA[value]]>", parse_cdata | parse_fragment)
57 {
58 	CHECK_NODE(doc, STR("<![CDATA[value]]>"));
59 	CHECK_NODE_EX(doc, STR("<![CDATA[value]]>"), STR(""), 0);
60 }
61 
62 TEST_XML_FLAGS(write_cdata_empty, "<![CDATA[]]>", parse_cdata | parse_fragment)
63 {
64 	CHECK_NODE(doc, STR("<![CDATA[]]>"));
65 	CHECK_NODE_EX(doc, STR("<![CDATA[]]>"), STR(""), 0);
66 }
67 
68 TEST_XML_FLAGS(write_cdata_escape, "<![CDATA[value]]>", parse_cdata | parse_fragment)
69 {
70 	CHECK_NODE(doc, STR("<![CDATA[value]]>"));
71 
72 	doc.first_child().set_value(STR("1]]>2]]>3"));
73 	CHECK_NODE(doc, STR("<![CDATA[1]]]]><![CDATA[>2]]]]><![CDATA[>3]]>"));
74 
75 	doc.first_child().set_value(STR("1]"));
76 	CHECK_NODE(doc, STR("<![CDATA[1]]]>"));
77 
78 	doc.first_child().set_value(STR("1]]"));
79 	CHECK_NODE(doc, STR("<![CDATA[1]]]]>"));
80 }
81 
82 TEST_XML(write_cdata_inner, "<node><![CDATA[value]]></node>")
83 {
84 	CHECK_NODE(doc, STR("<node><![CDATA[value]]></node>"));
85 	CHECK_NODE_EX(doc, STR("<node><![CDATA[value]]></node>\n"), STR(""), 0);
86 }
87 
TEST(write_cdata_null)88 TEST(write_cdata_null)
89 {
90 	xml_document doc;
91 	doc.append_child(node_cdata);
92 	doc.append_child(STR("node")).append_child(node_cdata);
93 
94 	CHECK_NODE(doc, STR("<![CDATA[]]><node><![CDATA[]]></node>"));
95 }
96 
97 TEST_XML_FLAGS(write_comment, "<!--text-->", parse_comments | parse_fragment)
98 {
99 	CHECK_NODE(doc, STR("<!--text-->"));
100 	CHECK_NODE_EX(doc, STR("<!--text-->\n"), STR(""), 0);
101 }
102 
TEST(write_comment_invalid)103 TEST(write_comment_invalid)
104 {
105 	xml_document doc;
106 	xml_node child = doc.append_child(node_comment);
107 
108 	CHECK_NODE(doc, STR("<!---->"));
109 
110 	child.set_value(STR("-"));
111 	CHECK_NODE(doc, STR("<!--- -->"));
112 
113 	child.set_value(STR("--"));
114 	CHECK_NODE(doc, STR("<!--- - -->"));
115 
116 	child.set_value(STR("---"));
117 	CHECK_NODE(doc, STR("<!--- - - -->"));
118 
119 	child.set_value(STR("-->"));
120 	CHECK_NODE(doc, STR("<!--- ->-->"));
121 
122 	child.set_value(STR("-->-"));
123 	CHECK_NODE(doc, STR("<!--- ->- -->"));
124 }
125 
TEST(write_comment_null)126 TEST(write_comment_null)
127 {
128 	xml_document doc;
129 	doc.append_child(node_comment);
130 
131 	CHECK_NODE(doc, STR("<!---->"));
132 }
133 
134 TEST_XML_FLAGS(write_pi, "<?name value?>", parse_pi | parse_fragment)
135 {
136 	CHECK_NODE(doc, STR("<?name value?>"));
137 	CHECK_NODE_EX(doc, STR("<?name value?>\n"), STR(""), 0);
138 }
139 
TEST(write_pi_null)140 TEST(write_pi_null)
141 {
142 	xml_document doc;
143 	xml_node node = doc.append_child(node_pi);
144 
145 	CHECK_NODE(doc, STR("<?:anonymous?>"));
146 
147 	node.set_value(STR("value"));
148 
149 	CHECK_NODE(doc, STR("<?:anonymous value?>"));
150 }
151 
TEST(write_pi_invalid)152 TEST(write_pi_invalid)
153 {
154 	xml_document doc;
155 	xml_node node = doc.append_child(node_pi);
156 
157 	node.set_name(STR("test"));
158 	node.set_value(STR("?"));
159 
160 	CHECK_NODE(doc, STR("<?test ?") STR("?>"));
161 
162 	node.set_value(STR("?>"));
163 
164 	CHECK_NODE(doc, STR("<?test ? >?>"));
165 
166 	node.set_value(STR("<?foo?>"));
167 
168 	CHECK_NODE(doc, STR("<?test <?foo? >?>"));
169 }
170 
171 TEST_XML_FLAGS(write_declaration, "<?xml version='2.0'?>", parse_declaration | parse_fragment)
172 {
173 	CHECK_NODE(doc, STR("<?xml version=\"2.0\"?>"));
174 	CHECK_NODE_EX(doc, STR("<?xml version=\"2.0\"?>\n"), STR(""), 0);
175 }
176 
177 TEST_XML_FLAGS(write_doctype, "<!DOCTYPE id [ foo ]>", parse_doctype | parse_fragment)
178 {
179 	CHECK_NODE(doc, STR("<!DOCTYPE id [ foo ]>"));
180 	CHECK_NODE_EX(doc, STR("<!DOCTYPE id [ foo ]>\n"), STR(""), 0);
181 }
182 
TEST(write_doctype_null)183 TEST(write_doctype_null)
184 {
185 	xml_document doc;
186 	doc.append_child(node_doctype);
187 
188 	CHECK_NODE(doc, STR("<!DOCTYPE>"));
189 }
190 
191 TEST_XML(write_escape, "<node attr=''>text</node>")
192 {
193 	doc.child(STR("node")).attribute(STR("attr")) = STR("<>'\"&\x04\r\n\t");
194 	doc.child(STR("node")).first_child().set_value(STR("<>'\"&\x04\r\n\t"));
195 
196 	CHECK_NODE(doc, STR("<node attr=\"&lt;&gt;'&quot;&amp;&#04;&#13;&#10;\t\">&lt;&gt;'\"&amp;&#04;\r\n\t</node>"));
197 }
198 
199 TEST_XML(write_escape_unicode, "<node attr='&#x3c00;'/>")
200 {
201 #ifdef PUGIXML_WCHAR_MODE
202 	#ifdef U_LITERALS
203 		CHECK_NODE(doc, STR("<node attr=\"\u3c00\"/>"));
204 	#else
205 		CHECK_NODE(doc, STR("<node attr=\"\x3c00\"/>"));
206 	#endif
207 #else
208 	CHECK_NODE(doc, STR("<node attr=\"\xe3\xb0\x80\"/>"));
209 #endif
210 }
211 
212 TEST_XML(write_no_escapes, "<node attr=''>text</node>")
213 {
214 	doc.child(STR("node")).attribute(STR("attr")) = STR("<>'\"&\x04\r\n\t");
215 	doc.child(STR("node")).first_child().set_value(STR("<>'\"&\x04\r\n\t"));
216 
217 	CHECK_NODE_EX(doc, STR("<node attr=\"<>'\"&\x04\r\n\t\"><>'\"&\x04\r\n\t</node>"), STR(""), format_raw | format_no_escapes);
218 }
219 
220 struct test_writer: xml_writer
221 {
222 	std::basic_string<char_t> contents;
223 
writetest_writer224 	virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE
225 	{
226 		CHECK(size % sizeof(char_t) == 0);
227 		contents.append(static_cast<const char_t*>(data), size / sizeof(char_t));
228 	}
229 };
230 
231 TEST_XML(write_print_writer, "<node/>")
232 {
233 	test_writer writer;
234 	doc.print(writer, STR(""), format_default, get_native_encoding());
235 
236 	CHECK(writer.contents == STR("<node />\n"));
237 }
238 
239 #ifndef PUGIXML_NO_STL
240 TEST_XML(write_print_stream, "<node/>")
241 {
242 	std::ostringstream oss;
243 	doc.print(oss, STR(""), format_default, encoding_utf8);
244 
245 	CHECK(oss.str() == "<node />\n");
246 }
247 
248 TEST_XML(write_print_stream_encode, "<n/>")
249 {
250 	std::ostringstream oss;
251 	doc.print(oss, STR(""), format_default, encoding_utf16_be);
252 
253 	CHECK(oss.str() == std::string("\x00<\x00n\x00 \x00/\x00>\x00\n", 12));
254 }
255 
256 TEST_XML(write_print_stream_wide, "<node/>")
257 {
258 	std::basic_ostringstream<wchar_t> oss;
259 	doc.print(oss, STR(""), format_default, encoding_utf8);
260 
261 	CHECK(oss.str() == L"<node />\n");
262 }
263 #endif
264 
265 TEST_XML(write_huge_chunk, "<node/>")
266 {
267 	std::basic_string<char_t> name(10000, STR('n'));
268 	doc.child(STR("node")).set_name(name.c_str());
269 
270 	test_writer writer;
271 	doc.print(writer, STR(""), format_default, get_native_encoding());
272 
273 	CHECK(writer.contents == STR("<") + name + STR(" />\n"));
274 }
275 
TEST(write_encodings)276 TEST(write_encodings)
277 {
278 	static char s_utf8[] = "<\x54\xC2\xA2\xE2\x82\xAC\xF0\xA4\xAD\xA2/>";
279 
280 	xml_document doc;
281 	CHECK(doc.load_buffer(s_utf8, sizeof(s_utf8), parse_default, encoding_utf8));
282 
283 	CHECK(write_narrow(doc, format_default, encoding_utf8) == "<\x54\xC2\xA2\xE2\x82\xAC\xF0\xA4\xAD\xA2 />\n");
284 
285 	CHECK(test_write_narrow(doc, format_default, encoding_utf32_le, "<\x00\x00\x00\x54\x00\x00\x00\xA2\x00\x00\x00\xAC\x20\x00\x00\x62\x4B\x02\x00 \x00\x00\x00/\x00\x00\x00>\x00\x00\x00\n\x00\x00\x00", 36));
286 	CHECK(test_write_narrow(doc, format_default, encoding_utf32_be, "\x00\x00\x00<\x00\x00\x00\x54\x00\x00\x00\xA2\x00\x00\x20\xAC\x00\x02\x4B\x62\x00\x00\x00 \x00\x00\x00/\x00\x00\x00>\x00\x00\x00\n", 36));
287 	CHECK(write_narrow(doc, format_default, encoding_utf32) == write_narrow(doc, format_default, is_little_endian() ? encoding_utf32_le : encoding_utf32_be));
288 
289 	CHECK(test_write_narrow(doc, format_default, encoding_utf16_le, "<\x00\x54\x00\xA2\x00\xAC\x20\x52\xd8\x62\xdf \x00/\x00>\x00\n\x00", 20));
290 	CHECK(test_write_narrow(doc, format_default, encoding_utf16_be, "\x00<\x00\x54\x00\xA2\x20\xAC\xd8\x52\xdf\x62\x00 \x00/\x00>\x00\n", 20));
291 	CHECK(write_narrow(doc, format_default, encoding_utf16) == write_narrow(doc, format_default, is_little_endian() ? encoding_utf16_le : encoding_utf16_be));
292 
293 	size_t wcharsize = sizeof(wchar_t);
294 	std::basic_string<wchar_t> v = write_wide(doc, format_default, encoding_wchar);
295 
296 	if (wcharsize == 4)
297 	{
298 		CHECK(v.size() == 9 && v[0] == '<' && v[1] == 0x54 && v[2] == 0xA2 && v[3] == 0x20AC && v[4] == wchar_cast(0x24B62) && v[5] == ' ' && v[6] == '/' && v[7] == '>' && v[8] == '\n');
299 	}
300 	else
301 	{
302 		CHECK(v.size() == 10 && v[0] == '<' && v[1] == 0x54 && v[2] == 0xA2 && v[3] == 0x20AC && v[4] == wchar_cast(0xd852) && v[5] == wchar_cast(0xdf62) && v[6] == ' ' && v[7] == '/' && v[8] == '>' && v[9] == '\n');
303 	}
304 
305     CHECK(test_write_narrow(doc, format_default, encoding_latin1, "<\x54\xA2?? />\n", 9));
306 }
307 
308 #ifdef PUGIXML_WCHAR_MODE
TEST(write_encoding_huge)309 TEST(write_encoding_huge)
310 {
311 	const unsigned int N = 16000;
312 
313 	// make a large utf16 name consisting of 6-byte char pairs (6 does not divide internal buffer size, so will need split correction)
314 	std::string s_utf16 = std::string("\x00<", 2);
315 
316 	for (unsigned int i = 0; i < N; ++i) s_utf16 += "\x20\xAC\xd8\x52\xdf\x62";
317 
318 	s_utf16 += std::string("\x00/\x00>", 4);
319 
320 	xml_document doc;
321 	CHECK(doc.load_buffer(&s_utf16[0], s_utf16.length(), parse_default, encoding_utf16_be));
322 
323 	std::string s_utf8 = "<";
324 
325 	for (unsigned int j = 0; j < N; ++j) s_utf8 += "\xE2\x82\xAC\xF0\xA4\xAD\xA2";
326 
327 	s_utf8 += " />\n";
328 
329 	CHECK(test_write_narrow(doc, format_default, encoding_utf8, s_utf8.c_str(), s_utf8.length()));
330 }
331 
TEST(write_encoding_huge_invalid)332 TEST(write_encoding_huge_invalid)
333 {
334 	size_t wcharsize = sizeof(wchar_t);
335 
336 	if (wcharsize == 2)
337 	{
338 		const unsigned int N = 16000;
339 
340 		// make a large utf16 name consisting of leading surrogate chars
341 		std::basic_string<wchar_t> s_utf16;
342 
343 		for (unsigned int i = 0; i < N; ++i) s_utf16 += static_cast<wchar_t>(0xd852);
344 
345 		xml_document doc;
346 		doc.append_child().set_name(s_utf16.c_str());
347 
348 		CHECK(test_write_narrow(doc, format_default, encoding_utf8, "< />\n", 5));
349 	}
350 }
351 #else
TEST(write_encoding_huge)352 TEST(write_encoding_huge)
353 {
354 	const unsigned int N = 16000;
355 
356 	// make a large utf8 name consisting of 3-byte chars (3 does not divide internal buffer size, so will need split correction)
357 	std::string s_utf8 = "<";
358 
359 	for (unsigned int i = 0; i < N; ++i) s_utf8 += "\xE2\x82\xAC";
360 
361 	s_utf8 += "/>";
362 
363 	xml_document doc;
364 	CHECK(doc.load_buffer(&s_utf8[0], s_utf8.length(), parse_default, encoding_utf8));
365 
366 	std::string s_utf16 = std::string("\x00<", 2);
367 
368 	for (unsigned int j = 0; j < N; ++j) s_utf16 += "\x20\xAC";
369 
370 	s_utf16 += std::string("\x00 \x00/\x00>\x00\n", 8);
371 
372 	CHECK(test_write_narrow(doc, format_default, encoding_utf16_be, s_utf16.c_str(), s_utf16.length()));
373 }
374 
TEST(write_encoding_huge_invalid)375 TEST(write_encoding_huge_invalid)
376 {
377 	const unsigned int N = 16000;
378 
379 	// make a large utf8 name consisting of non-leading chars
380 	std::string s_utf8;
381 
382 	for (unsigned int i = 0; i < N; ++i) s_utf8 += "\x82";
383 
384 	xml_document doc;
385 	doc.append_child().set_name(s_utf8.c_str());
386 
387 	std::string s_utf16 = std::string("\x00<\x00 \x00/\x00>\x00\n", 10);
388 
389 	CHECK(test_write_narrow(doc, format_default, encoding_utf16_be, s_utf16.c_str(), s_utf16.length()));
390 }
391 #endif
392 
TEST(write_unicode_escape)393 TEST(write_unicode_escape)
394 {
395 	char s_utf8[] = "<\xE2\x82\xAC \xC2\xA2='\"\xF0\xA4\xAD\xA2&#x0a;\"'>&amp;\x14\xF0\xA4\xAD\xA2&lt;</\xE2\x82\xAC>";
396 
397 	xml_document doc;
398 	CHECK(doc.load_buffer(s_utf8, sizeof(s_utf8), parse_default, encoding_utf8));
399 
400 	CHECK(write_narrow(doc, format_default, encoding_utf8) == "<\xE2\x82\xAC \xC2\xA2=\"&quot;\xF0\xA4\xAD\xA2&#10;&quot;\">&amp;&#20;\xF0\xA4\xAD\xA2&lt;</\xE2\x82\xAC>\n");
401 }
402 
403 #ifdef PUGIXML_WCHAR_MODE
test_write_unicode_invalid(const wchar_t * name,const char * expected)404 static bool test_write_unicode_invalid(const wchar_t* name, const char* expected)
405 {
406 	xml_document doc;
407 	doc.append_child(node_pcdata).set_value(name);
408 
409 	return write_narrow(doc, format_raw, encoding_utf8) == expected;
410 }
411 
TEST(write_unicode_invalid_utf16)412 TEST(write_unicode_invalid_utf16)
413 {
414 	size_t wcharsize = sizeof(wchar_t);
415 
416 	if (wcharsize == 2)
417 	{
418 		// check non-terminated degenerate handling
419 	#ifdef U_LITERALS
420 		CHECK(test_write_unicode_invalid(L"a\uda1d", "a"));
421 		CHECK(test_write_unicode_invalid(L"a\uda1d_", "a_"));
422 	#else
423 		CHECK(test_write_unicode_invalid(L"a\xda1d", "a"));
424 		CHECK(test_write_unicode_invalid(L"a\xda1d_", "a_"));
425 	#endif
426 
427 		// check incorrect leading code
428 	#ifdef U_LITERALS
429 		CHECK(test_write_unicode_invalid(L"a\ude24", "a"));
430 		CHECK(test_write_unicode_invalid(L"a\ude24_", "a_"));
431 	#else
432 		CHECK(test_write_unicode_invalid(L"a\xde24", "a"));
433 		CHECK(test_write_unicode_invalid(L"a\xde24_", "a_"));
434 	#endif
435 	}
436 }
437 #else
test_write_unicode_invalid(const char * name,const wchar_t * expected)438 static bool test_write_unicode_invalid(const char* name, const wchar_t* expected)
439 {
440 	xml_document doc;
441 	doc.append_child(node_pcdata).set_value(name);
442 
443 	return write_wide(doc, format_raw, encoding_wchar) == expected;
444 }
445 
TEST(write_unicode_invalid_utf8)446 TEST(write_unicode_invalid_utf8)
447 {
448 	// invalid 1-byte input
449 	CHECK(test_write_unicode_invalid("a\xb0", L"a"));
450 	CHECK(test_write_unicode_invalid("a\xb0_", L"a_"));
451 
452 	// invalid 2-byte input
453 	CHECK(test_write_unicode_invalid("a\xc0", L"a"));
454 	CHECK(test_write_unicode_invalid("a\xd0", L"a"));
455 	CHECK(test_write_unicode_invalid("a\xc0_", L"a_"));
456 	CHECK(test_write_unicode_invalid("a\xd0_", L"a_"));
457 
458 	// invalid 3-byte input
459 	CHECK(test_write_unicode_invalid("a\xe2\x80", L"a"));
460 	CHECK(test_write_unicode_invalid("a\xe2", L"a"));
461 	CHECK(test_write_unicode_invalid("a\xe2\x80_", L"a_"));
462 	CHECK(test_write_unicode_invalid("a\xe2_", L"a_"));
463 
464 	// invalid 4-byte input
465 	CHECK(test_write_unicode_invalid("a\xf2\x97\x98", L"a"));
466 	CHECK(test_write_unicode_invalid("a\xf2\x97", L"a"));
467 	CHECK(test_write_unicode_invalid("a\xf2", L"a"));
468 	CHECK(test_write_unicode_invalid("a\xf2\x97\x98_", L"a_"));
469 	CHECK(test_write_unicode_invalid("a\xf2\x97_", L"a_"));
470 	CHECK(test_write_unicode_invalid("a\xf2_", L"a_"));
471 
472 	// invalid 5-byte input
473 	CHECK(test_write_unicode_invalid("a\xf8_", L"a_"));
474 }
475 #endif
476 
TEST(write_no_name_element)477 TEST(write_no_name_element)
478 {
479 	xml_document doc;
480 	xml_node root = doc.append_child();
481 	root.append_child();
482 	root.append_child().append_child(node_pcdata).set_value(STR("text"));
483 
484 	CHECK_NODE(doc, STR("<:anonymous><:anonymous/><:anonymous>text</:anonymous></:anonymous>"));
485 	CHECK_NODE_EX(doc, STR("<:anonymous>\n\t<:anonymous />\n\t<:anonymous>text</:anonymous>\n</:anonymous>\n"), STR("\t"), format_default);
486 }
487 
TEST(write_no_name_pi)488 TEST(write_no_name_pi)
489 {
490 	xml_document doc;
491 	doc.append_child(node_pi);
492 
493 	CHECK_NODE(doc, STR("<?:anonymous?>"));
494 }
495 
TEST(write_no_name_attribute)496 TEST(write_no_name_attribute)
497 {
498 	xml_document doc;
499 	doc.append_child().set_name(STR("root"));
500 	doc.child(STR("root")).append_attribute(STR(""));
501 
502 	CHECK_NODE(doc, STR("<root :anonymous=\"\"/>"));
503 }
504 
TEST(write_print_empty)505 TEST(write_print_empty)
506 {
507 	test_writer writer;
508 	xml_node().print(writer);
509 }
510 
511 #ifndef PUGIXML_NO_STL
TEST(write_print_stream_empty)512 TEST(write_print_stream_empty)
513 {
514 	std::ostringstream oss;
515 	xml_node().print(oss);
516 }
517 
TEST(write_print_stream_empty_wide)518 TEST(write_print_stream_empty_wide)
519 {
520 	std::basic_ostringstream<wchar_t> oss;
521 	xml_node().print(oss);
522 }
523 #endif
524 
TEST(write_stackless)525 TEST(write_stackless)
526 {
527 	unsigned int count = 20000;
528 	std::basic_string<char_t> data;
529 
530 	for (unsigned int i = 0; i < count; ++i)
531 		data += STR("<a>");
532 
533 	data += STR("text");
534 
535 	for (unsigned int j = 0; j < count; ++j)
536 		data += STR("</a>");
537 
538 	xml_document doc;
539 	CHECK(doc.load_string(data.c_str()));
540 
541 	CHECK_NODE(doc, data.c_str());
542 }
543 
544 TEST_XML(write_indent_custom, "<node attr='1'><child><sub>text</sub></child></node>")
545 {
546 	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n<child>\n<sub>text</sub>\n</child>\n</node>\n"), STR(""), format_indent);
547 	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\nA<child>\nAA<sub>text</sub>\nA</child>\n</node>\n"), STR("A"), format_indent);
548 	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\nAB<child>\nABAB<sub>text</sub>\nAB</child>\n</node>\n"), STR("AB"), format_indent);
549 	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\nABC<child>\nABCABC<sub>text</sub>\nABC</child>\n</node>\n"), STR("ABC"), format_indent);
550 	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\nABCD<child>\nABCDABCD<sub>text</sub>\nABCD</child>\n</node>\n"), STR("ABCD"), format_indent);
551 	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\nABCDE<child>\nABCDEABCDE<sub>text</sub>\nABCDE</child>\n</node>\n"), STR("ABCDE"), format_indent);
552 }
553 
TEST(write_pcdata_null)554 TEST(write_pcdata_null)
555 {
556 	xml_document doc;
557 	doc.append_child(STR("node")).append_child(node_pcdata);
558 
559 	CHECK_NODE(doc, STR("<node></node>"));
560 	CHECK_NODE_EX(doc, STR("<node></node>\n"), STR("\t"), format_indent);
561 
562 	doc.first_child().append_child(node_pcdata);
563 
564 	CHECK_NODE_EX(doc, STR("<node></node>\n"), STR("\t"), format_indent);
565 }
566 
TEST(write_pcdata_whitespace_fixedpoint)567 TEST(write_pcdata_whitespace_fixedpoint)
568 {
569 	const char_t* data = STR("<node>  test  <child>\n  <sub/>\n   </child>\n</node>");
570 
571 	static const unsigned int flags_parse[] =
572 	{
573 		0,
574 		parse_ws_pcdata,
575 		parse_ws_pcdata_single,
576 		parse_trim_pcdata
577 	};
578 
579 	static const unsigned int flags_format[] =
580 	{
581 		0,
582 		format_raw,
583 		format_indent
584 	};
585 
586 	for (unsigned int i = 0; i < sizeof(flags_parse) / sizeof(flags_parse[0]); ++i)
587 	{
588 		xml_document doc;
589 		CHECK(doc.load_string(data, flags_parse[i]));
590 
591 		for (unsigned int j = 0; j < sizeof(flags_format) / sizeof(flags_format[0]); ++j)
592 		{
593 			std::string saved = write_narrow(doc, flags_format[j], encoding_auto);
594 
595 			xml_document rdoc;
596 			CHECK(rdoc.load_buffer(&saved[0], saved.size(), flags_parse[i]));
597 
598 			std::string rsaved = write_narrow(rdoc, flags_format[j], encoding_auto);
599 
600 			CHECK(saved == rsaved);
601 		}
602 	}
603 }
604 
605 TEST_XML_FLAGS(write_mixed, "<node><child1/><child2>pre<![CDATA[data]]>mid<!--comment--><test/>post<?pi value?>fin</child2><child3/></node>", parse_full)
606 {
607 	CHECK_NODE(doc, STR("<node><child1/><child2>pre<![CDATA[data]]>mid<!--comment--><test/>post<?pi value?>fin</child2><child3/></node>"));
608 	CHECK_NODE_EX(doc, STR("<node>\n<child1 />\n<child2>pre<![CDATA[data]]>mid<!--comment-->\n<test />post<?pi value?>fin</child2>\n<child3 />\n</node>\n"), STR("\t"), 0);
609 	CHECK_NODE_EX(doc, STR("<node>\n\t<child1 />\n\t<child2>pre<![CDATA[data]]>mid<!--comment-->\n\t\t<test />post<?pi value?>fin</child2>\n\t<child3 />\n</node>\n"), STR("\t"), format_indent);
610 }
611 
612 TEST_XML(write_no_empty_element_tags, "<node><child1/><child2>text</child2><child3></child3></node>")
613 {
614 	CHECK_NODE(doc, STR("<node><child1/><child2>text</child2><child3/></node>"));
615 	CHECK_NODE_EX(doc, STR("<node><child1></child1><child2>text</child2><child3></child3></node>"), STR("\t"), format_raw | format_no_empty_element_tags);
616 	CHECK_NODE_EX(doc, STR("<node>\n\t<child1></child1>\n\t<child2>text</child2>\n\t<child3></child3>\n</node>\n"), STR("\t"), format_indent | format_no_empty_element_tags);
617 }
618 
619 TEST_XML_FLAGS(write_roundtrip, "<node><child1 attr1='value1' attr2='value2'/><child2 attr='value'>pre<![CDATA[data]]>mid&lt;text&amp;escape<!--comment--><test/>post<?pi value?>fin</child2><child3/></node>", parse_full)
620 {
621 	const unsigned int flagset[] = { format_indent, format_raw, format_no_declaration, format_indent_attributes, format_no_empty_element_tags };
622 	size_t flagcount = sizeof(flagset) / sizeof(flagset[0]);
623 
624 	for (size_t i = 0; i < (size_t(1) << flagcount); ++i)
625 	{
626 		unsigned int flags = 0;
627 
628 		for (size_t j = 0; j < flagcount; ++j)
629 			if (i & (size_t(1) << j))
630 				flags |= flagset[j];
631 
632 		std::string contents = write_narrow(doc, flags, encoding_utf8);
633 
634 		xml_document verify;
635 		CHECK(verify.load_buffer(contents.c_str(), contents.size(), parse_full));
636 		CHECK(test_write_narrow(verify, flags, encoding_utf8, contents.c_str(), contents.size()));
637 
638 		xml_document verifyws;
639 		CHECK(verifyws.load_buffer(contents.c_str(), contents.size(), parse_full | parse_ws_pcdata));
640 		CHECK(test_write_narrow(verifyws, flags, encoding_utf8, contents.c_str(), contents.size()));
641 	}
642 }
643 
TEST(write_flush_coverage)644 TEST(write_flush_coverage)
645 {
646 	xml_document doc;
647 
648 	// this creates a node that uses short sequences of lengths 1-6 for output
649 	xml_node n = doc.append_child(STR("n"));
650 
651 	xml_attribute a = n.append_attribute(STR("a"));
652 
653 	xml_attribute b = n.append_attribute(STR("b"));
654 	b.set_value(STR("<&\""));
655 
656 	n.append_child(node_comment);
657 
658 	size_t basel = save_narrow(doc, format_raw, encoding_auto).size();
659 	size_t bufl = 2048;
660 
661 	for (size_t l = 0; l <= basel; ++l)
662 	{
663 		std::basic_string<char_t> pad(bufl - l, STR('v'));
664 		a.set_value(pad.c_str());
665 
666 		std::string s = save_narrow(doc, format_raw, encoding_auto);
667 		CHECK(s.size() == basel + bufl - l);
668 	}
669 }
670 
671 #ifndef PUGIXML_NO_EXCEPTIONS
672 struct throwing_writer: xml_writer
673 {
writethrowing_writer674 	virtual void write(const void*, size_t) PUGIXML_OVERRIDE
675 	{
676 		throw std::runtime_error("write failed");
677 	}
678 };
679 
680 TEST_XML(write_throw_simple, "<node><child/></node>")
681 {
682 	try
683 	{
684 		throwing_writer w;
685 		doc.print(w);
686 
687 		CHECK_FORCE_FAIL("Expected exception");
688 	}
689 	catch (std::runtime_error&)
690 	{
691 	}
692 }
693 
694 TEST_XML(write_throw_encoding, "<node><child/></node>")
695 {
696 	try
697 	{
698 		throwing_writer w;
699 		doc.print(w, STR("\t"), format_default, encoding_utf32_be);
700 
701 		CHECK_FORCE_FAIL("Expected exception");
702 	}
703 	catch (std::runtime_error&)
704 	{
705 	}
706 }
707 #endif
708