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