#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();
}