#include "unity/unity.h" #include #include #include #include #include #include /* Wrapper prototype provided by the source module for the static function */ int test_htmlInitParserCtxt(htmlParserCtxtPtr ctxt, const htmlSAXHandler *sax, void *userData); /* Helpers for cleanup of allocations performed by htmlInitParserCtxt */ static void cleanup_ctxt_allocs(htmlParserCtxt *ctxt) { if (ctxt == NULL) return; if (ctxt->sax) { xmlFree((void *)ctxt->sax); ctxt->sax = NULL; } if (ctxt->inputTab) { xmlFree((void *)ctxt->inputTab); ctxt->inputTab = NULL; ctxt->inputMax = 0; ctxt->inputNr = 0; } if (ctxt->nodeTab) { xmlFree((void *)ctxt->nodeTab); ctxt->nodeTab = NULL; ctxt->nodeMax = 0; ctxt->nodeNr = 0; } if (ctxt->nameTab) { xmlFree((void *)ctxt->nameTab); ctxt->nameTab = NULL; ctxt->nameMax = 0; ctxt->nameNr = 0; } if (ctxt->dict) { xmlDictFree(ctxt->dict); ctxt->dict = NULL; } } /* Memory hook machinery to simulate allocation failure */ static int g_fail_alloc_enable = 0; static int g_fail_alloc_always = 0; static int g_alloc_call_count = 0; static void myXmlFree(void *ptr) { free(ptr); } static void *myXmlMalloc(size_t size) { if (g_fail_alloc_enable && (g_fail_alloc_always || (g_alloc_call_count++ == 0))) { return NULL; } return malloc(size); } static void *myXmlRealloc(void *ptr, size_t size) { if (g_fail_alloc_enable && g_fail_alloc_always) { return NULL; } return realloc(ptr, size); } static char *myXmlStrdup(const char *str) { if (g_fail_alloc_enable && g_fail_alloc_always) { return NULL; } size_t len = strlen(str) + 1; char *p = (char *)malloc(len); if (p) memcpy(p, str, len); return p; } static xmlFreeFunc oldFreeF = NULL; static xmlMallocFunc oldMallocF = NULL; static xmlReallocFunc oldReallocF = NULL; static xmlStrdupFunc oldStrdupF = NULL; static void enable_alloc_failure_always(void) { xmlMemGet(&oldFreeF, &oldMallocF, &oldReallocF, &oldStrdupF); g_fail_alloc_enable = 1; g_fail_alloc_always = 1; g_alloc_call_count = 0; xmlMemSetup(myXmlFree, myXmlMalloc, myXmlRealloc, myXmlStrdup); } static void restore_alloc_functions(void) { xmlMemSetup(oldFreeF, oldMallocF, oldReallocF, oldStrdupF); g_fail_alloc_enable = 0; g_fail_alloc_always = 0; g_alloc_call_count = 0; } void setUp(void) { /* Ensure libxml is initialized */ xmlInitParser(); } void tearDown(void) { /* Cleanup global parser state (safe between tests) */ xmlCleanupParser(); } /* Test: NULL context should return error (-1) */ void test_htmlInitParserCtxt_null_context_returns_error(void) { int rc = test_htmlInitParserCtxt(NULL, NULL, NULL); TEST_ASSERT_EQUAL_INT(-1, rc); } /* Test: Initialize with default SAX (sax == NULL); userData must be ctxt; key fields set */ void test_htmlInitParserCtxt_init_with_default_sax(void) { htmlParserCtxt ctxt; /* Pass non-NULL userData to ensure it's ignored when sax == NULL */ int dummy = 123; int rc = test_htmlInitParserCtxt(&ctxt, NULL, &dummy); TEST_ASSERT_EQUAL_INT(0, rc); /* Basic allocations */ TEST_ASSERT_NOT_NULL(ctxt.dict); TEST_ASSERT_NOT_NULL(ctxt.sax); TEST_ASSERT_NOT_NULL(ctxt.inputTab); TEST_ASSERT_NOT_NULL(ctxt.nodeTab); TEST_ASSERT_NOT_NULL(ctxt.nameTab); /* Input stack state */ TEST_ASSERT_EQUAL_INT(0, ctxt.inputNr); TEST_ASSERT_EQUAL_INT(1, ctxt.inputMax); TEST_ASSERT_NULL(ctxt.input); /* Node and name stacks */ TEST_ASSERT_EQUAL_INT(0, ctxt.nodeNr); TEST_ASSERT_TRUE(ctxt.nodeMax == 1 || ctxt.nodeMax == 10); TEST_ASSERT_NULL(ctxt.node); TEST_ASSERT_EQUAL_INT(0, ctxt.nameNr); TEST_ASSERT_TRUE(ctxt.nameMax == 1 || ctxt.nameMax == 10); TEST_ASSERT_NULL(ctxt.name); /* Document and flags */ TEST_ASSERT_NULL(ctxt.myDoc); TEST_ASSERT_EQUAL_INT(1, ctxt.wellFormed); TEST_ASSERT_EQUAL_INT(0, ctxt.replaceEntities); TEST_ASSERT_EQUAL_INT(xmlKeepBlanksDefaultValue, ctxt.keepBlanks); TEST_ASSERT_EQUAL_INT(0, ctxt.validate); TEST_ASSERT_EQUAL_INT(0, ctxt.checkIndex); TEST_ASSERT_EQUAL_INT(0, ctxt.record_info); TEST_ASSERT_EQUAL_INT(-1, ctxt.standalone); /* vctxt sanity */ TEST_ASSERT_EQUAL_PTR(&ctxt, ctxt.vctxt.userData); TEST_ASSERT_NOT_NULL(ctxt.vctxt.error); TEST_ASSERT_NOT_NULL(ctxt.vctxt.warning); /* Default SAX handler should be initialized */ TEST_ASSERT_NOT_NULL(ctxt.sax); TEST_ASSERT_TRUE(((htmlSAXHandler *)ctxt.sax)->initialized != 0); /* userData must be ctxt when sax == NULL */ TEST_ASSERT_EQUAL_PTR(&ctxt, ctxt.userData); cleanup_ctxt_allocs(&ctxt); } /* Test: Initialize with custom SAX handler and explicit userData */ void test_htmlInitParserCtxt_init_with_custom_sax_and_userData(void) { htmlParserCtxt ctxt; htmlSAXHandler customSax; memset(&customSax, 0, sizeof(customSax)); customSax.initialized = 0x1234; /* marker */ int userToken = 42; void *ud = &userToken; int rc = test_htmlInitParserCtxt(&ctxt, &customSax, ud); TEST_ASSERT_EQUAL_INT(0, rc); TEST_ASSERT_NOT_NULL(ctxt.dict); TEST_ASSERT_NOT_NULL(ctxt.sax); TEST_ASSERT_NOT_NULL(ctxt.inputTab); TEST_ASSERT_NOT_NULL(ctxt.nodeTab); TEST_ASSERT_NOT_NULL(ctxt.nameTab); /* Copied SAX handler must be byte-wise identical */ TEST_ASSERT_EQUAL_INT(0, memcmp(ctxt.sax, &customSax, sizeof(customSax))); /* userData should equal the provided pointer when sax != NULL */ TEST_ASSERT_EQUAL_PTR(ud, ctxt.userData); /* Stack sizes sane */ TEST_ASSERT_TRUE(ctxt.nodeMax == 1 || ctxt.nodeMax == 10); TEST_ASSERT_TRUE(ctxt.nameMax == 1 || ctxt.nameMax == 10); cleanup_ctxt_allocs(&ctxt); } /* Test: Initialize with custom SAX handler and NULL userData: userData must default to ctxt */ void test_htmlInitParserCtxt_init_with_custom_sax_null_userData_defaults_to_ctxt(void) { htmlParserCtxt ctxt; htmlSAXHandler customSax; memset(&customSax, 0, sizeof(customSax)); customSax.initialized = 777; int rc = test_htmlInitParserCtxt(&ctxt, &customSax, NULL); TEST_ASSERT_EQUAL_INT(0, rc); TEST_ASSERT_NOT_NULL(ctxt.sax); TEST_ASSERT_EQUAL_INT(0, memcmp(ctxt.sax, &customSax, sizeof(customSax))); TEST_ASSERT_EQUAL_PTR(&ctxt, ctxt.userData); cleanup_ctxt_allocs(&ctxt); } /* Test: Allocation failure path returns error (-1) */ void test_htmlInitParserCtxt_allocation_failure_returns_error(void) { enable_alloc_failure_always(); htmlParserCtxt ctxt; int rc = test_htmlInitParserCtxt(&ctxt, NULL, NULL); restore_alloc_functions(); TEST_ASSERT_EQUAL_INT(-1, rc); /* Nothing to clean up; initialization failed before allocations were assigned */ } int main(void) { UNITY_BEGIN(); RUN_TEST(test_htmlInitParserCtxt_null_context_returns_error); RUN_TEST(test_htmlInitParserCtxt_init_with_default_sax); RUN_TEST(test_htmlInitParserCtxt_init_with_custom_sax_and_userData); RUN_TEST(test_htmlInitParserCtxt_init_with_custom_sax_null_userData_defaults_to_ctxt); RUN_TEST(test_htmlInitParserCtxt_allocation_failure_returns_error); return UNITY_END(); }