| | #include "unity/unity.h" |
| | #include <libxml/HTMLparser.h> |
| |
|
| | #include <stdlib.h> |
| | #include <string.h> |
| |
|
| | |
| | extern void test_htmlCheckImplied(htmlParserCtxtPtr ctxt, const xmlChar *newtag); |
| |
|
| | typedef struct { |
| | char **names; |
| | int count; |
| | int capacity; |
| | } Capture; |
| |
|
| | static char *xstrdup(const char *s) { |
| | if (s == NULL) return NULL; |
| | size_t len = strlen(s) + 1; |
| | char *d = (char *)malloc(len); |
| | if (d) memcpy(d, s, len); |
| | return d; |
| | } |
| |
|
| | static void cap_init(Capture *cap) { |
| | cap->names = NULL; |
| | cap->count = 0; |
| | cap->capacity = 0; |
| | } |
| |
|
| | static void cap_add(Capture *cap, const char *name) { |
| | if (cap->count == cap->capacity) { |
| | int newcap = (cap->capacity == 0) ? 4 : cap->capacity * 2; |
| | char **nn = (char **)realloc(cap->names, (size_t)newcap * sizeof(char *)); |
| | if (!nn) return; |
| | cap->names = nn; |
| | cap->capacity = newcap; |
| | } |
| | cap->names[cap->count++] = xstrdup(name); |
| | } |
| |
|
| | static void cap_free(Capture *cap) { |
| | if (cap->names) { |
| | for (int i = 0; i < cap->count; i++) { |
| | free(cap->names[i]); |
| | } |
| | free(cap->names); |
| | } |
| | cap->names = NULL; |
| | cap->count = 0; |
| | cap->capacity = 0; |
| | } |
| |
|
| | static void testStartElement(void *userData, const xmlChar *name, const xmlChar **atts) { |
| | (void)atts; |
| | Capture *cap = (Capture *)userData; |
| | cap_add(cap, (const char *)name); |
| | } |
| |
|
| | static htmlParserCtxtPtr new_ctxt(Capture *cap, xmlSAXHandlerPtr *out_sax) { |
| | xmlSAXHandler *sax = (xmlSAXHandler *)calloc(1, sizeof(xmlSAXHandler)); |
| | if (sax == NULL) return NULL; |
| | memset(sax, 0, sizeof(xmlSAXHandler)); |
| | sax->startElement = testStartElement; |
| |
|
| | htmlParserCtxtPtr ctxt = htmlCreatePushParserCtxt(sax, cap, "", 0, NULL, XML_CHAR_ENCODING_NONE); |
| | if (out_sax) *out_sax = sax; |
| | return ctxt; |
| | } |
| |
|
| | static void free_ctxt(htmlParserCtxtPtr ctxt, xmlSAXHandlerPtr sax, Capture *cap) { |
| | if (ctxt) htmlFreeParserCtxt(ctxt); |
| | if (sax) free(sax); |
| | if (cap) cap_free(cap); |
| | } |
| |
|
| | |
| | void setUp(void) { |
| | |
| | } |
| |
|
| | void tearDown(void) { |
| | |
| | } |
| |
|
| | |
| |
|
| | void test_htmlCheckImplied_with_p_generates_html_and_body(void) { |
| | Capture cap; cap_init(&cap); |
| | xmlSAXHandlerPtr sax = NULL; |
| | htmlParserCtxtPtr ctxt = new_ctxt(&cap, &sax); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"p"); |
| |
|
| | TEST_ASSERT_EQUAL_INT(2, cap.count); |
| | TEST_ASSERT_NOT_NULL(cap.names[0]); |
| | TEST_ASSERT_NOT_NULL(cap.names[1]); |
| | TEST_ASSERT_EQUAL_STRING("html", cap.names[0]); |
| | TEST_ASSERT_EQUAL_STRING("body", cap.names[1]); |
| |
|
| | free_ctxt(ctxt, sax, &cap); |
| | } |
| |
|
| | void test_htmlCheckImplied_with_head_generates_only_html(void) { |
| | Capture cap; cap_init(&cap); |
| | xmlSAXHandlerPtr sax = NULL; |
| | htmlParserCtxtPtr ctxt = new_ctxt(&cap, &sax); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"head"); |
| |
|
| | TEST_ASSERT_EQUAL_INT(1, cap.count); |
| | TEST_ASSERT_EQUAL_STRING("html", cap.names[0]); |
| |
|
| | free_ctxt(ctxt, sax, &cap); |
| | } |
| |
|
| | void test_htmlCheckImplied_with_html_generates_nothing(void) { |
| | Capture cap; cap_init(&cap); |
| | xmlSAXHandlerPtr sax = NULL; |
| | htmlParserCtxtPtr ctxt = new_ctxt(&cap, &sax); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"html"); |
| |
|
| | TEST_ASSERT_EQUAL_INT(0, cap.count); |
| |
|
| | free_ctxt(ctxt, sax, &cap); |
| | } |
| |
|
| | void test_htmlCheckImplied_with_script_generates_html_and_head(void) { |
| | Capture cap; cap_init(&cap); |
| | xmlSAXHandlerPtr sax = NULL; |
| | htmlParserCtxtPtr ctxt = new_ctxt(&cap, &sax); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"script"); |
| |
|
| | TEST_ASSERT_TRUE(cap.count >= 1); |
| | TEST_ASSERT_EQUAL_STRING("html", cap.names[0]); |
| | |
| | TEST_ASSERT_EQUAL_INT(2, cap.count); |
| | TEST_ASSERT_EQUAL_STRING("head", cap.names[1]); |
| |
|
| | free_ctxt(ctxt, sax, &cap); |
| | } |
| |
|
| | void test_htmlCheckImplied_subsequent_p_does_not_generate_body_if_head_present(void) { |
| | Capture cap; cap_init(&cap); |
| | xmlSAXHandlerPtr sax = NULL; |
| | htmlParserCtxtPtr ctxt = new_ctxt(&cap, &sax); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | |
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"script"); |
| | TEST_ASSERT_EQUAL_INT(2, cap.count); |
| | TEST_ASSERT_EQUAL_STRING("html", cap.names[0]); |
| | TEST_ASSERT_EQUAL_STRING("head", cap.names[1]); |
| |
|
| | |
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"p"); |
| | TEST_ASSERT_EQUAL_INT(2, cap.count); |
| |
|
| | free_ctxt(ctxt, sax, &cap); |
| | } |
| |
|
| | void test_htmlCheckImplied_no_duplicate_body_on_subsequent_calls(void) { |
| | Capture cap; cap_init(&cap); |
| | xmlSAXHandlerPtr sax = NULL; |
| | htmlParserCtxtPtr ctxt = new_ctxt(&cap, &sax); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | |
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"p"); |
| | TEST_ASSERT_EQUAL_INT(2, cap.count); |
| | TEST_ASSERT_EQUAL_STRING("html", cap.names[0]); |
| | TEST_ASSERT_EQUAL_STRING("body", cap.names[1]); |
| |
|
| | |
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"span"); |
| | TEST_ASSERT_EQUAL_INT(2, cap.count); |
| |
|
| | free_ctxt(ctxt, sax, &cap); |
| | } |
| |
|
| | void test_htmlCheckImplied_frame_like_tags_do_not_generate_body(void) { |
| | |
| | { |
| | Capture cap; cap_init(&cap); |
| | xmlSAXHandlerPtr sax = NULL; |
| | htmlParserCtxtPtr ctxt = new_ctxt(&cap, &sax); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"frame"); |
| | TEST_ASSERT_EQUAL_INT(1, cap.count); |
| | TEST_ASSERT_EQUAL_STRING("html", cap.names[0]); |
| |
|
| | free_ctxt(ctxt, sax, &cap); |
| | } |
| | |
| | { |
| | Capture cap; cap_init(&cap); |
| | xmlSAXHandlerPtr sax = NULL; |
| | htmlParserCtxtPtr ctxt = new_ctxt(&cap, &sax); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"frameset"); |
| | TEST_ASSERT_EQUAL_INT(1, cap.count); |
| | TEST_ASSERT_EQUAL_STRING("html", cap.names[0]); |
| |
|
| | free_ctxt(ctxt, sax, &cap); |
| | } |
| | |
| | { |
| | Capture cap; cap_init(&cap); |
| | xmlSAXHandlerPtr sax = NULL; |
| | htmlParserCtxtPtr ctxt = new_ctxt(&cap, &sax); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"noframes"); |
| | TEST_ASSERT_EQUAL_INT(1, cap.count); |
| | TEST_ASSERT_EQUAL_STRING("html", cap.names[0]); |
| |
|
| | free_ctxt(ctxt, sax, &cap); |
| | } |
| | } |
| |
|
| | void test_htmlCheckImplied_noimplied_option_suppresses_generation(void) { |
| | Capture cap; cap_init(&cap); |
| | xmlSAXHandlerPtr sax = NULL; |
| | htmlParserCtxtPtr ctxt = new_ctxt(&cap, &sax); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | |
| | int rc = htmlCtxtUseOptions(ctxt, HTML_PARSE_NOIMPLIED); |
| | (void)rc; |
| |
|
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"p"); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_INT(0, cap.count); |
| |
|
| | free_ctxt(ctxt, sax, &cap); |
| | } |
| |
|
| | void test_htmlCheckImplied_html5_option_suppresses_generation(void) { |
| | Capture cap; cap_init(&cap); |
| | xmlSAXHandlerPtr sax = NULL; |
| | htmlParserCtxtPtr ctxt = new_ctxt(&cap, &sax); |
| | TEST_ASSERT_NOT_NULL(ctxt); |
| |
|
| | |
| | int rc = htmlCtxtUseOptions(ctxt, HTML_PARSE_HTML5); |
| | (void)rc; |
| |
|
| | test_htmlCheckImplied(ctxt, (const xmlChar *)"p"); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_INT(0, cap.count); |
| |
|
| | free_ctxt(ctxt, sax, &cap); |
| | } |
| |
|
| | int main(void) { |
| | UNITY_BEGIN(); |
| |
|
| | RUN_TEST(test_htmlCheckImplied_with_p_generates_html_and_body); |
| | RUN_TEST(test_htmlCheckImplied_with_head_generates_only_html); |
| | RUN_TEST(test_htmlCheckImplied_with_html_generates_nothing); |
| | RUN_TEST(test_htmlCheckImplied_with_script_generates_html_and_head); |
| | RUN_TEST(test_htmlCheckImplied_subsequent_p_does_not_generate_body_if_head_present); |
| | RUN_TEST(test_htmlCheckImplied_no_duplicate_body_on_subsequent_calls); |
| | RUN_TEST(test_htmlCheckImplied_frame_like_tags_do_not_generate_body); |
| | RUN_TEST(test_htmlCheckImplied_noimplied_option_suppresses_generation); |
| | RUN_TEST(test_htmlCheckImplied_html5_option_suppresses_generation); |
| |
|
| | return UNITY_END(); |
| | } |