1 /*
2  * Copyright © 2007 Ryan Lortie
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * See the included COPYING file for more information.
10  */
11 
12 #include <stdlib.h>
13 #include <string.h>
14 #include <glib.h>
15 
16 static void
start(GMarkupParseContext * context,const char * element_name,const char ** attribute_names,const char ** attribute_values,gpointer user_data,GError ** error)17 start (GMarkupParseContext  *context,
18        const char           *element_name,
19        const char          **attribute_names,
20        const char          **attribute_values,
21        gpointer              user_data,
22        GError              **error)
23 {
24   GString *string = user_data;
25   gboolean result;
26 
27 #define collect(...) \
28   g_markup_collect_attributes (element_name, attribute_names, \
29                                attribute_values, error, __VA_ARGS__, \
30                                G_MARKUP_COLLECT_INVALID)
31 #define BOOL    G_MARKUP_COLLECT_BOOLEAN
32 #define OPTBOOL G_MARKUP_COLLECT_BOOLEAN | G_MARKUP_COLLECT_OPTIONAL
33 #define TRI     G_MARKUP_COLLECT_TRISTATE
34 #define STR     G_MARKUP_COLLECT_STRING
35 #define STRDUP  G_MARKUP_COLLECT_STRDUP
36 #define OPTSTR  G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL
37 #define OPTDUP  G_MARKUP_COLLECT_STRDUP | G_MARKUP_COLLECT_OPTIONAL
38 #define n(x)    ((x)?(x):"(null)")
39 
40   if (strcmp (element_name, "bool") == 0)
41     {
42       gboolean mb = 2, ob = 2, tri = 2;
43 
44       result = collect (BOOL,    "mb", &mb,
45                         OPTBOOL, "ob", &ob,
46                         TRI,     "tri", &tri);
47 
48       g_assert (result ||
49                 (mb == FALSE && ob == FALSE && tri != TRUE && tri != FALSE));
50 
51       if (tri != FALSE && tri != TRUE)
52         tri = -1;
53 
54       g_string_append_printf (string, "<bool(%d) %d %d %d>",
55                               result, mb, ob, tri);
56     }
57 
58   else if (strcmp (element_name, "str") == 0)
59     {
60       const char *cm, *co;
61       char *am, *ao;
62 
63       result = collect (STR,    "cm", &cm,
64                         STRDUP, "am", &am,
65                         OPTDUP, "ao", &ao,
66                         OPTSTR, "co", &co);
67 
68       g_assert (result ||
69                 (cm == NULL && am == NULL && ao == NULL && co == NULL));
70 
71       g_string_append_printf (string, "<str(%d) %s %s %s %s>",
72                               result, n (cm), n (am), n (ao), n (co));
73 
74       g_free (am);
75       g_free (ao);
76     }
77 }
78 
79 static GMarkupParser parser = { start, NULL, NULL, NULL, NULL };
80 
81 struct test
82 {
83   const char   *document;
84   const char   *result;
85   GMarkupError  error_code;
86   const char   *error_info;
87 };
88 
89 static struct test tests[] =
90 {
91   { "<bool mb='y'>", "<bool(1) 1 0 -1>",
92     G_MARKUP_ERROR_PARSE, "'bool'" },
93 
94   { "<bool mb='false'/>", "<bool(1) 0 0 -1>", 0, NULL },
95   { "<bool mb='true'/>", "<bool(1) 1 0 -1>", 0, NULL },
96   { "<bool mb='t' ob='f' tri='1'/>", "<bool(1) 1 0 1>", 0, NULL },
97   { "<bool mb='y' ob='n' tri='0'/>", "<bool(1) 1 0 0>", 0, NULL },
98 
99   { "<bool mb='y' my:attr='q'><my:tag/></bool>", "<bool(1) 1 0 -1>", 0, NULL },
100   { "<bool mb='y' my:attr='q'><my:tag>some <b>text</b> is in here</my:tag></bool>",
101     "<bool(1) 1 0 -1>", 0, NULL },
102 
103   { "<bool ob='y'/>", "<bool(0) 0 0 -1>",
104     G_MARKUP_ERROR_MISSING_ATTRIBUTE, "'mb'" },
105 
106   { "<bool mb='y' mb='y'/>", "<bool(0) 0 0 -1>",
107     G_MARKUP_ERROR_INVALID_CONTENT, "'mb'" },
108 
109   { "<bool mb='y' tri='y' tri='n'/>", "<bool(0) 0 0 -1>",
110     G_MARKUP_ERROR_INVALID_CONTENT, "'tri'" },
111 
112   { "<str cm='x' am='y'/>", "<str(1) x y (null) (null)>", 0, NULL },
113 
114   { "<str am='x' co='y'/>", "<str(0) (null) (null) (null) (null)>",
115     G_MARKUP_ERROR_MISSING_ATTRIBUTE, "'cm'" },
116 
117   { "<str am='x'/>", "<str(0) (null) (null) (null) (null)>",
118     G_MARKUP_ERROR_MISSING_ATTRIBUTE, "'cm'" },
119 
120   { "<str am='x' cm='x' am='y'/>", "<str(0) (null) (null) (null) (null)>",
121     G_MARKUP_ERROR_INVALID_CONTENT, "'am'" },
122 
123   { "<str am='x' qm='y' cm='x'/>", "<str(0) (null) (null) (null) (null)>",
124     G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "'qm'" },
125 
126   { "<str am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' cm='x'/>", "<str(0) (null) (null) (null) (null)>",
127     G_MARKUP_ERROR_INVALID_CONTENT, "'am'" },
128 
129   { "<str cm='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x' am='x'/>", "<str(0) (null) (null) (null) (null)>",
130     G_MARKUP_ERROR_INVALID_CONTENT, "'am'" },
131 
132   { "<str a='x' b='x' c='x' d='x' e='x' f='x' g='x' h='x' i='x' j='x' k='x' l='x' m='x' n='x' o='x' p='x' q='x' r='x' s='x' t='x' u='x' v='x' w='x' x='x' y='x' z='x' aa='x' bb='x' cc='x' dd='x' ee='x' ff='x' gg='x' hh='x' ii='x' jj='x' kk='x' ll='x' mm='x' nn='x' oo='x' pp='x' qq='x' rr='x' ss='x' tt='x' uu='x' vv='x' ww='x' xx='x' yy='x' zz='x' am='x' cm='x'/>",
133     "<str(0) (null) (null) (null) (null)>",
134     G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "'a'" },
135 
136   { "<bool mb='ja'/>", "<bool(0) 0 0 -1>",
137     G_MARKUP_ERROR_INVALID_CONTENT, "'mb'" },
138 
139   { "<bool mb='nein'/>", "<bool(0) 0 0 -1>",
140     G_MARKUP_ERROR_INVALID_CONTENT, "'mb'" }
141 };
142 
143 static void
test_collect(gconstpointer d)144 test_collect (gconstpointer d)
145 {
146   const struct test *test = d;
147 
148   GMarkupParseContext *ctx;
149   GError *error = NULL;
150   GString *string;
151   gboolean result;
152 
153   string = g_string_new ("");
154   ctx = g_markup_parse_context_new (&parser, G_MARKUP_IGNORE_QUALIFIED, string, NULL);
155   result = g_markup_parse_context_parse (ctx,
156                                          test->document,
157                                          -1, &error);
158   if (result)
159     result = g_markup_parse_context_end_parse (ctx, &error);
160 
161   if (result)
162     {
163       g_assert_no_error (error);
164       g_assert_cmpint (test->error_code, ==, 0);
165       g_assert_cmpstr (test->result, ==, string->str);
166     }
167   else
168     {
169       g_assert_error (error, G_MARKUP_ERROR, (gint) test->error_code);
170     }
171 
172   g_markup_parse_context_free (ctx);
173   g_string_free (string, TRUE);
174   g_clear_error (&error);
175 }
176 
177 #define XML "<element a='1' b='2' c='3'/>"
178 
179 static void
start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)180 start_element (GMarkupParseContext  *context,
181                const gchar          *element_name,
182                const gchar         **attribute_names,
183                const gchar         **attribute_values,
184                gpointer              user_data,
185                GError              **error)
186 {
187   /* Omitting "c" attribute intentionally to trigger crash. */
188   g_markup_collect_attributes (element_name,
189                                attribute_names,
190                                attribute_values,
191                                error,
192                                G_MARKUP_COLLECT_STRING, "a", NULL,
193                                G_MARKUP_COLLECT_STRING, "b", NULL,
194                                G_MARKUP_COLLECT_INVALID);
195 }
196 
197 static GMarkupParser cleanup_parser = {
198   start_element, NULL, NULL, NULL, NULL
199 };
200 
201 static void
test_cleanup(void)202 test_cleanup (void)
203 {
204   GMarkupParseContext *context;
205 
206   if (!g_test_undefined ())
207     return;
208 
209   context = g_markup_parse_context_new (&cleanup_parser, 0, NULL, NULL);
210   g_markup_parse_context_parse (context, XML, -1, NULL);
211 
212   g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
213                          "g_markup_parse_context_end_parse: assertion 'context->state != STATE_ERROR' failed");
214   g_markup_parse_context_end_parse (context, NULL);
215   g_test_assert_expected_messages ();
216 
217   g_markup_parse_context_free (context);
218 }
219 
220 int
main(int argc,char ** argv)221 main (int argc, char **argv)
222 {
223   gsize i;
224   gchar *path;
225 
226   g_test_init (&argc, &argv, NULL);
227 
228   for (i = 0; i < G_N_ELEMENTS (tests); i++)
229     {
230       path = g_strdup_printf ("/markup/collect/%" G_GSIZE_FORMAT, i);
231       g_test_add_data_func (path, &tests[i], test_collect);
232       g_free (path);
233     }
234 
235   g_test_add_func ("/markup/collect/cleanup", test_cleanup);
236 
237   return g_test_run ();
238 }
239