1 #define _CRT_SECURE_NO_WARNINGS
2 #define _SCL_SECURE_NO_WARNINGS
3 #define _SCL_SECURE_NO_DEPRECATE
4 #define _CRT_NONSTDC_NO_DEPRECATE 0
5 
6 #include <string.h> // because Borland's STL is braindead, we have to include <string.h> _before_ <string> in order to get memcpy
7 
8 #include "common.hpp"
9 
10 #include "writer_string.hpp"
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 
15 #include <fstream>
16 #include <sstream>
17 
18 #include <string>
19 #include <algorithm>
20 
21 #ifdef __MINGW32__
22 #	include <io.h> // for unlink in C++0x mode
23 #endif
24 
25 #if defined(__CELLOS_LV2__) || defined(ANDROID) || defined(_GLIBCXX_HAVE_UNISTD_H) || defined(__APPLE__)
26 #	include <unistd.h> // for unlink
27 #endif
28 
load_file_in_memory(const char * path,char * & data,size_t & size)29 static bool load_file_in_memory(const char* path, char*& data, size_t& size)
30 {
31 	FILE* file = fopen(path, "rb");
32 	if (!file) return false;
33 
34 	fseek(file, 0, SEEK_END);
35 	long length = ftell(file);
36 	fseek(file, 0, SEEK_SET);
37 
38 	CHECK(length >= 0);
39 	size = static_cast<size_t>(length);
40 	data = new char[size];
41 
42 	CHECK(fread(data, 1, size, file) == size);
43 	fclose(file);
44 
45 	return true;
46 }
47 
test_file_contents(const char * path,const char * data,size_t size)48 static bool test_file_contents(const char* path, const char* data, size_t size)
49 {
50     char* fdata;
51     size_t fsize;
52     if (!load_file_in_memory(path, fdata, fsize)) return false;
53 
54     bool result = (size == fsize && memcmp(data, fdata, size) == 0);
55 
56     delete[] fdata;
57 
58     return result;
59 }
60 
TEST(document_create_empty)61 TEST(document_create_empty)
62 {
63 	pugi::xml_document doc;
64 	CHECK_NODE(doc, STR(""));
65 }
66 
TEST(document_create)67 TEST(document_create)
68 {
69 	pugi::xml_document doc;
70 	doc.append_child().set_name(STR("node"));
71 	CHECK_NODE(doc, STR("<node/>"));
72 }
73 
74 #ifndef PUGIXML_NO_STL
TEST(document_load_stream)75 TEST(document_load_stream)
76 {
77 	pugi::xml_document doc;
78 
79 	std::istringstream iss("<node/>");
80 	CHECK(doc.load(iss));
81 	CHECK_NODE(doc, STR("<node/>"));
82 }
83 
TEST(document_load_stream_offset)84 TEST(document_load_stream_offset)
85 {
86 	pugi::xml_document doc;
87 
88 	std::istringstream iss("<foobar> <node/>");
89 
90 	std::string s;
91 	iss >> s;
92 
93 	CHECK(doc.load(iss));
94 	CHECK_NODE(doc, STR("<node/>"));
95 }
96 
TEST(document_load_stream_text)97 TEST(document_load_stream_text)
98 {
99 	pugi::xml_document doc;
100 
101 	std::ifstream iss("tests/data/multiline.xml");
102 	CHECK(doc.load(iss));
103 	CHECK_NODE(doc, STR("<node1/><node2/><node3/>"));
104 }
105 
TEST(document_load_stream_error)106 TEST(document_load_stream_error)
107 {
108 	pugi::xml_document doc;
109 
110 	std::ifstream fs("filedoesnotexist");
111 	CHECK(doc.load(fs).status == status_io_error);
112 
113 	std::istringstream iss("<node/>");
114 	test_runner::_memory_fail_threshold = 1;
115 	CHECK_ALLOC_FAIL(CHECK(doc.load(iss).status == status_out_of_memory));
116 }
117 
TEST(document_load_stream_empty)118 TEST(document_load_stream_empty)
119 {
120 	std::istringstream iss;
121 
122 	pugi::xml_document doc;
123 	doc.load(iss); // parse result depends on STL implementation
124 	CHECK(!doc.first_child());
125 }
126 
TEST(document_load_stream_wide)127 TEST(document_load_stream_wide)
128 {
129 	pugi::xml_document doc;
130 
131 	std::basic_istringstream<wchar_t> iss(L"<node/>");
132 	CHECK(doc.load(iss));
133 	CHECK_NODE(doc, STR("<node/>"));
134 }
135 
136 #ifndef PUGIXML_NO_EXCEPTIONS
TEST(document_load_stream_exceptions)137 TEST(document_load_stream_exceptions)
138 {
139 	pugi::xml_document doc;
140 
141 	// Windows has newline translation for text-mode files, so reading from this stream reaches eof and sets fail|eof bits.
142 	// This test does not cause stream to throw an exception on Linux - I have no idea how to get read() to fail except
143 	// newline translation.
144 	std::ifstream iss("tests/data/multiline.xml");
145 	iss.exceptions(std::ios::eofbit | std::ios::badbit | std::ios::failbit);
146 
147 	try
148 	{
149 		doc.load(iss);
150 
151 		CHECK(iss.good()); // if the exception was not thrown, stream reading should succeed without errors
152 	}
153 	catch (const std::ios_base::failure&)
154 	{
155 		CHECK(!doc.first_child());
156 	}
157 }
158 #endif
159 
TEST(document_load_stream_error_previous)160 TEST(document_load_stream_error_previous)
161 {
162 	pugi::xml_document doc;
163 	CHECK(doc.load_string(STR("<node/>")));
164 	CHECK(doc.first_child());
165 
166 	std::ifstream fs1("filedoesnotexist");
167 	CHECK(doc.load(fs1).status == status_io_error);
168 	CHECK(!doc.first_child());
169 }
170 
TEST(document_load_stream_wide_error_previous)171 TEST(document_load_stream_wide_error_previous)
172 {
173 	pugi::xml_document doc;
174 	CHECK(doc.load_string(STR("<node/>")));
175 	CHECK(doc.first_child());
176 
177 	std::basic_ifstream<wchar_t> fs1("filedoesnotexist");
178 	CHECK(doc.load(fs1).status == status_io_error);
179 	CHECK(!doc.first_child());
180 }
181 
182 template <typename T> class char_array_buffer: public std::basic_streambuf<T>
183 {
184 public:
char_array_buffer(T * begin,T * end)185     char_array_buffer(T* begin, T* end)
186     {
187         this->setg(begin, begin, end);
188     }
189 
underflow()190     typename std::basic_streambuf<T>::int_type underflow() PUGIXML_OVERRIDE
191     {
192         return this->gptr() == this->egptr() ? std::basic_streambuf<T>::traits_type::eof() : std::basic_streambuf<T>::traits_type::to_int_type(*this->gptr());
193     }
194 };
195 
TEST(document_load_stream_nonseekable)196 TEST(document_load_stream_nonseekable)
197 {
198     char contents[] = "<node />";
199     char_array_buffer<char> buffer(contents, contents + sizeof(contents) / sizeof(contents[0]));
200     std::istream in(&buffer);
201 
202     pugi::xml_document doc;
203     CHECK(doc.load(in));
204     CHECK_NODE(doc, STR("<node/>"));
205 }
206 
TEST(document_load_stream_wide_nonseekable)207 TEST(document_load_stream_wide_nonseekable)
208 {
209     wchar_t contents[] = L"<node />";
210     char_array_buffer<wchar_t> buffer(contents, contents + sizeof(contents) / sizeof(contents[0]));
211     std::basic_istream<wchar_t> in(&buffer);
212 
213     pugi::xml_document doc;
214     CHECK(doc.load(in));
215     CHECK_NODE(doc, STR("<node/>"));
216 }
217 
TEST(document_load_stream_nonseekable_large)218 TEST(document_load_stream_nonseekable_large)
219 {
220 	std::basic_string<pugi::char_t> str;
221 	str += STR("<node>");
222 	for (int i = 0; i < 10000; ++i) str += STR("<node/>");
223 	str += STR("</node>");
224 
225     char_array_buffer<pugi::char_t> buffer(&str[0], &str[0] + str.length());
226     std::basic_istream<pugi::char_t> in(&buffer);
227 
228     pugi::xml_document doc;
229     CHECK(doc.load(in));
230     CHECK_NODE(doc, str.c_str());
231 }
232 
TEST(document_load_stream_nonseekable_out_of_memory)233 TEST(document_load_stream_nonseekable_out_of_memory)
234 {
235     char contents[] = "<node />";
236     char_array_buffer<char> buffer(contents, contents + sizeof(contents) / sizeof(contents[0]));
237     std::istream in(&buffer);
238 
239     test_runner::_memory_fail_threshold = 1;
240 
241     pugi::xml_document doc;
242     CHECK_ALLOC_FAIL(CHECK(doc.load(in).status == status_out_of_memory));
243 }
244 
TEST(document_load_stream_nonseekable_out_of_memory_large)245 TEST(document_load_stream_nonseekable_out_of_memory_large)
246 {
247 	std::basic_string<pugi::char_t> str;
248 	str += STR("<node>");
249 	for (int i = 0; i < 10000; ++i) str += STR("<node />");
250 	str += STR("</node>");
251 
252     char_array_buffer<pugi::char_t> buffer(&str[0], &str[0] + str.length());
253     std::basic_istream<pugi::char_t> in(&buffer);
254 
255     test_runner::_memory_fail_threshold = 10000 * 8 * 3 / 2;
256 
257     pugi::xml_document doc;
258     CHECK_ALLOC_FAIL(CHECK(doc.load(in).status == status_out_of_memory));
259 }
260 #endif
261 
TEST(document_load_string)262 TEST(document_load_string)
263 {
264 	pugi::xml_document doc;
265 
266 	CHECK(doc.load_string(STR("<node/>")));
267 	CHECK_NODE(doc, STR("<node/>"));
268 }
269 
TEST(document_load_file)270 TEST(document_load_file)
271 {
272 	pugi::xml_document doc;
273 
274 	CHECK(doc.load_file("tests/data/small.xml"));
275 	CHECK_NODE(doc, STR("<node/>"));
276 }
277 
TEST(document_load_file_empty)278 TEST(document_load_file_empty)
279 {
280 	pugi::xml_document doc;
281 
282 	CHECK(doc.load_file("tests/data/empty.xml").status == status_no_document_element);
283 	CHECK(!doc.first_child());
284 }
285 
TEST(document_load_file_large)286 TEST(document_load_file_large)
287 {
288 	pugi::xml_document doc;
289 
290 	CHECK(doc.load_file("tests/data/large.xml"));
291 
292 	std::basic_string<pugi::char_t> str;
293 	str += STR("<node>");
294 	for (int i = 0; i < 10000; ++i) str += STR("<node/>");
295 	str += STR("</node>");
296 
297 	CHECK_NODE(doc, str.c_str());
298 }
299 
TEST(document_load_file_error)300 TEST(document_load_file_error)
301 {
302 	pugi::xml_document doc;
303 
304 	CHECK(doc.load_file("filedoesnotexist").status == status_file_not_found);
305 }
306 
TEST(document_load_file_out_of_memory)307 TEST(document_load_file_out_of_memory)
308 {
309 	test_runner::_memory_fail_threshold = 1;
310 
311 	pugi::xml_document doc;
312 	CHECK_ALLOC_FAIL(CHECK(doc.load_file("tests/data/small.xml").status == status_out_of_memory));
313 }
314 
TEST(document_load_file_out_of_memory_file_leak)315 TEST(document_load_file_out_of_memory_file_leak)
316 {
317 	test_runner::_memory_fail_threshold = 1;
318 
319 	pugi::xml_document doc;
320 
321 	for (int i = 0; i < 256; ++i)
322 		CHECK_ALLOC_FAIL(CHECK(doc.load_file("tests/data/small.xml").status == status_out_of_memory));
323 
324 	test_runner::_memory_fail_threshold = 0;
325 
326 	CHECK(doc.load_file("tests/data/small.xml"));
327 	CHECK_NODE(doc, STR("<node/>"));
328 }
329 
TEST(document_load_file_wide_out_of_memory_file_leak)330 TEST(document_load_file_wide_out_of_memory_file_leak)
331 {
332 	test_runner::_memory_fail_threshold = 256;
333 
334 	pugi::xml_document doc;
335 
336 	for (int i = 0; i < 256; ++i)
337 		CHECK_ALLOC_FAIL(CHECK(doc.load_file(L"tests/data/small.xml").status == status_out_of_memory));
338 
339 	test_runner::_memory_fail_threshold = 0;
340 
341 	CHECK(doc.load_file(L"tests/data/small.xml"));
342 	CHECK_NODE(doc, STR("<node/>"));
343 }
344 
TEST(document_load_file_error_previous)345 TEST(document_load_file_error_previous)
346 {
347 	pugi::xml_document doc;
348 	CHECK(doc.load_string(STR("<node/>")));
349 	CHECK(doc.first_child());
350 
351 	CHECK(doc.load_file("filedoesnotexist").status == status_file_not_found);
352 	CHECK(!doc.first_child());
353 }
354 
TEST(document_load_file_wide_ascii)355 TEST(document_load_file_wide_ascii)
356 {
357 	pugi::xml_document doc;
358 
359 	CHECK(doc.load_file(L"tests/data/small.xml"));
360 	CHECK_NODE(doc, STR("<node/>"));
361 }
362 
363 #if !defined(__DMC__) && !defined(__MWERKS__) && !(defined(__MINGW32__) && defined(__STRICT_ANSI__) && !defined(__MINGW64_VERSION_MAJOR)) && !defined(__BORLANDC__)
TEST(document_load_file_wide_unicode)364 TEST(document_load_file_wide_unicode)
365 {
366 	pugi::xml_document doc;
367 
368 	CHECK(doc.load_file(L"tests/data/\x0442\x0435\x0441\x0442.xml"));
369 	CHECK_NODE(doc, STR("<node/>"));
370 }
371 #endif
372 
TEST(document_load_file_wide_out_of_memory)373 TEST(document_load_file_wide_out_of_memory)
374 {
375 	test_runner::_memory_fail_threshold = 1;
376 
377 	pugi::xml_document doc;
378 
379 	pugi::xml_parse_result result;
380 	result.status = status_out_of_memory;
381 	CHECK_ALLOC_FAIL(result = doc.load_file(L"tests/data/small.xml"));
382 
383 	CHECK(result.status == status_out_of_memory || result.status == status_file_not_found);
384 }
385 
386 TEST_XML(document_save, "<node/>")
387 {
388 	xml_writer_string writer;
389 
390 	doc.save(writer, STR(""), pugi::format_no_declaration | pugi::format_raw, get_native_encoding());
391 
392 	CHECK(writer.as_string() == STR("<node/>"));
393 }
394 
395 #ifndef PUGIXML_NO_STL
396 TEST_XML(document_save_stream, "<node/>")
397 {
398 	std::ostringstream oss;
399 
400 	doc.save(oss, STR(""), pugi::format_no_declaration | pugi::format_raw);
401 
402 	CHECK(oss.str() == "<node/>");
403 }
404 
405 TEST_XML(document_save_stream_wide, "<node/>")
406 {
407 	std::basic_ostringstream<wchar_t> oss;
408 
409 	doc.save(oss, STR(""), pugi::format_no_declaration | pugi::format_raw);
410 
411 	CHECK(oss.str() == L"<node/>");
412 }
413 #endif
414 
415 TEST_XML(document_save_bom, "<n/>")
416 {
417 	unsigned int flags = format_no_declaration | format_raw | format_write_bom;
418 
419 	// specific encodings
420 	CHECK(test_save_narrow(doc, flags, encoding_utf8, "\xef\xbb\xbf<n/>", 7));
421 	CHECK(test_save_narrow(doc, flags, encoding_utf16_be, "\xfe\xff\x00<\x00n\x00/\x00>", 10));
422 	CHECK(test_save_narrow(doc, flags, encoding_utf16_le, "\xff\xfe<\x00n\x00/\x00>\x00", 10));
423 	CHECK(test_save_narrow(doc, flags, encoding_utf32_be, "\x00\x00\xfe\xff\x00\x00\x00<\x00\x00\x00n\x00\x00\x00/\x00\x00\x00>", 20));
424 	CHECK(test_save_narrow(doc, flags, encoding_utf32_le, "\xff\xfe\x00\x00<\x00\x00\x00n\x00\x00\x00/\x00\x00\x00>\x00\x00\x00", 20));
425 	CHECK(test_save_narrow(doc, flags, encoding_latin1, "<n/>", 4));
426 
427 	// encodings synonyms
428 	CHECK(save_narrow(doc, flags, encoding_utf16) == save_narrow(doc, flags, (is_little_endian() ? encoding_utf16_le : encoding_utf16_be)));
429 	CHECK(save_narrow(doc, flags, encoding_utf32) == save_narrow(doc, flags, (is_little_endian() ? encoding_utf32_le : encoding_utf32_be)));
430 
431 	size_t wcharsize = sizeof(wchar_t);
432 	CHECK(save_narrow(doc, flags, encoding_wchar) == save_narrow(doc, flags, (wcharsize == 2 ? encoding_utf16 : encoding_utf32)));
433 }
434 
435 TEST_XML(document_save_declaration, "<node/>")
436 {
437 	xml_writer_string writer;
438 
439 	doc.save(writer, STR(""), pugi::format_default, get_native_encoding());
440 
441 	CHECK(writer.as_string() == STR("<?xml version=\"1.0\"?>\n<node />\n"));
442 }
443 
TEST(document_save_declaration_empty)444 TEST(document_save_declaration_empty)
445 {
446 	xml_document doc;
447 
448 	xml_writer_string writer;
449 
450 	doc.save(writer, STR(""), pugi::format_default, get_native_encoding());
451 
452 	CHECK(writer.as_string() == STR("<?xml version=\"1.0\"?>\n"));
453 }
454 
455 TEST_XML(document_save_declaration_present_first, "<node/>")
456 {
457 	doc.insert_child_before(node_declaration, doc.first_child()).append_attribute(STR("encoding")) = STR("utf8");
458 
459 	xml_writer_string writer;
460 
461 	doc.save(writer, STR(""), pugi::format_default, get_native_encoding());
462 
463 	CHECK(writer.as_string() == STR("<?xml encoding=\"utf8\"?>\n<node />\n"));
464 }
465 
466 TEST_XML(document_save_declaration_present_second, "<node/>")
467 {
468 	doc.insert_child_before(node_declaration, doc.first_child()).append_attribute(STR("encoding")) = STR("utf8");
469 	doc.insert_child_before(node_comment, doc.first_child()).set_value(STR("text"));
470 
471 	xml_writer_string writer;
472 
473 	doc.save(writer, STR(""), pugi::format_default, get_native_encoding());
474 
475 	CHECK(writer.as_string() == STR("<!--text-->\n<?xml encoding=\"utf8\"?>\n<node />\n"));
476 }
477 
478 TEST_XML(document_save_declaration_present_last, "<node/>")
479 {
480 	doc.append_child(node_declaration).append_attribute(STR("encoding")) = STR("utf8");
481 
482 	xml_writer_string writer;
483 
484 	doc.save(writer, STR(""), pugi::format_default, get_native_encoding());
485 
486 	// node writer only looks for declaration before the first element child
487 	CHECK(writer.as_string() == STR("<?xml version=\"1.0\"?>\n<node />\n<?xml encoding=\"utf8\"?>\n"));
488 }
489 
490 TEST_XML(document_save_declaration_latin1, "<node/>")
491 {
492 	xml_writer_string writer;
493 
494 	doc.save(writer, STR(""), pugi::format_default, encoding_latin1);
495 
496 	CHECK(writer.as_narrow() == "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<node />\n");
497 }
498 
499 struct temp_file
500 {
501 	char path[512];
502 
temp_filetemp_file503 	temp_file()
504 	{
505 		static int index = 0;
506 		sprintf(path, "%stempfile%d", test_runner::_temp_path, index++);
507 	}
508 
~temp_filetemp_file509 	~temp_file()
510 	{
511 	#ifndef _WIN32_WCE
512 		CHECK(unlink(path) == 0);
513 	#endif
514 	}
515 };
516 
517 TEST_XML(document_save_file, "<node/>")
518 {
519 	temp_file f;
520 
521 	CHECK(doc.save_file(f.path));
522 
523 	CHECK(doc.load_file(f.path, pugi::parse_default | pugi::parse_declaration));
524 	CHECK_NODE(doc, STR("<?xml version=\"1.0\"?><node/>"));
525 }
526 
527 TEST_XML(document_save_file_wide, "<node/>")
528 {
529 	temp_file f;
530 
531 	// widen the path
532 	wchar_t wpath[sizeof(f.path)];
533 	std::copy(f.path, f.path + strlen(f.path) + 1, wpath + 0);
534 
535 	CHECK(doc.save_file(wpath));
536 
537 	CHECK(doc.load_file(f.path, pugi::parse_default | pugi::parse_declaration));
538 	CHECK_NODE(doc, STR("<?xml version=\"1.0\"?><node/>"));
539 }
540 
541 TEST_XML(document_save_file_error, "<node/>")
542 {
543 	CHECK(!doc.save_file("tests/data/unknown/output.xml"));
544 }
545 
546 TEST_XML(document_save_file_text, "<node/>")
547 {
548 	temp_file f;
549 
550 	CHECK(doc.save_file(f.path, STR(""), pugi::format_no_declaration | pugi::format_save_file_text));
551     CHECK(test_file_contents(f.path, "<node />\n", 9) || test_file_contents(f.path, "<node />\r\n", 10));
552 
553 	CHECK(doc.save_file(f.path, STR(""), pugi::format_no_declaration));
554     CHECK(test_file_contents(f.path, "<node />\n", 9));
555 }
556 
557 TEST_XML(document_save_file_wide_text, "<node/>")
558 {
559 	temp_file f;
560 
561 	// widen the path
562 	wchar_t wpath[sizeof(f.path)];
563 	std::copy(f.path, f.path + strlen(f.path) + 1, wpath + 0);
564 
565 	CHECK(doc.save_file(wpath, STR(""), pugi::format_no_declaration | pugi::format_save_file_text));
566     CHECK(test_file_contents(f.path, "<node />\n", 9) || test_file_contents(f.path, "<node />\r\n", 10));
567 
568 	CHECK(doc.save_file(wpath, STR(""), pugi::format_no_declaration));
569     CHECK(test_file_contents(f.path, "<node />\n", 9));
570 }
571 
572 TEST_XML(document_save_file_leak, "<node/>")
573 {
574 	temp_file f;
575 
576 	for (int i = 0; i < 256; ++i)
577 		CHECK(doc.save_file(f.path));
578 }
579 
580 TEST_XML(document_save_file_wide_leak, "<node/>")
581 {
582 	temp_file f;
583 
584 	// widen the path
585 	wchar_t wpath[sizeof(f.path)];
586 	std::copy(f.path, f.path + strlen(f.path) + 1, wpath + 0);
587 
588 	for (int i = 0; i < 256; ++i)
589 		CHECK(doc.save_file(wpath));
590 }
591 
TEST(document_load_buffer)592 TEST(document_load_buffer)
593 {
594 	const pugi::char_t text[] = STR("<?xml?><node/>");
595 
596 	pugi::xml_document doc;
597 
598 	CHECK(doc.load_buffer(text, sizeof(text)));
599 	CHECK_NODE(doc, STR("<node/>"));
600 }
601 
TEST(document_load_buffer_inplace)602 TEST(document_load_buffer_inplace)
603 {
604 	pugi::char_t text[] = STR("<?xml?><node/>");
605 
606 	pugi::xml_document doc;
607 
608 	CHECK(doc.load_buffer_inplace(text, sizeof(text)));
609 	CHECK_NODE(doc, STR("<node/>"));
610 }
611 
TEST(document_load_buffer_inplace_own)612 TEST(document_load_buffer_inplace_own)
613 {
614 	allocation_function alloc = get_memory_allocation_function();
615 
616 	size_t size = strlen("<?xml?><node/>") * sizeof(pugi::char_t);
617 
618 	pugi::char_t* text = static_cast<pugi::char_t*>(alloc(size));
619 	CHECK(text);
620 
621 	memcpy(text, STR("<?xml?><node/>"), size);
622 
623 	pugi::xml_document doc;
624 
625 	CHECK(doc.load_buffer_inplace_own(text, size));
626 	CHECK_NODE(doc, STR("<node/>"));
627 }
628 
TEST(document_parse_result_bool)629 TEST(document_parse_result_bool)
630 {
631 	xml_parse_result result;
632 
633 	result.status = status_ok;
634 	CHECK(result);
635 	CHECK(!!result);
636 	CHECK(result == true);
637 
638 	for (int i = 1; i < 20; ++i)
639 	{
640 		result.status = static_cast<xml_parse_status>(i);
641 		CHECK(!result);
642 		CHECK(result == false);
643 	}
644 }
645 
TEST(document_parse_result_description)646 TEST(document_parse_result_description)
647 {
648 	xml_parse_result result;
649 
650 	for (int i = 0; i < 20; ++i)
651 	{
652 		result.status = static_cast<xml_parse_status>(i);
653 
654 		CHECK(result.description() != 0);
655 		CHECK(result.description()[0] != 0);
656 	}
657 }
658 
TEST(document_load_fail)659 TEST(document_load_fail)
660 {
661 	xml_document doc;
662 	CHECK(!doc.load_string(STR("<foo><bar/>")));
663 	CHECK(doc.child(STR("foo")).child(STR("bar")));
664 }
665 
check_utftest_document(const xml_document & doc)666 inline void check_utftest_document(const xml_document& doc)
667 {
668 	// ascii text
669 	CHECK_STRING(doc.last_child().first_child().name(), STR("English"));
670 
671 	// check that we have parsed some non-ascii text
672 	CHECK(static_cast<unsigned int>(doc.last_child().last_child().name()[0]) >= 0x80);
673 
674 	// check magic string
675 	const pugi::char_t* v = doc.last_child().child(STR("Heavy")).previous_sibling().child_value();
676 
677 #ifdef PUGIXML_WCHAR_MODE
678 	CHECK(v[0] == 0x4e16 && v[1] == 0x754c && v[2] == 0x6709 && v[3] == 0x5f88 && v[4] == 0x591a && v[5] == wchar_cast(0x8bed) && v[6] == wchar_cast(0x8a00));
679 
680 	// last character is a surrogate pair
681 	size_t wcharsize = sizeof(wchar_t);
682 
683 	CHECK(wcharsize == 2 ? (v[7] == wchar_cast(0xd852) && v[8] == wchar_cast(0xdf62)) : (v[7] == wchar_cast(0x24b62)));
684 #else
685 	// unicode string
686 	CHECK_STRING(v, "\xe4\xb8\x96\xe7\x95\x8c\xe6\x9c\x89\xe5\xbe\x88\xe5\xa4\x9a\xe8\xaf\xad\xe8\xa8\x80\xf0\xa4\xad\xa2");
687 #endif
688 }
689 
TEST(document_load_file_convert_auto)690 TEST(document_load_file_convert_auto)
691 {
692 	const char* files[] =
693 	{
694 		"tests/data/utftest_utf16_be.xml",
695 		"tests/data/utftest_utf16_be_bom.xml",
696 		"tests/data/utftest_utf16_be_nodecl.xml",
697 		"tests/data/utftest_utf16_le.xml",
698 		"tests/data/utftest_utf16_le_bom.xml",
699 		"tests/data/utftest_utf16_le_nodecl.xml",
700 		"tests/data/utftest_utf32_be.xml",
701 		"tests/data/utftest_utf32_be_bom.xml",
702 		"tests/data/utftest_utf32_be_nodecl.xml",
703 		"tests/data/utftest_utf32_le.xml",
704 		"tests/data/utftest_utf32_le_bom.xml",
705 		"tests/data/utftest_utf32_le_nodecl.xml",
706 		"tests/data/utftest_utf8.xml",
707 		"tests/data/utftest_utf8_bom.xml",
708 		"tests/data/utftest_utf8_nodecl.xml"
709 	};
710 
711 	xml_encoding encodings[] =
712 	{
713 		encoding_utf16_be, encoding_utf16_be, encoding_utf16_be,
714 		encoding_utf16_le, encoding_utf16_le, encoding_utf16_le,
715 		encoding_utf32_be, encoding_utf32_be, encoding_utf32_be,
716 		encoding_utf32_le, encoding_utf32_le, encoding_utf32_le,
717 		encoding_utf8, encoding_utf8, encoding_utf8
718 	};
719 
720 	for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i)
721 	{
722 		xml_document doc;
723 		xml_parse_result res = doc.load_file(files[i]);
724 
725 		CHECK(res);
726 		CHECK(res.encoding == encodings[i]);
727 		check_utftest_document(doc);
728 	}
729 }
730 
TEST(document_load_file_convert_specific)731 TEST(document_load_file_convert_specific)
732 {
733 	const char* files[] =
734 	{
735 		"tests/data/utftest_utf16_be.xml",
736 		"tests/data/utftest_utf16_be_bom.xml",
737 		"tests/data/utftest_utf16_be_nodecl.xml",
738 		"tests/data/utftest_utf16_le.xml",
739 		"tests/data/utftest_utf16_le_bom.xml",
740 		"tests/data/utftest_utf16_le_nodecl.xml",
741 		"tests/data/utftest_utf32_be.xml",
742 		"tests/data/utftest_utf32_be_bom.xml",
743 		"tests/data/utftest_utf32_be_nodecl.xml",
744 		"tests/data/utftest_utf32_le.xml",
745 		"tests/data/utftest_utf32_le_bom.xml",
746 		"tests/data/utftest_utf32_le_nodecl.xml",
747 		"tests/data/utftest_utf8.xml",
748 		"tests/data/utftest_utf8_bom.xml",
749 		"tests/data/utftest_utf8_nodecl.xml"
750 	};
751 
752 	xml_encoding encodings[] =
753 	{
754 		encoding_utf16_be, encoding_utf16_be, encoding_utf16_be,
755 		encoding_utf16_le, encoding_utf16_le, encoding_utf16_le,
756 		encoding_utf32_be, encoding_utf32_be, encoding_utf32_be,
757 		encoding_utf32_le, encoding_utf32_le, encoding_utf32_le,
758 		encoding_utf8, encoding_utf8, encoding_utf8
759 	};
760 
761 	for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i)
762 	{
763 		for (unsigned int j = 0; j < sizeof(files) / sizeof(files[0]); ++j)
764 		{
765 			xml_encoding encoding = encodings[j];
766 
767 			xml_document doc;
768 			xml_parse_result res = doc.load_file(files[i], parse_default, encoding);
769 
770 			if (encoding == encodings[i])
771 			{
772 				CHECK(res);
773 				CHECK(res.encoding == encoding);
774 				check_utftest_document(doc);
775 			}
776 			else
777 			{
778 				// should not get past first tag
779 				CHECK(!doc.first_child());
780 			}
781 		}
782 	}
783 }
784 
TEST(document_load_file_convert_native_endianness)785 TEST(document_load_file_convert_native_endianness)
786 {
787 	const char* files[2][6] =
788 	{
789 		{
790 			"tests/data/utftest_utf16_be.xml",
791 			"tests/data/utftest_utf16_be_bom.xml",
792 			"tests/data/utftest_utf16_be_nodecl.xml",
793 			"tests/data/utftest_utf32_be.xml",
794 			"tests/data/utftest_utf32_be_bom.xml",
795 			"tests/data/utftest_utf32_be_nodecl.xml",
796 		},
797 		{
798 			"tests/data/utftest_utf16_le.xml",
799 			"tests/data/utftest_utf16_le_bom.xml",
800 			"tests/data/utftest_utf16_le_nodecl.xml",
801 			"tests/data/utftest_utf32_le.xml",
802 			"tests/data/utftest_utf32_le_bom.xml",
803 			"tests/data/utftest_utf32_le_nodecl.xml",
804 		}
805 	};
806 
807 	xml_encoding encodings[] =
808 	{
809 		encoding_utf16, encoding_utf16, encoding_utf16,
810 		encoding_utf32, encoding_utf32, encoding_utf32
811 	};
812 
813 	for (unsigned int i = 0; i < sizeof(files[0]) / sizeof(files[0][0]); ++i)
814 	{
815 		const char* right_file = files[is_little_endian()][i];
816 		const char* wrong_file = files[!is_little_endian()][i];
817 
818 		for (unsigned int j = 0; j < sizeof(encodings) / sizeof(encodings[0]); ++j)
819 		{
820 			xml_encoding encoding = encodings[j];
821 
822 			// check file with right endianness
823 			{
824 				xml_document doc;
825 				xml_parse_result res = doc.load_file(right_file, parse_default, encoding);
826 
827 				if (encoding == encodings[i])
828 				{
829 					CHECK(res);
830 					check_utftest_document(doc);
831 				}
832 				else
833 				{
834 					// should not get past first tag
835 					CHECK(!doc.first_child());
836 				}
837 			}
838 
839 			// check file with wrong endianness
840 			{
841 				xml_document doc;
842 				doc.load_file(wrong_file, parse_default, encoding);
843 				CHECK(!doc.first_child());
844 			}
845 		}
846 	}
847 }
848 
849 struct file_data_t
850 {
851     const char* path;
852     xml_encoding encoding;
853 
854     char* data;
855     size_t size;
856 };
857 
858 
TEST(document_contents_preserve)859 TEST(document_contents_preserve)
860 {
861 	file_data_t files[] =
862 	{
863 		{"tests/data/utftest_utf16_be_clean.xml", encoding_utf16_be, 0, 0},
864 		{"tests/data/utftest_utf16_le_clean.xml", encoding_utf16_le, 0, 0},
865 		{"tests/data/utftest_utf32_be_clean.xml", encoding_utf32_be, 0, 0},
866 		{"tests/data/utftest_utf32_le_clean.xml", encoding_utf32_le, 0, 0},
867 		{"tests/data/utftest_utf8_clean.xml", encoding_utf8, 0, 0}
868 	};
869 
870 	// load files in memory
871 	for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i)
872 	{
873 		CHECK(load_file_in_memory(files[i].path, files[i].data, files[i].size));
874 	}
875 
876 	// convert each file to each format and compare bitwise
877 	for (unsigned int src = 0; src < sizeof(files) / sizeof(files[0]); ++src)
878 	{
879 		for (unsigned int dst = 0; dst < sizeof(files) / sizeof(files[0]); ++dst)
880 		{
881 			// parse into document (preserve comments, declaration and whitespace pcdata)
882 			xml_document doc;
883 			CHECK(doc.load_buffer(files[src].data, files[src].size, parse_default | parse_ws_pcdata | parse_declaration | parse_comments));
884 
885 			// compare saved document with the original (raw formatting, without extra declaration, write bom if it was in original file)
886 			CHECK(test_save_narrow(doc, format_raw | format_no_declaration | format_write_bom, files[dst].encoding, files[dst].data, files[dst].size));
887 		}
888 	}
889 
890 	// cleanup
891 	for (unsigned int j = 0; j < sizeof(files) / sizeof(files[0]); ++j)
892 	{
893 		delete[] files[j].data;
894 	}
895 }
896 
TEST(document_contents_preserve_latin1)897 TEST(document_contents_preserve_latin1)
898 {
899 	file_data_t files[] =
900 	{
901 		{"tests/data/latintest_utf8.xml", encoding_utf8, 0, 0},
902 		{"tests/data/latintest_latin1.xml", encoding_latin1, 0, 0}
903 	};
904 
905 	// load files in memory
906 	for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i)
907 	{
908 		CHECK(load_file_in_memory(files[i].path, files[i].data, files[i].size));
909 	}
910 
911 	// convert each file to each format and compare bitwise
912 	for (unsigned int src = 0; src < sizeof(files) / sizeof(files[0]); ++src)
913 	{
914 		for (unsigned int dst = 0; dst < sizeof(files) / sizeof(files[0]); ++dst)
915 		{
916 			// parse into document (preserve comments, declaration and whitespace pcdata)
917 			xml_document doc;
918 			CHECK(doc.load_buffer(files[src].data, files[src].size, parse_default | parse_ws_pcdata | parse_declaration | parse_comments));
919 
920 			// compare saved document with the original (raw formatting, without extra declaration, write bom if it was in original file)
921 			CHECK(test_save_narrow(doc, format_raw | format_no_declaration | format_write_bom, files[dst].encoding, files[dst].data, files[dst].size));
922 		}
923 	}
924 
925 	// cleanup
926 	for (unsigned int j = 0; j < sizeof(files) / sizeof(files[0]); ++j)
927 	{
928 		delete[] files[j].data;
929 	}
930 }
931 
test_parse_fail(const void * buffer,size_t size,xml_encoding encoding=encoding_utf8)932 static bool test_parse_fail(const void* buffer, size_t size, xml_encoding encoding = encoding_utf8)
933 {
934 	// copy buffer to heap (to enable out-of-bounds checks)
935 	void* temp = malloc(size);
936 	memcpy(temp, buffer, size);
937 
938 	// check that this parses without buffer overflows (yielding an error)
939 	xml_document doc;
940 	bool result = doc.load_buffer_inplace(temp, size, parse_default, encoding);
941 
942 	free(temp);
943 
944 	return !result;
945 }
946 
TEST(document_convert_invalid_utf8)947 TEST(document_convert_invalid_utf8)
948 {
949 	// invalid 1-byte input
950 	CHECK(test_parse_fail("<\xb0", 2));
951 
952 	// invalid 2-byte input
953 	CHECK(test_parse_fail("<\xc0", 2));
954 	CHECK(test_parse_fail("<\xd0", 2));
955 
956 	// invalid 3-byte input
957 	CHECK(test_parse_fail("<\xe2\x80", 3));
958 	CHECK(test_parse_fail("<\xe2", 2));
959 
960 	// invalid 4-byte input
961 	CHECK(test_parse_fail("<\xf2\x97\x98", 4));
962 	CHECK(test_parse_fail("<\xf2\x97", 3));
963 	CHECK(test_parse_fail("<\xf2", 2));
964 
965 	// invalid 5-byte input
966 	CHECK(test_parse_fail("<\xf8", 2));
967 }
968 
TEST(document_convert_invalid_utf16)969 TEST(document_convert_invalid_utf16)
970 {
971 	// check non-terminated degenerate handling
972 	CHECK(test_parse_fail("\x00<\xda\x1d", 4, encoding_utf16_be));
973 	CHECK(test_parse_fail("<\x00\x1d\xda", 4, encoding_utf16_le));
974 
975 	// check incorrect leading code
976 	CHECK(test_parse_fail("\x00<\xde\x24", 4, encoding_utf16_be));
977 	CHECK(test_parse_fail("<\x00\x24\xde", 4, encoding_utf16_le));
978 }
979 
TEST(document_load_buffer_empty)980 TEST(document_load_buffer_empty)
981 {
982 	xml_encoding encodings[] =
983 	{
984 		encoding_auto,
985 		encoding_utf8,
986 		encoding_utf16_le,
987 		encoding_utf16_be,
988 		encoding_utf16,
989 		encoding_utf32_le,
990 		encoding_utf32_be,
991 		encoding_utf32,
992 		encoding_wchar,
993         encoding_latin1
994 	};
995 
996 	char buffer[1];
997 
998 	for (unsigned int i = 0; i < sizeof(encodings) / sizeof(encodings[0]); ++i)
999 	{
1000 		xml_encoding encoding = encodings[i];
1001 
1002 		xml_document doc;
1003 		CHECK(doc.load_buffer(buffer, 0, parse_default, encoding).status == status_no_document_element && !doc.first_child());
1004 		CHECK(doc.load_buffer(0, 0, parse_default, encoding).status == status_no_document_element && !doc.first_child());
1005 
1006 		CHECK(doc.load_buffer_inplace(buffer, 0, parse_default, encoding).status == status_no_document_element && !doc.first_child());
1007 		CHECK(doc.load_buffer_inplace(0, 0, parse_default, encoding).status == status_no_document_element && !doc.first_child());
1008 
1009 		void* own_buffer = pugi::get_memory_allocation_function()(1);
1010 
1011 		CHECK(doc.load_buffer_inplace_own(own_buffer, 0, parse_default, encoding).status == status_no_document_element && !doc.first_child());
1012 		CHECK(doc.load_buffer_inplace_own(0, 0, parse_default, encoding).status == status_no_document_element && !doc.first_child());
1013 	}
1014 }
1015 
TEST(document_load_buffer_empty_fragment)1016 TEST(document_load_buffer_empty_fragment)
1017 {
1018 	xml_encoding encodings[] =
1019 	{
1020 		encoding_auto,
1021 		encoding_utf8,
1022 		encoding_utf16_le,
1023 		encoding_utf16_be,
1024 		encoding_utf16,
1025 		encoding_utf32_le,
1026 		encoding_utf32_be,
1027 		encoding_utf32,
1028 		encoding_wchar,
1029         encoding_latin1
1030 	};
1031 
1032 	char buffer[1];
1033 
1034 	for (unsigned int i = 0; i < sizeof(encodings) / sizeof(encodings[0]); ++i)
1035 	{
1036 		xml_encoding encoding = encodings[i];
1037 
1038 		xml_document doc;
1039 		CHECK(doc.load_buffer(buffer, 0, parse_fragment, encoding) && !doc.first_child());
1040 		CHECK(doc.load_buffer(0, 0, parse_fragment, encoding) && !doc.first_child());
1041 
1042 		CHECK(doc.load_buffer_inplace(buffer, 0, parse_fragment, encoding) && !doc.first_child());
1043 		CHECK(doc.load_buffer_inplace(0, 0, parse_fragment, encoding) && !doc.first_child());
1044 
1045 		void* own_buffer = pugi::get_memory_allocation_function()(1);
1046 
1047 		CHECK(doc.load_buffer_inplace_own(own_buffer, 0, parse_fragment, encoding) && !doc.first_child());
1048 		CHECK(doc.load_buffer_inplace_own(0, 0, parse_fragment, encoding) && !doc.first_child());
1049 	}
1050 }
1051 
TEST(document_load_buffer_null)1052 TEST(document_load_buffer_null)
1053 {
1054 	xml_document doc;
1055 
1056 	CHECK(doc.load_buffer(0, 12).status == status_io_error && !doc.first_child());
1057 	CHECK(doc.load_buffer(0, 12, parse_fragment).status == status_io_error && !doc.first_child());
1058 
1059 	CHECK(doc.load_buffer_inplace(0, 12).status == status_io_error && !doc.first_child());
1060 	CHECK(doc.load_buffer_inplace_own(0, 12).status == status_io_error && !doc.first_child());
1061 }
1062 
TEST(document_progressive_truncation)1063 TEST(document_progressive_truncation)
1064 {
1065 	char* original_data;
1066 	size_t original_size;
1067 
1068 	CHECK(load_file_in_memory("tests/data/truncation.xml", original_data, original_size));
1069 
1070 	char* buffer = new char[original_size];
1071 
1072 	for (size_t i = 1; i <= original_size; ++i)
1073 	{
1074 		char* truncated_data = buffer + original_size - i;
1075 
1076 		// default flags
1077 		{
1078 			memcpy(truncated_data, original_data, i);
1079 
1080 			xml_document doc;
1081 			bool result = doc.load_buffer_inplace(truncated_data, i);
1082 
1083 			// only eof is parseable
1084 			CHECK((i == original_size) ? result : !result);
1085 		}
1086 
1087 		// fragment mode
1088 		{
1089 			memcpy(truncated_data, original_data, i);
1090 
1091 			xml_document doc;
1092 			bool result = doc.load_buffer_inplace(truncated_data, i, parse_default | parse_fragment);
1093 
1094 			// some truncate locations are parseable - those that come after declaration, declaration + doctype, declaration + doctype + comment and eof
1095 			CHECK(((i - 21) < 3 || (i - 66) < 3 || (i - 95) < 3 || i == original_size) ? result : !result);
1096 		}
1097 	}
1098 
1099 	delete[] buffer;
1100 	delete[] original_data;
1101 }
1102 
TEST(document_load_buffer_short)1103 TEST(document_load_buffer_short)
1104 {
1105 	char* data = new char[4];
1106 	memcpy(data, "abcd", 4);
1107 
1108 	xml_document doc;
1109 
1110 	CHECK(doc.load_buffer(data, 4).status == status_no_document_element);
1111 	CHECK(doc.load_buffer(data + 1, 3).status == status_no_document_element);
1112 	CHECK(doc.load_buffer(data + 2, 2).status == status_no_document_element);
1113 	CHECK(doc.load_buffer(data + 3, 1).status == status_no_document_element);
1114 	CHECK(doc.load_buffer(data + 4, 0).status == status_no_document_element);
1115 	CHECK(doc.load_buffer(0, 0).status == status_no_document_element);
1116 
1117 	delete[] data;
1118 }
1119 
TEST(document_load_buffer_short_fragment)1120 TEST(document_load_buffer_short_fragment)
1121 {
1122 	char* data = new char[4];
1123 	memcpy(data, "abcd", 4);
1124 
1125 	xml_document doc;
1126 
1127 	CHECK(doc.load_buffer(data, 4, parse_fragment) && test_string_equal(doc.text().get(), STR("abcd")));
1128 	CHECK(doc.load_buffer(data + 1, 3, parse_fragment) && test_string_equal(doc.text().get(), STR("bcd")));
1129 	CHECK(doc.load_buffer(data + 2, 2, parse_fragment) && test_string_equal(doc.text().get(), STR("cd")));
1130 	CHECK(doc.load_buffer(data + 3, 1, parse_fragment) && test_string_equal(doc.text().get(), STR("d")));
1131 	CHECK(doc.load_buffer(data + 4, 0, parse_fragment) && !doc.first_child());
1132 	CHECK(doc.load_buffer(0, 0, parse_fragment) && !doc.first_child());
1133 
1134 	delete[] data;
1135 }
1136 
TEST(document_load_buffer_inplace_short)1137 TEST(document_load_buffer_inplace_short)
1138 {
1139 	char* data = new char[4];
1140 	memcpy(data, "abcd", 4);
1141 
1142 	xml_document doc;
1143 
1144 	CHECK(doc.load_buffer_inplace(data, 4).status == status_no_document_element);
1145 	CHECK(doc.load_buffer_inplace(data + 1, 3).status == status_no_document_element);
1146 	CHECK(doc.load_buffer_inplace(data + 2, 2).status == status_no_document_element);
1147 	CHECK(doc.load_buffer_inplace(data + 3, 1).status == status_no_document_element);
1148 	CHECK(doc.load_buffer_inplace(data + 4, 0).status == status_no_document_element);
1149 	CHECK(doc.load_buffer_inplace(0, 0).status == status_no_document_element);
1150 
1151 	delete[] data;
1152 }
1153 
1154 #ifndef PUGIXML_NO_EXCEPTIONS
TEST(document_load_exceptions)1155 TEST(document_load_exceptions)
1156 {
1157     bool thrown = false;
1158 
1159     try
1160     {
1161         pugi::xml_document doc;
1162         if (!doc.load_string(STR("<node attribute='value"))) throw std::bad_alloc();
1163 
1164         CHECK_FORCE_FAIL("Expected parsing failure");
1165     }
1166     catch (const std::bad_alloc&)
1167     {
1168         thrown = true;
1169     }
1170 
1171     CHECK(thrown);
1172 }
1173 #endif
1174 
1175 TEST_XML_FLAGS(document_element, "<?xml version='1.0'?><node><child/></node><!---->", parse_default | parse_declaration | parse_comments)
1176 {
1177     CHECK(doc.document_element() == doc.child(STR("node")));
1178 }
1179 
1180 TEST_XML_FLAGS(document_element_absent, "<!---->", parse_comments | parse_fragment)
1181 {
1182     CHECK(doc.document_element() == xml_node());
1183 }
1184 
1185 TEST_XML(document_reset, "<node><child/></node>")
1186 {
1187     CHECK(doc.first_child());
1188 
1189     doc.reset();
1190     CHECK(!doc.first_child());
1191     CHECK_NODE(doc, STR(""));
1192 
1193     doc.reset();
1194     CHECK(!doc.first_child());
1195     CHECK_NODE(doc, STR(""));
1196 
1197     CHECK(doc.load_string(STR("<node/>")));
1198     CHECK(doc.first_child());
1199     CHECK_NODE(doc, STR("<node/>"));
1200 
1201     doc.reset();
1202     CHECK(!doc.first_child());
1203     CHECK_NODE(doc, STR(""));
1204 }
1205 
TEST(document_reset_empty)1206 TEST(document_reset_empty)
1207 {
1208     xml_document doc;
1209 
1210     doc.reset();
1211     CHECK(!doc.first_child());
1212     CHECK_NODE(doc, STR(""));
1213 }
1214 
1215 TEST_XML(document_reset_copy, "<node><child/></node>")
1216 {
1217     xml_document doc2;
1218 
1219     CHECK_NODE(doc2, STR(""));
1220 
1221     doc2.reset(doc);
1222 
1223     CHECK_NODE(doc2, STR("<node><child/></node>"));
1224     CHECK(doc.first_child() != doc2.first_child());
1225 
1226     doc.reset(doc2);
1227 
1228     CHECK_NODE(doc, STR("<node><child/></node>"));
1229     CHECK(doc.first_child() != doc2.first_child());
1230 
1231     CHECK(doc.first_child().offset_debug() == -1);
1232 }
1233 
1234 TEST_XML(document_reset_copy_self, "<node><child/></node>")
1235 {
1236     CHECK_NODE(doc, STR("<node><child/></node>"));
1237 
1238     doc.reset(doc);
1239 
1240     CHECK(!doc.first_child());
1241     CHECK_NODE(doc, STR(""));
1242 }
1243 
TEST(document_load_buffer_utf_truncated)1244 TEST(document_load_buffer_utf_truncated)
1245 {
1246 	const unsigned char utf8[] = {'<', 0xe2, 0x82, 0xac, '/', '>'};
1247 	const unsigned char utf16_be[] = {0, '<', 0x20, 0xac, 0, '/', 0, '>'};
1248 	const unsigned char utf16_le[] = {'<', 0, 0xac, 0x20, '/', 0, '>', 0};
1249 	const unsigned char utf32_be[] = {0, 0, 0, '<', 0, 0, 0x20, 0xac, 0, 0, 0, '/', 0, 0, 0, '>'};
1250 	const unsigned char utf32_le[] = {'<', 0, 0, 0, 0xac, 0x20, 0, 0, '/', 0, 0, 0, '>', 0, 0, 0};
1251 
1252 	struct document_data_t
1253 	{
1254 	    xml_encoding encoding;
1255 
1256 	    const unsigned char* data;
1257 	    size_t size;
1258 	};
1259 
1260 	const document_data_t data[] =
1261 	{
1262 		{ encoding_utf8, utf8, sizeof(utf8) },
1263 		{ encoding_utf16_be, utf16_be, sizeof(utf16_be) },
1264 		{ encoding_utf16_le, utf16_le, sizeof(utf16_le) },
1265 		{ encoding_utf32_be, utf32_be, sizeof(utf32_be) },
1266 		{ encoding_utf32_le, utf32_le, sizeof(utf32_le) },
1267 	};
1268 
1269 	for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); ++i)
1270 	{
1271 		const document_data_t& d = data[i];
1272 
1273 		for (size_t j = 0; j <= d.size; ++j)
1274 		{
1275 			char* buffer = new char[j];
1276 			memcpy(buffer, d.data, j);
1277 
1278 			xml_document doc;
1279 			xml_parse_result res = doc.load_buffer(buffer, j, parse_default, d.encoding);
1280 
1281 			if (j == d.size)
1282 			{
1283 				CHECK(res);
1284 
1285 				const char_t* name = doc.first_child().name();
1286 
1287 			#ifdef PUGIXML_WCHAR_MODE
1288 				CHECK(name[0] == 0x20ac && name[1] == 0);
1289 			#else
1290 				CHECK_STRING(name, "\xe2\x82\xac");
1291 			#endif
1292 			}
1293 			else
1294 			{
1295 				CHECK(!res || !doc.first_child());
1296 			}
1297 
1298 			delete[] buffer;
1299 		}
1300 	}
1301 }
1302 
1303 #ifndef PUGIXML_NO_STL
TEST(document_load_stream_truncated)1304 TEST(document_load_stream_truncated)
1305 {
1306 	const unsigned char utf32_be[] = {0, 0, 0, '<', 0, 0, 0x20, 0xac, 0, 0, 0, '/', 0, 0, 0, '>'};
1307 
1308 	for (size_t i = 0; i <= sizeof(utf32_be); ++i)
1309 	{
1310 		std::string prefix(reinterpret_cast<const char*>(utf32_be), i);
1311 		std::istringstream iss(prefix);
1312 
1313 		xml_document doc;
1314 		xml_parse_result res = doc.load(iss);
1315 
1316 		if (i == sizeof(utf32_be))
1317 		{
1318 			CHECK(res);
1319 		}
1320 		else
1321 		{
1322 			CHECK(!res || !doc.first_child());
1323 
1324 			if (i < 8)
1325 			{
1326 				CHECK(!doc.first_child());
1327 			}
1328 			else
1329 			{
1330 				const char_t* name = doc.first_child().name();
1331 
1332 			#ifdef PUGIXML_WCHAR_MODE
1333 				CHECK(name[0] == 0x20ac && name[1] == 0);
1334 			#else
1335 				CHECK_STRING(name, "\xe2\x82\xac");
1336 			#endif
1337 			}
1338 		}
1339 	}
1340 }
1341 #endif
1342 
TEST(document_alignment)1343 TEST(document_alignment)
1344 {
1345 	char buf[256 + sizeof(xml_document)];
1346 
1347 	for (size_t offset = 0; offset < 256; offset += sizeof(void*))
1348 	{
1349 		xml_document* doc = new (buf + offset) xml_document;
1350 
1351 		CHECK(doc->load_string(STR("<node />")));
1352 		CHECK_NODE(*doc, STR("<node/>"));
1353 
1354 		doc->~xml_document();
1355 	}
1356 }
1357 
TEST(document_convert_out_of_memory)1358 TEST(document_convert_out_of_memory)
1359 {
1360 	file_data_t files[] =
1361 	{
1362 		{"tests/data/utftest_utf16_be_clean.xml", encoding_utf16_be, 0, 0},
1363 		{"tests/data/utftest_utf16_le_clean.xml", encoding_utf16_le, 0, 0},
1364 		{"tests/data/utftest_utf32_be_clean.xml", encoding_utf32_be, 0, 0},
1365 		{"tests/data/utftest_utf32_le_clean.xml", encoding_utf32_le, 0, 0},
1366 		{"tests/data/utftest_utf8_clean.xml", encoding_utf8, 0, 0},
1367 		{"tests/data/latintest_latin1.xml", encoding_latin1, 0, 0}
1368 	};
1369 
1370 	// load files in memory
1371 	for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i)
1372 	{
1373 		CHECK(load_file_in_memory(files[i].path, files[i].data, files[i].size));
1374 	}
1375 
1376 	// disallow allocations
1377 	test_runner::_memory_fail_threshold = 1;
1378 
1379 	for (unsigned int src = 0; src < sizeof(files) / sizeof(files[0]); ++src)
1380 	{
1381 		xml_document doc;
1382 		CHECK_ALLOC_FAIL(CHECK(doc.load_buffer(files[src].data, files[src].size, parse_default, files[src].encoding).status == status_out_of_memory));
1383 	}
1384 
1385 	// cleanup
1386 	for (unsigned int j = 0; j < sizeof(files) / sizeof(files[0]); ++j)
1387 	{
1388 		delete[] files[j].data;
1389 	}
1390 }
1391 
TEST(document_deprecated_load)1392 TEST(document_deprecated_load)
1393 {
1394 	xml_document doc;
1395 	CHECK(doc.load(STR("<node/>")));
1396 	CHECK_NODE(doc, STR("<node/>"));
1397 }
1398