File size: 4,493 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 |
#include "unity/unity.h"
#include <libxml/HTMLparser.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
/* Simple event logger for SAX callbacks */
typedef struct {
char buf[2048];
} EventLog;
static void log_append(EventLog *log, const char *s) {
size_t cur = strlen(log->buf);
size_t add = strlen(s);
if (cur + add >= sizeof(log->buf)) {
add = sizeof(log->buf) - cur - 1;
}
if (add > 0) {
memcpy(log->buf + cur, s, add);
log->buf[cur + add] = '\0';
}
}
/* SAX callbacks we care about */
static void onStartElement(void *ctx, const xmlChar *name, const xmlChar **atts) {
(void)atts;
EventLog *log = (EventLog *)ctx;
char tmp[256];
snprintf(tmp, sizeof(tmp), "[S:%s]", (const char *)name);
log_append(log, tmp);
}
static void onEndElement(void *ctx, const xmlChar *name) {
EventLog *log = (EventLog *)ctx;
char tmp[256];
snprintf(tmp, sizeof(tmp), "[E:%s]", (const char *)name);
log_append(log, tmp);
}
static void onCharacters(void *ctx, const xmlChar *ch, int len) {
EventLog *log = (EventLog *)ctx;
if (len <= 0)
return;
char tmp[512];
int copy = len;
if (copy > (int)sizeof(tmp) - 1)
copy = (int)sizeof(tmp) - 1;
memcpy(tmp, ch, (size_t)copy);
tmp[copy] = '\0';
char out[600];
snprintf(out, sizeof(out), "[T:%s]", tmp);
log_append(log, out);
}
/* Global SAX handler; configured in setUp */
static xmlSAXHandler gSAX;
void setUp(void) {
memset(&gSAX, 0, sizeof(gSAX));
gSAX.startElement = onStartElement;
gSAX.endElement = onEndElement;
gSAX.characters = onCharacters;
}
void tearDown(void) {
/* nothing */
}
/* Helper to run one parsing case through htmlParseElement and check events */
static void run_case(const char *html, int options, const char *expectedLog) {
EventLog log;
log.buf[0] = '\0';
htmlParserCtxtPtr ctxt = htmlCreateMemoryParserCtxt(html, (int)strlen(html));
TEST_ASSERT_NOT_NULL_MESSAGE(ctxt, "htmlCreateMemoryParserCtxt returned NULL");
ctxt->sax = &gSAX;
ctxt->userData = &log;
ctxt->options = options;
htmlParseElement(ctxt);
TEST_ASSERT_EQUAL_STRING_MESSAGE(expectedLog, log.buf, "Unexpected SAX event sequence");
htmlFreeParserCtxt(ctxt);
}
/* Tests */
void test_htmlParseElement_null_context_safe(void) {
/* Should be a no-op and must not crash */
htmlParseElement(NULL);
TEST_ASSERT_TRUE(1);
}
void test_htmlParseElement_empty_input_no_events(void) {
run_case("", 0, "");
}
void test_htmlParseElement_parses_simple_element(void) {
run_case("<p>hi</p>", 0, "[S:p][T:hi][E:p]");
}
void test_htmlParseElement_self_closing_xml_style_non_html5(void) {
/* In non-HTML5 mode, '/>' triggers an explicit endElement */
run_case("<br/>", 0, "[S:br][E:br]");
}
void test_htmlParseElement_self_closing_xml_style_html5(void) {
/* In HTML5 mode, '/>' does NOT call endElement in htmlParseElementInternal */
run_case("<br/>", HTML_PARSE_HTML5, "[S:br]");
}
void test_htmlParseElement_dtd_empty_element_non_html5(void) {
/* DTD-empty element like <br> should auto-close in non-HTML5 mode */
run_case("<br>", 0, "[S:br][E:br]");
}
void test_htmlParseElement_dtd_empty_element_html5(void) {
/* In HTML5 mode, no endElement is emitted for DTD-empty element in this path */
run_case("<br>", HTML_PARSE_HTML5, "[S:br]");
}
void test_htmlParseElement_auto_close_on_eof_non_html5(void) {
/* Unclosed nested elements should be auto-closed at end of input in non-HTML5 mode */
run_case("<div><span>t", 0, "[S:div][S:span][T:t][E:span][E:div]");
}
void test_htmlParseElement_auto_close_on_eof_html5(void) {
/* In HTML5 mode, htmlAutoCloseOnEnd() is a no-op, so no endElement callbacks */
run_case("<div><span>t", HTML_PARSE_HTML5, "[S:div][S:span][T:t]");
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_htmlParseElement_null_context_safe);
RUN_TEST(test_htmlParseElement_empty_input_no_events);
RUN_TEST(test_htmlParseElement_parses_simple_element);
RUN_TEST(test_htmlParseElement_self_closing_xml_style_non_html5);
RUN_TEST(test_htmlParseElement_self_closing_xml_style_html5);
RUN_TEST(test_htmlParseElement_dtd_empty_element_non_html5);
RUN_TEST(test_htmlParseElement_dtd_empty_element_html5);
RUN_TEST(test_htmlParseElement_auto_close_on_eof_non_html5);
RUN_TEST(test_htmlParseElement_auto_close_on_eof_html5);
return UNITY_END();
} |