summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--CMakeLists.txt4
-rw-r--r--src/pugixml.cpp77
-rw-r--r--tests/main.cpp2
-rw-r--r--tests/test_compact.cpp34
-rw-r--r--tests/test_document.cpp104
-rw-r--r--tests/test_write.cpp27
-rw-r--r--tests/test_xpath.cpp16
-rw-r--r--tests/test_xpath_functions.cpp2
-rw-r--r--tests/test_xpath_paths.cpp75
-rw-r--r--tests/test_xpath_variables.cpp31
11 files changed, 326 insertions, 50 deletions
diff --git a/.travis.yml b/.travis.yml
index df5569c..f35124d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,4 +14,6 @@ script:
- make test cxxstd=c++11 defines=$DEFINES config=release -j2
- make test cxxstd=c++98 defines=$DEFINES config=debug -j2
-after_success: bash <(curl -s https://codecov.io/bash) -f pugixml.cpp.gcov
+after_success:
+ - sed -e "s/#####\(.*\)\(\/\/ unreachable.*\)/ 1\1\2/" -i pugixml.cpp.gcov
+ - bash <(curl -s https://codecov.io/bash) -f pugixml.cpp.gcov
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8a1169b..855045c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
project(pugixml)
-cmake_minimum_required(VERSION 2.6)
+cmake_minimum_required(VERSION 2.8.12)
option(BUILD_SHARED_LIBS "Build shared instead of static library" OFF)
option(BUILD_TESTS "Build tests" OFF)
@@ -79,4 +79,4 @@ if(BUILD_TESTS)
add_executable(check ${TEST_SOURCES})
target_link_libraries(check pugixml)
add_custom_command(TARGET check POST_BUILD COMMAND check WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
-endif() \ No newline at end of file
+endif()
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index 56d7c75..4bc971b 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -127,6 +127,16 @@ using std::memset;
# define PUGI__MSVC_CRT_VERSION _MSC_VER
#endif
+// Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size.
+#if __cplusplus >= 201103
+# define PUGI__SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__)
+#elif defined(_MSC_VER) && _MSC_VER >= 1400
+# define PUGI__SNPRINTF(buf, ...) _snprintf_s(buf, sizeof(buf), _TRUNCATE, __VA_ARGS__)
+#else
+# define PUGI__SNPRINTF sprintf
+#endif
+
+// We put implementation details into an anonymous namespace in source mode, but have to keep it in non-anonymous namespace in header-only mode to prevent binary bloat.
#ifdef PUGIXML_HEADER_ONLY
# define PUGI__NS_BEGIN namespace pugi { namespace impl {
# define PUGI__NS_END } }
@@ -353,7 +363,7 @@ PUGI__NS_BEGIN
bucket = (bucket + probe + 1) & hashmod;
}
- assert(false && "Hash table is full");
+ assert(false && "Hash table is full"); // unreachable
return 0;
}
@@ -2144,7 +2154,7 @@ PUGI__NS_BEGIN
if (encoding == encoding_latin1)
return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder());
- assert(false && "Invalid encoding");
+ assert(false && "Invalid encoding"); // unreachable
return false;
}
#else
@@ -2249,7 +2259,7 @@ PUGI__NS_BEGIN
if (encoding == encoding_latin1)
return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);
- assert(false && "Invalid encoding");
+ assert(false && "Invalid encoding"); // unreachable
return false;
}
#endif
@@ -2696,7 +2706,7 @@ PUGI__NS_BEGIN
case 5: return strconv_pcdata_impl<opt_true, opt_false, opt_true>::parse;
case 6: return strconv_pcdata_impl<opt_true, opt_true, opt_false>::parse;
case 7: return strconv_pcdata_impl<opt_true, opt_true, opt_true>::parse;
- default: assert(false); return 0; // should not get here
+ default: assert(false); return 0; // unreachable
}
}
@@ -2873,7 +2883,7 @@ PUGI__NS_BEGIN
case 13: return strconv_attribute_impl<opt_true>::parse_wnorm;
case 14: return strconv_attribute_impl<opt_false>::parse_wnorm;
case 15: return strconv_attribute_impl<opt_true>::parse_wnorm;
- default: assert(false); return 0; // should not get here
+ default: assert(false); return 0; // unreachable
}
}
@@ -3622,7 +3632,7 @@ PUGI__NS_BEGIN
if (encoding == encoding_latin1)
return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer());
- assert(false && "Invalid encoding");
+ assert(false && "Invalid encoding"); // unreachable
return 0;
}
#else
@@ -3661,7 +3671,7 @@ PUGI__NS_BEGIN
if (encoding == encoding_latin1)
return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer());
- assert(false && "Invalid encoding");
+ assert(false && "Invalid encoding"); // unreachable
return 0;
}
#endif
@@ -4188,7 +4198,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(false && "Invalid node type");
+ assert(false && "Invalid node type"); // unreachable
}
}
@@ -4632,7 +4642,7 @@ PUGI__NS_BEGIN
PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value)
{
char buf[128];
- sprintf(buf, "%.9g", value);
+ PUGI__SNPRINTF(buf, "%.9g", value);
return set_value_ascii(dest, header, header_mask, buf);
}
@@ -4641,7 +4651,7 @@ PUGI__NS_BEGIN
PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value)
{
char buf[128];
- sprintf(buf, "%.17g", value);
+ PUGI__SNPRINTF(buf, "%.17g", value);
return set_value_ascii(dest, header, header_mask, buf);
}
@@ -6290,7 +6300,7 @@ namespace pugi
return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1;
default:
- assert(false && "Invalid node type");
+ assert(false && "Invalid node type"); // unreachable
return -1;
}
}
@@ -7252,7 +7262,7 @@ PUGI__NS_BEGIN
return middle;
}
- template <typename T, typename Pred> void partition(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend)
+ template <typename T, typename Pred> void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend)
{
// invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups)
T* eq = begin;
@@ -7290,7 +7300,7 @@ PUGI__NS_BEGIN
// partition in three chunks (< = >)
I eqbeg, eqend;
- partition(begin, end, *median, pred, &eqbeg, &eqend);
+ partition3(begin, end, *median, pred, &eqbeg, &eqend);
// loop on larger half
if (eqbeg - begin > end - eqend)
@@ -7980,11 +7990,11 @@ PUGI__NS_BEGIN
// gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent
#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
- PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent)
+ PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
{
// get base values
int sign, exponent;
- _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign);
+ _ecvt_s(buffer, sizeof(buffer), value, DBL_DIG + 1, &exponent, &sign);
// truncate redundant zeros
truncate_zeros(buffer, buffer + strlen(buffer));
@@ -7994,12 +8004,10 @@ PUGI__NS_BEGIN
*out_exponent = exponent;
}
#else
- PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent)
+ PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
{
// get a scientific notation value with IEEE DBL_DIG decimals
- sprintf(buffer, "%.*e", DBL_DIG, value);
- assert(strlen(buffer) < buffer_size);
- (void)!buffer_size;
+ PUGI__SNPRINTF(buffer, "%.*e", DBL_DIG, value);
// get the exponent (possibly negative)
char* exponent_string = strchr(buffer, 'e');
@@ -8036,7 +8044,7 @@ PUGI__NS_BEGIN
char* mantissa;
int exponent;
- convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent);
+ convert_number_to_mantissa_exponent(value, mantissa_buffer, &mantissa, &exponent);
// allocate a buffer of suitable length for the number
size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4;
@@ -8498,7 +8506,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(false && "Invalid variable type");
+ assert(false && "Invalid variable type"); // unreachable
}
}
@@ -8519,7 +8527,7 @@ PUGI__NS_BEGIN
return lhs->set(static_cast<const xpath_variable_boolean*>(rhs)->value);
default:
- assert(false && "Invalid variable type");
+ assert(false && "Invalid variable type"); // unreachable
return false;
}
}
@@ -8606,7 +8614,7 @@ PUGI__NS_BEGIN
return *min_element(begin, end, document_order_comparator());
default:
- assert(false && "Invalid node set type");
+ assert(false && "Invalid node set type"); // unreachable
return xpath_node();
}
}
@@ -9336,7 +9344,7 @@ PUGI__NS_BEGIN
}
}
- assert(false && "Wrong types");
+ assert(false && "Wrong types"); // unreachable
return false;
}
@@ -9411,7 +9419,7 @@ PUGI__NS_BEGIN
}
else
{
- assert(false && "Wrong types");
+ assert(false && "Wrong types"); // unreachable
return false;
}
}
@@ -9629,7 +9637,7 @@ PUGI__NS_BEGIN
break;
default:
- assert(false && "Unknown axis");
+ assert(false && "Unknown axis"); // unreachable
}
return false;
@@ -9824,7 +9832,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Unimplemented axis");
+ assert(false && "Unimplemented axis"); // unreachable
}
}
@@ -9905,7 +9913,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Unimplemented axis");
+ assert(false && "Unimplemented axis"); // unreachable
}
}
@@ -10146,7 +10154,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Wrong expression for return type boolean");
+ assert(false && "Wrong expression for return type boolean"); // unreachable
return false;
}
}
@@ -10281,7 +10289,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Wrong expression for return type number");
+ assert(false && "Wrong expression for return type number"); // unreachable
return 0;
}
@@ -10571,7 +10579,7 @@ PUGI__NS_BEGIN
}
default:
- assert(false && "Wrong expression for return type string");
+ assert(false && "Wrong expression for return type string"); // unreachable
return xpath_string();
}
}
@@ -10662,7 +10670,7 @@ PUGI__NS_BEGIN
return step_do(c, stack, eval, axis_to_type<axis_self>());
default:
- assert(false && "Unknown axis");
+ assert(false && "Unknown axis"); // unreachable
return xpath_node_set_raw();
}
}
@@ -10700,7 +10708,7 @@ PUGI__NS_BEGIN
// fallthrough
default:
- assert(false && "Wrong expression for return type node set");
+ assert(false && "Wrong expression for return type node set"); // unreachable
return xpath_node_set_raw();
}
}
@@ -12042,7 +12050,7 @@ namespace pugi
return static_cast<const impl::xpath_variable_boolean*>(this)->name;
default:
- assert(false && "Invalid variable type");
+ assert(false && "Invalid variable type"); // unreachable
return 0;
}
}
@@ -12593,6 +12601,7 @@ namespace pugi
#undef PUGI__DMC_VOLATILE
#undef PUGI__UNSIGNED_OVERFLOW
#undef PUGI__MSVC_CRT_VERSION
+#undef PUGI__SNPRINTF
#undef PUGI__NS_BEGIN
#undef PUGI__NS_END
#undef PUGI__FN
diff --git a/tests/main.cpp b/tests/main.cpp
index 712edda..352b58b 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -41,7 +41,7 @@ static void* custom_allocate(size_t size)
else
{
void* ptr = memory_allocate(size);
- assert(ptr);
+ if (!ptr) return 0;
g_memory_total_size += memory_size(ptr);
g_memory_total_count++;
diff --git a/tests/test_compact.cpp b/tests/test_compact.cpp
index f9560c9..f5dc4ee 100644
--- a/tests/test_compact.cpp
+++ b/tests/test_compact.cpp
@@ -111,4 +111,38 @@ TEST_XML(compact_out_of_memory_remove, "<n a='v'/>")
CHECK_ALLOC_FAIL(CHECK(!n.remove_attribute(a)));
CHECK_ALLOC_FAIL(CHECK(!doc.remove_child(n)));
}
+
+TEST_XML(compact_pointer_attribute_list, "<n a='v'/>")
+{
+ xml_node n = doc.child(STR("n"));
+ xml_attribute a = n.attribute(STR("a"));
+
+ // make sure we fill the page with node x
+ for (int i = 0; i < 1000; ++i)
+ doc.append_child(STR("x"));
+
+ // this requires extended encoding for prev_attribute_c/next_attribute
+ n.append_attribute(STR("b"));
+
+ // this requires extended encoding for first_attribute
+ n.remove_attribute(a);
+
+ CHECK(!n.attribute(STR("a")));
+ CHECK(n.attribute(STR("b")));
+}
+
+TEST_XML(compact_pointer_node_list, "<n/>")
+{
+ xml_node n = doc.child(STR("n"));
+
+ // make sure we fill the page with node x
+ // this requires extended encoding for prev_sibling_c/next_sibling
+ for (int i = 0; i < 1000; ++i)
+ doc.append_child(STR("x"));
+
+ // this requires extended encoding for first_child
+ n.append_child(STR("child"));
+
+ CHECK(n.child(STR("child")));
+}
#endif
diff --git a/tests/test_document.cpp b/tests/test_document.cpp
index 9860737..b702a07 100644
--- a/tests/test_document.cpp
+++ b/tests/test_document.cpp
@@ -109,12 +109,26 @@ TEST(document_load_stream_error)
std::ifstream fs("filedoesnotexist");
CHECK(doc.load(fs).status == status_io_error);
+}
+
+TEST(document_load_stream_out_of_memory)
+{
+ pugi::xml_document doc;
std::istringstream iss("<node/>");
test_runner::_memory_fail_threshold = 1;
CHECK_ALLOC_FAIL(CHECK(doc.load(iss).status == status_out_of_memory));
}
+TEST(document_load_stream_wide_out_of_memory)
+{
+ pugi::xml_document doc;
+
+ std::basic_istringstream<wchar_t> iss(L"<node/>");
+ test_runner::_memory_fail_threshold = 1;
+ CHECK_ALLOC_FAIL(CHECK(doc.load(iss).status == status_out_of_memory));
+}
+
TEST(document_load_stream_empty)
{
std::istringstream iss;
@@ -186,11 +200,6 @@ public:
{
this->setg(begin, begin, end);
}
-
- typename std::basic_streambuf<T>::int_type underflow() PUGIXML_OVERRIDE
- {
- return this->gptr() == this->egptr() ? std::basic_streambuf<T>::traits_type::eof() : std::basic_streambuf<T>::traits_type::to_int_type(*this->gptr());
- }
};
TEST(document_load_stream_nonseekable)
@@ -242,21 +251,77 @@ TEST(document_load_stream_nonseekable_out_of_memory)
CHECK_ALLOC_FAIL(CHECK(doc.load(in).status == status_out_of_memory));
}
+TEST(document_load_stream_wide_nonseekable_out_of_memory)
+{
+ wchar_t contents[] = L"<node />";
+ char_array_buffer<wchar_t> buffer(contents, contents + sizeof(contents) / sizeof(contents[0]));
+ std::basic_istream<wchar_t> in(&buffer);
+
+ test_runner::_memory_fail_threshold = 1;
+
+ pugi::xml_document doc;
+ CHECK_ALLOC_FAIL(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>");
+ std::basic_string<char> str;
+ str += "<node>";
+ for (int i = 0; i < 10000; ++i) str += "<node />";
+ str += "</node>";
- char_array_buffer<pugi::char_t> buffer(&str[0], &str[0] + str.length());
- std::basic_istream<pugi::char_t> in(&buffer);
+ char_array_buffer<char> buffer(&str[0], &str[0] + str.length());
+ std::basic_istream<char> in(&buffer);
test_runner::_memory_fail_threshold = 10000 * 8 * 3 / 2;
pugi::xml_document doc;
CHECK_ALLOC_FAIL(CHECK(doc.load(in).status == status_out_of_memory));
}
+
+TEST(document_load_stream_wide_nonseekable_out_of_memory_large)
+{
+ std::basic_string<wchar_t> str;
+ str += L"<node>";
+ for (int i = 0; i < 10000; ++i) str += L"<node />";
+ str += L"</node>";
+
+ char_array_buffer<wchar_t> buffer(&str[0], &str[0] + str.length());
+ std::basic_istream<wchar_t> in(&buffer);
+
+ test_runner::_memory_fail_threshold = 10000 * 8 * 3 / 2;
+
+ pugi::xml_document doc;
+ CHECK_ALLOC_FAIL(CHECK(doc.load(in).status == status_out_of_memory));
+}
+
+template <typename T> class seek_fail_buffer: public std::basic_streambuf<T>
+{
+public:
+ typename std::basic_streambuf<T>::pos_type seekoff(typename std::basic_streambuf<T>::off_type, std::ios_base::seekdir, std::ios_base::openmode) PUGIXML_OVERRIDE
+ {
+ // pretend that our buffer is seekable (this is called by tellg); actual seeks will fail
+ return 0;
+ }
+};
+
+TEST(document_load_stream_seekable_fail_seek)
+{
+ seek_fail_buffer<char> buffer;
+ std::basic_istream<char> in(&buffer);
+
+ pugi::xml_document doc;
+ CHECK(doc.load(in).status == status_io_error);
+}
+
+TEST(document_load_stream_wide_seekable_fail_seek)
+{
+ seek_fail_buffer<wchar_t> buffer;
+ std::basic_istream<wchar_t> in(&buffer);
+
+ pugi::xml_document doc;
+ CHECK(doc.load(in).status == status_io_error);
+}
#endif
TEST(document_load_string)
@@ -383,6 +448,23 @@ TEST(document_load_file_wide_out_of_memory)
CHECK(result.status == status_out_of_memory || result.status == status_file_not_found);
}
+#if defined(__linux__) || defined(__APPLE__)
+TEST(document_load_file_special_folder)
+{
+ xml_document doc;
+ xml_parse_result result = doc.load_file(".");
+ // status_out_of_memory is somewhat counter-intuitive but on Linux ftell returns LONG_MAX for directories
+ CHECK(result.status == status_file_not_found || result.status == status_io_error || result.status == status_out_of_memory);
+}
+
+TEST(document_load_file_special_device)
+{
+ xml_document doc;
+ xml_parse_result result = doc.load_file("/dev/tty");
+ CHECK(result.status == status_file_not_found || result.status == status_io_error);
+}
+#endif
+
TEST_XML(document_save, "<node/>")
{
xml_writer_string writer;
diff --git a/tests/test_write.cpp b/tests/test_write.cpp
index 5cd92a5..be77aa8 100644
--- a/tests/test_write.cpp
+++ b/tests/test_write.cpp
@@ -639,6 +639,33 @@ TEST_XML_FLAGS(write_roundtrip, "<node><child1 attr1='value1' attr2='value2'/><c
}
}
+TEST(write_flush_coverage)
+{
+ xml_document doc;
+
+ // this creates a node that uses short sequences of lengths 1-6 for output
+ xml_node n = doc.append_child(STR("n"));
+
+ xml_attribute a = n.append_attribute(STR("a"));
+
+ xml_attribute b = n.append_attribute(STR("b"));
+ b.set_value(STR("<&\""));
+
+ n.append_child(node_comment);
+
+ size_t basel = save_narrow(doc, format_raw, encoding_auto).size();
+ size_t bufl = 2048;
+
+ for (size_t l = 0; l <= basel; ++l)
+ {
+ std::basic_string<pugi::char_t> pad(bufl - l, STR('v'));
+ a.set_value(pad.c_str());
+
+ std::string s = save_narrow(doc, format_raw, encoding_auto);
+ CHECK(s.size() == basel + bufl - l);
+ }
+}
+
#ifndef PUGIXML_NO_EXCEPTIONS
struct throwing_writer: pugi::xml_writer
{
diff --git a/tests/test_xpath.cpp b/tests/test_xpath.cpp
index 69513fc..3f5d084 100644
--- a/tests/test_xpath.cpp
+++ b/tests/test_xpath.cpp
@@ -407,6 +407,22 @@ TEST(xpath_out_of_memory_evaluate_concat)
CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, xml_node()) == 1));
}
+TEST(xpath_out_of_memory_evaluate_concat_list)
+{
+ std::basic_string<char_t> query = STR("concat(");
+
+ for (size_t i = 0; i < 500; ++i)
+ query += STR("\"\",");
+
+ query += STR("\"\")");
+
+ pugi::xpath_query q(query.c_str());
+
+ test_runner::_memory_fail_threshold = 1;
+
+ CHECK_ALLOC_FAIL(CHECK(q.evaluate_string(0, 0, xml_node()) == 1));
+}
+
TEST(xpath_out_of_memory_evaluate_substring)
{
test_runner::_memory_fail_threshold = 4196 * sizeof(char_t) + 4096 * 2;
diff --git a/tests/test_xpath_functions.cpp b/tests/test_xpath_functions.cpp
index 480eb97..604da78 100644
--- a/tests/test_xpath_functions.cpp
+++ b/tests/test_xpath_functions.cpp
@@ -809,7 +809,7 @@ TEST(xpath_unknown_functions)
query[0] = ch;
CHECK_XPATH_FAIL(query);
- query[0] = ch - 32;
+ query[0] = char_t(ch - 32);
CHECK_XPATH_FAIL(query);
}
}
diff --git a/tests/test_xpath_paths.cpp b/tests/test_xpath_paths.cpp
index 7915df1..dd97019 100644
--- a/tests/test_xpath_paths.cpp
+++ b/tests/test_xpath_paths.cpp
@@ -703,4 +703,79 @@ TEST_XML(xpath_paths_null_nodeset_entries, "<node attr='value'/>")
CHECK(rs[0] == nodes[0]);
CHECK(rs[1] == nodes[2]);
}
+
+TEST_XML(xpath_paths_step_leaf_coverage, "<n><n1/><n2 a='v'><child/></n2><n3/></n>")
+{
+ xml_node n = doc.child(STR("n")).child(STR("n2"));
+
+ CHECK_XPATH_NODESET(n, STR("ancestor::node()")) % 2 % 1;
+ CHECK_XPATH_NODESET(n, STR("ancestor-or-self::node()")) % 4 % 2 % 1;
+ CHECK_XPATH_NODESET(n, STR("attribute::node()")) % 5;
+ CHECK_XPATH_NODESET(n, STR("child::node()")) % 6;
+ CHECK_XPATH_NODESET(n, STR("descendant::node()")) % 6;
+ CHECK_XPATH_NODESET(n, STR("descendant-or-self::node()")) % 4 % 6;
+ CHECK_XPATH_NODESET(n, STR("following::node()")) % 7;
+ CHECK_XPATH_NODESET(n, STR("following-sibling::node()")) % 7;
+ CHECK_XPATH_NODESET(n, STR("namespace::node()"));
+ CHECK_XPATH_NODESET(n, STR("parent::node()")) % 2;
+ CHECK_XPATH_NODESET(n, STR("preceding::node()")) % 3;
+ CHECK_XPATH_NODESET(n, STR("preceding-sibling::node()")) % 3;
+ CHECK_XPATH_NODESET(n, STR("self::node()")) % 4;
+}
+TEST_XML(xpath_paths_step_leaf_predicate_coverage, "<n><n1/><n2 a='v'><child/></n2><n3/></n>")
+{
+ xml_node n = doc.child(STR("n")).child(STR("n2"));
+
+ CHECK_XPATH_NODESET(n, STR("ancestor::node()[1]")) % 2;
+ CHECK_XPATH_NODESET(n, STR("ancestor-or-self::node()[1]")) % 4;
+ CHECK_XPATH_NODESET(n, STR("attribute::node()[1]")) % 5;
+ CHECK_XPATH_NODESET(n, STR("child::node()[1]")) % 6;
+ CHECK_XPATH_NODESET(n, STR("descendant::node()[1]")) % 6;
+ CHECK_XPATH_NODESET(n, STR("descendant-or-self::node()[1]")) % 4;
+ CHECK_XPATH_NODESET(n, STR("following::node()[1]")) % 7;
+ CHECK_XPATH_NODESET(n, STR("following-sibling::node()[1]")) % 7;
+ CHECK_XPATH_NODESET(n, STR("namespace::node()[1]"));
+ CHECK_XPATH_NODESET(n, STR("parent::node()[1]")) % 2;
+ CHECK_XPATH_NODESET(n, STR("preceding::node()[1]")) % 3;
+ CHECK_XPATH_NODESET(n, STR("preceding-sibling::node()[1]")) % 3;
+ CHECK_XPATH_NODESET(n, STR("self::node()[1]")) % 4;
+}
+
+TEST_XML(xpath_paths_step_step_coverage, "<n><n1/><n2 a='v'><child/></n2><n3/></n>")
+{
+ xml_node n = doc.child(STR("n")).child(STR("n2"));
+
+ CHECK_XPATH_NODESET(n, STR("./ancestor::node()")) % 2 % 1;
+ CHECK_XPATH_NODESET(n, STR("./ancestor-or-self::node()")) % 4 % 2 % 1;
+ CHECK_XPATH_NODESET(n, STR("./attribute::node()")) % 5;
+ CHECK_XPATH_NODESET(n, STR("./child::node()")) % 6;
+ CHECK_XPATH_NODESET(n, STR("./descendant::node()")) % 6;
+ CHECK_XPATH_NODESET(n, STR("./descendant-or-self::node()")) % 4 % 6;
+ CHECK_XPATH_NODESET(n, STR("./following::node()")) % 7;
+ CHECK_XPATH_NODESET(n, STR("./following-sibling::node()")) % 7;
+ CHECK_XPATH_NODESET(n, STR("./namespace::node()"));
+ CHECK_XPATH_NODESET(n, STR("./parent::node()")) % 2;
+ CHECK_XPATH_NODESET(n, STR("./preceding::node()")) % 3;
+ CHECK_XPATH_NODESET(n, STR("./preceding-sibling::node()")) % 3;
+ CHECK_XPATH_NODESET(n, STR("./self::node()")) % 4;
+}
+
+TEST_XML(xpath_paths_step_step_predicate_coverage, "<n><n1/><n2 a='v'><child/></n2><n3/></n>")
+{
+ xml_node n = doc.child(STR("n")).child(STR("n2"));
+
+ CHECK_XPATH_NODESET(n, STR("./ancestor::node()[1]")) % 2;
+ CHECK_XPATH_NODESET(n, STR("./ancestor-or-self::node()[1]")) % 4;
+ CHECK_XPATH_NODESET(n, STR("./attribute::node()[1]")) % 5;
+ CHECK_XPATH_NODESET(n, STR("./child::node()[1]")) % 6;
+ CHECK_XPATH_NODESET(n, STR("./descendant::node()[1]")) % 6;
+ CHECK_XPATH_NODESET(n, STR("./descendant-or-self::node()[1]")) % 4;
+ CHECK_XPATH_NODESET(n, STR("./following::node()[1]")) % 7;
+ CHECK_XPATH_NODESET(n, STR("./following-sibling::node()[1]")) % 7;
+ CHECK_XPATH_NODESET(n, STR("./namespace::node()[1]"));
+ CHECK_XPATH_NODESET(n, STR("./parent::node()[1]")) % 2;
+ CHECK_XPATH_NODESET(n, STR("./preceding::node()[1]")) % 3;
+ CHECK_XPATH_NODESET(n, STR("./preceding-sibling::node()[1]")) % 3;
+ CHECK_XPATH_NODESET(n, STR("./self::node()[1]")) % 4;
+}
#endif
diff --git a/tests/test_xpath_variables.cpp b/tests/test_xpath_variables.cpp
index c64e0e6..9349004 100644
--- a/tests/test_xpath_variables.cpp
+++ b/tests/test_xpath_variables.cpp
@@ -302,7 +302,23 @@ TEST_XML(xpath_variables_select, "<node attr='1'/><node attr='2'/>")
TEST(xpath_variables_empty_name)
{
xpath_variable_set set;
+ CHECK(!set.add(STR(""), xpath_type_node_set));
CHECK(!set.add(STR(""), xpath_type_number));
+ CHECK(!set.add(STR(""), xpath_type_string));
+ CHECK(!set.add(STR(""), xpath_type_boolean));
+}
+
+TEST(xpath_variables_long_name_out_of_memory_add)
+{
+ std::basic_string<char_t> name(1000, 'a');
+
+ test_runner::_memory_fail_threshold = 1000;
+
+ xpath_variable_set set;
+ CHECK_ALLOC_FAIL(CHECK(!set.add(name.c_str(), xpath_type_node_set)));
+ CHECK_ALLOC_FAIL(CHECK(!set.add(name.c_str(), xpath_type_number)));
+ CHECK_ALLOC_FAIL(CHECK(!set.add(name.c_str(), xpath_type_string)));
+ CHECK_ALLOC_FAIL(CHECK(!set.add(name.c_str(), xpath_type_boolean)));
}
TEST_XML(xpath_variables_inside_filter, "<node key='1' value='2'/><node key='2' value='1'/><node key='1' value='1'/>")
@@ -591,4 +607,19 @@ TEST(xpath_variables_copy_big_out_of_memory)
CHECK(!copy.get(name));
}
}
+
+TEST(xpath_variables_copy_big_value_out_of_memory)
+{
+ xpath_variable_set set;
+
+ std::basic_string<char_t> var(10000, 'a');
+ set.set(STR("x"), var.c_str());
+
+ test_runner::_memory_fail_threshold = 15000;
+
+ xpath_variable_set copy;
+ CHECK_ALLOC_FAIL(copy = set);
+
+ CHECK(!copy.get(STR("x")));
+}
#endif