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 |
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 (s1: element_name, s2: "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, format: "<bool(%d) %d %d %d>" , |
55 | result, mb, ob, tri); |
56 | } |
57 | |
58 | else if (strcmp (s1: element_name, s2: "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, format: "<str(%d) %s %s %s %s>" , |
72 | result, n (cm), n (am), n (ao), n (co)); |
73 | |
74 | g_free (mem: am); |
75 | g_free (mem: 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 |
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 (init: "" ); |
154 | ctx = g_markup_parse_context_new (parser: &parser, flags: G_MARKUP_IGNORE_QUALIFIED, user_data: string, NULL); |
155 | result = g_markup_parse_context_parse (context: ctx, |
156 | text: test->document, |
157 | text_len: -1, error: &error); |
158 | if (result) |
159 | result = g_markup_parse_context_end_parse (context: ctx, error: &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 (context: ctx); |
173 | g_string_free (string, TRUE); |
174 | g_clear_error (err: &error); |
175 | } |
176 | |
177 | #define XML "<element a='1' b='2' c='3'/>" |
178 | |
179 | static void |
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 | first_type: G_MARKUP_COLLECT_STRING, first_attr: "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 |
202 | test_cleanup (void) |
203 | { |
204 | GMarkupParseContext *context; |
205 | |
206 | if (!g_test_undefined ()) |
207 | return; |
208 | |
209 | context = g_markup_parse_context_new (parser: &cleanup_parser, flags: 0, NULL, NULL); |
210 | g_markup_parse_context_parse (context, XML, text_len: -1, NULL); |
211 | |
212 | g_test_expect_message (G_LOG_DOMAIN, log_level: G_LOG_LEVEL_CRITICAL, |
213 | pattern: "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 |
221 | main (int argc, char **argv) |
222 | { |
223 | gsize i; |
224 | gchar *path; |
225 | |
226 | g_test_init (argc: &argc, argv: &argv, NULL); |
227 | |
228 | for (i = 0; i < G_N_ELEMENTS (tests); i++) |
229 | { |
230 | path = g_strdup_printf (format: "/markup/collect/%" G_GSIZE_FORMAT, i); |
231 | g_test_add_data_func (testpath: path, test_data: &tests[i], test_func: test_collect); |
232 | g_free (mem: path); |
233 | } |
234 | |
235 | g_test_add_func (testpath: "/markup/collect/cleanup" , test_func: test_cleanup); |
236 | |
237 | return g_test_run (); |
238 | } |
239 | |