Imported Upstream version 2.73.3
[platform/upstream/glib.git] / glib / tests / markup-parse.c
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
9 static int depth = 0;
10 static GString *string;
11
12 static void
13 indent (int extra)
14 {
15   int i = 0;
16   while (i < depth)
17     {
18       g_string_append (string, "  ");
19       ++i;
20     }
21 }
22
23 static void
24 start_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 (0);
34   g_string_append_printf (string, "ELEMENT '%s'\n", element_name);
35
36   i = 0;
37   while (attribute_names[i] != NULL)
38     {
39       indent (1);
40
41       g_string_append_printf (string, "%s=\"%s\"\n",
42                               attribute_names[i],
43                               attribute_values[i]);
44       
45       ++i;
46     }
47   
48   ++depth;
49 }
50
51 static void
52 end_element_handler (GMarkupParseContext *context,
53                      const gchar         *element_name,
54                      gpointer             user_data,
55                      GError             **error)
56 {
57   --depth;
58   indent (0);
59   g_string_append_printf (string, "END '%s'\n", element_name);
60   }
61
62 static void
63 text_handler (GMarkupParseContext *context,
64               const gchar         *text,
65               gsize                text_len,
66               gpointer             user_data,
67               GError             **error)
68 {
69   indent (0);
70   g_string_append_printf (string, "TEXT '%.*s'\n", (int)text_len, text);
71 }
72
73
74 static void
75 passthrough_handler (GMarkupParseContext *context,
76                      const gchar         *passthrough_text,
77                      gsize                text_len,
78                      gpointer             user_data,
79                      GError             **error)
80 {
81   indent (0);
82
83   g_string_append_printf (string, "PASS '%.*s'\n", (int)text_len, passthrough_text);
84 }
85
86 static void
87 error_handler (GMarkupParseContext *context,
88                GError              *error,
89                gpointer             user_data)
90 {
91   g_string_append_printf (string, "ERROR %s\n", error->message);
92 }
93
94 static const GMarkupParser parser = {
95   start_element_handler,
96   end_element_handler,
97   text_handler,
98   passthrough_handler,
99   error_handler
100 };
101
102 static const GMarkupParser silent_parser = {
103   NULL,
104   NULL,
105   NULL,
106   NULL,
107   error_handler
108 };
109
110 static int
111 test_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 (&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                                          contents + i,
127                                          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. */
152 static gboolean
153 test_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, &length_bytes, &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 (length_bytes);
172   if (contents_unterminated != NULL)
173     memcpy (contents_unterminated, contents, length_bytes);
174
175   /* Test with nul termination. */
176   context = g_markup_parse_context_new (&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, &col);
179   g_assert_cmpint (line, ==, 1);
180   g_assert_cmpint (col, ==, 1);
181
182   if (!g_markup_parse_context_parse (context, contents, -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 ("");
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_bytes, 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_unterminated, length_bytes, 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_bytes, chunk_sizes_bytes[i], flags) != 0)
210         n_failures++;
211       n_tests++;
212     }
213
214   g_free (contents);
215   g_free (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
230 static gchar *
231 get_expected_filename (const gchar       *filename,
232                        GMarkupParseFlags  flags)
233 {
234   gchar *f, *p, *expected;
235
236   f = g_strdup (filename);
237   p = strstr (f, ".gmarkup");
238   if (p)
239     *p = 0;
240   if (flags == 0)
241     expected = g_strconcat (f, ".expected", NULL);
242   else if (flags == G_MARKUP_TREAT_CDATA_AS_TEXT)
243     expected = g_strconcat (f, ".cdata-as-text", NULL);
244   else
245     g_assert_not_reached ();
246
247   g_free (f);
248
249   return expected;
250 }
251
252 static void
253 test_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 (filename, "valid") != NULL;
263   expected_file = get_expected_filename (filename, 0);
264
265   depth = 0;
266   string = g_string_sized_new (0);
267
268   res = test_file (filename, 0);
269   g_assert_cmpint (res, ==, valid_input);
270
271   g_file_get_contents (expected_file, &expected, NULL, &error);
272   g_assert_no_error (error);
273   g_assert_cmpstr (string->str, ==, expected);
274   g_free (expected);
275
276   g_string_free (string, TRUE);
277
278   g_free (expected_file);
279
280   expected_file = get_expected_filename (filename, G_MARKUP_TREAT_CDATA_AS_TEXT);
281   if (g_file_test (expected_file, G_FILE_TEST_EXISTS))
282     {
283       depth = 0;
284       string = g_string_sized_new (0);
285
286       res = test_file (filename, G_MARKUP_TREAT_CDATA_AS_TEXT);
287       g_assert_cmpint (res, ==, valid_input);
288
289       g_file_get_contents (expected_file, &expected, NULL, &error);
290       g_assert_no_error (error);
291       g_assert_cmpstr (string->str, ==, expected);
292       g_free (expected);
293
294       g_string_free (string, TRUE);
295     }
296
297   g_free (expected_file);
298 }
299
300 int
301 main (int argc, char *argv[])
302 {
303   GDir *dir;
304   GError *error;
305   const gchar *name;
306   gchar *path;
307
308   g_setenv ("LC_ALL", "C", TRUE);
309   setlocale (LC_ALL, "");
310
311   g_test_init (&argc, &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 = G_MARKUP_DEFAULT_FLAGS;
318
319       if (strcmp (argv[1], "--cdata-as-text") == 0)
320         {
321           flags = G_MARKUP_TREAT_CDATA_AS_TEXT;
322           arg = 2;
323         }
324       string = g_string_sized_new (0);
325       test_file (argv[arg], flags);
326       g_print ("%s", string->str);
327       return 0;
328     }
329
330   error = NULL;
331   path = g_test_build_filename (G_TEST_DIST, "markups", NULL);
332   dir = g_dir_open (path, 0, &error);
333   g_free (path);
334   g_assert_no_error (error);
335   while ((name = g_dir_read_name (dir)) != NULL)
336     {
337       if (!strstr (name, "gmarkup"))
338         continue;
339
340       path = g_strdup_printf ("/markup/parse/%s", name);
341       g_test_add_data_func_full (path, g_test_build_filename (G_TEST_DIST, "markups", name, NULL),
342                                  test_parse, g_free);
343       g_free (path);
344     }
345   g_dir_close (dir);
346
347   return g_test_run ();
348 }