1 // Copyright 2018 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <expat.h>
16 #include <string.h>
17 #include "unity.h"
18 
19 typedef struct {
20     int depth;
21     char output[512];
22     int output_off;
23 } user_data_t;
24 
insert_space(user_data_t * user_data)25 static void insert_space(user_data_t *user_data)
26 {
27     const char align_str[] = "    ";
28 
29     TEST_ASSERT(sizeof(user_data->output) >= user_data->output_off);
30     user_data->output[user_data->output_off++] = '\n';
31 
32     for (int i = 0; i < user_data->depth; i++) {
33         for (int j = 0; j < strlen(align_str); ++j) {
34             TEST_ASSERT(sizeof(user_data->output) >= user_data->output_off);
35             user_data->output[user_data->output_off++] = align_str[j];
36         }
37     }
38 }
39 
start_element(void * userData,const XML_Char * name,const XML_Char ** atts)40 static void XMLCALL start_element(void *userData, const XML_Char *name, const XML_Char **atts)
41 {
42     user_data_t *user_data = (user_data_t *) userData;
43 
44     insert_space(user_data);
45 
46     const int ret = snprintf(user_data->output + user_data->output_off,
47             sizeof(user_data->output) - user_data->output_off,
48             "<%s>", name);
49     TEST_ASSERT_EQUAL(strlen(name) + 2, ret); // 2 are the tag characters: "<>"
50     user_data->output_off += ret;
51     ++user_data->depth;
52 }
53 
end_element(void * userData,const XML_Char * name)54 static void XMLCALL end_element(void *userData, const XML_Char *name)
55 {
56     user_data_t *user_data = (user_data_t *) userData;
57 
58     --user_data->depth;
59     insert_space(user_data);
60 
61     int ret = snprintf(user_data->output + user_data->output_off, sizeof(user_data->output) - user_data->output_off,
62                 "</%s>", name);
63     TEST_ASSERT_EQUAL(strlen(name) + 3, ret); // 3 are the tag characters: "</>"
64     user_data->output_off += ret;
65 }
66 
data_handler(void * userData,const XML_Char * s,int len)67 static void data_handler(void *userData, const XML_Char *s, int len)
68 {
69     user_data_t *user_data = (user_data_t *) userData;
70 
71     insert_space(user_data);
72 
73     // s is not zero-terminated
74     char tmp_str[len+1];
75     strlcpy(tmp_str, s, len+1);
76 
77     int ret = snprintf(user_data->output + user_data->output_off, sizeof(user_data->output) - user_data->output_off,
78                 "%s", tmp_str);
79     TEST_ASSERT_EQUAL(strlen(tmp_str), ret);
80     user_data->output_off += ret;
81 }
82 
83 TEST_CASE("Expat parses XML", "[expat]")
84 {
85     const char test_in[] = "<html><title>Page title</title><body><h>header</h><ol><li>A</li>"\
86                            "<li>B</li><li>C</li></ol></body></html>";
87     const char test_expected[] =    "\n"\
88                                     "<html>\n"\
89                                     "    <title>\n"\
90                                     "        Page title\n"\
91                                     "    </title>\n"\
92                                     "    <body>\n"\
93                                     "        <h>\n"\
94                                     "            header\n"\
95                                     "        </h>\n"\
96                                     "        <ol>\n"\
97                                     "            <li>\n"\
98                                     "                A\n"\
99                                     "            </li>\n"\
100                                     "            <li>\n"\
101                                     "                B\n"\
102                                     "            </li>\n"\
103                                     "            <li>\n"\
104                                     "                C\n"\
105                                     "            </li>\n"\
106                                     "        </ol>\n"\
107                                     "    </body>\n"\
108                                     "</html>";
109     user_data_t user_data = {
110         .depth = 0,
111         .output = { '\0' },
112         .output_off = 0
113     };
114 
115     XML_Parser parser = XML_ParserCreate(NULL);
116     XML_SetUserData(parser, &user_data);
117     XML_SetElementHandler(parser, start_element, end_element);
118     XML_SetCharacterDataHandler(parser, data_handler);
119 
120     TEST_ASSERT_NOT_EQUAL(XML_STATUS_ERROR, XML_Parse(parser, test_in, strlen(test_in), 1));
121     XML_ParserFree(parser);
122 
123     TEST_ASSERT_EQUAL(0, user_data.depth); // all closing tags have been found
124 
125     TEST_ASSERT_EQUAL(strlen(test_expected), strlen(user_data.output));
126     TEST_ASSERT_EQUAL_STRING(test_expected, user_data.output);
127 }
128