diff options
-rw-r--r-- | .travis.yml | 4 | ||||
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/pugixml.cpp | 77 | ||||
-rw-r--r-- | tests/main.cpp | 2 | ||||
-rw-r--r-- | tests/test_compact.cpp | 34 | ||||
-rw-r--r-- | tests/test_document.cpp | 104 | ||||
-rw-r--r-- | tests/test_write.cpp | 27 | ||||
-rw-r--r-- | tests/test_xpath.cpp | 16 | ||||
-rw-r--r-- | tests/test_xpath_functions.cpp | 2 | ||||
-rw-r--r-- | tests/test_xpath_paths.cpp | 75 | ||||
-rw-r--r-- | tests/test_xpath_variables.cpp | 31 |
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 |