|
|
#include "unity/unity.h" |
|
|
#include <libxml/HTMLparser.h> |
|
|
|
|
|
#include <string.h> |
|
|
#include <stdlib.h> |
|
|
#include <stdio.h> |
|
|
|
|
|
|
|
|
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'; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
static xmlSAXHandler gSAX; |
|
|
|
|
|
void setUp(void) { |
|
|
memset(&gSAX, 0, sizeof(gSAX)); |
|
|
gSAX.startElement = onStartElement; |
|
|
gSAX.endElement = onEndElement; |
|
|
gSAX.characters = onCharacters; |
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_htmlParseElement_null_context_safe(void) { |
|
|
|
|
|
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) { |
|
|
|
|
|
run_case("<br/>", 0, "[S:br][E:br]"); |
|
|
} |
|
|
|
|
|
void test_htmlParseElement_self_closing_xml_style_html5(void) { |
|
|
|
|
|
run_case("<br/>", HTML_PARSE_HTML5, "[S:br]"); |
|
|
} |
|
|
|
|
|
void test_htmlParseElement_dtd_empty_element_non_html5(void) { |
|
|
|
|
|
run_case("<br>", 0, "[S:br][E:br]"); |
|
|
} |
|
|
|
|
|
void test_htmlParseElement_dtd_empty_element_html5(void) { |
|
|
|
|
|
run_case("<br>", HTML_PARSE_HTML5, "[S:br]"); |
|
|
} |
|
|
|
|
|
void test_htmlParseElement_auto_close_on_eof_non_html5(void) { |
|
|
|
|
|
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) { |
|
|
|
|
|
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(); |
|
|
} |