#include "unity/unity.h" #include #include #include #include #include #include /* Wrapper for the static function provided in the source module */ extern int test_htmlNodeInfoPush(htmlParserCtxtPtr ctxt, htmlParserNodeInfo *value); static xmlNodePtr make_dummy_node(const char *name) { return xmlNewNode(NULL, (const xmlChar *)name); } void setUp(void) { xmlInitParser(); } void tearDown(void) { xmlCleanupParser(); } /* Helper to compare two htmlParserNodeInfo records */ static void assert_nodeinfo_equal(const htmlParserNodeInfo *a, const htmlParserNodeInfo *b) { TEST_ASSERT_NOT_NULL(a); TEST_ASSERT_NOT_NULL(b); TEST_ASSERT_EQUAL_PTR(a->node, b->node); TEST_ASSERT_EQUAL_UINT64(a->begin_pos, b->begin_pos); TEST_ASSERT_EQUAL_UINT64(a->begin_line, b->begin_line); TEST_ASSERT_EQUAL_UINT64(a->end_pos, b->end_pos); TEST_ASSERT_EQUAL_UINT64(a->end_line, b->end_line); } /* Test: initial allocation when table is NULL and max=0 */ void test_htmlNodeInfoPush_initial_alloc_and_store(void) { htmlParserCtxtPtr ctxt = htmlNewParserCtxt(); TEST_ASSERT_NOT_NULL(ctxt); /* Force an empty stack state */ ctxt->nodeInfoTab = NULL; ctxt->nodeInfoMax = 0; ctxt->nodeInfoNr = 0; ctxt->nodeInfo = NULL; xmlNodePtr n1 = make_dummy_node("n1"); TEST_ASSERT_NOT_NULL(n1); htmlParserNodeInfo info1; memset(&info1, 0, sizeof(info1)); info1.node = n1; info1.begin_pos = 10; info1.begin_line = 2; info1.end_pos = 20; info1.end_line = 3; int ret = test_htmlNodeInfoPush(ctxt, &info1); /* ret can be 0 for first element; ensure push succeeded */ TEST_ASSERT_NOT_NULL(ctxt->nodeInfoTab); TEST_ASSERT_TRUE(ctxt->nodeInfoMax > 0); TEST_ASSERT_EQUAL_INT(1, ctxt->nodeInfoNr); TEST_ASSERT_EQUAL_PTR(&ctxt->nodeInfoTab[0], ctxt->nodeInfo); assert_nodeinfo_equal(&info1, &ctxt->nodeInfoTab[0]); xmlFreeNode(n1); htmlFreeParserCtxt(ctxt); } /* Test: push when there is already capacity (no growth path) */ void test_htmlNodeInfoPush_no_growth_needed(void) { htmlParserCtxtPtr ctxt = htmlNewParserCtxt(); TEST_ASSERT_NOT_NULL(ctxt); /* Pre-allocate capacity for 4 entries */ int cap = 4; ctxt->nodeInfoTab = (xmlParserNodeInfo *)xmlMalloc(sizeof(ctxt->nodeInfoTab[0]) * cap); TEST_ASSERT_NOT_NULL(ctxt->nodeInfoTab); ctxt->nodeInfoMax = cap; ctxt->nodeInfoNr = 0; ctxt->nodeInfo = NULL; xmlNodePtr n1 = make_dummy_node("a"); xmlNodePtr n2 = make_dummy_node("b"); TEST_ASSERT_NOT_NULL(n1); TEST_ASSERT_NOT_NULL(n2); htmlParserNodeInfo info1, info2; memset(&info1, 0, sizeof(info1)); memset(&info2, 0, sizeof(info2)); info1.node = n1; info1.begin_pos = 1; info1.begin_line = 1; info1.end_pos = 2; info1.end_line = 2; info2.node = n2; info2.begin_pos = 3; info2.begin_line = 3; info2.end_pos = 4; info2.end_line = 4; int ret0 = test_htmlNodeInfoPush(ctxt, &info1); TEST_ASSERT_EQUAL_INT(0, ret0); TEST_ASSERT_EQUAL_INT(1, ctxt->nodeInfoNr); TEST_ASSERT_EQUAL_PTR(&ctxt->nodeInfoTab[0], ctxt->nodeInfo); assert_nodeinfo_equal(&info1, &ctxt->nodeInfoTab[0]); int ret1 = test_htmlNodeInfoPush(ctxt, &info2); TEST_ASSERT_EQUAL_INT(1, ret1); TEST_ASSERT_EQUAL_INT(2, ctxt->nodeInfoNr); TEST_ASSERT_EQUAL_PTR(&ctxt->nodeInfoTab[1], ctxt->nodeInfo); assert_nodeinfo_equal(&info2, &ctxt->nodeInfoTab[1]); xmlFreeNode(n1); xmlFreeNode(n2); htmlFreeParserCtxt(ctxt); } /* Test: growth when full and preservation of existing entries */ void test_htmlNodeInfoPush_growth_and_preserve_existing(void) { htmlParserCtxtPtr ctxt = htmlNewParserCtxt(); TEST_ASSERT_NOT_NULL(ctxt); /* Capacity 1, already filled with one element */ ctxt->nodeInfoTab = (xmlParserNodeInfo *)xmlMalloc(sizeof(ctxt->nodeInfoTab[0]) * 1); TEST_ASSERT_NOT_NULL(ctxt->nodeInfoTab); ctxt->nodeInfoMax = 1; ctxt->nodeInfoNr = 1; ctxt->nodeInfo = &ctxt->nodeInfoTab[0]; xmlNodePtr n0 = make_dummy_node("first"); xmlNodePtr n1 = make_dummy_node("second"); TEST_ASSERT_NOT_NULL(n0); TEST_ASSERT_NOT_NULL(n1); htmlParserNodeInfo first, second; memset(&first, 0, sizeof(first)); memset(&second, 0, sizeof(second)); first.node = n0; first.begin_pos = 10; first.begin_line = 1; first.end_pos = 11; first.end_line = 2; second.node = n1; second.begin_pos = 20; second.begin_line = 3; second.end_pos = 21; second.end_line = 4; /* Initialize first entry */ ctxt->nodeInfoTab[0] = first; /* Push second; should trigger growth and append at index 1 */ int ret = test_htmlNodeInfoPush(ctxt, &second); TEST_ASSERT_EQUAL_INT(1, ret); TEST_ASSERT_TRUE(ctxt->nodeInfoMax > 1); TEST_ASSERT_EQUAL_INT(2, ctxt->nodeInfoNr); TEST_ASSERT_NOT_NULL(ctxt->nodeInfoTab); TEST_ASSERT_EQUAL_PTR(&ctxt->nodeInfoTab[1], ctxt->nodeInfo); assert_nodeinfo_equal(&first, &ctxt->nodeInfoTab[0]); assert_nodeinfo_equal(&second, &ctxt->nodeInfoTab[1]); xmlFreeNode(n0); xmlFreeNode(n1); htmlFreeParserCtxt(ctxt); } /* Test: multiple pushes to ensure repeated growth and correct indexing */ void test_htmlNodeInfoPush_multiple_pushes(void) { htmlParserCtxtPtr ctxt = htmlNewParserCtxt(); TEST_ASSERT_NOT_NULL(ctxt); /* Start with an empty stack */ ctxt->nodeInfoTab = NULL; ctxt->nodeInfoMax = 0; ctxt->nodeInfoNr = 0; ctxt->nodeInfo = NULL; const int count = 100; xmlNodePtr nodes[count]; for (int i = 0; i < count; i++) { nodes[i] = make_dummy_node("x"); TEST_ASSERT_NOT_NULL(nodes[i]); htmlParserNodeInfo info; memset(&info, 0, sizeof(info)); info.node = nodes[i]; info.begin_pos = (unsigned long)i; info.begin_line = (unsigned long)(i + 1); info.end_pos = (unsigned long)(2 * i); info.end_line = (unsigned long)(2 * i + 1); int ret = test_htmlNodeInfoPush(ctxt, &info); TEST_ASSERT_EQUAL_INT(i, ret); TEST_ASSERT_EQUAL_INT(i + 1, ctxt->nodeInfoNr); /* Verify the just-pushed element */ assert_nodeinfo_equal(&info, &ctxt->nodeInfoTab[i]); TEST_ASSERT_EQUAL_PTR(&ctxt->nodeInfoTab[i], ctxt->nodeInfo); } TEST_ASSERT_TRUE(ctxt->nodeInfoMax >= ctxt->nodeInfoNr); TEST_ASSERT_EQUAL_INT(count, ctxt->nodeInfoNr); /* Cleanup created nodes */ for (int i = 0; i < count; i++) { xmlFreeNode(nodes[i]); } htmlFreeParserCtxt(ctxt); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_htmlNodeInfoPush_initial_alloc_and_store); RUN_TEST(test_htmlNodeInfoPush_no_growth_needed); RUN_TEST(test_htmlNodeInfoPush_growth_and_preserve_existing); RUN_TEST(test_htmlNodeInfoPush_multiple_pushes); return UNITY_END(); }