#define _CRT_SECURE_NO_WARNINGS

#include "common.hpp"

#include <stdio.h>

#include <string.h>
#include <wchar.h>

#include <utility>
#include <vector>
#include <iterator>
#include <string>

#include "helpers.hpp"

#ifdef PUGIXML_NO_STL
template <typename I> static I move_iter(I base, int n)
{
	if (n > 0) while (n--) ++base;
	else while (n++) --base;
	return base;
}
#else
template <typename I> static I move_iter(I base, int n)
{
	std::advance(base, n);
	return base;
}
#endif

template <typename T> static void generic_empty_test(const T& obj)
{
	T null;

	CHECK(null.empty());
	CHECK(!obj.empty());
}

TEST_XML(dom_attr_bool_ops, "<node attr='1'/>")
{
	generic_bool_ops_test(doc.child(STR("node")).attribute(STR("attr")));
}

TEST_XML(dom_attr_eq_ops, "<node attr1='1' attr2='2'/>")
{
	generic_eq_ops_test(doc.child(STR("node")).attribute(STR("attr1")), doc.child(STR("node")).attribute(STR("attr2")));
}

TEST_XML(dom_attr_rel_ops, "<node attr1='1' attr2='2'/>")
{
	generic_rel_ops_test(doc.child(STR("node")).attribute(STR("attr1")), doc.child(STR("node")).attribute(STR("attr2")));
}

TEST_XML(dom_attr_empty, "<node attr='1'/>")
{
	generic_empty_test(doc.child(STR("node")).attribute(STR("attr")));
}

TEST_XML(dom_attr_next_previous_attribute, "<node attr1='1' attr2='2' />")
{
	xml_attribute attr1 = doc.child(STR("node")).attribute(STR("attr1"));
	xml_attribute attr2 = doc.child(STR("node")).attribute(STR("attr2"));

	CHECK(attr1.next_attribute() == attr2);
	CHECK(attr2.next_attribute() == xml_attribute());
	
	CHECK(attr1.previous_attribute() == xml_attribute());
	CHECK(attr2.previous_attribute() == attr1);
	
	CHECK(xml_attribute().next_attribute() == xml_attribute());
	CHECK(xml_attribute().previous_attribute() == xml_attribute());
}

TEST_XML(dom_attr_name_value, "<node attr='1'/>")
{
	xml_attribute attr = doc.child(STR("node")).attribute(STR("attr"));

	CHECK_NAME_VALUE(attr, STR("attr"), STR("1"));
	CHECK_NAME_VALUE(xml_attribute(), STR(""), STR(""));
}

TEST_XML(dom_attr_as_int, "<node attr1='1' attr2='-1' attr3='-2147483648' attr4='2147483647'/>")
{
	xml_node node = doc.child(STR("node"));

	CHECK(xml_attribute().as_int() == 0);
	CHECK(node.attribute(STR("attr1")).as_int() == 1);
	CHECK(node.attribute(STR("attr2")).as_int() == -1);
	CHECK(node.attribute(STR("attr3")).as_int() == -2147483647 - 1);
	CHECK(node.attribute(STR("attr4")).as_int() == 2147483647);
}

TEST_XML(dom_attr_as_uint, "<node attr1='0' attr2='1' attr3='2147483647' attr4='4294967295'/>")
{
	xml_node node = doc.child(STR("node"));

	CHECK(xml_attribute().as_uint() == 0);
	CHECK(node.attribute(STR("attr1")).as_uint() == 0);
	CHECK(node.attribute(STR("attr2")).as_uint() == 1);
	CHECK(node.attribute(STR("attr3")).as_uint() == 2147483647);
	CHECK(node.attribute(STR("attr4")).as_uint() == 4294967295u);
}

TEST_XML(dom_attr_as_float, "<node attr1='0' attr2='1' attr3='0.12' attr4='-5.1' attr5='3e-4' attr6='3.14159265358979323846'/>")
{
	xml_node node = doc.child(STR("node"));

	CHECK(xml_attribute().as_float() == 0);
	CHECK_DOUBLE(node.attribute(STR("attr1")).as_float(), 0);
	CHECK_DOUBLE(node.attribute(STR("attr2")).as_float(), 1);
	CHECK_DOUBLE(node.attribute(STR("attr3")).as_float(), 0.12);
	CHECK_DOUBLE(node.attribute(STR("attr4")).as_float(), -5.1);
	CHECK_DOUBLE(node.attribute(STR("attr5")).as_float(), 3e-4);
	CHECK_DOUBLE(node.attribute(STR("attr6")).as_float(), 3.14159265358979323846);
}

TEST_XML(dom_attr_as_double, "<node attr1='0' attr2='1' attr3='0.12' attr4='-5.1' attr5='3e-4' attr6='3.14159265358979323846'/>")
{
	xml_node node = doc.child(STR("node"));

	CHECK(xml_attribute().as_double() == 0);
	CHECK_DOUBLE(node.attribute(STR("attr1")).as_double(), 0);
	CHECK_DOUBLE(node.attribute(STR("attr2")).as_double(), 1);
	CHECK_DOUBLE(node.attribute(STR("attr3")).as_double(), 0.12);
	CHECK_DOUBLE(node.attribute(STR("attr4")).as_double(), -5.1);
	CHECK_DOUBLE(node.attribute(STR("attr5")).as_double(), 3e-4);
	CHECK_DOUBLE(node.attribute(STR("attr6")).as_double(), 3.14159265358979323846);
}

TEST_XML(dom_attr_as_bool, "<node attr1='0' attr2='1' attr3='true' attr4='True' attr5='Yes' attr6='yes' attr7='false'/>")
{
	xml_node node = doc.child(STR("node"));

	CHECK(!xml_attribute().as_bool());
	CHECK(!node.attribute(STR("attr1")).as_bool());
	CHECK(node.attribute(STR("attr2")).as_bool());
	CHECK(node.attribute(STR("attr3")).as_bool());
	CHECK(node.attribute(STR("attr4")).as_bool());
	CHECK(node.attribute(STR("attr5")).as_bool());
	CHECK(node.attribute(STR("attr6")).as_bool());
	CHECK(!node.attribute(STR("attr7")).as_bool());
}

TEST_XML(dom_attr_iterator, "<node><node1 attr1='0'/><node2 attr1='0' attr2='1'/><node3/></node>")
{
	xml_node node1 = doc.child(STR("node")).child(STR("node1"));
	xml_node node2 = doc.child(STR("node")).child(STR("node2"));
	xml_node node3 = doc.child(STR("node")).child(STR("node3"));

	CHECK(xml_node().attributes_begin() == xml_attribute_iterator());
	CHECK(xml_node().attributes_end() == xml_attribute_iterator());

	CHECK(node1.attributes_begin() == xml_attribute_iterator(node1.attribute(STR("attr1"))));
	CHECK(move_iter(node1.attributes_begin(), 1) == node1.attributes_end());
	CHECK(move_iter(node1.attributes_end(), -1) == node1.attributes_begin());
	CHECK(*node1.attributes_begin() == node1.attribute(STR("attr1")));
	CHECK_STRING(node1.attributes_begin()->name(), STR("attr1"));

	CHECK(move_iter(node2.attributes_begin(), 2) == node2.attributes_end());
	CHECK(move_iter(node2.attributes_end(), -2) == node2.attributes_begin());

	CHECK(node3.attributes_begin() == xml_attribute_iterator());
	CHECK(node3.attributes_begin() == node3.attributes_end());

	xml_attribute_iterator it = node2.attribute(STR("attr2"));
	xml_attribute_iterator itt = it;

	CHECK(itt++ == it);
	CHECK(itt == node2.attributes_end());

	CHECK(itt-- == node2.attributes_end());
	CHECK(itt == it);

	CHECK(++itt == node2.attributes_end());
	CHECK(itt == node2.attributes_end());

	CHECK(--itt == it);
	CHECK(itt == it);

	CHECK(++itt != it);
}

TEST_XML(dom_node_bool_ops, "<node/>")
{
	generic_bool_ops_test(doc.child(STR("node")));
}

TEST_XML(dom_node_eq_ops, "<node><node1/><node2/></node>")
{
	generic_eq_ops_test(doc.child(STR("node")).child(STR("node1")), doc.child(STR("node")).child(STR("node2")));
}

TEST_XML(dom_node_rel_ops, "<node><node1/><node2/></node>")
{
	generic_rel_ops_test(doc.child(STR("node")).child(STR("node1")), doc.child(STR("node")).child(STR("node2")));
}

TEST_XML(dom_node_empty, "<node/>")
{
	generic_empty_test(doc.child(STR("node")));
}

TEST_XML(dom_node_iterator, "<node><node1><child1/></node1><node2><child1/><child2/></node2><node3/></node>")
{
	xml_node node1 = doc.child(STR("node")).child(STR("node1"));
	xml_node node2 = doc.child(STR("node")).child(STR("node2"));
	xml_node node3 = doc.child(STR("node")).child(STR("node3"));

	CHECK(xml_node().begin() == xml_node_iterator());
	CHECK(xml_node().end() == xml_node_iterator());

	CHECK(node1.begin() == xml_node_iterator(node1.child(STR("child1"))));
	CHECK(move_iter(node1.begin(), 1) == node1.end());
	CHECK(move_iter(node1.end(), -1) == node1.begin());
	CHECK(*node1.begin() == node1.child(STR("child1")));
	CHECK_STRING(node1.begin()->name(), STR("child1"));

	CHECK(move_iter(node2.begin(), 2) == node2.end());
	CHECK(move_iter(node2.end(), -2) == node2.begin());

	CHECK(node3.begin() == xml_node_iterator());
	CHECK(node3.begin() == node3.end());

	xml_node_iterator it = node2.child(STR("child2"));
	xml_node_iterator itt = it;

	CHECK(itt++ == it);
	CHECK(itt == node2.end());

	CHECK(itt-- == node2.end());
	CHECK(itt == it);

	CHECK(++itt == node2.end());
	CHECK(itt == node2.end());

	CHECK(--itt == it);
	CHECK(itt == it);

	CHECK(++itt != it);
}

TEST_XML(dom_node_parent, "<node><child/></node>")
{
	CHECK(xml_node().parent() == xml_node());
	CHECK(doc.child(STR("node")).child(STR("child")).parent() == doc.child(STR("node")));
	CHECK(doc.child(STR("node")).parent() == doc);
}

TEST_XML(dom_node_root, "<node><child/></node>")
{
	CHECK(xml_node().root() == xml_node());
	CHECK(doc.child(STR("node")).child(STR("child")).root() == doc);
	CHECK(doc.child(STR("node")).root() == doc);
}

TEST_XML_FLAGS(dom_node_type, "<?xml?><?pi?><!--comment--><node>pcdata<![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments | parse_declaration)
{
	CHECK(xml_node().type() == node_null);
	CHECK(doc.type() == node_document);

	xml_node_iterator it = doc.begin();

	CHECK((it++)->type() == node_declaration);
	CHECK((it++)->type() == node_pi);
	CHECK((it++)->type() == node_comment);
	CHECK((it++)->type() == node_element);

	xml_node_iterator cit = doc.child(STR("node")).begin();
	
	CHECK((cit++)->type() == node_pcdata);
	CHECK((cit++)->type() == node_cdata);
}

TEST_XML_FLAGS(dom_node_name_value, "<?xml?><?pi?><!--comment--><node>pcdata<![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments | parse_declaration)
{
	CHECK_NAME_VALUE(xml_node(), STR(""), STR(""));
	CHECK_NAME_VALUE(doc, STR(""), STR(""));

	xml_node_iterator it = doc.begin();

	CHECK_NAME_VALUE(*it++, STR("xml"), STR(""));
	CHECK_NAME_VALUE(*it++, STR("pi"), STR(""));
	CHECK_NAME_VALUE(*it++, STR(""), STR("comment"));
	CHECK_NAME_VALUE(*it++, STR("node"), STR(""));

	xml_node_iterator cit = doc.child(STR("node")).begin();
	
	CHECK_NAME_VALUE(*cit++, STR(""), STR("pcdata"));
	CHECK_NAME_VALUE(*cit++, STR(""), STR("cdata"));
}

TEST_XML(dom_node_child, "<node><child1/><child2/></node>")
{
	CHECK(xml_node().child(STR("n")) == xml_node());

	CHECK(doc.child(STR("n")) == xml_node());
	CHECK_NAME_VALUE(doc.child(STR("node")), STR("node"), STR(""));
	CHECK(doc.child(STR("node")).child(STR("child2")) == doc.child(STR("node")).last_child());
}

TEST_XML(dom_node_attribute, "<node attr1='0' attr2='1'/>")
{
	CHECK(xml_node().attribute(STR("a")) == xml_attribute());

	xml_node node = doc.child(STR("node"));

	CHECK(node.attribute(STR("n")) == xml_attribute());
	CHECK_NAME_VALUE(node.attribute(STR("attr1")), STR("attr1"), STR("0"));
	CHECK(node.attribute(STR("attr2")) == node.last_attribute());
}

TEST_XML(dom_node_next_previous_sibling, "<node><child1/><child2/><child3/></node>")
{
	CHECK(xml_node().next_sibling() == xml_node());
	CHECK(xml_node().next_sibling(STR("n")) == xml_node());

	CHECK(xml_node().previous_sibling() == xml_node());
	CHECK(xml_node().previous_sibling(STR("n")) == xml_node());

	xml_node child1 = doc.child(STR("node")).child(STR("child1"));
	xml_node child2 = doc.child(STR("node")).child(STR("child2"));
	xml_node child3 = doc.child(STR("node")).child(STR("child3"));

	CHECK(child1.next_sibling() == child2);
	CHECK(child3.next_sibling() == xml_node());
	
	CHECK(child1.previous_sibling() == xml_node());
	CHECK(child3.previous_sibling() == child2);
	
	CHECK(child1.next_sibling(STR("child3")) == child3);
	CHECK(child1.next_sibling(STR("child")) == xml_node());

	CHECK(child3.previous_sibling(STR("child1")) == child1);
	CHECK(child3.previous_sibling(STR("child")) == xml_node());
}

TEST_XML(dom_node_child_value, "<node><novalue/><child1>value1</child1><child2>value2<n/></child2><child3><![CDATA[value3]]></child3>value4</node>")
{
	CHECK_STRING(xml_node().child_value(), STR(""));
	CHECK_STRING(xml_node().child_value(STR("n")), STR(""));

	xml_node node = doc.child(STR("node"));

	CHECK_STRING(node.child_value(), STR("value4"));
	CHECK_STRING(node.child(STR("child1")).child_value(), STR("value1"));
	CHECK_STRING(node.child(STR("child2")).child_value(), STR("value2"));
	CHECK_STRING(node.child(STR("child3")).child_value(), STR("value3"));
	CHECK_STRING(node.child_value(STR("child3")), STR("value3"));
}

TEST_XML(dom_node_first_last_attribute, "<node attr1='0' attr2='1'/>")
{
	xml_node node = doc.child(STR("node"));

	CHECK(node.first_attribute() == node.attribute(STR("attr1")));
	CHECK(node.last_attribute() == node.attribute(STR("attr2")));

	CHECK(xml_node().first_attribute() == xml_attribute());
	CHECK(xml_node().last_attribute() == xml_attribute());

	CHECK(doc.first_attribute() == xml_attribute());
	CHECK(doc.last_attribute() == xml_attribute());
}

TEST_XML(dom_node_first_last_child, "<node><child1/><child2/></node>")
{
	xml_node node = doc.child(STR("node"));

	CHECK(node.first_child() == node.child(STR("child1")));
	CHECK(node.last_child() == node.child(STR("child2")));

	CHECK(xml_node().first_child() == xml_node());
	CHECK(xml_node().last_child() == xml_node());

	CHECK(doc.first_child() == node);
	CHECK(doc.last_child() == node);
}

TEST_XML(dom_node_find_child_by_attribute, "<node><child1 attr='value1'/><child2 attr='value2'/><child2 attr='value3'/></node>")
{
	CHECK(xml_node().find_child_by_attribute(STR("name"), STR("attr"), STR("value")) == xml_node());
	CHECK(xml_node().find_child_by_attribute(STR("attr"), STR("value")) == xml_node());

	xml_node node = doc.child(STR("node"));

	CHECK(node.find_child_by_attribute(STR("child2"), STR("attr"), STR("value3")) == node.last_child());
	CHECK(node.find_child_by_attribute(STR("child2"), STR("attr3"), STR("value3")) == xml_node());
	CHECK(node.find_child_by_attribute(STR("attr"), STR("value2")) == node.child(STR("child2")));
	CHECK(node.find_child_by_attribute(STR("attr3"), STR("value")) == xml_node());
}

TEST_XML(dom_node_all_elements_by_name, "<node><child><child/><child/></child></node>")
{
	std::vector<xml_node> v;

	v.clear();
	xml_node().all_elements_by_name(STR("node"), std::back_inserter(v));
	CHECK(v.empty());

	v.clear();
	doc.all_elements_by_name(STR("node"), std::back_inserter(v));
	CHECK(v.size() == 1 && v[0] == doc.child(STR("node")));

	v.clear();
	doc.all_elements_by_name(STR("child"), std::back_inserter(v));
	CHECK(v.size() == 3);
	CHECK(v[0] == doc.child(STR("node")).child(STR("child")));
	CHECK(v[1] == doc.child(STR("node")).child(STR("child")).first_child());
	CHECK(v[2] == doc.child(STR("node")).child(STR("child")).last_child());
}

struct find_predicate_const
{
	bool result;

	find_predicate_const(bool result): result(result)
	{
	}

	template <typename T> bool operator()(const T&) const
	{
		return result;
	}
};

struct find_predicate_prefix
{
	const pugi::char_t* prefix;

	find_predicate_prefix(const pugi::char_t* prefix): prefix(prefix)
	{
	}

	template <typename T> bool operator()(const T& obj) const
	{
	#ifdef PUGIXML_WCHAR_MODE
		// can't use wcsncmp here because of a bug in DMC
		return std::basic_string<pugi::char_t>(obj.name()).compare(0, wcslen(prefix), prefix) == 0;
	#else
		return strncmp(obj.name(), prefix, strlen(prefix)) == 0;
	#endif
	}
};

TEST_XML(dom_node_find_attribute, "<node attr1='0' attr2='1'/>")
{
	CHECK(xml_node().find_attribute(find_predicate_const(true)) == xml_attribute());

	xml_node node = doc.child(STR("node"));

	CHECK(doc.find_attribute(find_predicate_const(true)) == xml_attribute());
	CHECK(node.find_attribute(find_predicate_const(true)) == node.first_attribute());
	CHECK(node.find_attribute(find_predicate_const(false)) == xml_attribute());
	CHECK(node.find_attribute(find_predicate_prefix(STR("attr2"))) == node.last_attribute());
	CHECK(node.find_attribute(find_predicate_prefix(STR("attr"))) == node.first_attribute());
}

TEST_XML(dom_node_find_child, "<node><child1/><child2/></node>")
{
	CHECK(xml_node().find_child(find_predicate_const(true)) == xml_node());

	xml_node node = doc.child(STR("node"));

	CHECK(node.child(STR("node")).child(STR("child1")).find_child(find_predicate_const(true)) == xml_node());
	CHECK(node.find_child(find_predicate_const(true)) == node.first_child());
	CHECK(node.find_child(find_predicate_const(false)) == xml_node());
	CHECK(node.find_child(find_predicate_prefix(STR("child2"))) == node.last_child());
	CHECK(node.find_child(find_predicate_prefix(STR("child"))) == node.first_child());
}

TEST_XML(dom_node_find_node, "<node><child1/><child2/></node>")
{
	CHECK(xml_node().find_node(find_predicate_const(true)) == xml_node());

	xml_node node = doc.child(STR("node"));

	CHECK(node.child(STR("node")).child(STR("child1")).find_node(find_predicate_const(true)) == xml_node());
	CHECK(node.find_node(find_predicate_const(true)) == node.first_child());
	CHECK(node.find_node(find_predicate_const(false)) == xml_node());
	CHECK(node.find_node(find_predicate_prefix(STR("child2"))) == node.last_child());
	CHECK(node.find_node(find_predicate_prefix(STR("child"))) == node.first_child());
	CHECK(doc.find_node(find_predicate_prefix(STR("child"))) == node.first_child());
	CHECK(doc.find_node(find_predicate_prefix(STR("child2"))) == node.last_child());
	CHECK(doc.find_node(find_predicate_prefix(STR("child3"))) == xml_node());
}

#ifndef PUGIXML_NO_STL
TEST_XML(dom_node_path, "<node><child1>text<child2/></child1></node>")
{
	CHECK(xml_node().path() == STR(""));
	
	CHECK(doc.path() == STR(""));
	CHECK(doc.child(STR("node")).path() == STR("/node"));
	CHECK(doc.child(STR("node")).child(STR("child1")).path() == STR("/node/child1"));
	CHECK(doc.child(STR("node")).child(STR("child1")).child(STR("child2")).path() == STR("/node/child1/child2"));
	CHECK(doc.child(STR("node")).child(STR("child1")).first_child().path() == STR("/node/child1/"));
	
	CHECK(doc.child(STR("node")).child(STR("child1")).path('\\') == STR("\\node\\child1"));
}
#endif

TEST_XML(dom_node_first_element_by_path, "<node><child1>text<child2/></child1></node>")
{
	CHECK(xml_node().first_element_by_path(STR("/")) == xml_node());
	
	CHECK(doc.first_element_by_path(STR("")) == doc);
	CHECK(doc.first_element_by_path(STR("/")) == doc);

	CHECK(doc.first_element_by_path(STR("/node/")) == doc.child(STR("node")));
	CHECK(doc.first_element_by_path(STR("node/")) == doc.child(STR("node")));
	CHECK(doc.first_element_by_path(STR("node")) == doc.child(STR("node")));
	CHECK(doc.first_element_by_path(STR("/node")) == doc.child(STR("node")));

#ifndef PUGIXML_NO_STL
	CHECK(doc.first_element_by_path(STR("/node/child1/child2")).path() == STR("/node/child1/child2"));
#endif

	CHECK(doc.first_element_by_path(STR("/node/child2")) == xml_node());
	
	CHECK(doc.first_element_by_path(STR("\\node\\child1"), '\\') == doc.child(STR("node")).child(STR("child1")));

	CHECK(doc.child(STR("node")).first_element_by_path(STR("..")) == doc);
	CHECK(doc.child(STR("node")).first_element_by_path(STR(".")) == doc.child(STR("node")));
}

struct test_walker: xml_tree_walker
{
	std::basic_string<pugi::char_t> log;
	unsigned int call_count;
	unsigned int stop_count;

	test_walker(unsigned int stop_count = 0): call_count(0), stop_count(stop_count)
	{
	}

	std::basic_string<pugi::char_t> depthstr() const
	{
		char buf[32];
		sprintf(buf, "%d", depth());

	#ifdef PUGIXML_WCHAR_MODE
		wchar_t wbuf[32];
		std::copy(buf, buf + strlen(buf) + 1, wbuf);

		return std::basic_string<pugi::char_t>(wbuf);
	#else
		return std::basic_string<pugi::char_t>(buf);
	#endif
	}

	virtual bool begin(xml_node& node)
	{
		log += STR("|");
		log += depthstr();
		log += STR(" <");
		log += node.name();
		log += STR("=");
		log += node.value();

		return ++call_count != stop_count && xml_tree_walker::begin(node);
	}

	virtual bool for_each(xml_node& node)
	{
		log += STR("|");
		log += depthstr();
		log += STR(" !");
		log += node.name();
		log += STR("=");
		log += node.value();

		return ++call_count != stop_count && xml_tree_walker::end(node);
	}

	virtual bool end(xml_node& node)
	{
		log += STR("|");
		log += depthstr();
		log += STR(" >");
		log += node.name();
		log += STR("=");
		log += node.value();

		return ++call_count != stop_count;
	}
};

TEST_XML(dom_node_traverse, "<node><child>text</child></node>")
{
	test_walker walker;

	CHECK(doc.traverse(walker));

	CHECK(walker.call_count == 5);
	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child=|2 !=text|-1 >="));
}

TEST_XML(dom_node_traverse_siblings, "<node><child/><child>text</child><child/></node>")
{
	test_walker walker;

	CHECK(doc.traverse(walker));

	CHECK(walker.call_count == 7);
	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child=|1 !child=|2 !=text|1 !child=|-1 >="));
}

TEST(dom_node_traverse_empty)
{
	test_walker walker;

	CHECK(xml_node().traverse(walker));

	CHECK(walker.call_count == 2);
	CHECK(walker.log == STR("|-1 <=|-1 >="));
}

TEST_XML(dom_node_traverse_child, "<node><child>text</child></node>")
{
	test_walker walker;

	CHECK(doc.child(STR("node")).traverse(walker));

	CHECK(walker.call_count == 4);
	CHECK(walker.log == STR("|-1 <node=|0 !child=|1 !=text|-1 >node="));
}

TEST_XML(dom_node_traverse_stop_begin, "<node><child>text</child></node>")
{
	test_walker walker(1);

	CHECK(!doc.traverse(walker));

	CHECK(walker.call_count == 1);
	CHECK(walker.log == STR("|-1 <="));
}

TEST_XML(dom_node_traverse_stop_for_each, "<node><child>text</child></node>")
{
	test_walker walker(3);

	CHECK(!doc.traverse(walker));

	CHECK(walker.call_count == 3);
	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child="));
}

TEST_XML(dom_node_traverse_stop_end, "<node><child>text</child></node>")
{
	test_walker walker(5);

	CHECK(!doc.traverse(walker));

	CHECK(walker.call_count == 5);
	CHECK(walker.log == STR("|-1 <=|0 !node=|1 !child=|2 !=text|-1 >="));
}

TEST_XML_FLAGS(dom_offset_debug, "<?xml?><?pi?><!--comment--><node>pcdata<![CDATA[cdata]]></node>", parse_default | parse_pi | parse_comments | parse_declaration)
{
	CHECK(xml_node().offset_debug() == -1);
	CHECK(doc.offset_debug() == 0);

	xml_node_iterator it = doc.begin();

	CHECK((it++)->offset_debug() == 2);
	CHECK((it++)->offset_debug() == 9);
	CHECK((it++)->offset_debug() == 17);
	CHECK((it++)->offset_debug() == 28);

	xml_node_iterator cit = doc.child(STR("node")).begin();
	
	CHECK((cit++)->offset_debug() == 33);
	CHECK((cit++)->offset_debug() == 48);
}