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 | |
25 | static char * |
26 | test_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 | |
46 | static char * |
47 | test_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 | |
67 | static GBytes * |
68 | diff_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 | |
108 | static void |
109 | append_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 | |
124 | static void |
125 | deserialize_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 | |
159 | static gboolean |
160 | parse_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 | |
243 | static gboolean |
244 | test_file (GFile *file) |
245 | { |
246 | return parse_node_file (file, FALSE); |
247 | } |
248 | |
249 | static int |
250 | compare_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 | |
268 | static gboolean |
269 | test_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 | |
313 | int |
314 | main (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 | |