diff --git a/.github/workflows/gtest.yml b/.github/workflows/gtest.yml index a848298..60a9455 100644 --- a/.github/workflows/gtest.yml +++ b/.github/workflows/gtest.yml @@ -23,18 +23,29 @@ jobs: run: sudo apt-get update - name: Install libcurl run: sudo apt-get install libcurl4-openssl-dev + - name: Set up Python 3.8 for gcovr + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: install gcovr 5.0 + run: | + pip install gcovr==5.0 # 5.1 is not supported - name: SonarQube install uses: SonarSource/sonarcloud-github-c-cpp@v3 - name: Make build directory run: mkdir gtest-build - name: CMake - run: cmake -S . -B gtest-build -DCMAKE_BUILD_TYPE=Debug -DCMAKE-CXX_FLAGS=-Werror + run: cmake -S . -B gtest-build -DCMAKE_BUILD_TYPE=Debug - name: Build Wrapper run: build-wrapper-linux-x86-64 --out-dir sonar-out cmake --build gtest-build + - name: Run Tests + run: cd ./gtest-build && ./rapidxml-test + - name: Show coverage + run: cd ./gtest-build && gcovr -r .. + - name: Collate coverage + run: cd ./gtest-build && gcovr -r .. --sonarqube >../coverage.xml - name: Sonar Scanner - run: sonar-scanner --define sonar.cfamily.compile-commands=sonar-out/compile_commands.json + run: sonar-scanner --define sonar.cfamily.compile-commands=sonar-out/compile_commands.json --define sonar.coverageReportPaths=coverage.xml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - name: Run Tests - run: cd gtest-build && ./rapidxml-test diff --git a/CMakeLists.txt b/CMakeLists.txt index 68fb421..89ae868 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ add_executable(rapidxml-test test/xpath.cpp rapidxml_generator.hpp test/main.cc + rapidxml_predicates.hpp ) target_link_libraries(rapidxml-test PRIVATE GTest::gtest @@ -51,6 +52,8 @@ target_include_directories(rapidxml-test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) +target_compile_options(rapidxml-test PRIVATE -Werror -Wall --coverage -g -O0) +target_link_options(rapidxml-test PRIVATE --coverage -g) if (RAPIDXML_PERF_TESTS) message("Running performance tests") file(DOWNLOAD https://www.w3.org/TR/xml/REC-xml-20081126.xml ${CMAKE_CURRENT_BINARY_DIR}/REC-xml-20081126.xml) diff --git a/rapidxml.hpp b/rapidxml.hpp index 1fcd54a..a5911c9 100644 --- a/rapidxml.hpp +++ b/rapidxml.hpp @@ -14,6 +14,7 @@ #include // For assert #include // For placement new #include + #include #include #include #endif @@ -102,38 +103,33 @@ namespace rapidxml class eof_error : public parse_error { public: - eof_error(const char * what, void * where) : parse_error(what, where) {} + using parse_error::parse_error; }; class validation_error : public std::runtime_error { public: - explicit validation_error(const char * what) - : std::runtime_error(what) {} + using std::runtime_error::runtime_error; }; class xmlns_unbound : public validation_error { public: - explicit xmlns_unbound(const char * what) - : validation_error(what) {} + using validation_error::validation_error; }; class duplicate_attribute : public validation_error { public: - explicit duplicate_attribute(const char * what) - : validation_error(what) {} + using validation_error::validation_error; }; class attr_xmlns_unbound : public xmlns_unbound { public: - explicit attr_xmlns_unbound(const char * what) - : xmlns_unbound(what) {} + using xmlns_unbound::xmlns_unbound; }; class element_xmlns_unbound : public xmlns_unbound { public: - explicit element_xmlns_unbound(const char * what) - : xmlns_unbound(what) {} + using xmlns_unbound::xmlns_unbound; }; } @@ -168,7 +164,7 @@ namespace rapidxml //! Enumeration listing all node types produced by the parser. //! Use xml_node::type() function to query node type. - enum node_type + enum class node_type { node_document, //!< A document node. Name and value are empty. node_element, //!< An element node. Name contains element name. Value contains text of first data node. @@ -200,13 +196,6 @@ namespace rapidxml //! See xml_document::parse() function. const int parse_no_element_values = 0x2; - //! Parse flag instructing the parser to not place zero terminators after strings in the source text. - //! By default zero terminators are placed, modifying source text. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - // const int parse_no_string_terminators = 0x4; - //! Parse flag instructing the parser to not translate entities in the source text. //! By default entities are translated, modifying source text. //! Can be combined with other flags by use of | operator. @@ -383,14 +372,16 @@ namespace rapidxml public: //! \cond internal - typedef void *(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory - typedef void (free_func)(void *); // Type of user-defined function used to free memory + using alloc_func = void * (*)(std::size_t); // Type of user-defined function used to allocate memory + using free_func = void (*)(void *); // Type of user-defined function used to free memory //! \endcond //! Constructs empty pool with default allocator functions. - memory_pool() : m_static_memory(0) { + memory_pool() { init(); } + memory_pool(memory_pool const &) = delete; + memory_pool(memory_pool &&) = delete; //! Destroys pool and frees all the memory. //! This causes memory occupied by nodes allocated by the pool to be freed. @@ -465,24 +456,24 @@ namespace rapidxml //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. //! \return Pointer to allocated char array. This pointer will never be NULL. template - view_type allocate_string(const Sch *source, std::size_t size) + std::span allocate_span(std::basic_string_view const & source) { - if (size == 0) return {}; // No need to allocate. - Ch *result = allocate_aligned(size); - if (source) - for (std::size_t i = 0; i < size; ++i) - result[i] = source[i]; - return {result, size}; + if (source.size() == 0) return {}; // No need to allocate. + Ch *result = allocate_aligned(source.size()); + for (std::size_t i = 0; i < source.size(); ++i) + result[i] = source[i]; + return {result, source.size()}; } template view_type allocate_string(std::basic_string_view const & source) { - return allocate_string(source.data(), source.size()); + auto span = allocate_span(source); + return {span.data(), span.size()}; } template view_type allocate_string(std::basic_string const & source) { - return allocate_string(source.data(), source.size()); + return allocate_string(std::basic_string_view{source.data(), source.size()}); } template @@ -542,7 +533,7 @@ namespace rapidxml //! Any nodes or strings allocated from the pool will no longer be valid. void clear() { - while (m_begin != m_static_memory) + while (m_begin != m_static_memory.data()) { std::size_t s = sizeof(header) * 2; void * h = m_begin; @@ -570,9 +561,9 @@ namespace rapidxml //!
//! \param af Allocation function, or 0 to restore default function //! \param ff Free function, or 0 to restore default function - [[maybe_unused]] void set_allocator(alloc_func *af, free_func *ff) + [[maybe_unused]] void set_allocator(alloc_func af, free_func ff) { - assert(m_begin == m_static_memory && m_ptr == m_begin); // Verify that no memory is allocated yet + assert(m_begin == m_static_memory.data() && m_ptr == m_begin); // Verify that no memory is allocated yet m_alloc_func = af; m_free_func = ff; } @@ -586,9 +577,9 @@ namespace rapidxml void init() { - m_begin = m_static_memory; + m_begin = m_static_memory.data(); m_ptr = m_begin; - m_space = sizeof(m_static_memory); + m_space = m_static_memory.size(); } void *allocate_raw(std::size_t size) @@ -651,9 +642,9 @@ namespace rapidxml void *m_begin = nullptr; // Start of raw memory making up current pool void *m_ptr = nullptr; // First free byte in current pool std::size_t m_space = RAPIDXML_STATIC_POOL_SIZE; // Available space remaining - char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory - alloc_func *m_alloc_func = nullptr; // Allocator function, or 0 if default is to be used - free_func *m_free_func = nullptr; // Free function, or 0 if default is to be used + std::array m_static_memory = {}; // Static raw memory + alloc_func m_alloc_func = nullptr; // Allocator function, or 0 if default is to be used + free_func m_free_func = nullptr; // Free function, or 0 if default is to be used view_type m_nullstr; view_type m_xmlns_xml; view_type m_xmlns_xmlns; @@ -676,8 +667,8 @@ namespace rapidxml // Construction & destruction // Construct a base with empty name, value and parent - xml_base() {} - xml_base(view_type const & name) : m_name(name) {} + xml_base() = default; + explicit xml_base(view_type const & name) : m_name(name) {} xml_base(view_type const & name, view_type const & value) : m_name(name), m_value(value) {} /////////////////////////////////////////////////////////////////////////// @@ -906,6 +897,7 @@ namespace rapidxml template class xml_node: public xml_base { + using enum node_type; public: using view_type = std::basic_string_view; @@ -1297,12 +1289,12 @@ namespace rapidxml } else { - child->m_prev_sibling = 0; + child->m_prev_sibling = nullptr; m_first_node = child; } m_last_node = child; child->m_parent = this; - child->m_next_sibling = 0; + child->m_next_sibling = nullptr; return child; } optional_ptr> append_node(optional_ptr> ptr) { @@ -1332,7 +1324,7 @@ namespace rapidxml dirty(); if (where == m_first_node) prepend_node(child); - else if (where == 0) + else if (!where) append_node(child); else { @@ -1370,10 +1362,10 @@ namespace rapidxml xml_node *child = m_first_node; m_first_node = child->m_next_sibling; if (child->m_next_sibling) - child->m_next_sibling->m_prev_sibling = 0; + child->m_next_sibling->m_prev_sibling = nullptr; else - m_last_node = 0; - child->m_parent = 0; + m_last_node = nullptr; + child->m_parent = nullptr; } //! Removes last child of the node. @@ -1387,11 +1379,11 @@ namespace rapidxml if (child->m_prev_sibling) { m_last_node = child->m_prev_sibling; - child->m_prev_sibling->m_next_sibling = 0; + child->m_prev_sibling->m_next_sibling = nullptr; } else - m_first_node = 0; - child->m_parent = 0; + m_first_node = nullptr; + child->m_parent = nullptr; } //! Removes specified child from the node @@ -1409,7 +1401,7 @@ namespace rapidxml { where->m_prev_sibling->m_next_sibling = where->m_next_sibling; where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; - where->m_parent = 0; + where->m_parent = nullptr; } } @@ -1418,8 +1410,9 @@ namespace rapidxml { if (!m_first_node) return; dirty(); - for (xml_node *node = m_first_node; node; node = node->m_next_sibling) + for (xml_node *node = m_first_node; node; node = node->m_next_sibling) { node->m_parent = nullptr; + } m_first_node = nullptr; m_last_node = nullptr; } @@ -1437,12 +1430,12 @@ namespace rapidxml } else { - attribute->m_next_attribute = 0; + attribute->m_next_attribute = nullptr; m_last_attribute = attribute; } m_first_attribute = attribute; attribute->m_parent = this; - attribute->m_prev_attribute = 0; + attribute->m_prev_attribute = nullptr; } //! Appends a new attribute to the node. @@ -1458,12 +1451,12 @@ namespace rapidxml } else { - attribute->m_prev_attribute = 0; + attribute->m_prev_attribute = nullptr; m_first_attribute = attribute; } m_last_attribute = attribute; attribute->m_parent = this; - attribute->m_next_attribute = 0; + attribute->m_next_attribute = nullptr; } //! Inserts a new attribute at specified place inside the node. @@ -1477,7 +1470,7 @@ namespace rapidxml dirty_parent(); if (where == m_first_attribute) prepend_attribute(attribute); - else if (where == 0) + else if (!where) append_attribute(attribute); else { @@ -1502,8 +1495,8 @@ namespace rapidxml attribute->m_next_attribute->m_prev_attribute = 0; } else - m_last_attribute = 0; - attribute->m_parent = 0; + m_last_attribute = nullptr; + attribute->m_parent = nullptr; m_first_attribute = attribute->m_next_attribute; } @@ -1521,8 +1514,8 @@ namespace rapidxml m_last_attribute = attribute->m_prev_attribute; } else - m_first_attribute = 0; - attribute->m_parent = 0; + m_first_attribute = nullptr; + attribute->m_parent = nullptr; } //! Removes specified attribute from node. @@ -1539,7 +1532,7 @@ namespace rapidxml { where->m_prev_attribute->m_next_attribute = where->m_next_attribute; where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; - where->m_parent = 0; + where->m_parent = nullptr; } } @@ -1548,8 +1541,9 @@ namespace rapidxml { if (!m_first_attribute) return; dirty_parent(); - for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) { attribute->m_parent = nullptr; + } m_first_attribute = nullptr; } @@ -1626,6 +1620,7 @@ namespace rapidxml template class xml_document: public xml_node, public memory_pool { + using enum node_type; public: using view_type = std::basic_string_view; @@ -1677,7 +1672,7 @@ namespace rapidxml parse_bom(text); // Parse children - while (1) + while (true) { // Skip whitespace before node skip(text); @@ -1690,9 +1685,8 @@ namespace rapidxml ++text; // Skip '<' if (xml_node *node = parse_node(text)) { this->append_node(node); - if (Flags & (parse_open_only|parse_parse_one)) { - if (node->type() == node_element) - break; + if (Flags & (parse_open_only|parse_parse_one) && node->type() == node_element) { + break; } } } @@ -1714,18 +1708,18 @@ namespace rapidxml template view_type decode_data_value_low(view_type const & v) { - Ch * init = const_cast(v.data()); - Ch * first = init; + auto * init = v.data(); + auto * first = init; if (Flags & parse_normalize_whitespace) { skip(first); } else { skip(first); } if (*first == '<') return v; - auto buf = this->allocate_string(v); - Ch * start = const_cast(buf.data()); - Ch * tmp = start; - Ch * end = (Flags & parse_normalize_whitespace) ? + auto buf = this->allocate_span(v); + auto * start = buf.data(); + auto * tmp = start; + auto * end = (Flags & parse_normalize_whitespace) ? skip_and_expand_character_refs(tmp) : skip_and_expand_character_refs(tmp); // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > @@ -1750,12 +1744,12 @@ namespace rapidxml template view_type decode_attr_value_low(view_type const & v) { - Ch * init = const_cast(v.data()); - Ch * first = init; + Ch const * init = v.data(); + Ch const * first = init; skip,0>(first); if (*first == Q) return v; - auto buf = this->allocate_string(v); - Ch * start = const_cast(buf.data()); + auto buf = this->allocate_span(v); + Ch * start = buf.data(); Ch * tmp = start; Ch * end = skip_and_expand_character_refs,attribute_value_pure_pred,0>(tmp); return {start, end}; diff --git a/rapidxml_predicates.hpp b/rapidxml_predicates.hpp index 83f63b1..2f778bd 100644 --- a/rapidxml_predicates.hpp +++ b/rapidxml_predicates.hpp @@ -30,7 +30,7 @@ namespace rapidxml { bool do_match(const xml_node & t) override { if (m_xmlns.has_value() && t.xmlns() != m_xmlns.value()) return false; - return (t.type() == node_element) && (t.name() == m_name || m_name == "*"); + return (t.type() == node_type::node_element) && (t.name() == m_name || m_name == "*"); } }; @@ -43,7 +43,7 @@ namespace rapidxml { : xpath_base(), m_value(v) {} bool do_match(const xml_node & t) override { - return (t.type() == node_element) && (t.value() == m_value); + return (t.type() == node_type::node_element) && (t.value() == m_value); } }; @@ -56,7 +56,7 @@ namespace rapidxml { : xpath_base(), m_xmlns(v) {} bool do_match(const xml_node & t) override { - return (t.type() == node_element) && (t.xmlns() == m_xmlns); + return (t.type() == node_type::node_element) && (t.xmlns() == m_xmlns); } }; @@ -74,7 +74,7 @@ namespace rapidxml { : xpath_base(), m_name(n), m_value(v), m_xmlns(x) {} bool do_match(const xml_node & t) override { - if (t.type() != node_element) return false; + if (t.type() != node_type::node_element) return false; for (auto const & attr : t.attributes()) { if (m_xmlns.has_value()) { if (m_name == "*" || attr.local_name() != m_name) continue; @@ -100,7 +100,7 @@ namespace rapidxml { } bool do_match(const xml_node & t) override { - return t.type() == node_document || t.type() == node_element; + return t.type() == node_type::node_document || t.type() == node_type::node_element; } }; @@ -117,7 +117,7 @@ namespace rapidxml { } bool do_match(const xml_node & t) override { - return t.type() == node_document || t.type() == node_element; + return t.type() == node_type::node_document || t.type() == node_type::node_element; } }; @@ -333,9 +333,9 @@ namespace rapidxml { return parse(internal::xmlns_empty, sv); } - xpath(std::map & xmlns) : m_xmlns(xmlns) {} + explicit xpath(std::map & xmlns) : m_xmlns(xmlns) {} - rapidxml::generator &> all(xml_node & current, int depth = 0) { + rapidxml::generator &> all(xml_node & current, unsigned int depth = 0) { if (depth >= m_chain.size()) throw std::logic_error("Depth exceeded"); auto & xp = m_chain[depth]; depth++; diff --git a/rapidxml_print.hpp b/rapidxml_print.hpp index ddc8dd4..52ba72a 100644 --- a/rapidxml_print.hpp +++ b/rapidxml_print.hpp @@ -163,7 +163,7 @@ namespace rapidxml template inline OutIt print_data_node(OutIt out, const optional_ptr> node, int flags, int indent) { - assert(node->type() == node_data); + assert(node->type() == node_type::node_data); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); if (!node->value_decoded()) { @@ -178,7 +178,7 @@ namespace rapidxml template inline OutIt print_cdata_node(OutIt out, const optional_ptr> node, int flags, int indent) { - assert(node->type() == node_cdata); + assert(node->type() == node_type::node_cdata); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); *out = Ch('<'); ++out; @@ -201,7 +201,7 @@ namespace rapidxml template inline OutIt print_element_node(OutIt out, const optional_ptr> node, int flags, int indent) { - assert(node->type() == node_element); + assert(node->type() == node_type::node_element); // Print element name and attributes, if any if (!(flags & print_no_indenting)) @@ -241,7 +241,7 @@ namespace rapidxml } else { out = copy_and_expand_chars(node->value(), Ch(0), out); } - } else if (!child->next_sibling() && child->type() == node_data) { + } else if (!child->next_sibling() && child->type() == node_type::node_data) { // If node has a sole data child, only print its value without indenting if (!child->value_decoded()) { out = copy_chars(child->value_raw(), out); @@ -298,7 +298,7 @@ namespace rapidxml template inline OutIt print_comment_node(OutIt out, const optional_ptr> node, int flags, int indent) { - assert(node->type() == node_comment); + assert(node->type() == node_type::node_comment); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); *out = Ch('<'), ++out; @@ -316,7 +316,7 @@ namespace rapidxml template inline OutIt print_doctype_node(OutIt out, const optional_ptr> node, int flags, int indent) { - assert(node->type() == node_doctype); + assert(node->type() == node_type::node_doctype); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); *out = Ch('<'), ++out; @@ -338,7 +338,7 @@ namespace rapidxml template inline OutIt print_pi_node(OutIt out, const optional_ptr> node, int flags, int indent) { - assert(node->type() == node_pi); + assert(node->type() == node_type::node_pi); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); *out = Ch('<'), ++out; @@ -355,7 +355,7 @@ namespace rapidxml template inline OutIt print_literal_node(OutIt out, const optional_ptr> node, int flags, int indent) { - assert(node->type() == node_literal); + assert(node->type() == node_type::node_literal); if (!(flags & print_no_indenting)) out = fill_chars(out, indent, Ch('\t')); out = copy_chars(node->value(), out); @@ -370,6 +370,7 @@ namespace rapidxml // Print proper node type switch (node->type()) { + using enum node_type; // Document case node_document: diff --git a/sonar-project.properties b/sonar-project.properties index a067ab7..a5cdece 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -13,4 +13,5 @@ sonar.organization=dwd-github #sonar.sourceEncoding=UTF-8 -sonar.exclusions=**/deps/**, **/_deps/**, **/sentry-native/** +sonar.exclusions=**/deps/**, **/_deps/**, **/sentry-native/**, **/*.html +sonar.sources=. diff --git a/test/manipulations.cpp b/test/manipulations.cpp index d1b4c4b..d33954f 100644 --- a/test/manipulations.cpp +++ b/test/manipulations.cpp @@ -17,7 +17,7 @@ namespace { TEST(Create, Node) { rapidxml::xml_document<> doc; - auto node = doc.allocate_node(rapidxml::node_element, "fish", "cakes"); + auto node = doc.allocate_node(rapidxml::node_type::node_element, "fish", "cakes"); doc.append_node(node); EXPECT_EQ( @@ -28,7 +28,7 @@ TEST(Create, Node) { TEST(Create, NodeEmpty) { rapidxml::xml_document<> doc; - auto node = doc.allocate_node(rapidxml::node_element, "fish"); + auto node = doc.allocate_node(rapidxml::node_type::node_element, "fish"); doc.append_node(node); EXPECT_EQ( @@ -39,7 +39,7 @@ TEST(Create, NodeEmpty) { TEST(Create, Node2) { rapidxml::xml_document<> doc; - auto node = doc.allocate_node(rapidxml::node_element, "fish", "cakes"); + auto node = doc.allocate_node(rapidxml::node_type::node_element, "fish", "cakes"); doc.append_node(node); EXPECT_EQ( @@ -56,7 +56,7 @@ std::string const & fn() { TEST(Create, NodeAttr) { rapidxml::xml_document<> doc; - auto node = doc.allocate_node(rapidxml::node_element, "fish", "cakes"); + auto node = doc.allocate_node(rapidxml::node_type::node_element, "fish", "cakes"); auto haddock = doc.allocate_attribute("id", "haddock"); node->append_attribute(haddock); doc.append_node(node); diff --git a/test/round-trips.cpp b/test/round-trips.cpp index 2015cba..1db4680 100644 --- a/test/round-trips.cpp +++ b/test/round-trips.cpp @@ -44,7 +44,7 @@ TEST(RoundTrip, SimpleMod) { std::string xmlns = "that"; std::string name = "this"; auto check = doc.first_node()->append_element(name, "the other"); - auto check2 = doc.first_node()->append_element(name, "another'"); + doc.first_node()->append_element(name, "another'"); EXPECT_EQ(name, "this"); EXPECT_EQ(check->name(), name); EXPECT_EQ(check->name().data(), name.data()); diff --git a/test/xpath.cpp b/test/xpath.cpp index de58d91..abe7bc7 100644 --- a/test/xpath.cpp +++ b/test/xpath.cpp @@ -83,7 +83,7 @@ TEST(XPathFirst, simple_all) { auto xp = rapidxml::xpath<>::parse(sv); auto r = xp->first(doc); ASSERT_TRUE(r); - EXPECT_EQ(r->type(), rapidxml::node_document); + EXPECT_EQ(r->type(), rapidxml::node_type::node_document); } TEST(XPathFirst, simple_any) {