summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArseny Kapoulkine <arseny.kapoulkine@gmail.com>2014-10-28 20:11:06 -0700
committerArseny Kapoulkine <arseny.kapoulkine@gmail.com>2014-10-28 20:11:06 -0700
commita49f932b6110bc46e02f80ba4a4264991202cbee (patch)
tree267edd41549aab49169ec460ccb868436fff15a8
parent21695288ecb32358034de0eaf56408cc9b994f86 (diff)
parent6229138d80380d582f16931d36b279807dcb82dd (diff)
Merge branch 'master' into compact
-rw-r--r--Makefile14
-rw-r--r--README.md6
-rw-r--r--src/pugixml.cpp230
-rwxr-xr-xtests/gitsvn.sh5
-rw-r--r--tests/main.cpp6
-rw-r--r--tests/test.hpp1
-rw-r--r--tests/test_document.cpp73
-rw-r--r--tests/test_dom_modify.cpp69
-rw-r--r--tests/test_dom_traverse.cpp10
-rw-r--r--tests/test_parse.cpp53
-rw-r--r--tests/test_parse_doctype.cpp9
-rw-r--r--tests/test_write.cpp50
-rw-r--r--tests/test_xpath.cpp66
-rw-r--r--tests/test_xpath_api.cpp5
-rw-r--r--tests/test_xpath_functions.cpp15
-rw-r--r--tests/test_xpath_paths.cpp49
-rw-r--r--tests/test_xpath_variables.cpp26
17 files changed, 592 insertions, 95 deletions
diff --git a/Makefile b/Makefile
index eddd4f2..d351e0f 100644
--- a/Makefile
+++ b/Makefile
@@ -13,6 +13,12 @@ ifeq ($(config),release)
CXXFLAGS+=-O3 -DNDEBUG
endif
+ifeq ($(config),coverage)
+ CXXFLAGS+=-DNDEBUG
+ CXXFLAGS+=-fprofile-arcs -ftest-coverage
+ LDFLAGS+=-fprofile-arcs
+endif
+
ifneq ($(defines),standard)
COMMA=,
CXXFLAGS+=-D $(subst $(COMMA), -D ,$(defines))
@@ -22,8 +28,16 @@ OBJECTS=$(SOURCES:%=$(BUILD)/%.o)
all: $(EXECUTABLE)
+ifeq ($(config),coverage)
test: $(EXECUTABLE)
+ @find $(BUILD) -name '*.gcda' | xargs rm
./$(EXECUTABLE)
+ @gcov -b -c $(BUILD)/src/pugixml.cpp.gcda | sed -e '/./{H;$!d;}' -e 'x;/pugixml.cpp/!d;'
+ @ls *.gcov | grep -v pugixml.cpp.gcov | xargs rm
+else
+test: $(EXECUTABLE)
+ ./$(EXECUTABLE)
+endif
clean:
rm -rf $(BUILD)
diff --git a/README.md b/README.md
index 01a53a0..2d8263d 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,8 @@ pugixml is used by a lot of projects, both open-source and proprietary, for perf
Documentation for the current release of pugixml is available on-line as two separate documents:
-* [Quick-start guide](http://pugixml.googlecode.com/svn/tags/latest/docs/quickstart.html), that aims to provide enough information to start using the library;
-* [Complete reference manual](http://pugixml.googlecode.com/svn/tags/latest/docs/manual.html), that describes all features of the library in detail.
+* [Quick-start guide](http://cdn.rawgit.com/zeux/pugixml/v1.4/docs/quickstart.html), that aims to provide enough information to start using the library;
+* [Complete reference manual](http://cdn.rawgit.com/zeux/pugixml/v1.4/docs/manual.html), that describes all features of the library in detail.
You’re advised to start with the quick-start guide; however, many important library features are either not described in it at all or only mentioned briefly; if you require more information you should read the complete manual.
@@ -41,4 +41,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index 2f1fe81..b565482 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -1,4 +1,5 @@
/**
+{
* pugixml parser - version 1.4
* --------------------------------------------------------
* Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
@@ -164,6 +165,8 @@ PUGI__NS_BEGIN
static deallocation_function deallocate;
};
+ // Global allocation functions are stored in class statics so that in header mode linker deduplicates them
+ // Without a template<> we'll get multiple definitions of the same static
template <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate;
template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;
@@ -1189,7 +1192,7 @@ PUGI__NS_BEGIN
if (node->next_sibling)
node->next_sibling->prev_sibling_c = node->prev_sibling_c;
- else if (parent->first_child)
+ else
parent->first_child->prev_sibling_c = node->prev_sibling_c;
if (node->prev_sibling_c->next_sibling)
@@ -1264,7 +1267,7 @@ PUGI__NS_BEGIN
{
if (attr->next_attribute)
attr->next_attribute->prev_attribute_c = attr->prev_attribute_c;
- else if (node->first_attribute)
+ else
node->first_attribute->prev_attribute_c = attr->prev_attribute_c;
if (attr->prev_attribute_c->next_attribute)
@@ -3945,36 +3948,37 @@ PUGI__NS_BEGIN
writer.write('-', '-', '>');
}
- PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node node, unsigned int flags)
+ PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
{
const char_t* default_name = PUGIXML_TEXT(":anonymous");
- for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute())
+ for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
{
writer.write(' ');
- writer.write_string(a.name()[0] ? a.name() : default_name);
+ writer.write_string(a->name ? a->name : default_name);
writer.write('=', '"');
- text_output(writer, a.value(), ctx_special_attr, flags);
+ if (a->value)
+ text_output(writer, a->value, ctx_special_attr, flags);
writer.write('"');
}
}
- PUGI__FN bool node_output_start(xml_buffered_writer& writer, const xml_node node, unsigned int flags)
+ PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
{
const char_t* default_name = PUGIXML_TEXT(":anonymous");
- const char_t* name = node.name()[0] ? node.name() : default_name;
+ const char_t* name = node->name ? node->name : default_name;
writer.write('<');
writer.write_string(name);
- if (node.first_attribute())
+ if (node->first_attribute)
node_output_attributes(writer, node, flags);
if (flags & format_raw)
{
- if (!node.first_child())
+ if (!node->first_child)
writer.write(' ', '/', '>');
else
{
@@ -3985,18 +3989,20 @@ PUGI__NS_BEGIN
}
else
{
- xml_node first = node.first_child();
+ xml_node_struct* first = node->first_child;
if (!first)
writer.write(' ', '/', '>', '\n');
- else if (!first.next_sibling() && (first.type() == node_pcdata || first.type() == node_cdata))
+ else if (!first->next_sibling && (PUGI__NODETYPE(first) == node_pcdata || PUGI__NODETYPE(first) == node_cdata))
{
writer.write('>');
- if (first.type() == node_pcdata)
- text_output(writer, first.value(), ctx_special_pcdata, flags);
+ const char_t* value = first->value ? first->value : PUGIXML_TEXT("");
+
+ if (PUGI__NODETYPE(first) == node_pcdata)
+ text_output(writer, value, ctx_special_pcdata, flags);
else
- text_output_cdata(writer, first.value());
+ text_output_cdata(writer, value);
writer.write('<', '/');
writer.write_string(name);
@@ -4013,10 +4019,10 @@ PUGI__NS_BEGIN
return false;
}
- PUGI__FN void node_output_end(xml_buffered_writer& writer, const xml_node node, unsigned int flags)
+ PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
{
const char_t* default_name = PUGIXML_TEXT(":anonymous");
- const char_t* name = node.name()[0] ? node.name() : default_name;
+ const char_t* name = node->name ? node->name : default_name;
writer.write('<', '/');
writer.write_string(name);
@@ -4027,35 +4033,35 @@ PUGI__NS_BEGIN
writer.write('>', '\n');
}
- PUGI__FN void node_output_simple(xml_buffered_writer& writer, const xml_node node, unsigned int flags)
+ PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
{
const char_t* default_name = PUGIXML_TEXT(":anonymous");
- switch (node.type())
+ switch (PUGI__NODETYPE(node))
{
case node_pcdata:
- text_output(writer, node.value(), ctx_special_pcdata, flags);
+ text_output(writer, node->value ? node->value : PUGIXML_TEXT(""), ctx_special_pcdata, flags);
if ((flags & format_raw) == 0) writer.write('\n');
break;
case node_cdata:
- text_output_cdata(writer, node.value());
+ text_output_cdata(writer, node->value ? node->value : PUGIXML_TEXT(""));
if ((flags & format_raw) == 0) writer.write('\n');
break;
case node_comment:
- node_output_comment(writer, node.value());
+ node_output_comment(writer, node->value ? node->value : PUGIXML_TEXT(""));
if ((flags & format_raw) == 0) writer.write('\n');
break;
case node_pi:
writer.write('<', '?');
- writer.write_string(node.name()[0] ? node.name() : default_name);
+ writer.write_string(node->name ? node->name : default_name);
- if (node.value()[0])
+ if (node->value)
{
writer.write(' ');
- writer.write_string(node.value());
+ writer.write_string(node->value);
}
writer.write('?', '>');
@@ -4064,7 +4070,7 @@ PUGI__NS_BEGIN
case node_declaration:
writer.write('<', '?');
- writer.write_string(node.name()[0] ? node.name() : default_name);
+ writer.write_string(node->name ? node->name : default_name);
node_output_attributes(writer, node, flags);
writer.write('?', '>');
if ((flags & format_raw) == 0) writer.write('\n');
@@ -4074,10 +4080,10 @@ PUGI__NS_BEGIN
writer.write('<', '!', 'D', 'O', 'C');
writer.write('T', 'Y', 'P', 'E');
- if (node.value()[0])
+ if (node->value)
{
writer.write(' ');
- writer.write_string(node.value());
+ writer.write_string(node->value);
}
writer.write('>');
@@ -4089,11 +4095,11 @@ PUGI__NS_BEGIN
}
}
- PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node root, const char_t* indent, unsigned int flags, unsigned int depth)
+ PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth)
{
size_t indent_length = ((flags & (format_indent | format_raw)) == format_indent) ? strlength(indent) : 0;
- xml_node node = root;
+ xml_node_struct* node = root;
do
{
@@ -4103,20 +4109,20 @@ PUGI__NS_BEGIN
if (indent_length)
text_output_indent(writer, indent, indent_length, depth);
- if (node.type() == node_element)
+ if (PUGI__NODETYPE(node) == node_element)
{
if (node_output_start(writer, node, flags))
{
- node = node.first_child();
+ node = node->first_child;
depth++;
continue;
}
}
- else if (node.type() == node_document)
+ else if (PUGI__NODETYPE(node) == node_document)
{
- if (node.first_child())
+ if (node->first_child)
{
- node = node.first_child();
+ node = node->first_child;
continue;
}
}
@@ -4128,17 +4134,17 @@ PUGI__NS_BEGIN
// continue to the next node
while (node != root)
{
- if (node.next_sibling())
+ if (node->next_sibling)
{
- node = node.next_sibling();
+ node = node->next_sibling;
break;
}
- node = node.parent();
+ node = node->parent;
depth--;
// write closing node
- if (node.type() == node_element)
+ if (PUGI__NODETYPE(node) == node_element)
{
if (indent_length)
text_output_indent(writer, indent, indent_length, depth);
@@ -4231,8 +4237,11 @@ PUGI__NS_BEGIN
{
xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn));
- node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);
- node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);
+ if (da)
+ {
+ node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);
+ node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);
+ }
}
}
@@ -5936,7 +5945,7 @@ namespace pugi
impl::xml_buffered_writer buffered_writer(writer, encoding);
- impl::node_output(buffered_writer, *this, indent, flags, depth);
+ impl::node_output(buffered_writer, _root, indent, flags, depth);
}
#ifndef PUGIXML_NO_STL
@@ -6621,7 +6630,7 @@ namespace pugi
if (!(flags & format_raw)) buffered_writer.write('\n');
}
- impl::node_output(buffered_writer, *this, indent, flags, 0);
+ impl::node_output(buffered_writer, _root, indent, flags, 0);
}
#ifndef PUGIXML_NO_STL
@@ -8746,7 +8755,6 @@ PUGI__NS_BEGIN
ast_op_union, // left | right
ast_predicate, // apply predicate to set; next points to next predicate
ast_filter, // select * from left where right
- ast_filter_posinv, // select * from left where right; proximity position invariant
ast_string_constant, // string constant
ast_number_constant, // number constant
ast_variable, // variable
@@ -8822,6 +8830,14 @@ PUGI__NS_BEGIN
nodetest_all_in_namespace
};
+ enum predicate_t
+ {
+ predicate_default,
+ predicate_posinv,
+ predicate_constant,
+ predicate_constant_one
+ };
+
enum nodeset_eval_t
{
nodeset_eval_all,
@@ -8843,8 +8859,10 @@ PUGI__NS_BEGIN
char _type;
char _rettype;
- // for ast_step / ast_predicate
+ // for ast_step
char _axis;
+
+ // for ast_step/ast_predicate/ast_filter
char _test;
// tree node structure
@@ -9041,7 +9059,7 @@ PUGI__NS_BEGIN
size_t size = ns.size() - first;
xpath_node* last = ns.begin() + first;
-
+
// remove_if... or well, sort of
for (xpath_node* it = last; it != ns.end(); ++it, ++i)
{
@@ -9056,17 +9074,48 @@ PUGI__NS_BEGIN
if (once) break;
}
}
- else if (expr->eval_boolean(c, stack))
+ else
{
- *last++ = *it;
+ if (expr->eval_boolean(c, stack))
+ {
+ *last++ = *it;
- if (once) break;
+ if (once) break;
+ }
}
}
ns.truncate(last);
}
+ static void apply_predicate_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
+ {
+ assert(ns.size() >= first);
+ assert(expr->rettype() == xpath_type_number);
+
+ size_t size = ns.size() - first;
+
+ xpath_node* last = ns.begin() + first;
+
+ xpath_context c(xpath_node(), 1, size);
+
+ double er = expr->eval_number(c, stack);
+
+ if (er >= 1.0 && er <= size)
+ {
+ size_t eri = static_cast<size_t>(er);
+
+ if (er == eri)
+ {
+ xpath_node r = last[eri - 1];
+
+ *last++ = r;
+ }
+ }
+
+ ns.truncate(last);
+ }
+
void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval)
{
if (ns.size() == first) return;
@@ -9075,7 +9124,12 @@ PUGI__NS_BEGIN
for (xpath_ast_node* pred = _right; pred; pred = pred->_next)
{
- apply_predicate(ns, first, pred->_left, stack, !pred->_next && last_once);
+ assert(pred->_type == ast_predicate);
+
+ if (pred->_test == predicate_constant || pred->_test == predicate_constant_one)
+ apply_predicate_const(ns, first, pred->_right, stack);
+ else
+ apply_predicate(ns, first, pred->_right, stack, !pred->_next && last_once);
}
}
@@ -9485,9 +9539,9 @@ PUGI__NS_BEGIN
bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling);
bool once =
- (axis == axis_attribute && _test == nodetest_name)
- ? true
- : !_right && eval_once(!axis_reverse, eval);
+ (axis == axis_attribute && _test == nodetest_name) ||
+ (!_right && eval_once(!axis_reverse, eval)) ||
+ (_right && !_right->_next && _right->_test == predicate_constant_one);
xpath_node_set_raw ns;
ns.set_type(axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted);
@@ -9554,9 +9608,16 @@ PUGI__NS_BEGIN
xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents):
_type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(static_cast<char>(axis)), _test(static_cast<char>(test)), _left(left), _right(0), _next(0)
{
+ assert(type == ast_step);
_data.nodetest = contents;
}
+ xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test):
+ _type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast<char>(test)), _left(left), _right(right), _next(0)
+ {
+ assert(type == ast_filter || type == ast_predicate);
+ }
+
void set_next(xpath_ast_node* value)
{
_next = value;
@@ -10130,27 +10191,33 @@ PUGI__NS_BEGIN
xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval);
xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval);
-
+
// we can optimize merging two sorted sets, but this is a very rare operation, so don't bother
rs.set_type(xpath_node_set::type_unsorted);
rs.append(ls.begin(), ls.end(), stack.result);
rs.remove_duplicates();
-
+
return rs;
}
case ast_filter:
- case ast_filter_posinv:
{
- xpath_node_set_raw set = _left->eval_node_set(c, stack, nodeset_eval_all);
+ xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all);
// either expression is a number or it contains position() call; sort by document order
- if (_type == ast_filter) set.sort_do();
+ if (_test != predicate_posinv) set.sort_do();
- bool once = eval_once(set.type() == xpath_node_set::type_sorted, eval);
+ if (_test == predicate_constant || _test == predicate_constant_one)
+ {
+ apply_predicate_const(set, 0, _right, stack);
+ }
+ else
+ {
+ bool once = eval_once(set.type() == xpath_node_set::type_sorted, eval);
- apply_predicate(set, 0, _right, stack, once);
+ apply_predicate(set, 0, _right, stack, once);
+ }
return set;
}
@@ -10253,10 +10320,31 @@ PUGI__NS_BEGIN
if (_right) _right->optimize(alloc);
if (_next) _next->optimize(alloc);
- // Replace descendant-or-self::node()/child::foo with descendant::foo
+ // Rewrite [position()=expr] with [expr]
+ // Note that this step has to go before classification to recognize [position()=1]
+ if ((_type == ast_filter || _type == ast_predicate) &&
+ _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number)
+ {
+ _right = _right->_right;
+ }
+
+ // Classify filter/predicate ops to perform various optimizations during evaluation
+ if (_type == ast_filter || _type == ast_predicate)
+ {
+ assert(_test == predicate_default);
+
+ if (_right->_type == ast_number_constant && _right->_data.number == 1.0)
+ _test = predicate_constant_one;
+ else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last))
+ _test = predicate_constant;
+ else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr())
+ _test = predicate_posinv;
+ }
+
+ // Rewrite descendant-or-self::node()/child::foo with descendant::foo
// The former is a full form of //foo, the latter is much faster since it executes the node test immediately
- // Do a similar kind of replacement for self/descendant/descendant-or-self axes
- // Note that we only replace positionally invariant steps (//foo[1] != /descendant::foo[1])
+ // Do a similar kind of rewrite for self/descendant/descendant-or-self axes
+ // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1])
if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left &&
_left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right &&
is_posinv_step())
@@ -10269,7 +10357,7 @@ PUGI__NS_BEGIN
_left = _left->_left;
}
- // Replace translate() with constant arguments with a table
+ // Use optimized lookup table implementation for translate() with constant arguments
if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant)
{
unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string);
@@ -10284,7 +10372,7 @@ PUGI__NS_BEGIN
// Use optimized path for @attr = 'value' or @attr = $value
if (_type == ast_op_equal &&
_left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right &&
- (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->rettype() == xpath_type_string)))
+ (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string)))
{
_type = ast_opt_compare_attribute;
}
@@ -10309,7 +10397,6 @@ PUGI__NS_BEGIN
case ast_predicate:
case ast_filter:
- case ast_filter_posinv:
return true;
default:
@@ -10330,10 +10417,7 @@ PUGI__NS_BEGIN
{
assert(n->_type == ast_predicate);
- xpath_ast_node* expr = n->_left;
- bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv_expr();
-
- if (!posinv)
+ if (n->_test != predicate_posinv)
return false;
}
@@ -10753,9 +10837,7 @@ PUGI__NS_BEGIN
if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set");
- bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv_expr();
-
- n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr);
+ n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default);
if (_lexer.current() != lex_close_square_brace)
throw_error("Unmatched square brace");
@@ -10899,7 +10981,7 @@ PUGI__NS_BEGIN
xpath_ast_node* expr = parse_expression();
- xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr);
+ xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default);
if (_lexer.current() != lex_close_square_brace)
throw_error("Unmatched square brace");
diff --git a/tests/gitsvn.sh b/tests/gitsvn.sh
deleted file mode 100755
index 222a09f..0000000
--- a/tests/gitsvn.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-
-git svn init https://pugixml.googlecode.com/svn/trunk
-git update-ref refs/remotes/git-svn refs/remotes/origin/master
-git svn rebase
diff --git a/tests/main.cpp b/tests/main.cpp
index 75b0108..c4c9341 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -20,6 +20,7 @@
test_runner* test_runner::_tests = 0;
size_t test_runner::_memory_fail_threshold = 0;
+bool test_runner::_memory_fail_triggered = false;
jmp_buf test_runner::_failure_buffer;
const char* test_runner::_failure_message;
const char* test_runner::_temp_path;
@@ -30,7 +31,11 @@ static size_t g_memory_total_count = 0;
static void* custom_allocate(size_t size)
{
if (test_runner::_memory_fail_threshold > 0 && test_runner::_memory_fail_threshold < g_memory_total_size + size)
+ {
+ test_runner::_memory_fail_triggered = true;
+
return 0;
+ }
else
{
void* ptr = memory_allocate(size);
@@ -84,6 +89,7 @@ static bool run_test(test_runner* test)
g_memory_total_size = 0;
g_memory_total_count = 0;
test_runner::_memory_fail_threshold = 0;
+ test_runner::_memory_fail_triggered = false;
pugi::set_memory_management_functions(custom_allocate, custom_deallocate);
diff --git a/tests/test.hpp b/tests/test.hpp
index 26260ad..509b0cd 100644
--- a/tests/test.hpp
+++ b/tests/test.hpp
@@ -23,6 +23,7 @@ struct test_runner
static test_runner* _tests;
static size_t _memory_fail_threshold;
+ static bool _memory_fail_triggered;
static jmp_buf _failure_buffer;
static const char* _failure_message;
diff --git a/tests/test_document.cpp b/tests/test_document.cpp
index c75ed8f..2cc39a6 100644
--- a/tests/test_document.cpp
+++ b/tests/test_document.cpp
@@ -227,6 +227,34 @@ TEST(document_load_stream_nonseekable_large)
CHECK(doc.load(in));
CHECK_NODE(doc, str.c_str());
}
+
+TEST(document_load_stream_nonseekable_out_of_memory)
+{
+ char contents[] = "<node />";
+ char_array_buffer<char> buffer(contents, contents + sizeof(contents) / sizeof(contents[0]));
+ std::istream in(&buffer);
+
+ test_runner::_memory_fail_threshold = 1;
+
+ pugi::xml_document doc;
+ CHECK(doc.load(in).status == status_out_of_memory);
+}
+
+TEST(document_load_stream_nonseekable_out_of_memory_large)
+{
+ std::basic_string<pugi::char_t> str;
+ str += STR("<node>");
+ for (int i = 0; i < 10000; ++i) str += STR("<node />");
+ str += STR("</node>");
+
+ char_array_buffer<pugi::char_t> buffer(&str[0], &str[0] + str.length());
+ std::basic_istream<pugi::char_t> in(&buffer);
+
+ test_runner::_memory_fail_threshold = 10000 * 8 * 3 / 2;
+
+ pugi::xml_document doc;
+ CHECK(doc.load(in).status == status_out_of_memory);
+}
#endif
TEST(document_load_string)
@@ -295,6 +323,17 @@ TEST(document_load_file_wide_ascii)
CHECK_NODE(doc, STR("<node />"));
}
+TEST(document_load_file_wide_out_of_memory)
+{
+ test_runner::_memory_fail_threshold = 1;
+
+ pugi::xml_document doc;
+
+ pugi::xml_parse_result result = doc.load_file(L"tests/data/small.xml");
+
+ CHECK(result.status == status_out_of_memory || result.status == status_file_not_found);
+}
+
TEST_XML(document_save, "<node/>")
{
xml_writer_string writer;
@@ -1235,3 +1274,37 @@ TEST(document_alignment)
doc->~xml_document();
}
}
+
+TEST(document_convert_out_of_memory)
+{
+ file_data_t files[] =
+ {
+ {"tests/data/utftest_utf16_be_clean.xml", encoding_utf16_be, 0, 0},
+ {"tests/data/utftest_utf16_le_clean.xml", encoding_utf16_le, 0, 0},
+ {"tests/data/utftest_utf32_be_clean.xml", encoding_utf32_be, 0, 0},
+ {"tests/data/utftest_utf32_le_clean.xml", encoding_utf32_le, 0, 0},
+ {"tests/data/utftest_utf8_clean.xml", encoding_utf8, 0, 0},
+ {"tests/data/latintest_latin1.xml", encoding_latin1, 0, 0}
+ };
+
+ // load files in memory
+ for (unsigned int i = 0; i < sizeof(files) / sizeof(files[0]); ++i)
+ {
+ CHECK(load_file_in_memory(files[i].path, files[i].data, files[i].size));
+ }
+
+ // disallow allocations
+ test_runner::_memory_fail_threshold = 1;
+
+ for (unsigned int src = 0; src < sizeof(files) / sizeof(files[0]); ++src)
+ {
+ xml_document doc;
+ CHECK(doc.load_buffer(files[src].data, files[src].size, parse_default, files[src].encoding).status == status_out_of_memory);
+ }
+
+ // cleanup
+ for (unsigned int j = 0; j < sizeof(files) / sizeof(files[0]); ++j)
+ {
+ delete[] files[j].data;
+ }
+}
diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp
index 612b017..07fe6dc 100644
--- a/tests/test_dom_modify.cpp
+++ b/tests/test_dom_modify.cpp
@@ -27,6 +27,16 @@ TEST_XML(dom_attr_assign, "<node/>")
CHECK_NODE(node, STR("<node attr1=\"v1\" attr2=\"-2147483647\" attr3=\"-2147483648\" attr4=\"4294967295\" attr5=\"4294967294\" attr6=\"0.5\" attr7=\"true\" />"));
}
+TEST_XML(dom_attr_set_name, "<node attr='value' />")
+{
+ xml_attribute attr = doc.child(STR("node")).attribute(STR("attr"));
+
+ CHECK(attr.set_name(STR("n")));
+ CHECK(!xml_attribute().set_name(STR("n")));
+
+ CHECK_NODE(doc, STR("<node n=\"value\" />"));
+}
+
TEST_XML(dom_attr_set_value, "<node/>")
{
xml_node node = doc.child(STR("node"));
@@ -758,8 +768,9 @@ TEST(dom_node_declaration_name)
doc.insert_child_after(node_declaration, doc.first_child());
doc.insert_child_before(node_declaration, doc.first_child());
+ doc.prepend_child(node_declaration);
- CHECK_NODE(doc, STR("<?xml?><?xml?><?xml?>"));
+ CHECK_NODE(doc, STR("<?xml?><?xml?><?xml?><?xml?>"));
}
TEST(dom_node_declaration_attributes)
@@ -867,17 +878,21 @@ TEST(dom_node_out_of_memory)
// verify all node modification operations
CHECK(!n.append_child());
+ CHECK(!n.prepend_child());
CHECK(!n.insert_child_after(node_element, n.first_child()));
CHECK(!n.insert_child_before(node_element, n.first_child()));
CHECK(!n.append_attribute(STR("")));
+ CHECK(!n.prepend_attribute(STR("")));
CHECK(!n.insert_attribute_after(STR(""), a));
CHECK(!n.insert_attribute_before(STR(""), a));
// verify node copy operations
CHECK(!n.append_copy(n.first_child()));
+ CHECK(!n.prepend_copy(n.first_child()));
CHECK(!n.insert_copy_after(n.first_child(), n.first_child()));
CHECK(!n.insert_copy_before(n.first_child(), n.first_child()));
CHECK(!n.append_copy(a));
+ CHECK(!n.prepend_copy(a));
CHECK(!n.insert_copy_after(a, a));
CHECK(!n.insert_copy_before(a, a));
}
@@ -1346,11 +1361,55 @@ TEST_XML(dom_node_copyless_taint, "<node attr=\"value\" />")
CHECK_NODE(doc, STR("<nod1 attr=\"value\" /><node attr=\"valu2\" /><node att3=\"value\" />"));
}
-TEST_XML(dom_node_copy_out_of_memory, "<node><child1 attr1='value1' attr2='value2' /><child2 /><child3>text1<child4 />text2</child3></node>")
+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;
- xml_document copy;
- for (int i = 0; i < 100; ++i)
- copy.append_copy(doc.first_child());
+ xml_document copy;
+ 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;
+ 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>"));
}
diff --git a/tests/test_dom_traverse.cpp b/tests/test_dom_traverse.cpp
index e42846f..83afec8 100644
--- a/tests/test_dom_traverse.cpp
+++ b/tests/test_dom_traverse.cpp
@@ -1048,14 +1048,14 @@ TEST_XML(dom_unspecified_bool_coverage, "<node attr='value'>text</node>")
{
xml_node node = doc.first_child();
- node(0);
- node.first_attribute()(0);
- node.text()(0);
+ static_cast<void (*)(xml_node***)>(node)(0);
+ static_cast<void (*)(xml_attribute***)>(node.first_attribute())(0);
+ static_cast<void (*)(xml_text***)>(node.text())(0);
#ifndef PUGIXML_NO_XPATH
xpath_query q(STR("/node"));
- q(0);
- q.evaluate_node(doc)(0);
+ static_cast<void (*)(xpath_query***)>(q)(0);
+ static_cast<void (*)(xpath_node***)>(q.evaluate_node(doc))(0);
#endif
}
diff --git a/tests/test_parse.cpp b/tests/test_parse.cpp
index 2094ef9..c45b783 100644
--- a/tests/test_parse.cpp
+++ b/tests/test_parse.cpp
@@ -876,7 +876,7 @@ TEST(parse_out_of_memory)
CHECK(!doc.first_child());
}
-TEST(parse_out_of_memory_halfway)
+TEST(parse_out_of_memory_halfway_node)
{
const unsigned int count = 10000;
static char_t text[count * 4];
@@ -896,6 +896,35 @@ TEST(parse_out_of_memory_halfway)
CHECK_NODE(doc.first_child(), STR("<n />"));
}
+TEST(parse_out_of_memory_halfway_attr)
+{
+ const unsigned int count = 10000;
+ static char_t text[count * 5 + 4];
+
+ text[0] = '<';
+ text[1] = 'n';
+
+ for (unsigned int i = 0; i < count; ++i)
+ {
+ text[5*i + 2] = ' ';
+ text[5*i + 3] = 'a';
+ text[5*i + 4] = '=';
+ text[5*i + 5] = '"';
+ text[5*i + 6] = '"';
+ }
+
+ text[5 * count + 2] = '/';
+ text[5 * count + 3] = '>';
+
+ test_runner::_memory_fail_threshold = 65536;
+
+ xml_document doc;
+ CHECK(doc.load_buffer_inplace(text, count * 5 + 4).status == status_out_of_memory);
+ CHECK_STRING(doc.first_child().name(), STR("n"));
+ CHECK_STRING(doc.first_child().first_attribute().name(), STR("a"));
+ CHECK_STRING(doc.first_child().last_attribute().name(), STR("a"));
+}
+
static bool test_offset(const char_t* contents, unsigned int options, pugi::xml_parse_status status, ptrdiff_t offset)
{
xml_document doc;
@@ -1039,3 +1068,25 @@ TEST(parse_pcdata_gap_fragment)
CHECK(doc.load(STR("a&amp;b"), parse_fragment | parse_escapes));
CHECK_STRING(doc.text().get(), STR("a&b"));
}
+
+TEST(parse_name_end_eof)
+{
+ char_t test[] = STR("<node>");
+
+ xml_document doc;
+ CHECK(doc.load_buffer_inplace(test, 6 * sizeof(char_t)).status == status_end_element_mismatch);
+ CHECK_STRING(doc.first_child().name(), STR("node"));
+}
+
+TEST(parse_close_tag_eof)
+{
+ char_t test1[] = STR("<node></node");
+ char_t test2[] = STR("<node></nodx");
+
+ xml_document doc;
+ CHECK(doc.load_buffer_inplace(test1, 12 * sizeof(char_t)).status == status_bad_end_element);
+ CHECK_STRING(doc.first_child().name(), STR("node"));
+
+ CHECK(doc.load_buffer_inplace(test2, 12 * sizeof(char_t)).status == status_end_element_mismatch);
+ CHECK_STRING(doc.first_child().name(), STR("node"));
+}
diff --git a/tests/test_parse_doctype.cpp b/tests/test_parse_doctype.cpp
index 8976890..f8619fd 100644
--- a/tests/test_parse_doctype.cpp
+++ b/tests/test_parse_doctype.cpp
@@ -313,3 +313,12 @@ TEST(parse_doctype_error_toplevel)
CHECK(doc.load(STR("<node><!DOCTYPE></node>")).status == status_bad_doctype);
CHECK(doc.load(STR("<node><!DOCTYPE></node>"), parse_doctype).status == status_bad_doctype);
}
+
+TEST(parse_doctype_error_ignore)
+{
+ xml_document doc;
+ CHECK(doc.load(STR("<!DOCTYPE root [ <![IGNORE[ ")).status == status_bad_doctype);
+ CHECK(doc.load(STR("<!DOCTYPE root [ <![IGNORE[ "), parse_doctype).status == status_bad_doctype);
+ CHECK(doc.load(STR("<!DOCTYPE root [ <![IGNORE[ <![INCLUDE[")).status == status_bad_doctype);
+ CHECK(doc.load(STR("<!DOCTYPE root [ <![IGNORE[ <![INCLUDE["), parse_doctype).status == status_bad_doctype);
+}
diff --git a/tests/test_write.cpp b/tests/test_write.cpp
index 98650ac..8fc88e1 100644
--- a/tests/test_write.cpp
+++ b/tests/test_write.cpp
@@ -51,6 +51,15 @@ TEST_XML(write_cdata_inner, "<node><![CDATA[value]]></node>")
CHECK_NODE_EX(doc, STR("<node><![CDATA[value]]></node>\n"), STR(""), 0);
}
+TEST(write_cdata_null)
+{
+ xml_document doc;
+ doc.append_child(node_cdata);
+ doc.append_child(STR("node")).append_child(node_cdata);
+
+ CHECK_NODE(doc, STR("<![CDATA[]]><node><![CDATA[]]></node>"));
+}
+
TEST_XML_FLAGS(write_comment, "<!--text-->", parse_comments | parse_fragment)
{
CHECK_NODE(doc, STR("<!--text-->"));
@@ -80,12 +89,32 @@ TEST(write_comment_invalid)
CHECK_NODE(doc, STR("<!--- ->- -->"));
}
+TEST(write_comment_null)
+{
+ xml_document doc;
+ doc.append_child(node_comment);
+
+ CHECK_NODE(doc, STR("<!---->"));
+}
+
TEST_XML_FLAGS(write_pi, "<?name value?>", parse_pi | parse_fragment)
{
CHECK_NODE(doc, STR("<?name value?>"));
CHECK_NODE_EX(doc, STR("<?name value?>\n"), STR(""), 0);
}
+TEST(write_pi_null)
+{
+ xml_document doc;
+ xml_node node = doc.append_child(node_pi);
+
+ CHECK_NODE(doc, STR("<?:anonymous?>"));
+
+ node.set_value(STR("value"));
+
+ CHECK_NODE(doc, STR("<?:anonymous value?>"));
+}
+
TEST_XML_FLAGS(write_declaration, "<?xml version='2.0'?>", parse_declaration | parse_fragment)
{
CHECK_NODE(doc, STR("<?xml version=\"2.0\"?>"));
@@ -98,6 +127,14 @@ TEST_XML_FLAGS(write_doctype, "<!DOCTYPE id [ foo ]>", parse_doctype | parse_fra
CHECK_NODE_EX(doc, STR("<!DOCTYPE id [ foo ]>\n"), STR(""), 0);
}
+TEST(write_doctype_null)
+{
+ xml_document doc;
+ doc.append_child(node_doctype);
+
+ CHECK_NODE(doc, STR("<!DOCTYPE>"));
+}
+
TEST_XML(write_escape, "<node attr=''>text</node>")
{
doc.child(STR("node")).attribute(STR("attr")) = STR("<>'\"&\x04\r\n\t");
@@ -460,3 +497,16 @@ TEST_XML(write_indent_custom, "<node attr='1'><child><sub>text</sub></child></no
CHECK_NODE_EX(doc, STR("<node attr=\"1\">\nABCD<child>\nABCDABCD<sub>text</sub>\nABCD</child>\n</node>\n"), STR("ABCD"), format_indent);
CHECK_NODE_EX(doc, STR("<node attr=\"1\">\nABCDE<child>\nABCDEABCDE<sub>text</sub>\nABCDE</child>\n</node>\n"), STR("ABCDE"), format_indent);
}
+
+TEST(write_pcdata_null)
+{
+ xml_document doc;
+ doc.append_child(STR("node")).append_child(node_pcdata);
+
+ CHECK_NODE(doc, STR("<node></node>"));
+ CHECK_NODE_EX(doc, STR("<node></node>\n"), STR("\t"), format_indent);
+
+ doc.first_child().append_child(node_pcdata);
+
+ CHECK_NODE_EX(doc, STR("<node>\n\t\n\t\n</node>\n"), STR("\t"), format_indent);
+}
diff --git a/tests/test_xpath.cpp b/tests/test_xpath.cpp
index 2143376..a65ee37 100644
--- a/tests/test_xpath.cpp
+++ b/tests/test_xpath.cpp
@@ -110,7 +110,7 @@ TEST_XML(xpath_sort_attributes, "<node/>")
n.append_attribute(STR("attr3"));
n.insert_attribute_before(STR("attr1"), n.attribute(STR("attr2")));
- xpath_node_set ns = n.select_nodes(STR("@*"));
+ xpath_node_set ns = n.select_nodes(STR("@* | @*"));
ns.sort(true);
xpath_node_set reverse_sorted = ns;
@@ -122,6 +122,25 @@ TEST_XML(xpath_sort_attributes, "<node/>")
xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 5 % 4 % 3;
}
+TEST_XML(xpath_sort_attributes_docorder, "<node attr1='' attr2='value' attr4='value' />")
+{
+ xml_node n = doc.child(STR("node"));
+
+ n.first_attribute().set_name(STR("attribute1"));
+ n.insert_attribute_after(STR("attr3"), n.attribute(STR("attr2")));
+
+ xpath_node_set ns = n.select_nodes(STR("@* | @*"));
+
+ ns.sort(true);
+ xpath_node_set reverse_sorted = ns;
+
+ ns.sort(false);
+ xpath_node_set sorted = ns;
+
+ xpath_node_set_tester(sorted, "sorted order failed") % 3 % 4 % 5 % 6;
+ xpath_node_set_tester(reverse_sorted, "reverse sorted order failed") % 6 % 5 % 4 % 3;
+}
+
TEST(xpath_sort_random_medium)
{
xml_document doc;
@@ -606,4 +625,49 @@ TEST(xpath_sort_crossdoc_different_depth)
CHECK(ns.size() == 2);
CHECK((ns[0] == ns1[0] && ns[1] == ns2[0]) || (ns[0] == ns2[0] && ns[1] == ns1[0]));
}
+
+TEST(xpath_allocate_string_out_of_memory)
+{
+ std::basic_string<char_t> query;
+
+ for (int i = 0; i < 1024; ++i) query += STR("abcdefgh");
+
+ test_runner::_memory_fail_threshold = 8*1024;
+
+#ifdef PUGIXML_NO_EXCEPTIONS
+ CHECK(!xpath_query(query.c_str()));
+#else
+ try
+ {
+ xpath_query q(query.c_str());
+
+ CHECK_FORCE_FAIL("Expected out of memory exception");
+ }
+ catch (const std::bad_alloc&)
+ {
+ }
+#endif
+}
+
+TEST(xpath_remove_duplicates)
+{
+ xml_document doc;
+
+ for (int i = 0; i < 20; ++i)
+ {
+ doc.append_child(STR("node2"));
+ doc.prepend_child(STR("node1"));
+ }
+
+ xpath_node_set ns = doc.select_nodes(STR("/node2/preceding::* | //node1 | /node() | /* | /node1/following-sibling::*"));
+
+ ns.sort();
+
+ {
+ xpath_node_set_tester tester(ns, "sorted order failed");
+
+ for (int i = 0; i < 40; ++i)
+ tester % (2 + i);
+ }
+}
#endif
diff --git a/tests/test_xpath_api.cpp b/tests/test_xpath_api.cpp
index 270f6aa..deb3beb 100644
--- a/tests/test_xpath_api.cpp
+++ b/tests/test_xpath_api.cpp
@@ -128,6 +128,11 @@ TEST_XML(xpath_api_nodeset_copy, "<node><foo/><foo/></node>")
copy4 = copy1;
CHECK(copy4.size() == 2);
CHECK_STRING(copy4[0].node().name(), STR("foo"));
+
+ xpath_node_set copy5;
+ copy5 = set;
+ copy5 = xpath_node_set();
+ CHECK(copy5.size() == 0);
}
TEST(xpath_api_nodeset_copy_empty)
diff --git a/tests/test_xpath_functions.cpp b/tests/test_xpath_functions.cpp
index da820ef..678bc2e 100644
--- a/tests/test_xpath_functions.cpp
+++ b/tests/test_xpath_functions.cpp
@@ -216,7 +216,7 @@ TEST(xpath_boolean_false)
CHECK_XPATH_FAIL(STR("false(1)"));
}
-TEST_XML(xpath_boolean_lang, "<node xml:lang='en'><child xml:lang='zh-UK'><subchild/></child></node><foo><bar/></foo>")
+TEST_XML(xpath_boolean_lang, "<node xml:lang='en'><child xml:lang='zh-UK'><subchild attr=''/></child></node><foo><bar/></foo>")
{
xml_node c;
@@ -244,6 +244,9 @@ TEST_XML(xpath_boolean_lang, "<node xml:lang='en'><child xml:lang='zh-UK'><subch
CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")), STR("lang('r')"), false);
CHECK_XPATH_BOOLEAN(doc.child(STR("node")).child(STR("child")).child(STR("subchild")), STR("lang('en')"), false);
+ // lang with 1 attribute argument
+ CHECK_XPATH_NODESET(doc, STR("//@*[lang('en')]"));
+
// lang with 2 arguments
CHECK_XPATH_FAIL(STR("lang(1, 2)"));
}
@@ -773,6 +776,16 @@ TEST_XML_FLAGS(xpath_string_value, "<node><c1>pcdata</c1><c2><child/></c2><c3 at
CHECK_XPATH_STRING(n, STR("string(c6/node())"), STR("cdata"));
}
+TEST(xpath_string_value_empty)
+{
+ xml_document doc;
+ doc.append_child(node_pcdata).set_value(STR("head"));
+ doc.append_child(node_pcdata);
+ doc.append_child(node_pcdata).set_value(STR("tail"));
+
+ CHECK_XPATH_STRING(doc, STR("string()"), STR("headtail"));
+}
+
TEST_XML(xpath_string_concat_translate, "<node>foobar</node>")
{
CHECK_XPATH_STRING(doc, STR("concat('a', 'b', 'c', translate(node, 'o', 'a'), 'd')"), STR("abcfaabard"));
diff --git a/tests/test_xpath_paths.cpp b/tests/test_xpath_paths.cpp
index da8811d..df0dfa4 100644
--- a/tests/test_xpath_paths.cpp
+++ b/tests/test_xpath_paths.cpp
@@ -437,6 +437,52 @@ TEST_XML(xpath_paths_predicate_number, "<node><chapter/><chapter/><chapter/><cha
CHECK_XPATH_NODESET(n, STR("preceding-sibling::chapter[2]")) % 3;
}
+TEST_XML(xpath_paths_predicate_number_boundary, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
+{
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[0.999999999999999]"));
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[1]")) % 3;
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[1.000000000000001]"));
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[1.999999999999999]"));
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[2]")) % 4;
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[2.000000000000001]"));
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[4.999999999999999]"));
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[5]")) % 7;
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[5.000000000000001]"));
+}
+
+TEST_XML(xpath_paths_predicate_number_out_of_range, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
+{
+ xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling();
+
+ CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[0]"));
+ CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[-1]"));
+ CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[-1000000000000]"));
+ CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[-1 div 0]"));
+ CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[1000000000000]"));
+ CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[1 div 0]"));
+ CHECK_XPATH_NODESET(n, STR("following-sibling::chapter[0 div 0]"));
+}
+
+TEST_XML(xpath_paths_predicate_constant_boolean, "<node><chapter/><chapter/><chapter/><chapter/><chapter/></node>")
+{
+ xml_node n = doc.child(STR("node")).child(STR("chapter")).next_sibling().next_sibling();
+
+ xpath_variable_set set;
+ set.set(STR("true"), true);
+ set.set(STR("false"), false);
+
+ CHECK_XPATH_NODESET_VAR(n, STR("following-sibling::chapter[$false]"), &set);
+ CHECK_XPATH_NODESET_VAR(n, STR("following-sibling::chapter[$true]"), &set) % 6 % 7;
+}
+
+TEST_XML(xpath_paths_predicate_position_eq, "<node><chapter/><chapter/><chapter>3</chapter><chapter/><chapter/></node>")
+{
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=1]")) % 3;
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=2+2]")) % 7;
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=last()]")) % 8;
+ CHECK_XPATH_NODESET(doc, STR("node/chapter[position()=string()]")) % 5;
+}
+
TEST_XML(xpath_paths_predicate_several, "<node><employee/><employee secretary=''/><employee assistant=''/><employee secretary='' assistant=''/><employee assistant='' secretary=''/></node>")
{
xml_node n = doc.child(STR("node"));
@@ -615,6 +661,9 @@ TEST_XML(xpath_paths_optimize_step_once, "<node><para1><para2/><para3/><para4><p
CHECK_XPATH_BOOLEAN(doc, STR("//@attr5/following::para6"), true);
CHECK_XPATH_STRING(doc, STR("name(//@attr5/following::para6)"), STR("para6"));
+
+ CHECK_XPATH_BOOLEAN(doc, STR("//para5/ancestor-or-self::*"), true);
+ CHECK_XPATH_BOOLEAN(doc, STR("//para5/ancestor::*"), true);
}
TEST_XML(xpath_paths_null_nodeset_entries, "<node attr='value'/>")
diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp
index 70bb4ea..53b40cf 100644
--- a/tests/test_xpath_variables.cpp
+++ b/tests/test_xpath_variables.cpp
@@ -88,6 +88,9 @@ TEST(xpath_variables_type_string)
CHECK_DOUBLE_NAN(var->get_number());
CHECK_STRING(var->get_string(), STR("abc"));
CHECK(var->get_node_set().empty());
+
+ CHECK(var->set(STR("abcdef")));
+ CHECK_STRING(var->get_string(), STR("abcdef"));
}
TEST_XML(xpath_variables_type_node_set, "<node/>")
@@ -273,6 +276,29 @@ TEST(xpath_variables_long_name)
CHECK_XPATH_BOOLEAN_VAR(xml_node(), STR("$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), &set, true);
}
+TEST(xpath_variables_long_name_out_of_memory)
+{
+ xpath_variable_set set;
+ set.set(STR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), true);
+
+ test_runner::_memory_fail_threshold = 4096 + 64 + 52 * sizeof(char_t);
+
+#ifdef PUGIXML_NO_EXCEPTIONS
+ xpath_query q(STR("$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), &set);
+ CHECK(!q);
+#else
+ try
+ {
+ xpath_query q(STR("$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), &set);
+
+ CHECK_FORCE_FAIL("Expected exception");
+ }
+ catch (const xpath_exception&)
+ {
+ }
+#endif
+}
+
TEST_XML(xpath_variables_select, "<node attr='1'/><node attr='2'/>")
{
xpath_variable_set set;