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