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

#include <libxml/xmlmemory.h>
#include <libxml/xmlstring.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

/* Internal type needed for the wrapper's return type */
#include "private/dict.h"

/* Wrapper provided in the module for the static function */
extern xmlHashedString test_htmlParseAttribute(htmlParserCtxtPtr ctxt, xmlChar **value);

static htmlParserCtxtPtr make_ctxt_from_mem_len(const char *data, int len) {
    htmlParserCtxtPtr ctxt = htmlCreateMemoryParserCtxt(data, len);
    TEST_ASSERT_NOT_NULL_MESSAGE(ctxt, "Failed to create HTML parser context");
    return ctxt;
}

void setUp(void) {
    /* Setup code here, or leave empty */
}

void tearDown(void) {
    /* Cleanup code here, or leave empty */
}

/* Simple unquoted value: id=val */
void test_htmlParseAttribute_unquoted_simple(void) {
    const char *src = "id=val";
    htmlParserCtxtPtr ctxt = make_ctxt_from_mem_len(src, (int)strlen(src));

    xmlChar *val = (xmlChar*)0x1; /* sentinel */
    xmlHashedString h = test_htmlParseAttribute(ctxt, &val);

    TEST_ASSERT_NOT_NULL(h.name);
    TEST_ASSERT_EQUAL_STRING("id", (const char*)h.name);
    TEST_ASSERT_NOT_NULL(val);
    TEST_ASSERT_EQUAL_STRING("val", (const char*)val);

    xmlFree(val);
    htmlFreeParserCtxt(ctxt);
}

/* Quoted value with whitespace around '=' and ASCII uppercased name that must be lowercased */
void test_htmlParseAttribute_quoted_double_with_spaces(void) {
    const char *src = "CLASS   =   \"MyVal\"";
    htmlParserCtxtPtr ctxt = make_ctxt_from_mem_len(src, (int)strlen(src));

    xmlChar *val = NULL;
    xmlHashedString h = test_htmlParseAttribute(ctxt, &val);

    TEST_ASSERT_NOT_NULL(h.name);
    TEST_ASSERT_EQUAL_STRING("class", (const char*)h.name);
    TEST_ASSERT_NOT_NULL(val);
    TEST_ASSERT_EQUAL_STRING("MyVal", (const char*)val);

    xmlFree(val);
    htmlFreeParserCtxt(ctxt);
}

/* Single-quoted value with entity decoding */
void test_htmlParseAttribute_quoted_single_with_entity_decoding(void) {
    const char *src = "title='a&amp;b'";
    htmlParserCtxtPtr ctxt = make_ctxt_from_mem_len(src, (int)strlen(src));

    xmlChar *val = NULL;
    xmlHashedString h = test_htmlParseAttribute(ctxt, &val);

    TEST_ASSERT_NOT_NULL(h.name);
    TEST_ASSERT_EQUAL_STRING("title", (const char*)h.name);
    TEST_ASSERT_NOT_NULL(val);
    TEST_ASSERT_EQUAL_STRING("a&b", (const char*)val);

    xmlFree(val);
    htmlFreeParserCtxt(ctxt);
}

/* Attribute with no value (no '=' present) */
void test_htmlParseAttribute_no_value_present(void) {
    const char *src = "checked   ";
    htmlParserCtxtPtr ctxt = make_ctxt_from_mem_len(src, (int)strlen(src));

    xmlChar *val = (xmlChar*)0x1; /* sentinel, must become NULL */
    xmlHashedString h = test_htmlParseAttribute(ctxt, &val);

    TEST_ASSERT_NOT_NULL(h.name);
    TEST_ASSERT_EQUAL_STRING("checked", (const char*)h.name);
    TEST_ASSERT_NULL(val);

    htmlFreeParserCtxt(ctxt);
}

/* Non-ASCII in name should be preserved; ASCII uppercase must be lowercased */
void test_htmlParseAttribute_non_ascii_name_lowercase_ascii_only(void) {
    /* "Dátâ=1" in UTF-8; expect name "dátâ" */
    const char src[] = "D\xc3\xa1t\xc3\xa2=1";
    htmlParserCtxtPtr ctxt = make_ctxt_from_mem_len(src, (int)sizeof(src) - 1);

    xmlChar *val = NULL;
    xmlHashedString h = test_htmlParseAttribute(ctxt, &val);

    TEST_ASSERT_NOT_NULL(h.name);
    const char expected_name[] = "d\xc3\xa1t\xc3\xa2";
    TEST_ASSERT_EQUAL_STRING(expected_name, (const char*)h.name);
    TEST_ASSERT_NOT_NULL(val);
    TEST_ASSERT_EQUAL_STRING("1", (const char*)val);

    xmlFree(val);
    htmlFreeParserCtxt(ctxt);
}

/* Embedded NUL in name must be replaced by U+FFFD (EF BF BD) */
void test_htmlParseAttribute_embedded_nul_in_name_replaced(void) {
    /* Construct "na\0me=1" with an embedded NUL. */
    const char src_with_nul[] = { 'n', 'a', '\0', 'm', 'e', '=', '1' };
    htmlParserCtxtPtr ctxt = make_ctxt_from_mem_len(src_with_nul, (int)sizeof(src_with_nul));

    xmlChar *val = NULL;
    xmlHashedString h = test_htmlParseAttribute(ctxt, &val);

    TEST_ASSERT_NOT_NULL(h.name);
    /* Expected name: "na" + EF BF BD + "me" */
    const char expected_name[] = { 'n', 'a', (char)0xEF, (char)0xBF, (char)0xBD, 'm', 'e', '\0' };
    TEST_ASSERT_EQUAL_STRING(expected_name, (const char*)h.name);

    TEST_ASSERT_NOT_NULL(val);
    TEST_ASSERT_EQUAL_STRING("1", (const char*)val);

    xmlFree(val);
    htmlFreeParserCtxt(ctxt);
}

/* Empty input should yield no name and no value */
void test_htmlParseAttribute_empty_input(void) {
    const char *src = "";
    htmlParserCtxtPtr ctxt = make_ctxt_from_mem_len(src, 0);

    xmlChar *val = (xmlChar*)0x1; /* sentinel */
    xmlHashedString h = test_htmlParseAttribute(ctxt, &val);

    TEST_ASSERT_NULL(h.name);
    TEST_ASSERT_NULL(val);

    htmlFreeParserCtxt(ctxt);
}

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

    RUN_TEST(test_htmlParseAttribute_unquoted_simple);
    RUN_TEST(test_htmlParseAttribute_quoted_double_with_spaces);
    RUN_TEST(test_htmlParseAttribute_quoted_single_with_entity_decoding);
    RUN_TEST(test_htmlParseAttribute_no_value_present);
    RUN_TEST(test_htmlParseAttribute_non_ascii_name_lowercase_ascii_only);
    RUN_TEST(test_htmlParseAttribute_embedded_nul_in_name_replaced);
    RUN_TEST(test_htmlParseAttribute_empty_input);

    int ret = UNITY_END();
    xmlCleanupParser();
    return ret;
}