#include "unity/unity.h" #include #include #include #include #include #include /* Wrapper provided in the source module for the static function */ extern htmlNodePtr test_htmlFindFirstChild(htmlNodePtr parent, const char *name); void setUp(void) { /* Optional: initialize libxml2 if needed */ /* xmlInitParser(); */ } void tearDown(void) { /* Optional: cleanup between tests if needed */ /* Do not call xmlCleanupParser() here; call once at program end if used */ } /* Helper to create a fresh document with a root "parent" node */ static void create_doc_with_parent(xmlDocPtr *outDoc, htmlNodePtr *outParent) { xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); TEST_ASSERT_NOT_NULL(doc); htmlNodePtr parent = (htmlNodePtr)xmlNewNode(NULL, BAD_CAST "parent"); TEST_ASSERT_NOT_NULL(parent); xmlDocSetRootElement(doc, (xmlNodePtr)parent); *outDoc = doc; *outParent = parent; } /* Test: No children -> should return NULL */ void test_htmlFindFirstChild_no_children_returns_NULL(void) { xmlDocPtr doc = NULL; htmlNodePtr parent = NULL; create_doc_with_parent(&doc, &parent); htmlNodePtr res = test_htmlFindFirstChild(parent, "div"); TEST_ASSERT_NULL(res); xmlFreeDoc(doc); } /* Test: Case-insensitive match returns the first matching child */ void test_htmlFindFirstChild_case_insensitive_first_match(void) { xmlDocPtr doc = NULL; htmlNodePtr parent = NULL; create_doc_with_parent(&doc, &parent); /* Create two element children named "div" */ htmlNodePtr firstDiv = (htmlNodePtr)xmlNewNode(NULL, BAD_CAST "div"); TEST_ASSERT_NOT_NULL(firstDiv); xmlAddChild((xmlNodePtr)parent, (xmlNodePtr)firstDiv); htmlNodePtr secondDiv = (htmlNodePtr)xmlNewNode(NULL, BAD_CAST "DiV"); TEST_ASSERT_NOT_NULL(secondDiv); xmlAddChild((xmlNodePtr)parent, (xmlNodePtr)secondDiv); /* Mixed-case search string */ htmlNodePtr res = test_htmlFindFirstChild(parent, "dIv"); TEST_ASSERT_NOT_NULL(res); TEST_ASSERT_EQUAL_PTR(firstDiv, res); xmlFreeDoc(doc); } /* Test: Skips non-element nodes (text and comments) before the first matching element */ void test_htmlFindFirstChild_skips_non_element_nodes(void) { xmlDocPtr doc = NULL; htmlNodePtr parent = NULL; create_doc_with_parent(&doc, &parent); xmlNodePtr text = xmlNewText(BAD_CAST "text node"); TEST_ASSERT_NOT_NULL(text); xmlAddChild((xmlNodePtr)parent, text); xmlNodePtr comment = xmlNewComment(BAD_CAST "a comment"); TEST_ASSERT_NOT_NULL(comment); xmlAddChild((xmlNodePtr)parent, comment); htmlNodePtr target = (htmlNodePtr)xmlNewNode(NULL, BAD_CAST "span"); TEST_ASSERT_NOT_NULL(target); xmlAddChild((xmlNodePtr)parent, (xmlNodePtr)target); htmlNodePtr res = test_htmlFindFirstChild(parent, "SPAN"); /* uppercase query */ TEST_ASSERT_NOT_NULL(res); TEST_ASSERT_EQUAL_PTR(target, res); xmlFreeDoc(doc); } /* Test: No matching child -> should return NULL */ void test_htmlFindFirstChild_no_matching_child_returns_NULL(void) { xmlDocPtr doc = NULL; htmlNodePtr parent = NULL; create_doc_with_parent(&doc, &parent); htmlNodePtr a = (htmlNodePtr)xmlNewNode(NULL, BAD_CAST "div"); TEST_ASSERT_NOT_NULL(a); xmlAddChild((xmlNodePtr)parent, (xmlNodePtr)a); htmlNodePtr b = (htmlNodePtr)xmlNewNode(NULL, BAD_CAST "p"); TEST_ASSERT_NOT_NULL(b); xmlAddChild((xmlNodePtr)parent, (xmlNodePtr)b); htmlNodePtr res = test_htmlFindFirstChild(parent, "span"); TEST_ASSERT_NULL(res); xmlFreeDoc(doc); } /* Test: Exact name match required; should not match prefixes (e.g., "div" vs "div2") */ void test_htmlFindFirstChild_exact_name_not_prefix(void) { xmlDocPtr doc = NULL; htmlNodePtr parent = NULL; create_doc_with_parent(&doc, &parent); htmlNodePtr notMatch = (htmlNodePtr)xmlNewNode(NULL, BAD_CAST "div2"); TEST_ASSERT_NOT_NULL(notMatch); xmlAddChild((xmlNodePtr)parent, (xmlNodePtr)notMatch); htmlNodePtr match = (htmlNodePtr)xmlNewNode(NULL, BAD_CAST "div"); TEST_ASSERT_NOT_NULL(match); xmlAddChild((xmlNodePtr)parent, (xmlNodePtr)match); htmlNodePtr res = test_htmlFindFirstChild(parent, "DIV"); TEST_ASSERT_NOT_NULL(res); TEST_ASSERT_EQUAL_PTR(match, res); xmlFreeDoc(doc); } /* Test: Child name uppercase vs. lowercase query (case-insensitive) */ void test_htmlFindFirstChild_child_uppercase_name(void) { xmlDocPtr doc = NULL; htmlNodePtr parent = NULL; create_doc_with_parent(&doc, &parent); htmlNodePtr child = (htmlNodePtr)xmlNewNode(NULL, BAD_CAST "SPAN"); TEST_ASSERT_NOT_NULL(child); xmlAddChild((xmlNodePtr)parent, (xmlNodePtr)child); htmlNodePtr res = test_htmlFindFirstChild(parent, "span"); TEST_ASSERT_NOT_NULL(res); TEST_ASSERT_EQUAL_PTR(child, res); xmlFreeDoc(doc); } int main(void) { /* Initialize libxml2 globally if desired; safe to omit for basic tree ops */ /* xmlInitParser(); */ UNITY_BEGIN(); RUN_TEST(test_htmlFindFirstChild_no_children_returns_NULL); RUN_TEST(test_htmlFindFirstChild_case_insensitive_first_match); RUN_TEST(test_htmlFindFirstChild_skips_non_element_nodes); RUN_TEST(test_htmlFindFirstChild_no_matching_child_returns_NULL); RUN_TEST(test_htmlFindFirstChild_exact_name_not_prefix); RUN_TEST(test_htmlFindFirstChild_child_uppercase_name); int ret = UNITY_END(); /* Cleanup libxml2 if initialized above */ /* xmlCleanupParser(); */ return ret; }