File size: 5,453 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
#include "unity/unity.h"
#include <libxml/HTMLparser.h>
#include <libxml/parser.h>
#include <stdlib.h>
#include <string.h>

/* Wrapper for the static function under test (provided in the module). */
extern void test_htmlParseTryOrFinish(htmlParserCtxtPtr ctxt, int terminate);

/* Helpers */
static htmlParserCtxtPtr make_ctx_from_mem(const char *buf, int len) {
    htmlParserCtxtPtr ctxt = htmlCreateMemoryParserCtxt(buf, len);
    TEST_ASSERT_NOT_NULL_MESSAGE(ctxt, "Failed to create HTML parser context");
    /* Be permissive to avoid errors affecting the test */
    (void)htmlCtxtUseOptions(ctxt, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
    return ctxt;
}

static size_t cur_offset(htmlParserCtxtPtr ctxt) {
    return (size_t)(ctxt->input->cur - ctxt->input->base);
}

static size_t end_offset(htmlParserCtxtPtr ctxt) {
    return (size_t)(ctxt->input->end - ctxt->input->base);
}

/* Unity hooks */
void setUp(void) {
    /* Initialize library per test to be safe */
    xmlInitParser();
}

void tearDown(void) {
    /* No global cleanup here; contexts are freed in tests */
}

/* Tests */

void test_htmlParseTryOrFinish_start_nonterminate_small_avail_returns_early(void) {
    const char *data = "abc"; /* less than 4 bytes */
    htmlParserCtxtPtr ctxt = make_ctx_from_mem(data, (int)strlen(data));
    TEST_ASSERT_EQUAL_INT(XML_PARSER_START, ctxt->instate);

    size_t before_cur = cur_offset(ctxt);
    test_htmlParseTryOrFinish(ctxt, 0);

    /* Should not have progressed from START due to insufficient bytes and non-terminate */
    TEST_ASSERT_EQUAL_INT(XML_PARSER_START, ctxt->instate);
    TEST_ASSERT_EQUAL_size_t(before_cur, cur_offset(ctxt));

    htmlFreeParserCtxt(ctxt);
}

void test_htmlParseTryOrFinish_start_terminate_progresses_and_consumes_text(void) {
    const char *data = "abc";
    htmlParserCtxtPtr ctxt = make_ctx_from_mem(data, (int)strlen(data));
    size_t end = end_offset(ctxt);

    test_htmlParseTryOrFinish(ctxt, 1);

    /* Should have progressed into CONTENT and consumed the text */
    TEST_ASSERT_EQUAL_INT(XML_PARSER_CONTENT, ctxt->instate);
    TEST_ASSERT_EQUAL_size_t(end, cur_offset(ctxt));
    /* endCheckState should be 0 after plain text consumption */
    TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState);

    htmlFreeParserCtxt(ctxt);
}

void test_htmlParseTryOrFinish_misc_to_start_tag_partial_nonterminate(void) {
    /* 4 spaces ensure we pass START; "<p" triggers START_TAG with missing '>' */
    const char *data = "    <p";
    htmlParserCtxtPtr ctxt = make_ctx_from_mem(data, (int)strlen(data));

    test_htmlParseTryOrFinish(ctxt, 0);

    /* With terminate == 0 and no '>', the function should return in START_TAG state */
    TEST_ASSERT_EQUAL_INT(XML_PARSER_START_TAG, ctxt->instate);
    /* htmlParseLookupGt should have set a non-zero checkIndex for rescanning */
    TEST_ASSERT_TRUE(ctxt->checkIndex != 0);

    /* Input cursor should not have consumed the '<p' (still at or before '<') */
    /* We expect no advance past the '<' since we bailed out before parsing tag */
    /* It's acceptable if whitespace was skipped while moving into MISC. Ensure not at end. */
    TEST_ASSERT_TRUE(cur_offset(ctxt) < end_offset(ctxt));

    htmlFreeParserCtxt(ctxt);
}

void test_htmlParseTryOrFinish_parses_comment_and_advances(void) {
    const char *data = "<!--x-->";
    htmlParserCtxtPtr ctxt = make_ctx_from_mem(data, (int)strlen(data));
    size_t end = end_offset(ctxt);

    test_htmlParseTryOrFinish(ctxt, 1);

    /* Cursor should be at end after consuming the comment */
    TEST_ASSERT_EQUAL_size_t(end, cur_offset(ctxt));
    /* State likely remains in MISC or CONTENT; just ensure not EOF */
    TEST_ASSERT_NOT_EQUAL_INT(XML_PARSER_EOF, ctxt->instate);

    htmlFreeParserCtxt(ctxt);
}

void test_htmlParseTryOrFinish_parses_simple_element_and_end_tag_full_terminate(void) {
    const char *data = "<p>Hi</p>";
    htmlParserCtxtPtr ctxt = make_ctx_from_mem(data, (int)strlen(data));
    size_t end = end_offset(ctxt);

    test_htmlParseTryOrFinish(ctxt, 1);

    /* Should have consumed the entire input */
    TEST_ASSERT_EQUAL_size_t(end, cur_offset(ctxt));
    /* Should be in CONTENT after handling tags and text */
    TEST_ASSERT_EQUAL_INT(XML_PARSER_CONTENT, ctxt->instate);
    TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState);

    htmlFreeParserCtxt(ctxt);
}

void test_htmlParseTryOrFinish_end_tag_only_transitions_and_consumes(void) {
    const char *data = "</p>";
    htmlParserCtxtPtr ctxt = make_ctx_from_mem(data, (int)strlen(data));
    size_t end = end_offset(ctxt);

    test_htmlParseTryOrFinish(ctxt, 1);

    /* End tag should be consumed fully */
    TEST_ASSERT_EQUAL_size_t(end, cur_offset(ctxt));
    /* After processing end tag, parser returns to CONTENT */
    TEST_ASSERT_EQUAL_INT(XML_PARSER_CONTENT, ctxt->instate);

    htmlFreeParserCtxt(ctxt);
}

/* Main */
int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_htmlParseTryOrFinish_start_nonterminate_small_avail_returns_early);
    RUN_TEST(test_htmlParseTryOrFinish_start_terminate_progresses_and_consumes_text);
    RUN_TEST(test_htmlParseTryOrFinish_misc_to_start_tag_partial_nonterminate);
    RUN_TEST(test_htmlParseTryOrFinish_parses_comment_and_advances);
    RUN_TEST(test_htmlParseTryOrFinish_parses_simple_element_and_end_tag_full_terminate);
    RUN_TEST(test_htmlParseTryOrFinish_end_tag_only_transitions_and_consumes);
    return UNITY_END();
}