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