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