File size: 7,090 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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#include "unity/unity.h"
#include <libxml/HTMLparser.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlmemory.h>

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

/* Wrapper for the static function under test provided in the module */
extern void test_htmlParserFinishElementParsing(htmlParserCtxtPtr ctxt);

static htmlParserCtxtPtr
create_ctxt_with_input(size_t base_len, size_t cur_offset, unsigned long consumed, unsigned long line,
                       xmlParserInputPtr *outInput, xmlChar **outBase) {
    htmlParserCtxtPtr ctxt = htmlNewParserCtxt();
    TEST_ASSERT_NOT_NULL_MESSAGE(ctxt, "Failed to create HTML parser context");

    xmlParserInputPtr in = xmlNewInputStream(ctxt);
    TEST_ASSERT_NOT_NULL_MESSAGE(in, "Failed to create new input stream");

    xmlChar *base = (xmlChar *)xmlMalloc(base_len);
    TEST_ASSERT_NOT_NULL_MESSAGE(base, "Failed to allocate base buffer");
    memset(base, 'A', base_len);

    /* Ensure cur_offset within base_len */
    TEST_ASSERT_MESSAGE(cur_offset <= base_len, "cur_offset exceeds base buffer length");

    in->base = base;
    in->cur = base + cur_offset;
    in->end = base + base_len;
    in->consumed = consumed;
    in->line = line;

    ctxt->input = in;

    if (outInput) *outInput = in;
    if (outBase) *outBase = base;

    return ctxt;
}

static void
destroy_ctxt_and_input(htmlParserCtxtPtr ctxt, xmlParserInputPtr in, xmlChar *base) {
    if (ctxt != NULL) {
        /* Detach input from context so we can free it explicitly to avoid double-free */
        ctxt->input = NULL;
    }
    if (in != NULL) {
        /* Avoid freeing base twice if libxml would attempt it; we own base */
        in->base = NULL;
        in->cur = NULL;
        in->end = NULL;
        xmlFreeInputStream(in);
    }
    if (base != NULL) {
        xmlFree(base);
    }
    if (ctxt != NULL) {
        htmlFreeParserCtxt(ctxt);
    }
}

void setUp(void) {
    xmlInitParser();
}

void tearDown(void) {
    xmlCleanupParser();
}

void test_htmlParserFinishElementParsing_records_end_info_basic(void) {
    xmlParserInputPtr in = NULL;
    xmlChar *base = NULL;
    /* base length 32, cur_offset 5, consumed 2, line 7 */
    htmlParserCtxtPtr ctxt = create_ctxt_with_input(32, 5, 2, 7, &in, &base);

    /* Prepare node and nodeInfo */
    xmlNodePtr node = xmlNewNode(NULL, (const xmlChar *)"test");
    TEST_ASSERT_NOT_NULL(node);
    ctxt->node = node;
    ctxt->record_info = 1;

    xmlParserNodeInfo *info = (xmlParserNodeInfo *)xmlMalloc(sizeof(xmlParserNodeInfo));
    TEST_ASSERT_NOT_NULL(info);
    memset(info, 0, sizeof(*info));
    ctxt->nodeInfo = info;

    /* Call the wrapper */
    test_htmlParserFinishElementParsing(ctxt);

    /* Verify info was added to the context's node info sequence */
    const xmlParserNodeInfo *found = xmlParserFindNodeInfo(ctxt, node);
    TEST_ASSERT_NOT_NULL_MESSAGE(found, "xmlParserNodeInfo not found for node");
    TEST_ASSERT_EQUAL_PTR(node, found->node);
    /* end_pos = consumed + (cur - base) = 2 + (5) = 7 */
    TEST_ASSERT_EQUAL_UINT(7u, (unsigned int)found->end_pos);
    TEST_ASSERT_EQUAL_UINT(7u, (unsigned int)found->end_line);

    /* Cleanup */
    xmlFreeNode(node);
    /* info was likely freed by htmlNodeInfoPop; freeing only if still referenced would be unsafe.
       We won't free 'info' here. */
    destroy_ctxt_and_input(ctxt, in, base);
}

void test_htmlParserFinishElementParsing_calculates_end_pos_with_consumed_offset(void) {
    xmlParserInputPtr in = NULL;
    xmlChar *base = NULL;
    /* base length 64, cur_offset 13, consumed 21, line 3 */
    htmlParserCtxtPtr ctxt = create_ctxt_with_input(64, 13, 21, 3, &in, &base);

    xmlNodePtr node = xmlNewNode(NULL, (const xmlChar *)"elem");
    TEST_ASSERT_NOT_NULL(node);
    ctxt->node = node;
    ctxt->record_info = 1;

    xmlParserNodeInfo *info = (xmlParserNodeInfo *)xmlMalloc(sizeof(xmlParserNodeInfo));
    TEST_ASSERT_NOT_NULL(info);
    memset(info, 0, sizeof(*info));
    ctxt->nodeInfo = info;

    test_htmlParserFinishElementParsing(ctxt);

    const xmlParserNodeInfo *found = xmlParserFindNodeInfo(ctxt, node);
    TEST_ASSERT_NOT_NULL_MESSAGE(found, "xmlParserNodeInfo not found for node");
    TEST_ASSERT_EQUAL_PTR(node, found->node);
    /* end_pos = 21 + 13 = 34; end_line = 3 */
    TEST_ASSERT_EQUAL_UINT(34u, (unsigned int)found->end_pos);
    TEST_ASSERT_EQUAL_UINT(3u, (unsigned int)found->end_line);

    xmlFreeNode(node);
    destroy_ctxt_and_input(ctxt, in, base);
}

void test_htmlParserFinishElementParsing_noop_when_node_null(void) {
    xmlParserInputPtr in = NULL;
    xmlChar *base = NULL;
    htmlParserCtxtPtr ctxt = create_ctxt_with_input(16, 4, 9, 11, &in, &base);

    /* node is NULL; enable record_info to test the node NULL guard */
    ctxt->node = NULL;
    ctxt->record_info = 1;

    /* Provide a nodeInfo struct; function must not use it since node is NULL */
    xmlParserNodeInfo *info = (xmlParserNodeInfo *)xmlMalloc(sizeof(xmlParserNodeInfo));
    TEST_ASSERT_NOT_NULL(info);
    memset(info, 0, sizeof(*info));
    ctxt->nodeInfo = info;

    test_htmlParserFinishElementParsing(ctxt);

    /* Create a node to search for; should not find any info since node was NULL */
    xmlNodePtr node = xmlNewNode(NULL, (const xmlChar *)"unused");
    TEST_ASSERT_NOT_NULL(node);
    const xmlParserNodeInfo *found = xmlParserFindNodeInfo(ctxt, node);
    TEST_ASSERT_NULL(found);

    xmlFreeNode(node);
    /* As above, assume htmlNodeInfoPop may have freed info; do not free directly. */
    destroy_ctxt_and_input(ctxt, in, base);
}

void test_htmlParserFinishElementParsing_noop_when_record_info_false_even_with_node(void) {
    /* Intentionally no input to ensure function doesn't touch input when record_info == 0 */
    htmlParserCtxtPtr ctxt = htmlNewParserCtxt();
    TEST_ASSERT_NOT_NULL(ctxt);

    xmlNodePtr node = xmlNewNode(NULL, (const xmlChar *)"n");
    TEST_ASSERT_NOT_NULL(node);
    ctxt->node = node;
    ctxt->record_info = 0; /* disabled */
    ctxt->input = NULL;    /* would crash if accessed */

    /* Provide a nodeInfo pointer; must not be used since record_info == 0 */
    xmlParserNodeInfo *info = (xmlParserNodeInfo *)xmlMalloc(sizeof(xmlParserNodeInfo));
    TEST_ASSERT_NOT_NULL(info);
    memset(info, 0, sizeof(*info));
    ctxt->nodeInfo = info;

    test_htmlParserFinishElementParsing(ctxt);

    /* Since recording is disabled, no node info should be present */
    const xmlParserNodeInfo *found = xmlParserFindNodeInfo(ctxt, node);
    TEST_ASSERT_NULL(found);

    xmlFreeNode(node);
    /* Do not free 'info' as it may be managed by internal pop logic in other cases. */
    htmlFreeParserCtxt(ctxt);
}

int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_htmlParserFinishElementParsing_records_end_info_basic);
    RUN_TEST(test_htmlParserFinishElementParsing_calculates_end_pos_with_consumed_offset);
    RUN_TEST(test_htmlParserFinishElementParsing_noop_when_node_null);
    RUN_TEST(test_htmlParserFinishElementParsing_noop_when_record_info_false_even_with_node);
    return UNITY_END();
}