File size: 4,961 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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include "unity/unity.h"
#include <libxml/HTMLparser.h>
#include <libxml/parser.h>
#include <libxml/xmlerror.h>
#include <libxml/encoding.h>

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

/* Simple SAX state to record callbacks */
typedef struct {
    int startCount;
    int endCount;
    int charsCount;
    char collected[512];
    size_t collectedLen;
} TestSAXState;

/* SAX callbacks */
static void test_startDocument(void *ctx) {
    TestSAXState *st = (TestSAXState *)ctx;
    st->startCount++;
}

static void test_endDocument(void *ctx) {
    TestSAXState *st = (TestSAXState *)ctx;
    st->endCount++;
}

static void test_characters(void *ctx, const xmlChar *ch, int len) {
    TestSAXState *st = (TestSAXState *)ctx;
    if (st->collectedLen + (size_t)len >= sizeof(st->collected))
        len = (int)(sizeof(st->collected) - st->collectedLen - 1);
    if (len > 0) {
        memcpy(st->collected + st->collectedLen, ch, (size_t)len);
        st->collectedLen += (size_t)len;
        st->collected[st->collectedLen] = '\0';
    }
    st->charsCount++;
}

/* Helper to create a push parser context with our SAX handler and state */
static htmlParserCtxtPtr create_push_ctxt(TestSAXState *state) {
    xmlSAXHandler sax;
    memset(&sax, 0, sizeof(sax));
    sax.startDocument = test_startDocument;
    sax.endDocument = test_endDocument;
    sax.characters = test_characters;

    htmlParserCtxtPtr ctxt = htmlCreatePushParserCtxt(&sax, state, NULL, 0, NULL, XML_CHAR_ENCODING_NONE);
    return ctxt;
}

/* Unity hooks */
void setUp(void) {
    xmlInitParser();
}

void tearDown(void) {
    /* Nothing specific per-test */
}

/* Tests */

static void test_htmlParseChunk_invalid_arguments(void) {
    /* NULL context */
    int ret = htmlParseChunk(NULL, "x", 1, 0);
    TEST_ASSERT_EQUAL_INT(XML_ERR_ARGUMENT, ret);

    /* Valid context for further invalid arg checks */
    TestSAXState st;
    memset(&st, 0, sizeof(st));
    htmlParserCtxtPtr ctxt = create_push_ctxt(&st);
    TEST_ASSERT_NOT_NULL(ctxt);
    TEST_ASSERT_NOT_NULL(ctxt->input);
    TEST_ASSERT_NOT_NULL(ctxt->input->buf);

    /* Positive size with NULL chunk */
    ret = htmlParseChunk(ctxt, NULL, 1, 0);
    TEST_ASSERT_EQUAL_INT(XML_ERR_ARGUMENT, ret);

    /* Negative size */
    ret = htmlParseChunk(ctxt, "x", -5, 0);
    TEST_ASSERT_EQUAL_INT(XML_ERR_ARGUMENT, ret);

    htmlFreeParserCtxt(ctxt);
}

static void test_htmlParseChunk_partial_then_resume_starts_document(void) {
    TestSAXState st;
    memset(&st, 0, sizeof(st));
    htmlParserCtxtPtr ctxt = create_push_ctxt(&st);
    TEST_ASSERT_NOT_NULL(ctxt);

    /* Push a single byte: not enough to leave XML_PARSER_START when terminate==0 */
    int ret = htmlParseChunk(ctxt, "<", 1, 0);
    TEST_ASSERT_EQUAL_INT(XML_ERR_OK, ret);
    TEST_ASSERT_EQUAL_INT(0, st.startCount);
    TEST_ASSERT_EQUAL_INT(0, st.endCount);

    /* Push the remainder of a simple start tag */
    ret = htmlParseChunk(ctxt, "html>", 5, 0);
    TEST_ASSERT_EQUAL_INT(XML_ERR_OK, ret);
    TEST_ASSERT_EQUAL_INT(1, st.startCount);
    TEST_ASSERT_EQUAL_INT(0, st.endCount);

    /* Push some character data to ensure characters callback fires */
    st.collectedLen = 0;
    st.collected[0] = '\0';
    ret = htmlParseChunk(ctxt, "<body>Hi", 9, 0); /* includes <body>Hi */
    TEST_ASSERT_EQUAL_INT(XML_ERR_OK, ret);
    TEST_ASSERT_TRUE(st.charsCount >= 1);
    TEST_ASSERT_NOT_NULL(strstr(st.collected, "Hi"));

    htmlFreeParserCtxt(ctxt);
}

static void test_htmlParseChunk_terminate_calls_endDocument(void) {
    TestSAXState st;
    memset(&st, 0, sizeof(st));
    htmlParserCtxtPtr ctxt = create_push_ctxt(&st);
    TEST_ASSERT_NOT_NULL(ctxt);

    /* Feed a minimal, not fully closed document */
    const char *chunk1 = "<!DOCTYPE html><html><body>Hello";
    int ret = htmlParseChunk(ctxt, chunk1, (int)strlen(chunk1), 0);
    TEST_ASSERT_EQUAL_INT(XML_ERR_OK, ret);
    TEST_ASSERT_EQUAL_INT(1, st.startCount);
    TEST_ASSERT_EQUAL_INT(0, st.endCount);

    /* Terminate: should auto-close and call endDocument */
    ret = htmlParseChunk(ctxt, NULL, 0, 1);
    TEST_ASSERT_EQUAL_INT(XML_ERR_OK, ret);
    TEST_ASSERT_EQUAL_INT(1, st.endCount);

    htmlFreeParserCtxt(ctxt);
}

static void test_htmlParseChunk_respects_stop_parser(void) {
    TestSAXState st;
    memset(&st, 0, sizeof(st));
    htmlParserCtxtPtr ctxt = create_push_ctxt(&st);
    TEST_ASSERT_NOT_NULL(ctxt);

    /* Stop the parser via public API */
    xmlStopParser(ctxt);

    int ret = htmlParseChunk(ctxt, "<html>", 6, 0);
    TEST_ASSERT_EQUAL_INT(XML_ERR_USER_STOP, ret);

    htmlFreeParserCtxt(ctxt);
}

int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_htmlParseChunk_invalid_arguments);
    RUN_TEST(test_htmlParseChunk_partial_then_resume_starts_document);
    RUN_TEST(test_htmlParseChunk_terminate_calls_endDocument);
    RUN_TEST(test_htmlParseChunk_respects_stop_parser);
    int rc = UNITY_END();
    xmlCleanupParser();
    return rc;
}