|
|
#include "unity/unity.h" |
|
|
#include <libxml/HTMLparser.h> |
|
|
|
|
|
#include <string.h> |
|
|
#include <stdlib.h> |
|
|
#include <stdio.h> |
|
|
|
|
|
|
|
|
extern void test_htmlParseDocTypeDecl(htmlParserCtxtPtr ctxt); |
|
|
|
|
|
|
|
|
static int cap_called = 0; |
|
|
static xmlChar *cap_name = NULL; |
|
|
static xmlChar *cap_public = NULL; |
|
|
static xmlChar *cap_system = NULL; |
|
|
|
|
|
static void capture_reset(void) { |
|
|
if (cap_name) { xmlFree(cap_name); cap_name = NULL; } |
|
|
if (cap_public) { xmlFree(cap_public); cap_public = NULL; } |
|
|
if (cap_system) { xmlFree(cap_system); cap_system = NULL; } |
|
|
cap_called = 0; |
|
|
} |
|
|
|
|
|
static void capture_internalSubset(void *ctx, |
|
|
const xmlChar *name, |
|
|
const xmlChar *ExternalID, |
|
|
const xmlChar *SystemID) { |
|
|
(void)ctx; |
|
|
cap_called++; |
|
|
cap_name = (name != NULL) ? xmlStrdup(name) : NULL; |
|
|
cap_public = (ExternalID != NULL) ? xmlStrdup(ExternalID) : NULL; |
|
|
cap_system = (SystemID != NULL) ? xmlStrdup(SystemID) : NULL; |
|
|
} |
|
|
|
|
|
static void attach_sax(htmlParserCtxtPtr ctxt, xmlSAXHandler *sax) { |
|
|
memset(sax, 0, sizeof(*sax)); |
|
|
sax->initialized = XML_SAX2_MAGIC; |
|
|
sax->internalSubset = capture_internalSubset; |
|
|
|
|
|
ctxt->sax = sax; |
|
|
ctxt->disableSAX = 0; |
|
|
} |
|
|
|
|
|
static htmlParserCtxtPtr make_ctxt(const char *input, int options, xmlSAXHandler *saxOut) { |
|
|
htmlParserCtxtPtr ctxt = htmlCreateMemoryParserCtxt(input, (int)strlen(input)); |
|
|
TEST_ASSERT_NOT_NULL_MESSAGE(ctxt, "Failed to create HTML parser context"); |
|
|
if (options != 0) { |
|
|
int rc = htmlCtxtUseOptions(ctxt, options); |
|
|
(void)rc; |
|
|
} |
|
|
attach_sax(ctxt, saxOut); |
|
|
return ctxt; |
|
|
} |
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
xmlInitParser(); |
|
|
capture_reset(); |
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
capture_reset(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
static void assert_xml_equals(const xmlChar *actual, const char *expected) { |
|
|
TEST_ASSERT_NOT_NULL_MESSAGE(actual, "Expected non-NULL xmlChar*"); |
|
|
TEST_ASSERT_TRUE_MESSAGE(xmlStrEqual(actual, BAD_CAST expected), "xmlChar string mismatch"); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlParseDocTypeDecl_basic_invokes_callback(void) { |
|
|
const char *buf = "<!DOCTYPE html>"; |
|
|
xmlSAXHandler sax; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt(buf, 0, &sax); |
|
|
|
|
|
test_htmlParseDocTypeDecl(ctxt); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(1, cap_called); |
|
|
assert_xml_equals(cap_name, "html"); |
|
|
TEST_ASSERT_NULL(cap_public); |
|
|
TEST_ASSERT_NULL(cap_system); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlParseDocTypeDecl_html5_lowers_name(void) { |
|
|
const char *buf = "<!DOCTYPE HTML>"; |
|
|
xmlSAXHandler sax; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt(buf, HTML_PARSE_HTML5, &sax); |
|
|
|
|
|
test_htmlParseDocTypeDecl(ctxt); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(1, cap_called); |
|
|
assert_xml_equals(cap_name, "html"); |
|
|
TEST_ASSERT_NULL(cap_public); |
|
|
TEST_ASSERT_NULL(cap_system); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlParseDocTypeDecl_no_html5_preserves_case(void) { |
|
|
const char *buf = "<!DOCTYPE Html>"; |
|
|
xmlSAXHandler sax; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt(buf, 0, &sax); |
|
|
|
|
|
test_htmlParseDocTypeDecl(ctxt); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(1, cap_called); |
|
|
assert_xml_equals(cap_name, "Html"); |
|
|
TEST_ASSERT_NULL(cap_public); |
|
|
TEST_ASSERT_NULL(cap_system); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlParseDocTypeDecl_public_and_system_ids(void) { |
|
|
const char *pub = "-//W3C//DTD HTML 4.01//EN"; |
|
|
const char *sys = "http://www.w3.org/TR/html4/strict.dtd"; |
|
|
char buf[512]; |
|
|
snprintf(buf, sizeof(buf), "<!DOCTYPE html PUBLIC \"%s\" \"%s\">", pub, sys); |
|
|
|
|
|
xmlSAXHandler sax; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt(buf, 0, &sax); |
|
|
|
|
|
test_htmlParseDocTypeDecl(ctxt); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(1, cap_called); |
|
|
assert_xml_equals(cap_name, "html"); |
|
|
assert_xml_equals(cap_public, pub); |
|
|
assert_xml_equals(cap_system, sys); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlParseDocTypeDecl_system_only(void) { |
|
|
const char *sys = "about:legacy-compat"; |
|
|
char buf[256]; |
|
|
snprintf(buf, sizeof(buf), "<!DOCTYPE html SYSTEM \"%s\">", sys); |
|
|
|
|
|
xmlSAXHandler sax; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt(buf, 0, &sax); |
|
|
|
|
|
test_htmlParseDocTypeDecl(ctxt); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(1, cap_called); |
|
|
assert_xml_equals(cap_name, "html"); |
|
|
TEST_ASSERT_NULL(cap_public); |
|
|
assert_xml_equals(cap_system, sys); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlParseDocTypeDecl_missing_name(void) { |
|
|
const char *buf = "<!DOCTYPE>"; |
|
|
xmlSAXHandler sax; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt(buf, 0, &sax); |
|
|
|
|
|
test_htmlParseDocTypeDecl(ctxt); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(1, cap_called); |
|
|
TEST_ASSERT_NULL(cap_name); |
|
|
TEST_ASSERT_NULL(cap_public); |
|
|
TEST_ASSERT_NULL(cap_system); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlParseDocTypeDecl_bogus_missing_gt(void) { |
|
|
const char *buf = "<!DOCTYPE html PUBLIC \"abc\" \"def\""; |
|
|
xmlSAXHandler sax; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt(buf, 0, &sax); |
|
|
|
|
|
test_htmlParseDocTypeDecl(ctxt); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(1, cap_called); |
|
|
assert_xml_equals(cap_name, "html"); |
|
|
assert_xml_equals(cap_public, "abc"); |
|
|
assert_xml_equals(cap_system, "def"); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlParseDocTypeDecl_advances_past_gt(void) { |
|
|
const char *buf = "<!DOCTYPE html><html>"; |
|
|
xmlSAXHandler sax; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt(buf, 0, &sax); |
|
|
|
|
|
test_htmlParseDocTypeDecl(ctxt); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(1, cap_called); |
|
|
|
|
|
TEST_ASSERT_NOT_NULL(ctxt->input); |
|
|
TEST_ASSERT_NOT_NULL(ctxt->input->cur); |
|
|
TEST_ASSERT_TRUE_MESSAGE(*(ctxt->input->cur) == '<', "Parser cursor not advanced past '>' as expected"); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
int main(void) { |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_htmlParseDocTypeDecl_basic_invokes_callback); |
|
|
RUN_TEST(test_htmlParseDocTypeDecl_html5_lowers_name); |
|
|
RUN_TEST(test_htmlParseDocTypeDecl_no_html5_preserves_case); |
|
|
RUN_TEST(test_htmlParseDocTypeDecl_public_and_system_ids); |
|
|
RUN_TEST(test_htmlParseDocTypeDecl_system_only); |
|
|
RUN_TEST(test_htmlParseDocTypeDecl_missing_name); |
|
|
RUN_TEST(test_htmlParseDocTypeDecl_bogus_missing_gt); |
|
|
RUN_TEST(test_htmlParseDocTypeDecl_advances_past_gt); |
|
|
return UNITY_END(); |
|
|
} |