|
|
#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(); |
|
|
} |