1#undef G_DISABLE_ASSERT
2#undef G_LOG_DOMAIN
3
4#include <locale.h>
5#include <string.h>
6#include <stdio.h>
7#include <glib.h>
8
9static int depth = 0;
10static GString *string;
11
12static void
13indent (int extra)
14{
15 int i = 0;
16 while (i < depth)
17 {
18 g_string_append (string, val: " ");
19 ++i;
20 }
21}
22
23static void
24start_element_handler (GMarkupParseContext *context,
25 const gchar *element_name,
26 const gchar **attribute_names,
27 const gchar **attribute_values,
28 gpointer user_data,
29 GError **error)
30{
31 int i;
32
33 indent (extra: 0);
34 g_string_append_printf (string, format: "ELEMENT '%s'\n", element_name);
35
36 i = 0;
37 while (attribute_names[i] != NULL)
38 {
39 indent (extra: 1);
40
41 g_string_append_printf (string, format: "%s=\"%s\"\n",
42 attribute_names[i],
43 attribute_values[i]);
44
45 ++i;
46 }
47
48 ++depth;
49}
50
51static void
52end_element_handler (GMarkupParseContext *context,
53 const gchar *element_name,
54 gpointer user_data,
55 GError **error)
56{
57 --depth;
58 indent (extra: 0);
59 g_string_append_printf (string, format: "END '%s'\n", element_name);
60 }
61
62static void
63text_handler (GMarkupParseContext *context,
64 const gchar *text,
65 gsize text_len,
66 gpointer user_data,
67 GError **error)
68{
69 indent (extra: 0);
70 g_string_append_printf (string, format: "TEXT '%.*s'\n", (int)text_len, text);
71}
72
73
74static void
75passthrough_handler (GMarkupParseContext *context,
76 const gchar *passthrough_text,
77 gsize text_len,
78 gpointer user_data,
79 GError **error)
80{
81 indent (extra: 0);
82
83 g_string_append_printf (string, format: "PASS '%.*s'\n", (int)text_len, passthrough_text);
84}
85
86static void
87error_handler (GMarkupParseContext *context,
88 GError *error,
89 gpointer user_data)
90{
91 g_string_append_printf (string, format: "ERROR %s\n", error->message);
92}
93
94static const GMarkupParser parser = {
95 start_element_handler,
96 end_element_handler,
97 text_handler,
98 passthrough_handler,
99 error_handler
100};
101
102static const GMarkupParser silent_parser = {
103 NULL,
104 NULL,
105 NULL,
106 NULL,
107 error_handler
108};
109
110static int
111test_in_chunks (const gchar *contents,
112 gint length,
113 gint chunk_size,
114 GMarkupParseFlags flags)
115{
116 GMarkupParseContext *context;
117 int i = 0;
118
119 context = g_markup_parse_context_new (parser: &silent_parser, flags, NULL, NULL);
120
121 while (i < length)
122 {
123 int this_chunk = MIN (length - i, chunk_size);
124
125 if (!g_markup_parse_context_parse (context,
126 text: contents + i,
127 text_len: this_chunk,
128 NULL))
129 {
130 g_markup_parse_context_free (context);
131 return 1;
132 }
133
134 i += this_chunk;
135 }
136
137 if (!g_markup_parse_context_end_parse (context, NULL))
138 {
139 g_markup_parse_context_free (context);
140 return 1;
141 }
142
143 g_markup_parse_context_free (context);
144
145 return 0;
146}
147
148/* Load the given @filename and parse it multiple times with different chunking
149 * and length handling. All results should be equal. %TRUE is returned if the
150 * file was parsed successfully on every attempt; %FALSE if it failed to parse
151 * on every attempt. The test aborts if some attempts succeed and some fail. */
152static gboolean
153test_file (const gchar *filename,
154 GMarkupParseFlags flags)
155{
156 gchar *contents = NULL, *contents_unterminated = NULL;
157 gsize length_bytes;
158 GError *local_error = NULL;
159 GMarkupParseContext *context;
160 gint line, col;
161 guint n_failures = 0;
162 guint n_tests = 0;
163 const gsize chunk_sizes_bytes[] = { 1, 2, 5, 12, 1024 };
164 gsize i;
165 GString *first_string = NULL;
166
167 g_file_get_contents (filename, contents: &contents, length: &length_bytes, error: &local_error);
168 g_assert_no_error (local_error);
169
170 /* Make a copy of the contents with no trailing nul. */
171 contents_unterminated = g_malloc (n_bytes: length_bytes);
172 if (contents_unterminated != NULL)
173 memcpy (dest: contents_unterminated, src: contents, n: length_bytes);
174
175 /* Test with nul termination. */
176 context = g_markup_parse_context_new (parser: &parser, flags, NULL, NULL);
177 g_assert (g_markup_parse_context_get_user_data (context) == NULL);
178 g_markup_parse_context_get_position (context, line_number: &line, char_number: &col);
179 g_assert_cmpint (line, ==, 1);
180 g_assert_cmpint (col, ==, 1);
181
182 if (!g_markup_parse_context_parse (context, text: contents, text_len: -1, NULL) ||
183 !g_markup_parse_context_end_parse (context, NULL))
184 n_failures++;
185 n_tests++;
186
187 g_markup_parse_context_free (context);
188
189 /* FIXME: Swap out the error string so we only return one copy of it, not
190 * @n_tests copies. This should be fixed properly by eliminating the global
191 * state in this file. */
192 first_string = g_steal_pointer (&string);
193 string = g_string_new (init: "");
194
195 /* With the length specified explicitly and a nul terminator present (since
196 * g_file_get_contents() always adds one). */
197 if (test_in_chunks (contents, length: length_bytes, chunk_size: length_bytes, flags) != 0)
198 n_failures++;
199 n_tests++;
200
201 /* With the length specified explicitly and no nul terminator present. */
202 if (test_in_chunks (contents: contents_unterminated, length: length_bytes, chunk_size: length_bytes, flags) != 0)
203 n_failures++;
204 n_tests++;
205
206 /* In various sized chunks. */
207 for (i = 0; i < G_N_ELEMENTS (chunk_sizes_bytes); i++)
208 {
209 if (test_in_chunks (contents, length: length_bytes, chunk_size: chunk_sizes_bytes[i], flags) != 0)
210 n_failures++;
211 n_tests++;
212 }
213
214 g_free (mem: contents);
215 g_free (mem: contents_unterminated);
216
217 /* FIXME: Restore the error string. */
218 g_string_free (string, TRUE);
219 string = g_steal_pointer (&first_string);
220
221 /* We expect the file to either always be parsed successfully, or never be
222 * parsed successfully. There’s a bug in GMarkup if it sometimes parses
223 * successfully depending on how you chunk or terminate the input. */
224 if (n_failures > 0)
225 g_assert_cmpint (n_failures, ==, n_tests);
226
227 return (n_failures == 0);
228}
229
230static gchar *
231get_expected_filename (const gchar *filename,
232 GMarkupParseFlags flags)
233{
234 gchar *f, *p, *expected;
235
236 f = g_strdup (str: filename);
237 p = strstr (haystack: f, needle: ".gmarkup");
238 if (p)
239 *p = 0;
240 if (flags == 0)
241 expected = g_strconcat (string1: f, ".expected", NULL);
242 else if (flags == G_MARKUP_TREAT_CDATA_AS_TEXT)
243 expected = g_strconcat (string1: f, ".cdata-as-text", NULL);
244 else
245 g_assert_not_reached ();
246
247 g_free (mem: f);
248
249 return expected;
250}
251
252static void
253test_parse (gconstpointer d)
254{
255 const gchar *filename = d;
256 gchar *expected_file;
257 gchar *expected;
258 gboolean valid_input;
259 GError *error = NULL;
260 gboolean res;
261
262 valid_input = strstr (haystack: filename, needle: "valid") != NULL;
263 expected_file = get_expected_filename (filename, flags: 0);
264
265 depth = 0;
266 string = g_string_sized_new (dfl_size: 0);
267
268 res = test_file (filename, flags: 0);
269 g_assert_cmpint (res, ==, valid_input);
270
271 g_file_get_contents (filename: expected_file, contents: &expected, NULL, error: &error);
272 g_assert_no_error (error);
273 g_assert_cmpstr (string->str, ==, expected);
274 g_free (mem: expected);
275
276 g_string_free (string, TRUE);
277
278 g_free (mem: expected_file);
279
280 expected_file = get_expected_filename (filename, flags: G_MARKUP_TREAT_CDATA_AS_TEXT);
281 if (g_file_test (filename: expected_file, test: G_FILE_TEST_EXISTS))
282 {
283 depth = 0;
284 string = g_string_sized_new (dfl_size: 0);
285
286 res = test_file (filename, flags: G_MARKUP_TREAT_CDATA_AS_TEXT);
287 g_assert_cmpint (res, ==, valid_input);
288
289 g_file_get_contents (filename: expected_file, contents: &expected, NULL, error: &error);
290 g_assert_no_error (error);
291 g_assert_cmpstr (string->str, ==, expected);
292 g_free (mem: expected);
293
294 g_string_free (string, TRUE);
295 }
296
297 g_free (mem: expected_file);
298}
299
300int
301main (int argc, char *argv[])
302{
303 GDir *dir;
304 GError *error;
305 const gchar *name;
306 gchar *path;
307
308 g_setenv (variable: "LC_ALL", value: "C", TRUE);
309 setlocale (LC_ALL, locale: "");
310
311 g_test_init (argc: &argc, argv: &argv, NULL);
312
313 /* allow to easily generate expected output for new test cases */
314 if (argc > 1)
315 {
316 gint arg = 1;
317 GMarkupParseFlags flags = 0;
318
319 if (strcmp (s1: argv[1], s2: "--cdata-as-text") == 0)
320 {
321 flags = G_MARKUP_TREAT_CDATA_AS_TEXT;
322 arg = 2;
323 }
324 string = g_string_sized_new (dfl_size: 0);
325 test_file (filename: argv[arg], flags);
326 g_print (format: "%s", string->str);
327 return 0;
328 }
329
330 error = NULL;
331 path = g_test_build_filename (file_type: G_TEST_DIST, first_path: "markups", NULL);
332 dir = g_dir_open (path, flags: 0, error: &error);
333 g_free (mem: path);
334 g_assert_no_error (error);
335 while ((name = g_dir_read_name (dir)) != NULL)
336 {
337 if (!strstr (haystack: name, needle: "gmarkup"))
338 continue;
339
340 path = g_strdup_printf (format: "/markup/parse/%s", name);
341 g_test_add_data_func_full (testpath: path, test_data: g_test_build_filename (file_type: G_TEST_DIST, first_path: "markups", name, NULL),
342 test_func: test_parse, data_free_func: g_free);
343 g_free (mem: path);
344 }
345 g_dir_close (dir);
346
347 return g_test_run ();
348}
349

source code of gtk/subprojects/glib/glib/tests/markup-parse.c