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