| | #include "unity/unity.h" |
| | #include <libxml/HTMLparser.h> |
| | #include <libxml/parser.h> |
| | #include <libxml/encoding.h> |
| |
|
| | #include <string.h> |
| | #include <stdlib.h> |
| |
|
| | |
| | typedef struct { |
| | int locatorCount; |
| | int startCount; |
| | int endCount; |
| | int commentCount; |
| | int internalSubsetCount; |
| | char lastComment[256]; |
| | char internalSubsetName[256]; |
| | } TestSAXState; |
| |
|
| | |
| | static void onSetDocumentLocator(void *ctx, const xmlSAXLocator *loc) { |
| | TestSAXState *st = (TestSAXState *)ctx; |
| | (void)loc; |
| | if (st) st->locatorCount++; |
| | } |
| |
|
| | static void onStartDocument(void *ctx) { |
| | TestSAXState *st = (TestSAXState *)ctx; |
| | if (st) st->startCount++; |
| | } |
| |
|
| | static void onEndDocument(void *ctx) { |
| | TestSAXState *st = (TestSAXState *)ctx; |
| | if (st) st->endCount++; |
| | } |
| |
|
| | static void onComment(void *ctx, const xmlChar *value) { |
| | TestSAXState *st = (TestSAXState *)ctx; |
| | if (!st) return; |
| | st->commentCount++; |
| | if (value) { |
| | strncpy(st->lastComment, (const char *)value, sizeof(st->lastComment) - 1); |
| | st->lastComment[sizeof(st->lastComment) - 1] = '\0'; |
| | } else { |
| | st->lastComment[0] = '\0'; |
| | } |
| | } |
| |
|
| | static void onInternalSubset(void *ctx, |
| | const xmlChar *name, |
| | const xmlChar *ExternalID, |
| | const xmlChar *SystemID) { |
| | (void)ExternalID; |
| | (void)SystemID; |
| | TestSAXState *st = (TestSAXState *)ctx; |
| | if (!st) return; |
| | st->internalSubsetCount++; |
| | if (name) { |
| | strncpy(st->internalSubsetName, (const char *)name, sizeof(st->internalSubsetName) - 1); |
| | st->internalSubsetName[sizeof(st->internalSubsetName) - 1] = '\0'; |
| | } else { |
| | st->internalSubsetName[0] = '\0'; |
| | } |
| | } |
| |
|
| | |
| | static htmlParserCtxtPtr makeCtxtWithHTML(xmlSAXHandler *sax, void *userData, const char *html) { |
| | int len = (int)strlen(html); |
| | |
| | htmlParserCtxtPtr ctxt = htmlCreatePushParserCtxt(sax, userData, html, len, NULL, XML_CHAR_ENCODING_NONE); |
| | return ctxt; |
| | } |
| |
|
| | |
| | void setUp(void) { |
| | xmlInitParser(); |
| | } |
| |
|
| | void tearDown(void) { |
| | xmlCleanupParser(); |
| | } |
| |
|
| | |
| |
|
| | void test_htmlParseDocument_null_ctxt_returns_minus1(void) { |
| | int ret = htmlParseDocument(NULL); |
| | TEST_ASSERT_EQUAL_INT(-1, ret); |
| | } |
| |
|
| | void test_htmlParseDocument_null_input_in_ctxt_returns_minus1(void) { |
| | htmlParserCtxtPtr ctxt = htmlNewParserCtxt(); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| | |
| | TEST_ASSERT_NULL(ctxt->input); |
| |
|
| | int ret = htmlParseDocument(ctxt); |
| | TEST_ASSERT_EQUAL_INT(-1, ret); |
| |
|
| | htmlFreeParserCtxt(ctxt); |
| | } |
| |
|
| | void test_htmlParseDocument_calls_locator_start_end(void) { |
| | const char *html = "<html><body>Hello</body></html>"; |
| | TestSAXState st = {0}; |
| | xmlSAXHandler sax; |
| | memset(&sax, 0, sizeof(sax)); |
| | sax.setDocumentLocator = onSetDocumentLocator; |
| | sax.startDocument = onStartDocument; |
| | sax.endDocument = onEndDocument; |
| |
|
| | htmlParserCtxtPtr ctxt = makeCtxtWithHTML(&sax, &st, html); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | int ret = htmlParseDocument(ctxt); |
| | TEST_ASSERT_EQUAL_INT(0, ret); |
| |
|
| | TEST_ASSERT_EQUAL_INT(1, st.locatorCount); |
| | TEST_ASSERT_EQUAL_INT(1, st.startCount); |
| | TEST_ASSERT_EQUAL_INT(1, st.endCount); |
| |
|
| | htmlFreeParserCtxt(ctxt); |
| | } |
| |
|
| | void test_htmlParseDocument_parses_leading_comment_and_doctype(void) { |
| | const char *html = "<!--A--><!DOCTYPE html><html><head></head><body></body></html>"; |
| | TestSAXState st = {0}; |
| | xmlSAXHandler sax; |
| | memset(&sax, 0, sizeof(sax)); |
| | sax.setDocumentLocator = onSetDocumentLocator; |
| | sax.startDocument = onStartDocument; |
| | sax.endDocument = onEndDocument; |
| | sax.comment = onComment; |
| | sax.internalSubset = onInternalSubset; |
| |
|
| | htmlParserCtxtPtr ctxt = makeCtxtWithHTML(&sax, &st, html); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | int ret = htmlParseDocument(ctxt); |
| | TEST_ASSERT_EQUAL_INT(0, ret); |
| |
|
| | TEST_ASSERT_EQUAL_INT(1, st.startCount); |
| | TEST_ASSERT_EQUAL_INT(1, st.endCount); |
| | TEST_ASSERT_EQUAL_INT(1, st.commentCount); |
| | TEST_ASSERT_EQUAL_STRING("A", st.lastComment); |
| | TEST_ASSERT_EQUAL_INT(1, st.internalSubsetCount); |
| | TEST_ASSERT_EQUAL_STRING("html", st.internalSubsetName); |
| |
|
| | htmlFreeParserCtxt(ctxt); |
| | } |
| |
|
| | void test_htmlParseDocument_parses_bogus_comment_and_pi_as_comments(void) { |
| | |
| | const char *html = "<!bogus><?pi-stuff?><html></html>"; |
| | TestSAXState st = {0}; |
| | xmlSAXHandler sax; |
| | memset(&sax, 0, sizeof(sax)); |
| | sax.startDocument = onStartDocument; |
| | sax.endDocument = onEndDocument; |
| | sax.comment = onComment; |
| |
|
| | htmlParserCtxtPtr ctxt = makeCtxtWithHTML(&sax, &st, html); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | int ret = htmlParseDocument(ctxt); |
| | TEST_ASSERT_EQUAL_INT(0, ret); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_INT(2, st.commentCount); |
| | TEST_ASSERT_EQUAL_INT(1, st.startCount); |
| | TEST_ASSERT_EQUAL_INT(1, st.endCount); |
| |
|
| | htmlFreeParserCtxt(ctxt); |
| | } |
| |
|
| | void test_htmlParseDocument_disableSAX_suppresses_callbacks(void) { |
| | const char *html = "<!--X--><!DOCTYPE html><html></html>"; |
| | TestSAXState st = {0}; |
| | xmlSAXHandler sax; |
| | memset(&sax, 0, sizeof(sax)); |
| | sax.setDocumentLocator = onSetDocumentLocator; |
| | sax.startDocument = onStartDocument; |
| | sax.endDocument = onEndDocument; |
| | sax.comment = onComment; |
| | sax.internalSubset = onInternalSubset; |
| |
|
| | htmlParserCtxtPtr ctxt = makeCtxtWithHTML(&sax, &st, html); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | |
| | ctxt->disableSAX = 1; |
| |
|
| | int ret = htmlParseDocument(ctxt); |
| | |
| | TEST_ASSERT_EQUAL_INT(0, ret); |
| |
|
| | TEST_ASSERT_EQUAL_INT(0, st.locatorCount); |
| | TEST_ASSERT_EQUAL_INT(0, st.startCount); |
| | TEST_ASSERT_EQUAL_INT(0, st.endCount); |
| | TEST_ASSERT_EQUAL_INT(0, st.commentCount); |
| | TEST_ASSERT_EQUAL_INT(0, st.internalSubsetCount); |
| |
|
| | htmlFreeParserCtxt(ctxt); |
| | } |
| |
|
| | |
| | int main(void) { |
| | UNITY_BEGIN(); |
| | RUN_TEST(test_htmlParseDocument_null_ctxt_returns_minus1); |
| | RUN_TEST(test_htmlParseDocument_null_input_in_ctxt_returns_minus1); |
| | RUN_TEST(test_htmlParseDocument_calls_locator_start_end); |
| | RUN_TEST(test_htmlParseDocument_parses_leading_comment_and_doctype); |
| | RUN_TEST(test_htmlParseDocument_parses_bogus_comment_and_pi_as_comments); |
| | RUN_TEST(test_htmlParseDocument_disableSAX_suppresses_callbacks); |
| | return UNITY_END(); |
| | } |