File size: 4,961 Bytes
6baed57 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
#include "unity/unity.h"
#include <libxml/HTMLparser.h>
#include <libxml/parser.h>
#include <libxml/xmlerror.h>
#include <libxml/encoding.h>
#include <string.h>
#include <stdlib.h>
/* Simple SAX state to record callbacks */
typedef struct {
int startCount;
int endCount;
int charsCount;
char collected[512];
size_t collectedLen;
} TestSAXState;
/* SAX callbacks */
static void test_startDocument(void *ctx) {
TestSAXState *st = (TestSAXState *)ctx;
st->startCount++;
}
static void test_endDocument(void *ctx) {
TestSAXState *st = (TestSAXState *)ctx;
st->endCount++;
}
static void test_characters(void *ctx, const xmlChar *ch, int len) {
TestSAXState *st = (TestSAXState *)ctx;
if (st->collectedLen + (size_t)len >= sizeof(st->collected))
len = (int)(sizeof(st->collected) - st->collectedLen - 1);
if (len > 0) {
memcpy(st->collected + st->collectedLen, ch, (size_t)len);
st->collectedLen += (size_t)len;
st->collected[st->collectedLen] = '\0';
}
st->charsCount++;
}
/* Helper to create a push parser context with our SAX handler and state */
static htmlParserCtxtPtr create_push_ctxt(TestSAXState *state) {
xmlSAXHandler sax;
memset(&sax, 0, sizeof(sax));
sax.startDocument = test_startDocument;
sax.endDocument = test_endDocument;
sax.characters = test_characters;
htmlParserCtxtPtr ctxt = htmlCreatePushParserCtxt(&sax, state, NULL, 0, NULL, XML_CHAR_ENCODING_NONE);
return ctxt;
}
/* Unity hooks */
void setUp(void) {
xmlInitParser();
}
void tearDown(void) {
/* Nothing specific per-test */
}
/* Tests */
static void test_htmlParseChunk_invalid_arguments(void) {
/* NULL context */
int ret = htmlParseChunk(NULL, "x", 1, 0);
TEST_ASSERT_EQUAL_INT(XML_ERR_ARGUMENT, ret);
/* Valid context for further invalid arg checks */
TestSAXState st;
memset(&st, 0, sizeof(st));
htmlParserCtxtPtr ctxt = create_push_ctxt(&st);
TEST_ASSERT_NOT_NULL(ctxt);
TEST_ASSERT_NOT_NULL(ctxt->input);
TEST_ASSERT_NOT_NULL(ctxt->input->buf);
/* Positive size with NULL chunk */
ret = htmlParseChunk(ctxt, NULL, 1, 0);
TEST_ASSERT_EQUAL_INT(XML_ERR_ARGUMENT, ret);
/* Negative size */
ret = htmlParseChunk(ctxt, "x", -5, 0);
TEST_ASSERT_EQUAL_INT(XML_ERR_ARGUMENT, ret);
htmlFreeParserCtxt(ctxt);
}
static void test_htmlParseChunk_partial_then_resume_starts_document(void) {
TestSAXState st;
memset(&st, 0, sizeof(st));
htmlParserCtxtPtr ctxt = create_push_ctxt(&st);
TEST_ASSERT_NOT_NULL(ctxt);
/* Push a single byte: not enough to leave XML_PARSER_START when terminate==0 */
int ret = htmlParseChunk(ctxt, "<", 1, 0);
TEST_ASSERT_EQUAL_INT(XML_ERR_OK, ret);
TEST_ASSERT_EQUAL_INT(0, st.startCount);
TEST_ASSERT_EQUAL_INT(0, st.endCount);
/* Push the remainder of a simple start tag */
ret = htmlParseChunk(ctxt, "html>", 5, 0);
TEST_ASSERT_EQUAL_INT(XML_ERR_OK, ret);
TEST_ASSERT_EQUAL_INT(1, st.startCount);
TEST_ASSERT_EQUAL_INT(0, st.endCount);
/* Push some character data to ensure characters callback fires */
st.collectedLen = 0;
st.collected[0] = '\0';
ret = htmlParseChunk(ctxt, "<body>Hi", 9, 0); /* includes <body>Hi */
TEST_ASSERT_EQUAL_INT(XML_ERR_OK, ret);
TEST_ASSERT_TRUE(st.charsCount >= 1);
TEST_ASSERT_NOT_NULL(strstr(st.collected, "Hi"));
htmlFreeParserCtxt(ctxt);
}
static void test_htmlParseChunk_terminate_calls_endDocument(void) {
TestSAXState st;
memset(&st, 0, sizeof(st));
htmlParserCtxtPtr ctxt = create_push_ctxt(&st);
TEST_ASSERT_NOT_NULL(ctxt);
/* Feed a minimal, not fully closed document */
const char *chunk1 = "<!DOCTYPE html><html><body>Hello";
int ret = htmlParseChunk(ctxt, chunk1, (int)strlen(chunk1), 0);
TEST_ASSERT_EQUAL_INT(XML_ERR_OK, ret);
TEST_ASSERT_EQUAL_INT(1, st.startCount);
TEST_ASSERT_EQUAL_INT(0, st.endCount);
/* Terminate: should auto-close and call endDocument */
ret = htmlParseChunk(ctxt, NULL, 0, 1);
TEST_ASSERT_EQUAL_INT(XML_ERR_OK, ret);
TEST_ASSERT_EQUAL_INT(1, st.endCount);
htmlFreeParserCtxt(ctxt);
}
static void test_htmlParseChunk_respects_stop_parser(void) {
TestSAXState st;
memset(&st, 0, sizeof(st));
htmlParserCtxtPtr ctxt = create_push_ctxt(&st);
TEST_ASSERT_NOT_NULL(ctxt);
/* Stop the parser via public API */
xmlStopParser(ctxt);
int ret = htmlParseChunk(ctxt, "<html>", 6, 0);
TEST_ASSERT_EQUAL_INT(XML_ERR_USER_STOP, ret);
htmlFreeParserCtxt(ctxt);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_htmlParseChunk_invalid_arguments);
RUN_TEST(test_htmlParseChunk_partial_then_resume_starts_document);
RUN_TEST(test_htmlParseChunk_terminate_calls_endDocument);
RUN_TEST(test_htmlParseChunk_respects_stop_parser);
int rc = UNITY_END();
xmlCleanupParser();
return rc;
} |