1/*
2 * Copyright (C) 2011 Red Hat Inc.
3 *
4 * Author:
5 * Benjamin Otte <otte@redhat.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "config.h"
22
23#include <gtk/gtk.h>
24
25static char *
26test_get_reference_file (const char *node_file)
27{
28 GString *file = g_string_new (NULL);
29
30 if (g_str_has_suffix (str: node_file, suffix: ".node"))
31 g_string_append_len (string: file, val: node_file, len: strlen (s: node_file) - 5);
32 else
33 g_string_append (string: file, val: node_file);
34
35 g_string_append (string: file, val: ".ref.node");
36
37 if (!g_file_test (filename: file->str, test: G_FILE_TEST_EXISTS))
38 {
39 g_string_free (string: file, TRUE);
40 return g_strdup (str: node_file);
41 }
42
43 return g_string_free (string: file, FALSE);
44}
45
46static char *
47test_get_errors_file (const char *node_file)
48{
49 GString *file = g_string_new (NULL);
50
51 if (g_str_has_suffix (str: node_file, suffix: ".node"))
52 g_string_append_len (string: file, val: node_file, len: strlen (s: node_file) - 5);
53 else
54 g_string_append (string: file, val: node_file);
55
56 g_string_append (string: file, val: ".errors");
57
58 if (!g_file_test (filename: file->str, test: G_FILE_TEST_EXISTS))
59 {
60 g_string_free (string: file, TRUE);
61 return NULL;
62 }
63
64 return g_string_free (string: file, FALSE);
65}
66
67static GBytes *
68diff_with_file (const char *file1,
69 GBytes *input,
70 GError **error)
71{
72 GSubprocess *process;
73 GBytes *output;
74
75 process = g_subprocess_new (flags: G_SUBPROCESS_FLAGS_STDIN_PIPE
76 | G_SUBPROCESS_FLAGS_STDOUT_PIPE,
77 error,
78 argv0: "diff", "-u", file1, "-", NULL);
79 if (process == NULL)
80 return NULL;
81
82 if (!g_subprocess_communicate (subprocess: process,
83 stdin_buf: input,
84 NULL,
85 stdout_buf: &output,
86 NULL,
87 error))
88 {
89 g_object_unref (object: process);
90 return NULL;
91 }
92
93 if (!g_subprocess_get_successful (subprocess: process) &&
94 /* this is the condition when the files differ */
95 !(g_subprocess_get_if_exited (subprocess: process) && g_subprocess_get_exit_status (subprocess: process) == 1))
96 {
97 g_clear_pointer (&output, g_bytes_unref);
98 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED,
99 format: "The `diff' process exited with error status %d",
100 g_subprocess_get_exit_status (subprocess: process));
101 }
102
103 g_object_unref (object: process);
104
105 return output;
106}
107
108static void
109append_error_value (GString *string,
110 GType enum_type,
111 guint value)
112{
113 GEnumClass *enum_class;
114 GEnumValue *enum_value;
115
116 enum_class = g_type_class_ref (type: enum_type);
117 enum_value = g_enum_get_value (enum_class, value);
118
119 g_string_append (string, val: enum_value->value_name);
120
121 g_type_class_unref (g_class: enum_class);
122}
123
124static void
125deserialize_error_func (const GskParseLocation *start,
126 const GskParseLocation *end,
127 const GError *error,
128 gpointer user_data)
129{
130 GString *errors = user_data;
131 GString *string = g_string_new (init: "<data>");
132
133 g_string_append_printf (string, format: ":%zu:%zu",
134 start->lines + 1, start->line_chars + 1);
135 if (start->lines != end->lines || start->line_chars != end->line_chars)
136 {
137 g_string_append (string, val: "-");
138 if (start->lines != end->lines)
139 g_string_append_printf (string, format: "%zu:", end->lines + 1);
140 g_string_append_printf (string, format: "%zu", end->line_chars + 1);
141 }
142
143 g_string_append_printf (string: errors, format: "%s: error: ", string->str);
144 g_string_free (string, TRUE);
145
146 if (error->domain == GTK_CSS_PARSER_ERROR)
147 append_error_value (string: errors, enum_type: GTK_TYPE_CSS_PARSER_ERROR, value: error->code);
148 else if (error->domain == GTK_CSS_PARSER_WARNING)
149 append_error_value (string: errors, enum_type: GTK_TYPE_CSS_PARSER_WARNING, value: error->code);
150 else
151 g_string_append_printf (string: errors,
152 format: "%s %u\n",
153 g_quark_to_string (quark: error->domain),
154 error->code);
155
156 g_string_append_c (errors, '\n');
157}
158
159static gboolean
160parse_node_file (GFile *file, gboolean generate)
161{
162 char *node_file, *reference_file, *errors_file;
163 GskRenderNode *node;
164 GString *errors;
165 GBytes *diff, *bytes;
166 GError *error = NULL;
167 gboolean result = TRUE;
168
169 bytes = g_file_load_bytes (file, NULL, NULL, error: &error);
170 if (error)
171 {
172 g_print (format: "Error loading file: %s\n", error->message);
173 g_clear_error (err: &error);
174 return FALSE;
175 }
176 g_assert_nonnull (bytes);
177
178 errors = g_string_new (init: "");
179
180 node = gsk_render_node_deserialize (bytes, error_func: deserialize_error_func, user_data: errors);
181 g_bytes_unref (bytes);
182 bytes = gsk_render_node_serialize (node);
183 gsk_render_node_unref (node);
184
185 if (generate)
186 {
187 g_print (format: "%s", (char *) g_bytes_get_data (bytes, NULL));
188 g_bytes_unref (bytes);
189 g_string_free (string: errors, TRUE);
190 return TRUE;
191 }
192
193 node_file = g_file_get_path (file);
194 reference_file = test_get_reference_file (node_file);
195
196 diff = diff_with_file (file1: reference_file, input: bytes, error: &error);
197 g_assert_no_error (error);
198
199 if (diff && g_bytes_get_size (bytes: diff) > 0)
200 {
201 g_print (format: "Resulting file doesn't match reference:\n%s\n",
202 (const char *) g_bytes_get_data (bytes: diff, NULL));
203 result = FALSE;
204 }
205 g_free (mem: reference_file);
206 g_clear_pointer (&diff, g_bytes_unref);
207
208 errors_file = test_get_errors_file (node_file);
209
210 if (errors_file)
211 {
212 GBytes *error_bytes = g_string_free_to_bytes (string: errors);
213 diff = diff_with_file (file1: errors_file, input: error_bytes, error: &error);
214 g_assert_no_error (error);
215
216 if (diff && g_bytes_get_size (bytes: diff) > 0)
217 {
218 g_print (format: "Errors don't match expected errors:\n%s\n",
219 (const char *) g_bytes_get_data (bytes: diff, NULL));
220 result = FALSE;
221 }
222 g_clear_pointer (&diff, g_bytes_unref);
223 g_clear_pointer (&error_bytes, g_bytes_unref);
224 }
225 else if (errors->str[0])
226 {
227 g_print (format: "Unexpected errors:\n%s\n", errors->str);
228 result = FALSE;
229 g_string_free (string: errors, TRUE);
230 }
231 else
232 {
233 g_string_free (string: errors, TRUE);
234 }
235
236 g_free (mem: errors_file);
237 g_free (mem: node_file);
238 g_bytes_unref (bytes);
239
240 return result;
241}
242
243static gboolean
244test_file (GFile *file)
245{
246 return parse_node_file (file, FALSE);
247}
248
249static int
250compare_files (gconstpointer a, gconstpointer b)
251{
252 GFile *file1 = G_FILE (a);
253 GFile *file2 = G_FILE (b);
254 char *path1, *path2;
255 int result;
256
257 path1 = g_file_get_path (file: file1);
258 path2 = g_file_get_path (file: file2);
259
260 result = strcmp (s1: path1, s2: path2);
261
262 g_free (mem: path1);
263 g_free (mem: path2);
264
265 return result;
266}
267
268static gboolean
269test_files_in_directory (GFile *dir)
270{
271 GFileEnumerator *enumerator;
272 GFileInfo *info;
273 GList *l, *files;
274 GError *error = NULL;
275 gboolean result = TRUE;
276
277 enumerator = g_file_enumerate_children (file: dir, G_FILE_ATTRIBUTE_STANDARD_NAME, flags: 0, NULL, error: &error);
278 g_assert_no_error (error);
279 files = NULL;
280
281 while ((info = g_file_enumerator_next_file (enumerator, NULL, error: &error)))
282 {
283 const char *filename;
284
285 filename = g_file_info_get_name (info);
286
287 if (!g_str_has_suffix (str: filename, suffix: ".node") ||
288 g_str_has_suffix (str: filename, suffix: ".out.node") ||
289 g_str_has_suffix (str: filename, suffix: ".ref.node"))
290 {
291 g_object_unref (object: info);
292 continue;
293 }
294
295 files = g_list_prepend (list: files, data: g_file_get_child (file: dir, name: filename));
296
297 g_object_unref (object: info);
298 }
299
300 g_assert_no_error (error);
301 g_object_unref (object: enumerator);
302
303 files = g_list_sort (list: files, compare_func: compare_files);
304 for (l = files; l; l = l->next)
305 {
306 result &= test_file (file: l->data);
307 }
308 g_list_free_full (list: files, free_func: g_object_unref);
309
310 return result;
311}
312
313int
314main (int argc, char **argv)
315{
316 gboolean success;
317
318 if (argc < 2)
319 {
320 const char *basedir;
321 GFile *dir;
322
323 gtk_test_init (argcp: &argc, argvp: &argv);
324
325 basedir = g_test_get_dir (file_type: G_TEST_DIST);
326 dir = g_file_new_for_path (path: basedir);
327 success = test_files_in_directory (dir);
328
329 g_object_unref (object: dir);
330 }
331 else if (strcmp (s1: argv[1], s2: "--generate") == 0)
332 {
333 if (argc >= 3)
334 {
335 GFile *file = g_file_new_for_commandline_arg (arg: argv[2]);
336
337 gtk_init ();
338
339 success = parse_node_file (file, TRUE);
340
341 g_object_unref (object: file);
342 }
343 else
344 success = FALSE;
345 }
346 else
347 {
348 guint i;
349
350 gtk_test_init (argcp: &argc, argvp: &argv);
351
352 success = TRUE;
353
354 for (i = 1; i < argc; i++)
355 {
356 GFile *file = g_file_new_for_commandline_arg (arg: argv[i]);
357
358 success &= test_file (file);
359
360 g_object_unref (object: file);
361 }
362 }
363
364 return success ? 0 : 1;
365}
366
367

source code of gtk/testsuite/gsk/node-parser.c