File size: 4,493 Bytes
6baed57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "unity/unity.h"
#include <libxml/HTMLparser.h>

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

/* Simple event logger for SAX callbacks */
typedef struct {
    char buf[2048];
} EventLog;

static void log_append(EventLog *log, const char *s) {
    size_t cur = strlen(log->buf);
    size_t add = strlen(s);
    if (cur + add >= sizeof(log->buf)) {
        add = sizeof(log->buf) - cur - 1;
    }
    if (add > 0) {
        memcpy(log->buf + cur, s, add);
        log->buf[cur + add] = '\0';
    }
}

/* SAX callbacks we care about */
static void onStartElement(void *ctx, const xmlChar *name, const xmlChar **atts) {
    (void)atts;
    EventLog *log = (EventLog *)ctx;
    char tmp[256];
    snprintf(tmp, sizeof(tmp), "[S:%s]", (const char *)name);
    log_append(log, tmp);
}

static void onEndElement(void *ctx, const xmlChar *name) {
    EventLog *log = (EventLog *)ctx;
    char tmp[256];
    snprintf(tmp, sizeof(tmp), "[E:%s]", (const char *)name);
    log_append(log, tmp);
}

static void onCharacters(void *ctx, const xmlChar *ch, int len) {
    EventLog *log = (EventLog *)ctx;
    if (len <= 0)
        return;
    char tmp[512];
    int copy = len;
    if (copy > (int)sizeof(tmp) - 1)
        copy = (int)sizeof(tmp) - 1;
    memcpy(tmp, ch, (size_t)copy);
    tmp[copy] = '\0';

    char out[600];
    snprintf(out, sizeof(out), "[T:%s]", tmp);
    log_append(log, out);
}

/* Global SAX handler; configured in setUp */
static xmlSAXHandler gSAX;

void setUp(void) {
    memset(&gSAX, 0, sizeof(gSAX));
    gSAX.startElement = onStartElement;
    gSAX.endElement = onEndElement;
    gSAX.characters = onCharacters;
}

void tearDown(void) {
    /* nothing */
}

/* Helper to run one parsing case through htmlParseElement and check events */
static void run_case(const char *html, int options, const char *expectedLog) {
    EventLog log;
    log.buf[0] = '\0';

    htmlParserCtxtPtr ctxt = htmlCreateMemoryParserCtxt(html, (int)strlen(html));
    TEST_ASSERT_NOT_NULL_MESSAGE(ctxt, "htmlCreateMemoryParserCtxt returned NULL");

    ctxt->sax = &gSAX;
    ctxt->userData = &log;
    ctxt->options = options;

    htmlParseElement(ctxt);

    TEST_ASSERT_EQUAL_STRING_MESSAGE(expectedLog, log.buf, "Unexpected SAX event sequence");

    htmlFreeParserCtxt(ctxt);
}

/* Tests */

void test_htmlParseElement_null_context_safe(void) {
    /* Should be a no-op and must not crash */
    htmlParseElement(NULL);
    TEST_ASSERT_TRUE(1);
}

void test_htmlParseElement_empty_input_no_events(void) {
    run_case("", 0, "");
}

void test_htmlParseElement_parses_simple_element(void) {
    run_case("<p>hi</p>", 0, "[S:p][T:hi][E:p]");
}

void test_htmlParseElement_self_closing_xml_style_non_html5(void) {
    /* In non-HTML5 mode, '/>' triggers an explicit endElement */
    run_case("<br/>", 0, "[S:br][E:br]");
}

void test_htmlParseElement_self_closing_xml_style_html5(void) {
    /* In HTML5 mode, '/>' does NOT call endElement in htmlParseElementInternal */
    run_case("<br/>", HTML_PARSE_HTML5, "[S:br]");
}

void test_htmlParseElement_dtd_empty_element_non_html5(void) {
    /* DTD-empty element like <br> should auto-close in non-HTML5 mode */
    run_case("<br>", 0, "[S:br][E:br]");
}

void test_htmlParseElement_dtd_empty_element_html5(void) {
    /* In HTML5 mode, no endElement is emitted for DTD-empty element in this path */
    run_case("<br>", HTML_PARSE_HTML5, "[S:br]");
}

void test_htmlParseElement_auto_close_on_eof_non_html5(void) {
    /* Unclosed nested elements should be auto-closed at end of input in non-HTML5 mode */
    run_case("<div><span>t", 0, "[S:div][S:span][T:t][E:span][E:div]");
}

void test_htmlParseElement_auto_close_on_eof_html5(void) {
    /* In HTML5 mode, htmlAutoCloseOnEnd() is a no-op, so no endElement callbacks */
    run_case("<div><span>t", HTML_PARSE_HTML5, "[S:div][S:span][T:t]");
}

int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_htmlParseElement_null_context_safe);
    RUN_TEST(test_htmlParseElement_empty_input_no_events);
    RUN_TEST(test_htmlParseElement_parses_simple_element);
    RUN_TEST(test_htmlParseElement_self_closing_xml_style_non_html5);
    RUN_TEST(test_htmlParseElement_self_closing_xml_style_html5);
    RUN_TEST(test_htmlParseElement_dtd_empty_element_non_html5);
    RUN_TEST(test_htmlParseElement_dtd_empty_element_html5);
    RUN_TEST(test_htmlParseElement_auto_close_on_eof_non_html5);
    RUN_TEST(test_htmlParseElement_auto_close_on_eof_html5);
    return UNITY_END();
}