|
|
#include "unity/unity.h" |
|
|
#include <libxml/HTMLparser.h> |
|
|
|
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <libxml/parser.h> |
|
|
#include <libxml/parserInternals.h> |
|
|
|
|
|
|
|
|
void test_htmlAutoClose(htmlParserCtxtPtr ctxt, const xmlChar * newtag); |
|
|
|
|
|
typedef struct { |
|
|
int count; |
|
|
const xmlChar *last; |
|
|
} TestSaxData; |
|
|
|
|
|
static void onEndElement(void *userData, const xmlChar *name) { |
|
|
TestSaxData *d = (TestSaxData *)userData; |
|
|
if (d) { |
|
|
d->count++; |
|
|
d->last = name; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static htmlParserCtxtPtr make_ctxt_with_stack(const char **tags, int count, |
|
|
int withSax, |
|
|
TestSaxData *saxDataOut) { |
|
|
htmlParserCtxtPtr ctxt = htmlNewParserCtxt(); |
|
|
TEST_ASSERT_NOT_NULL(ctxt); |
|
|
|
|
|
|
|
|
ctxt->record_info = 0; |
|
|
|
|
|
|
|
|
if (count > 0) { |
|
|
ctxt->nameMax = count; |
|
|
ctxt->nameNr = count; |
|
|
ctxt->nameTab = (const xmlChar **)xmlMalloc(sizeof(xmlChar *) * (size_t)count); |
|
|
TEST_ASSERT_NOT_NULL(ctxt->nameTab); |
|
|
for (int i = 0; i < count; i++) { |
|
|
|
|
|
ctxt->nameTab[i] = xmlStrdup((const xmlChar *)tags[i]); |
|
|
TEST_ASSERT_NOT_NULL(ctxt->nameTab[i]); |
|
|
} |
|
|
ctxt->name = ctxt->nameTab[count - 1]; |
|
|
} else { |
|
|
ctxt->nameMax = 0; |
|
|
ctxt->nameNr = 0; |
|
|
ctxt->nameTab = NULL; |
|
|
ctxt->name = NULL; |
|
|
} |
|
|
|
|
|
|
|
|
ctxt->options = 0; |
|
|
|
|
|
if (withSax) { |
|
|
static xmlSAXHandler sax; |
|
|
memset(&sax, 0, sizeof(sax)); |
|
|
sax.endElement = onEndElement; |
|
|
|
|
|
if (saxDataOut) { |
|
|
saxDataOut->count = 0; |
|
|
saxDataOut->last = NULL; |
|
|
} |
|
|
|
|
|
ctxt->sax = &sax; |
|
|
ctxt->userData = saxDataOut; |
|
|
} else { |
|
|
ctxt->sax = NULL; |
|
|
ctxt->userData = NULL; |
|
|
} |
|
|
|
|
|
return ctxt; |
|
|
} |
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlAutoClose_returns_immediately_with_HTML5_option(void) { |
|
|
const char *stack[] = { "ul", "li" }; |
|
|
TestSaxData data; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt_with_stack(stack, 2, 1, &data); |
|
|
|
|
|
|
|
|
ctxt->options |= HTML_PARSE_HTML5; |
|
|
|
|
|
const xmlChar *prevName = ctxt->name; |
|
|
int prevNr = ctxt->nameNr; |
|
|
|
|
|
test_htmlAutoClose(ctxt, (const xmlChar *)"li"); |
|
|
|
|
|
TEST_ASSERT_EQUAL_PTR(prevName, ctxt->name); |
|
|
TEST_ASSERT_EQUAL_INT(prevNr, ctxt->nameNr); |
|
|
TEST_ASSERT_EQUAL_INT(0, data.count); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlAutoClose_ignores_null_newtag(void) { |
|
|
const char *stack[] = { "ul", "li" }; |
|
|
TestSaxData data; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt_with_stack(stack, 2, 1, &data); |
|
|
|
|
|
const xmlChar *prevName = ctxt->name; |
|
|
int prevNr = ctxt->nameNr; |
|
|
|
|
|
test_htmlAutoClose(ctxt, NULL); |
|
|
|
|
|
TEST_ASSERT_EQUAL_PTR(prevName, ctxt->name); |
|
|
TEST_ASSERT_EQUAL_INT(prevNr, ctxt->nameNr); |
|
|
TEST_ASSERT_EQUAL_INT(0, data.count); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlAutoClose_closes_single_matching_tag_and_calls_endElement(void) { |
|
|
const char *stack[] = { "ul", "li" }; |
|
|
TestSaxData data; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt_with_stack(stack, 2, 1, &data); |
|
|
|
|
|
test_htmlAutoClose(ctxt, (const xmlChar *)"li"); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(1, data.count); |
|
|
TEST_ASSERT_NOT_NULL_MESSAGE(data.last, "endElement should be called with closed tag name"); |
|
|
TEST_ASSERT_EQUAL_INT(1, ctxt->nameNr); |
|
|
TEST_ASSERT_EQUAL_STRING("ul", (const char *)ctxt->name); |
|
|
TEST_ASSERT_EQUAL_INT(0, xmlStrcmp((const xmlChar *)"li", data.last)); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlAutoClose_closes_multiple_in_a_row(void) { |
|
|
const char *stack[] = { "ul", "li", "li", "li" }; |
|
|
TestSaxData data; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt_with_stack(stack, 4, 1, &data); |
|
|
|
|
|
test_htmlAutoClose(ctxt, (const xmlChar *)"li"); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(3, data.count); |
|
|
TEST_ASSERT_EQUAL_INT(1, ctxt->nameNr); |
|
|
TEST_ASSERT_EQUAL_STRING("ul", (const char *)ctxt->name); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlAutoClose_no_action_when_no_match(void) { |
|
|
const char *stack[] = { "ul" }; |
|
|
TestSaxData data; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt_with_stack(stack, 1, 1, &data); |
|
|
|
|
|
const xmlChar *prevName = ctxt->name; |
|
|
int prevNr = ctxt->nameNr; |
|
|
|
|
|
test_htmlAutoClose(ctxt, (const xmlChar *)"li"); |
|
|
|
|
|
TEST_ASSERT_EQUAL_PTR(prevName, ctxt->name); |
|
|
TEST_ASSERT_EQUAL_INT(prevNr, ctxt->nameNr); |
|
|
TEST_ASSERT_EQUAL_INT(0, data.count); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlAutoClose_handles_null_sax(void) { |
|
|
const char *stack[] = { "ul", "li" }; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt_with_stack(stack, 2, 0, NULL); |
|
|
|
|
|
test_htmlAutoClose(ctxt, (const xmlChar *)"li"); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(1, ctxt->nameNr); |
|
|
TEST_ASSERT_EQUAL_STRING("ul", (const char *)ctxt->name); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlAutoClose_noop_when_name_is_null(void) { |
|
|
TestSaxData data; |
|
|
htmlParserCtxtPtr ctxt = make_ctxt_with_stack(NULL, 0, 1, &data); |
|
|
|
|
|
test_htmlAutoClose(ctxt, (const xmlChar *)"li"); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, data.count); |
|
|
TEST_ASSERT_EQUAL_INT(0, ctxt->nameNr); |
|
|
TEST_ASSERT_NULL(ctxt->name); |
|
|
|
|
|
htmlFreeParserCtxt(ctxt); |
|
|
} |
|
|
|
|
|
int main(void) { |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_htmlAutoClose_returns_immediately_with_HTML5_option); |
|
|
RUN_TEST(test_htmlAutoClose_ignores_null_newtag); |
|
|
RUN_TEST(test_htmlAutoClose_closes_single_matching_tag_and_calls_endElement); |
|
|
RUN_TEST(test_htmlAutoClose_closes_multiple_in_a_row); |
|
|
RUN_TEST(test_htmlAutoClose_no_action_when_no_match); |
|
|
RUN_TEST(test_htmlAutoClose_handles_null_sax); |
|
|
RUN_TEST(test_htmlAutoClose_noop_when_name_is_null); |
|
|
return UNITY_END(); |
|
|
} |