Skip to content
Snippets Groups Projects
test_dom_modify.cpp 54.8 KiB
Newer Older
	const char_t data1[] = STR("<child1 id='1' /><child2>text</child2>");
	const char_t data2[] = STR("<child3 />");

	CHECK(node.append_buffer(data1, sizeof(data1)));
	CHECK(node.append_buffer(data2, sizeof(data2)));
	CHECK(node.append_buffer(data1, sizeof(data1)));
	CHECK(node.append_buffer(data2, sizeof(data2)));
	CHECK(node.append_buffer(data2, sizeof(data2)));

	CHECK_NODE(doc, STR("<node>test<child1 id=\"1\" /><child2>text</child2><child3 /><child1 id=\"1\" /><child2>text</child2><child3 /><child3 /></node>"));
}

TEST_XML(dom_node_append_buffer_convert, "<node>test</node>")
{
	xml_node node = doc.child(STR("node"));

	const char data[] = {0, 0, 0, '<', 0, 0, 0, 'n', 0, 0, 0, '/', 0, 0, 0, '>'};

	CHECK(node.append_buffer(data, sizeof(data)));
	CHECK(node.append_buffer(data, sizeof(data), parse_default, encoding_utf32_be));

	CHECK_NODE(doc, STR("<node>test<n /><n /></node>"));
}


TEST_XML(dom_node_append_buffer_remove, "<node>test</node>")
{
	xml_node node = doc.child(STR("node"));

	const char data1[] = "<child1 id='1' /><child2>text</child2>";
	const char data2[] = "<child3 />";

	CHECK(node.append_buffer(data1, sizeof(data1)));
	CHECK(node.append_buffer(data2, sizeof(data2)));
	CHECK(node.append_buffer(data1, sizeof(data1)));
	CHECK(node.append_buffer(data2, sizeof(data2)));

	CHECK_NODE(doc, STR("<node>test<child1 id=\"1\" /><child2>text</child2><child3 /><child1 id=\"1\" /><child2>text</child2><child3 /></node>"));

	while (node.remove_child(STR("child2"))) {}

	CHECK_NODE(doc, STR("<node>test<child1 id=\"1\" /><child3 /><child1 id=\"1\" /><child3 /></node>"));

	while (node.remove_child(STR("child1"))) {}

	CHECK_NODE(doc, STR("<node>test<child3 /><child3 /></node>"));

	while (node.remove_child(STR("child3"))) {}

	CHECK_NODE(doc, STR("<node>test</node>"));

	CHECK(doc.remove_child(STR("node")));

	CHECK(!doc.first_child());
}

TEST(dom_node_append_buffer_empty_document)
{
	xml_document doc;

	const char data[] = "<child1 id='1' /><child2>text</child2>";

	doc.append_buffer(data, sizeof(data));

	CHECK_NODE(doc, STR("<child1 id=\"1\" /><child2>text</child2>"));
}

TEST_XML(dom_node_append_buffer_invalid_type, "<node>test</node>")
{
	const char data[] = "<child1 id='1' /><child2>text</child2>";

	CHECK(xml_node().append_buffer(data, sizeof(data)).status == status_append_invalid_root);
	CHECK(doc.first_child().first_child().append_buffer(data, sizeof(data)).status == status_append_invalid_root);
}

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

	const char data[] = "<child1 /></node><child2 />";

	CHECK(node.append_buffer(data, sizeof(data)).status == status_end_element_mismatch);
	CHECK_NODE(doc, STR("<node><child1 /></node>"));

	CHECK(node.append_buffer(data, sizeof(data)).status == status_end_element_mismatch);
	CHECK_NODE(doc, STR("<node><child1 /><child1 /></node>"));
}

TEST(dom_node_append_buffer_out_of_memory_extra)
{
	test_runner::_memory_fail_threshold = 1;

	xml_document doc;
	CHECK_ALLOC_FAIL(CHECK(doc.append_buffer("<n/>", 4).status == status_out_of_memory));
	CHECK(!doc.first_child());
}

TEST(dom_node_append_buffer_out_of_memory_buffer)
{
	test_runner::_memory_fail_threshold = 32768 + 128;

	CHECK_ALLOC_FAIL(CHECK(doc.append_buffer(data, sizeof(data)).status == status_out_of_memory));
	CHECK(!doc.first_child());
}
TEST(dom_node_append_buffer_out_of_memory_nodes)
{
	unsigned int count = 4000;
	std::basic_string<char_t> data;

	for (unsigned int i = 0; i < count; ++i)
		data += STR("<a/>");

	test_runner::_memory_fail_threshold = 32768 + 128 + data.length() * sizeof(char_t) + 32;
	CHECK_ALLOC_FAIL(CHECK(doc.append_buffer(data.c_str(), data.length() * sizeof(char_t), parse_fragment).status == status_out_of_memory));

	unsigned int valid = 0;

	for (xml_node n = doc.first_child(); n; n = n.next_sibling())
	{
		CHECK_STRING(n.name(), STR("a"));
		valid++;
	}

	CHECK(valid > 0 && valid < count);
}

TEST(dom_node_append_buffer_out_of_memory_name)
{
	test_runner::_memory_fail_threshold = 32768 + 128;

	char data[128] = {0};

	xml_document doc;
	CHECK(doc.append_child(STR("root")));
	CHECK_ALLOC_FAIL(CHECK(doc.first_child().append_buffer(data, sizeof(data)).status == status_out_of_memory));
	CHECK_STRING(doc.first_child().name(), STR("root"));
}

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

	CHECK(node.append_buffer("1", 1).status == status_no_document_element);
	CHECK_NODE(doc, STR("<node>1</node>"));

	CHECK(node.append_buffer("2", 1, parse_fragment));
	CHECK_NODE(doc, STR("<node>12</node>"));

	CHECK(node.append_buffer("3", 1).status == status_no_document_element);
	CHECK_NODE(doc, STR("<node>123</node>"));

	CHECK(node.append_buffer("4", 1, parse_fragment));
	CHECK_NODE(doc, STR("<node>1234</node>"));
}
TEST_XML(dom_node_append_buffer_empty, "<node />")
{
	xml_node node = doc.child(STR("node"));

	CHECK(node.append_buffer("", 0).status == status_no_document_element);
	CHECK(node.append_buffer("", 0, parse_fragment).status == status_ok);

	CHECK(node.append_buffer(0, 0).status == status_no_document_element);
	CHECK(node.append_buffer(0, 0, parse_fragment).status == status_ok);

	CHECK_NODE(doc, STR("<node />"));
}

TEST_XML(dom_node_prepend_move, "<node>foo<child/></node>")
{
	xml_node child = doc.child(STR("node")).child(STR("child"));

	CHECK(xml_node().prepend_move(xml_node()) == xml_node());
	CHECK(doc.child(STR("node")).first_child().prepend_move(child) == xml_node());
	CHECK(doc.prepend_move(doc) == xml_node());
	CHECK(doc.prepend_move(xml_node()) == xml_node());

	xml_node n1 = doc.child(STR("node")).prepend_move(doc.child(STR("node")).first_child());
	CHECK(n1 && n1 == doc.child(STR("node")).first_child());
	CHECK_STRING(n1.value(), STR("foo"));
	CHECK_NODE(doc, STR("<node>foo<child /></node>"));

	xml_node n2 = doc.child(STR("node")).prepend_move(doc.child(STR("node")).child(STR("child")));
	CHECK(n2 && n2 != n1 && n2 == child);
	CHECK_STRING(n2.name(), STR("child"));
	CHECK_NODE(doc, STR("<node><child />foo</node>"));

	xml_node n3 = doc.child(STR("node")).child(STR("child")).prepend_move(doc.child(STR("node")).first_child().next_sibling());
	CHECK(n3 && n3 == n1 && n3 != n2);
	CHECK_STRING(n3.value(), STR("foo"));
	CHECK_NODE(doc, STR("<node><child>foo</child></node>"));
}

TEST_XML(dom_node_append_move, "<node>foo<child/></node>")
{
	xml_node child = doc.child(STR("node")).child(STR("child"));

	CHECK(xml_node().append_move(xml_node()) == xml_node());
	CHECK(doc.child(STR("node")).first_child().append_move(child) == xml_node());
	CHECK(doc.append_move(doc) == xml_node());
	CHECK(doc.append_move(xml_node()) == xml_node());

	xml_node n1 = doc.child(STR("node")).append_move(doc.child(STR("node")).first_child());
	CHECK(n1 && n1 == doc.child(STR("node")).last_child());
	CHECK_STRING(n1.value(), STR("foo"));
	CHECK_NODE(doc, STR("<node><child />foo</node>"));

	xml_node n2 = doc.child(STR("node")).append_move(doc.child(STR("node")).last_child());
	CHECK(n2 && n2 == n1);
	CHECK_STRING(n2.value(), STR("foo"));
	CHECK_NODE(doc, STR("<node><child />foo</node>"));

	xml_node n3 = doc.child(STR("node")).child(STR("child")).append_move(doc.child(STR("node")).last_child());
	CHECK(n3 && n3 == n1 && n3 == n2);
	CHECK_STRING(n3.value(), STR("foo"));
	CHECK_NODE(doc, STR("<node><child>foo</child></node>"));
}

TEST_XML(dom_node_insert_move_after, "<node>foo<child>bar</child></node>")
{
	xml_node child = doc.child(STR("node")).child(STR("child"));

	CHECK(xml_node().insert_move_after(xml_node(), xml_node()) == xml_node());
	CHECK(doc.child(STR("node")).first_child().insert_move_after(doc.child(STR("node")), doc.child(STR("node"))) == xml_node());
	CHECK(doc.insert_move_after(doc, doc) == xml_node());
	CHECK(doc.insert_move_after(xml_node(), doc.child(STR("node"))) == xml_node());
	CHECK(doc.insert_move_after(doc.child(STR("node")), xml_node()) == xml_node());

	xml_node n1 = doc.child(STR("node")).insert_move_after(child, doc.child(STR("node")).first_child());
	CHECK(n1 && n1 == child);
	CHECK_STRING(n1.name(), STR("child"));
	CHECK_NODE(doc, STR("<node>foo<child>bar</child></node>"));

	xml_node n2 = doc.child(STR("node")).insert_move_after(doc.child(STR("node")).first_child(), child);
	CHECK(n2 && n2 != n1);
	CHECK_STRING(n2.value(), STR("foo"));
	CHECK_NODE(doc, STR("<node><child>bar</child>foo</node>"));

	xml_node n3 = child.insert_move_after(doc.child(STR("node")).last_child(), child.first_child());
	CHECK(n3 && n3 != n1 && n3 == n2);
	CHECK_STRING(n3.value(), STR("foo"));
	CHECK_NODE(doc, STR("<node><child>barfoo</child></node>"));
}

TEST_XML(dom_node_insert_move_before, "<node>foo<child>bar</child></node>")
{
	xml_node child = doc.child(STR("node")).child(STR("child"));

	CHECK(xml_node().insert_move_before(xml_node(), xml_node()) == xml_node());
	CHECK(doc.child(STR("node")).first_child().insert_move_before(doc.child(STR("node")), doc.child(STR("node"))) == xml_node());
	CHECK(doc.insert_move_before(doc, doc) == xml_node());
	CHECK(doc.insert_move_before(xml_node(), doc.child(STR("node"))) == xml_node());
	CHECK(doc.insert_move_before(doc.child(STR("node")), xml_node()) == xml_node());

	xml_node n1 = doc.child(STR("node")).insert_move_before(child, doc.child(STR("node")).first_child());
	CHECK(n1 && n1 == child);
	CHECK_STRING(n1.name(), STR("child"));
	CHECK_NODE(doc, STR("<node><child>bar</child>foo</node>"));

	xml_node n2 = doc.child(STR("node")).insert_move_before(doc.child(STR("node")).last_child(), child);
	CHECK(n2 && n2 != n1);
	CHECK_STRING(n2.value(), STR("foo"));
	CHECK_NODE(doc, STR("<node>foo<child>bar</child></node>"));

	xml_node n3 = child.insert_move_before(doc.child(STR("node")).first_child(), child.first_child());
	CHECK(n3 && n3 != n1 && n3 == n2);
	CHECK_STRING(n3.value(), STR("foo"));
	CHECK_NODE(doc, STR("<node><child>foobar</child></node>"));
}

TEST_XML(dom_node_move_recursive, "<root><node>foo<child/></node></root>")
{
	xml_node root = doc.child(STR("root"));
	xml_node node = root.child(STR("node"));
	xml_node foo = node.first_child();
	xml_node child = node.last_child();

	CHECK(node.prepend_move(node) == xml_node());
	CHECK(node.prepend_move(root) == xml_node());

	CHECK(node.append_move(node) == xml_node());
	CHECK(node.append_move(root) == xml_node());

	CHECK(node.insert_move_before(node, foo) == xml_node());
	CHECK(node.insert_move_before(root, foo) == xml_node());

	CHECK(node.insert_move_after(node, foo) == xml_node());
	CHECK(node.insert_move_after(root, foo) == xml_node());

	CHECK(child.append_move(node) == xml_node());

	CHECK_NODE(doc, STR("<root><node>foo<child /></node></root>"));
}

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

	CHECK(doc.insert_move_before(node, node) == xml_node());
	CHECK(doc.insert_move_after(node, node) == xml_node());

	CHECK_NODE(doc, STR("<node />"));
}

TEST_XML(dom_node_move_crossdoc, "<node/>")
{
	xml_document newdoc;
	CHECK(newdoc.append_move(doc.child(STR("node"))) == xml_node());
	CHECK_NODE(newdoc, STR(""));
}

TEST_XML(dom_node_move_tree, "<root><n1 a1='v1'><c1/>t1</n1><n2 a2='v2'><c2/>t2</n2><n3 a3='v3'><c3/>t3</n3><n4 a4='v4'><c4/>t4</n4></root>")
{
	xml_node root = doc.child(STR("root"));
	xml_node n1 = root.child(STR("n1"));
	xml_node n2 = root.child(STR("n2"));
	xml_node n3 = root.child(STR("n3"));
	xml_node n4 = root.child(STR("n4"));

	// n2 n1 n3 n4
	CHECK(n2 == root.prepend_move(n2));

	// n2 n3 n4 n1
	CHECK(n1 == root.append_move(n1));

	// n2 n4 n3 n1
	CHECK(n4 == root.insert_move_before(n4, n3));

	// n2 n4 n1 + n3
	CHECK(n3 == doc.insert_move_after(n3, root));

	CHECK_NODE(doc, STR("<root><n2 a2=\"v2\"><c2 />t2</n2><n4 a4=\"v4\"><c4 />t4</n4><n1 a1=\"v1\"><c1 />t1</n1></root><n3 a3=\"v3\"><c3 />t3</n3>"));

	CHECK(n1 == root.child(STR("n1")));
	CHECK(n2 == root.child(STR("n2")));
	CHECK(n3 == doc.child(STR("n3")));
	CHECK(n4 == root.child(STR("n4")));
}

TEST(dom_node_copy_stackless)
{
	unsigned int count = 20000;
	std::basic_string<char_t> data;

	for (unsigned int i = 0; i < count; ++i)
		data += STR("<a>");

	data += STR("text");

	for (unsigned int j = 0; j < count; ++j)
		data += STR("</a>");

	xml_document doc;
	CHECK(doc.load_string(data.c_str()));

	xml_document copy;
	CHECK(copy.append_copy(doc.first_child()));

	CHECK_NODE(doc, data.c_str());
}

TEST(dom_node_copy_copyless)
{
	std::basic_string<char_t> data;
	data += STR("<node>");
	for (int i = 0; i < 10000; ++i)
		data += STR("pcdata");
	data += STR("<?name value?><child attr1=\"\" attr2=\"value2\" /></node>");

	std::basic_string<char_t> datacopy = data;

	// the document is parsed in-place so there should only be 1 page worth of allocations
	test_runner::_memory_fail_threshold = 32768 + 128;

	xml_document doc;
	CHECK(doc.load_buffer_inplace(&datacopy[0], datacopy.size() * sizeof(char_t), parse_full));

	// this copy should share all string storage; since there are not a lot of nodes we should not have *any* allocations here (everything will fit in the same page in the document)
	xml_node copy = doc.append_copy(doc.child(STR("node")));
	xml_node copy2 = doc.append_copy(copy);

	CHECK_NODE(copy, data.c_str());
	CHECK_NODE(copy2, data.c_str());
}

TEST(dom_node_copy_copyless_mix)
{
	xml_document doc;
	CHECK(doc.load_string(STR("<node>pcdata<?name value?><child attr1=\"\" attr2=\"value2\" /></node>"), parse_full));

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

	child.set_name(STR("copychild"));
	child.attribute(STR("attr2")).set_name(STR("copyattr2"));
	child.attribute(STR("attr1")).set_value(STR("copyvalue1"));

	std::basic_string<char_t> data;
	for (int i = 0; i < 10000; ++i)
		data += STR("pcdata");

	doc.child(STR("node")).text().set(data.c_str());

	xml_node copy = doc.append_copy(doc.child(STR("node")));
	xml_node copy2 = doc.append_copy(copy);

	std::basic_string<char_t> dataxml;
	dataxml += STR("<node>");
	dataxml += data;
	dataxml += STR("<?name value?><copychild attr1=\"copyvalue1\" copyattr2=\"value2\" /></node>");

	CHECK_NODE(copy, dataxml.c_str());
	CHECK_NODE(copy2, dataxml.c_str());
}

TEST_XML(dom_node_copy_copyless_taint, "<node attr=\"value\" />")
{
	xml_node node = doc.child(STR("node"));
	xml_node copy = doc.append_copy(node);

	CHECK_NODE(doc, STR("<node attr=\"value\" /><node attr=\"value\" />"));

	node.set_name(STR("nod1"));

	CHECK_NODE(doc, STR("<nod1 attr=\"value\" /><node attr=\"value\" />"));

	xml_node copy2 = doc.append_copy(copy);

	CHECK_NODE(doc, STR("<nod1 attr=\"value\" /><node attr=\"value\" /><node attr=\"value\" />"));

	copy.attribute(STR("attr")).set_value(STR("valu2"));

	CHECK_NODE(doc, STR("<nod1 attr=\"value\" /><node attr=\"valu2\" /><node attr=\"value\" />"));

	copy2.attribute(STR("attr")).set_name(STR("att3"));

	CHECK_NODE(doc, STR("<nod1 attr=\"value\" /><node attr=\"valu2\" /><node att3=\"value\" />"));
}
TEST(dom_node_copy_attribute_copyless)
{
	std::basic_string<char_t> data;
	data += STR("<node attr=\"");
	for (int i = 0; i < 10000; ++i)
		data += STR("data");
	data += STR("\" />");

	std::basic_string<char_t> datacopy = data;

	// the document is parsed in-place so there should only be 1 page worth of allocations
	test_runner::_memory_fail_threshold = 32768 + 128;

	xml_document doc;
	CHECK(doc.load_buffer_inplace(&datacopy[0], datacopy.size() * sizeof(char_t), parse_full));

	// this copy should share all string storage; since there are not a lot of nodes we should not have *any* allocations here (everything will fit in the same page in the document)
	xml_node copy1 = doc.append_child(STR("node"));
	copy1.append_copy(doc.first_child().first_attribute());

	xml_node copy2 = doc.append_child(STR("node"));
	copy2.append_copy(copy1.first_attribute());

	CHECK_NODE(copy1, data.c_str());
	CHECK_NODE(copy2, data.c_str());
}

TEST_XML(dom_node_copy_attribute_copyless_taint, "<node attr=\"value\" />")
{
	xml_node node = doc.child(STR("node"));
	xml_attribute attr = node.first_attribute();

	xml_node copy1 = doc.append_child(STR("copy1"));
	xml_node copy2 = doc.append_child(STR("copy2"));
	xml_node copy3 = doc.append_child(STR("copy3"));

	CHECK_NODE(doc, STR("<node attr=\"value\" /><copy1 /><copy2 /><copy3 />"));

	copy1.append_copy(attr);

	CHECK_NODE(doc, STR("<node attr=\"value\" /><copy1 attr=\"value\" /><copy2 /><copy3 />"));

	attr.set_name(STR("att1"));
	copy2.append_copy(attr);

	CHECK_NODE(doc, STR("<node att1=\"value\" /><copy1 attr=\"value\" /><copy2 att1=\"value\" /><copy3 />"));

	copy1.first_attribute().set_value(STR("valu2"));
	copy3.append_copy(copy1.first_attribute());

	CHECK_NODE(doc, STR("<node att1=\"value\" /><copy1 attr=\"valu2\" /><copy2 att1=\"value\" /><copy3 attr=\"valu2\" />"));
}

TEST_XML(dom_node_copy_out_of_memory_node, "<node><child1 /><child2 /><child3>text1<child4 />text2</child3></node>")
{
	test_runner::_memory_fail_threshold = 32768 * 2 + 4096;

	CHECK_ALLOC_FAIL(for (int i = 0; i < 1000; ++i) copy.append_copy(doc.first_child()));
}

TEST_XML(dom_node_copy_out_of_memory_attr, "<node attr1='' attr2='' attr3='' attr4='' attr5='' attr6='' attr7='' attr8='' attr9='' attr10='' attr11='' attr12='' attr13='' attr14='' attr15='' />")
{
	test_runner::_memory_fail_threshold = 32768 * 2 + 4096;

	xml_document copy;
	CHECK_ALLOC_FAIL(for (int i = 0; i < 1000; ++i) copy.append_copy(doc.first_child()));

TEST_XML(dom_node_remove_deallocate, "<node attr='value'>text</node>")
{
	xml_node node = doc.child(STR("node"));

	xml_attribute attr = node.attribute(STR("attr"));
	attr.set_name(STR("longattr"));
	attr.set_value(STR("longvalue"));

	node.set_name(STR("longnode"));
	node.text().set(STR("longtext"));

	node.remove_attribute(attr);
	doc.remove_child(node);

	CHECK_NODE(doc, STR(""));
}

TEST_XML(dom_node_set_deallocate, "<node attr='value'>text</node>")
{
	xml_node node = doc.child(STR("node"));

	xml_attribute attr = node.attribute(STR("attr"));

	attr.set_name(STR("longattr"));
	attr.set_value(STR("longvalue"));
	node.set_name(STR("longnode"));

	attr.set_name(STR(""));
	attr.set_value(STR(""));
	node.set_name(STR(""));
	node.text().set(STR(""));

	CHECK_NODE(doc, STR("<:anonymous :anonymous=\"\"></:anonymous>"));
}

TEST(dom_node_copy_declaration_empty_name)
{
	xml_document doc1;
	xml_node decl1 = doc1.append_child(node_declaration);
	decl1.set_name(STR(""));

	xml_document doc2;
	xml_node decl2 = doc2.append_copy(decl1);

	CHECK_STRING(decl2.name(), STR(""));
}
template <typename T> bool fp_equal(T lhs, T rhs)
{
	// Several compilers compare float/double values on x87 stack without proper rounding
	// This causes roundtrip tests to fail, although they correctly preserve the data.
#if (defined(_MSC_VER) && _MSC_VER < 1400) || defined(__MWERKS__)
	return memcmp(&lhs, &rhs, sizeof(T)) == 0;
#else
	return lhs == rhs;
#endif
}

TEST(dom_fp_roundtrip_min_max)
{
	xml_document doc;
	xml_node node = doc.append_child(STR("node"));
	xml_attribute attr = node.append_attribute(STR("attr"));

	node.text().set(std::numeric_limits<float>::min());
	CHECK(fp_equal(node.text().as_float(), std::numeric_limits<float>::min()));

	attr.set_value(std::numeric_limits<float>::max());
	CHECK(fp_equal(attr.as_float(), std::numeric_limits<float>::max()));

	attr.set_value(std::numeric_limits<double>::min());
	CHECK(fp_equal(attr.as_double(), std::numeric_limits<double>::min()));

	node.text().set(std::numeric_limits<double>::max());
	CHECK(fp_equal(node.text().as_double(), std::numeric_limits<double>::max()));
}

const double fp_roundtrip_base[] =
{
	0.31830988618379067154,
	0.43429448190325182765,
	0.57721566490153286061,
	0.69314718055994530942,
	0.70710678118654752440,
	0.78539816339744830962,
};

TEST(dom_fp_roundtrip_float)
{
	xml_document doc;

	for (int e = -125; e <= 128; ++e)
	{
		for (size_t i = 0; i < sizeof(fp_roundtrip_base) / sizeof(fp_roundtrip_base[0]); ++i)
		{
			float value = ldexpf(static_cast<float>(fp_roundtrip_base[i]), e);

			doc.text().set(value);
			CHECK(fp_equal(doc.text().as_float(), value));
// Borland C does not print double values with enough precision
#ifndef __BORLANDC__
TEST(dom_fp_roundtrip_double)
{
	xml_document doc;

	for (int e = -1021; e <= 1024; ++e)
	{
		for (size_t i = 0; i < sizeof(fp_roundtrip_base) / sizeof(fp_roundtrip_base[0]); ++i)
		{
		#if (defined(_MSC_VER) && _MSC_VER < 1400) || defined(__MWERKS__)
			// Not all runtime libraries guarantee roundtripping for denormals
			if (e == -1021 && fp_roundtrip_base[i] < 0.5)
				continue;
		#endif

		#ifdef __DMC__
			// Digital Mars C does not roundtrip on exactly one combination
			if (e == -12 && i == 1)
				continue;
		#endif

			double value = ldexp(fp_roundtrip_base[i], e);

			doc.text().set(value);
			CHECK(fp_equal(doc.text().as_double(), value));