From 65e582d6d7f2fff8f3224c2d307922f2b57183ad Mon Sep 17 00:00:00 2001 From: ooxi Date: Mon, 5 Mar 2018 14:02:25 +0100 Subject: [PATCH] Recursive search example for issue #14 --- test/CMakeLists.txt | 26 +++++++ test/test-huitre39.c | 181 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 test/test-huitre39.c diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 071518b..99e50dc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -96,3 +96,29 @@ add_test( COMMAND valgrind --tool=memcheck --leak-check=full --track-origins=yes -v "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-test-cpp" ) + + +# Test huitre39 +add_executable( + "${PROJECT_NAME}-test-huitre39" + "${CMAKE_CURRENT_LIST_DIR}/test-huitre39.c" +) + +target_compile_options( + "${PROJECT_NAME}-test-huitre39" + PRIVATE + -std=c11 +) + +target_link_libraries( + "${PROJECT_NAME}-test-huitre39" + PRIVATE + xml +) + + +add_test( + NAME "${PROJECT_NAME}-test-huitre39" + COMMAND "${PROJECT_NAME}-test-huitre39" +) + diff --git a/test/test-huitre39.c b/test/test-huitre39.c new file mode 100644 index 0000000..43b9ae7 --- /dev/null +++ b/test/test-huitre39.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include "./xml.h" + + + + + +/** + * Will halt the program iff assertion fails + */ +static void _assert_that(_Bool condition, char const* message, char const* func, char const* file, int line) { + if (!condition) { + fprintf(stderr, "Assertion failed: %s, in %s (%s:%i)\n", message, func, file, line); + exit(EXIT_FAILURE); + } +} + +#define assert_that(condition, message) \ + _assert_that(condition, message, __func__, __FILE__, __LINE__) + + + + + +/** + * Behaves similar to `getElementsByTagName`, however returns just the first and + * not all elements with a given tag name + * + * @param base Node in tree which should be the start of te recursive search + * @param 0-terminated tag name, case sensitive + * + * @return First node below `base` iff found, otherwise 0 + * @warning Depth-First search! + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName + */ +static struct xml_node* get_node_by_name(struct xml_node* base, uint8_t* name) { + + + /* Check whether `base` already has the tag name we are searching for + */ + size_t const name_length = strlen(name); + + uint8_t* base_name = xml_easy_name(base); + size_t const base_name_length = strlen(base_name); + + + /* Length of `name` and `base_name` do match, we should now do a real + * comparison + */ + if (name_length == base_name_length) { + int const rs = memcmp(name, base_name, name_length); + + /* Names match! We have found an element which fullfills our + * search criteria + */ + if (!rs) { + free(base_name); + return base; + } + } + + + /* Unfortunately, `base` is not the element we are looking for :-( + */ + free(base_name); + + + /* Let's take a look at the children of `base` + */ + size_t const number_of_children = xml_node_children(base); + + + /* No children → No luck with base + */ + if (!number_of_children) { + return 0; + } + + + /* Recursivly look through all children + */ + size_t child = 0; for (; child < number_of_children; ++child) { + struct xml_node* child_node = xml_node_child(base, child); + + /* Maybe this child does contain the element we are looking for? + */ + struct xml_node* search_result = get_node_by_name( + child_node, name + ); + + /* We are lucky! + */ + if (search_result) { + return search_result; + } + } + + + /* No luck :-( + */ + return 0; +} + + + + + +int main(int argc, char** argv) { + + /* XML source, could be read from disk + */ + uint8_t* source = "" + "" + "World" + + "" + "" + "testas one" + "testos" + "" + + "" + "testis" + "testus" + "testls" + "" + + "" + "testmn" + "testas two" + "" + "" + "" + ; + + struct xml_document* document = xml_parse_document(source, strlen(source)); + + if (!document) { + printf("Could parse document\n"); + exit(EXIT_FAILURE); + } + struct xml_node* root = xml_document_root(document); + + + + /* We expect to find Root / Functions / Function#1 / us + */ + struct xml_node* us = get_node_by_name(root, "us"); + assert_that(us, "Did not find element by tag name `us'"); + + uint8_t* us_content = xml_easy_content(us); + assert_that(us_content, "`us' should have content"); + assert_that(!strcmp(us_content, "testus"), "Unexpected content for node `us'"); + free(us_content); + + + /* We expect to find Root / Functions / Function#0 / as + */ + struct xml_node* as = get_node_by_name(root, "as"); + assert_that(as, "Did not find element by tag name `as'"); + + uint8_t* as_content = xml_easy_content(as); + assert_that(as_content, "`as' should have content"); + assert_that(!strcmp(as_content, "testas one"), "Unexpected content for first `as' node"); + free(as_content); + + + /* We do not expect do find a node with tag name `does_not_exist' + */ + struct xml_node* does_not_exist = get_node_by_name(root, "does_not_exist"); + assert_that(!does_not_exist, "Found node that should not exist"); + + + + xml_document_free(document, false); +} +