File size: 6,629 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
200
201
#include "unity/unity.h"
#include <libxml/HTMLparser.h>

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

/* Wrapper provided in the module for calling the static function */
int test_htmlParseLookupGt(xmlParserCtxtPtr ctxt);

/* Unity hooks */
void setUp(void) {
    /* No per-test setup needed */
}
void tearDown(void) {
    /* No per-test teardown needed */
}

/* Helper to create an HTML parser context from a C string */
static htmlParserCtxtPtr make_ctx(const char *s) {
    int len = (int)strlen(s);
    htmlParserCtxtPtr ctxt = htmlCreateMemoryParserCtxt(s, len);
    TEST_ASSERT_NOT_NULL_MESSAGE(ctxt, "Failed to create HTML parser context");
    TEST_ASSERT_NOT_NULL_MESSAGE(ctxt->input, "Parser input is NULL");
    /* Ensure cur starts at the beginning of the buffer for our tests */
    if (ctxt->input->base != NULL)
        ctxt->input->cur = ctxt->input->base;
    return ctxt;
}

/* Basic tag: should find '>' immediately */
void test_htmlParseLookupGt_basic_tag(void) {
    htmlParserCtxtPtr ctxt = make_ctx("<a>");
    ctxt->checkIndex = 0;
    ctxt->endCheckState = 0;

    int ret = test_htmlParseLookupGt(ctxt);
    TEST_ASSERT_EQUAL_INT(0, ret);
    TEST_ASSERT_EQUAL_UINT(0, ctxt->checkIndex);
    TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState);

    htmlFreeParserCtxt(ctxt);
}

/* Tag with attributes and spaces before closing '>' */
void test_htmlParseLookupGt_attrs_and_spaces(void) {
    htmlParserCtxtPtr ctxt = make_ctx("<a href=\"test\" >");
    ctxt->checkIndex = 0;
    ctxt->endCheckState = 0;

    int ret = test_htmlParseLookupGt(ctxt);
    TEST_ASSERT_EQUAL_INT(0, ret);
    TEST_ASSERT_EQUAL_UINT(0, ctxt->checkIndex);
    TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState);

    htmlFreeParserCtxt(ctxt);
}

/* '>' inside single-quoted attribute value must not terminate the tag */
void test_htmlParseLookupGt_gt_inside_single_quotes(void) {
    htmlParserCtxtPtr ctxt = make_ctx("<a title='> not end' data='x'>");
    ctxt->checkIndex = 0;
    ctxt->endCheckState = 0;

    int ret = test_htmlParseLookupGt(ctxt);
    TEST_ASSERT_EQUAL_INT(0, ret);
    TEST_ASSERT_EQUAL_UINT(0, ctxt->checkIndex);
    TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState);

    htmlFreeParserCtxt(ctxt);
}

/* '>' inside double-quoted attribute value must not terminate the tag */
void test_htmlParseLookupGt_gt_inside_double_quotes(void) {
    htmlParserCtxtPtr ctxt = make_ctx("<a title=\"> not end\" data=\"x\">");
    ctxt->checkIndex = 0;
    ctxt->endCheckState = 0;

    int ret = test_htmlParseLookupGt(ctxt);
    TEST_ASSERT_EQUAL_INT(0, ret);
    TEST_ASSERT_EQUAL_UINT(0, ctxt->checkIndex);
    TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState);

    htmlFreeParserCtxt(ctxt);
}

/* Incomplete tag: no closing '>' in buffer */
void test_htmlParseLookupGt_incomplete_no_gt(void) {
    const char *buf = "<a href='incomplete'";
    htmlParserCtxtPtr ctxt = make_ctx(buf);
    ctxt->checkIndex = 0;
    ctxt->endCheckState = 0;

    size_t expectedIndex = (size_t)(ctxt->input->end - ctxt->input->cur);

    int ret = test_htmlParseLookupGt(ctxt);
    TEST_ASSERT_EQUAL_INT(-1, ret);
    TEST_ASSERT_EQUAL_size_t(expectedIndex, ctxt->checkIndex);
    /* endCheckState is implementation-defined but should be preserved and non-negative */
    TEST_ASSERT_TRUE(ctxt->endCheckState >= 0);

    htmlFreeParserCtxt(ctxt);
}

/* Repeated call on the same incomplete buffer should be idempotent */
void test_htmlParseLookupGt_incomplete_repeat_call_stable(void) {
    const char *buf = "<a href='x' attr=y";
    htmlParserCtxtPtr ctxt = make_ctx(buf);
    ctxt->checkIndex = 0;
    ctxt->endCheckState = 0;

    size_t expectedIndex = (size_t)(ctxt->input->end - ctxt->input->cur);

    int ret1 = test_htmlParseLookupGt(ctxt);
    TEST_ASSERT_EQUAL_INT(-1, ret1);
    int savedState = ctxt->endCheckState;
    size_t savedIndex = ctxt->checkIndex;

    int ret2 = test_htmlParseLookupGt(ctxt);
    TEST_ASSERT_EQUAL_INT(-1, ret2);
    TEST_ASSERT_EQUAL_size_t(expectedIndex, ctxt->checkIndex);
    TEST_ASSERT_EQUAL_size_t(savedIndex, ctxt->checkIndex);
    TEST_ASSERT_EQUAL_INT(savedState, ctxt->endCheckState);

    htmlFreeParserCtxt(ctxt);
}

/* Resume scanning across buffers: first incomplete, then complete */
void test_htmlParseLookupGt_resume_scanning_across_buffers(void) {
    const char *part1 = "<a href='value'";
    const char *part2 = "<a href='value' data=x>";

    /* First, incomplete buffer */
    htmlParserCtxtPtr ctxt1 = make_ctx(part1);
    ctxt1->checkIndex = 0;
    ctxt1->endCheckState = 0;

    int ret1 = test_htmlParseLookupGt(ctxt1);
    TEST_ASSERT_EQUAL_INT(-1, ret1);
    size_t savedIndex = ctxt1->checkIndex;
    int savedState = ctxt1->endCheckState;

    htmlFreeParserCtxt(ctxt1);

    /* Now, new buffer that continues and closes the tag */
    htmlParserCtxtPtr ctxt2 = make_ctx(part2);
    ctxt2->checkIndex = savedIndex;      /* resume offset */
    ctxt2->endCheckState = savedState;   /* resume state */

    int ret2 = test_htmlParseLookupGt(ctxt2);
    TEST_ASSERT_EQUAL_INT(0, ret2);
    TEST_ASSERT_EQUAL_UINT(0, ctxt2->checkIndex);
    TEST_ASSERT_EQUAL_INT(0, ctxt2->endCheckState);

    htmlFreeParserCtxt(ctxt2);
}

/* Closing tag should be handled correctly */
void test_htmlParseLookupGt_closing_tag(void) {
    htmlParserCtxtPtr ctxt = make_ctx("</div>");
    ctxt->checkIndex = 0;
    ctxt->endCheckState = 0;

    int ret = test_htmlParseLookupGt(ctxt);
    TEST_ASSERT_EQUAL_INT(0, ret);
    TEST_ASSERT_EQUAL_UINT(0, ctxt->checkIndex);
    TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState);

    htmlFreeParserCtxt(ctxt);
}

/* Self-closing tag with '/' before '>' */
void test_htmlParseLookupGt_self_closing(void) {
    htmlParserCtxtPtr ctxt = make_ctx("<img alt='x'/>");
    ctxt->checkIndex = 0;
    ctxt->endCheckState = 0;

    int ret = test_htmlParseLookupGt(ctxt);
    TEST_ASSERT_EQUAL_INT(0, ret);
    TEST_ASSERT_EQUAL_UINT(0, ctxt->checkIndex);
    TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState);

    htmlFreeParserCtxt(ctxt);
}

int main(void) {
    UNITY_BEGIN();
    xmlInitParser();

    RUN_TEST(test_htmlParseLookupGt_basic_tag);
    RUN_TEST(test_htmlParseLookupGt_attrs_and_spaces);
    RUN_TEST(test_htmlParseLookupGt_gt_inside_single_quotes);
    RUN_TEST(test_htmlParseLookupGt_gt_inside_double_quotes);
    RUN_TEST(test_htmlParseLookupGt_incomplete_no_gt);
    RUN_TEST(test_htmlParseLookupGt_incomplete_repeat_call_stable);
    RUN_TEST(test_htmlParseLookupGt_resume_scanning_across_buffers);
    RUN_TEST(test_htmlParseLookupGt_closing_tag);
    RUN_TEST(test_htmlParseLookupGt_self_closing);

    xmlCleanupParser();
    return UNITY_END();
}