1 | /* |
2 | * Copyright (C) 2015 Red Hat Inc. |
3 | * |
4 | * Author: |
5 | * Matthias Clasen <mclasen@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 <string.h> |
22 | #include <glib/gstdio.h> |
23 | #include <gtk/gtk.h> |
24 | #include "testsuite/testutils.h" |
25 | |
26 | #ifdef G_OS_WIN32 |
27 | # include <io.h> |
28 | #endif |
29 | |
30 | /* There shall be no other styles */ |
31 | #define GTK_STYLE_PROVIDER_PRIORITY_FORCE G_MAXUINT |
32 | |
33 | static char * |
34 | test_get_other_file (const char *ui_file, const char *extension) |
35 | { |
36 | GString *file = g_string_new (NULL); |
37 | |
38 | if (g_str_has_suffix (str: ui_file, suffix: ".ui" )) |
39 | g_string_append_len (string: file, val: ui_file, len: strlen (s: ui_file) - strlen (s: ".ui" )); |
40 | else |
41 | g_string_append (string: file, val: ui_file); |
42 | |
43 | g_string_append (string: file, val: extension); |
44 | |
45 | if (!g_file_test (filename: file->str, test: G_FILE_TEST_EXISTS)) |
46 | { |
47 | g_string_free (string: file, TRUE); |
48 | return NULL; |
49 | } |
50 | |
51 | return g_string_free (string: file, FALSE); |
52 | } |
53 | |
54 | static char * |
55 | fixup_style_differences (const char *str) |
56 | { |
57 | GRegex *regex; |
58 | char *result; |
59 | |
60 | /* normalize differences that creep in from hard-to-control environmental influences */ |
61 | regex = g_regex_new (pattern: "[.]solid-csd" , compile_options: 0, match_options: 0, NULL); |
62 | result = g_regex_replace_literal (regex, string: str, string_len: -1, start_position: 0, replacement: ".csd" , match_options: 0, NULL); |
63 | g_regex_unref (regex); |
64 | |
65 | return result; |
66 | } |
67 | |
68 | static void |
69 | style_context_changed (GtkWidget *window, const char **output) |
70 | { |
71 | GtkStyleContext *context; |
72 | char *str; |
73 | |
74 | context = gtk_widget_get_style_context (widget: window); |
75 | |
76 | str = gtk_style_context_to_string (context, flags: GTK_STYLE_CONTEXT_PRINT_RECURSE | |
77 | GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE); |
78 | |
79 | *output = fixup_style_differences (str); |
80 | |
81 | g_free (mem: str); |
82 | |
83 | g_main_context_wakeup (NULL); |
84 | } |
85 | |
86 | static void |
87 | load_ui_file (GFile *file, gboolean generate) |
88 | { |
89 | GtkBuilder *builder; |
90 | GtkWidget *window; |
91 | char *output; |
92 | char *diff; |
93 | char *ui_file, *css_file, *reference_file; |
94 | GtkCssProvider *provider; |
95 | GError *error = NULL; |
96 | |
97 | ui_file = g_file_get_path (file); |
98 | |
99 | css_file = test_get_other_file (ui_file, extension: ".css" ); |
100 | g_assert_nonnull (css_file); |
101 | |
102 | provider = gtk_css_provider_new (); |
103 | gtk_css_provider_load_from_path (css_provider: provider, path: css_file); |
104 | gtk_style_context_add_provider_for_display (display: gdk_display_get_default (), |
105 | GTK_STYLE_PROVIDER (provider), |
106 | GTK_STYLE_PROVIDER_PRIORITY_FORCE); |
107 | |
108 | builder = gtk_builder_new_from_file (filename: ui_file); |
109 | window = GTK_WIDGET (gtk_builder_get_object (builder, "window1" )); |
110 | |
111 | g_assert_nonnull (window); |
112 | |
113 | output = NULL; |
114 | g_signal_connect (window, "map" , G_CALLBACK (style_context_changed), &output); |
115 | |
116 | gtk_widget_show (widget: window); |
117 | |
118 | while (!output) |
119 | g_main_context_iteration (NULL, FALSE); |
120 | |
121 | if (generate) |
122 | { |
123 | g_print (format: "%s" , output); |
124 | goto out; |
125 | } |
126 | |
127 | reference_file = test_get_other_file (ui_file, extension: ".nodes" ); |
128 | g_assert_nonnull (reference_file); |
129 | |
130 | diff = diff_with_file (file1: reference_file, text: output, len: -1, error: &error); |
131 | g_assert_no_error (error); |
132 | |
133 | if (diff && diff[0]) |
134 | { |
135 | g_test_message (format: "Resulting output doesn't match reference:\n%s" , diff); |
136 | g_test_fail (); |
137 | } |
138 | g_free (mem: reference_file); |
139 | g_free (mem: diff); |
140 | |
141 | out: |
142 | gtk_style_context_remove_provider_for_display (display: gdk_display_get_default (), |
143 | GTK_STYLE_PROVIDER (provider)); |
144 | g_object_unref (object: provider); |
145 | |
146 | g_free (mem: output); |
147 | g_free (mem: ui_file); |
148 | g_free (mem: css_file); |
149 | } |
150 | |
151 | static void |
152 | test_ui_file (GFile *file) |
153 | { |
154 | load_ui_file (file, FALSE); |
155 | } |
156 | |
157 | static void |
158 | add_test_for_file (GFile *file) |
159 | { |
160 | char *path; |
161 | |
162 | path = g_file_get_path (file); |
163 | |
164 | g_test_add_vtable (testpath: path, |
165 | data_size: 0, |
166 | g_object_ref (file), |
167 | NULL, |
168 | data_test: (GTestFixtureFunc) test_ui_file, |
169 | data_teardown: (GTestFixtureFunc) g_object_unref); |
170 | |
171 | g_free (mem: path); |
172 | } |
173 | |
174 | static int |
175 | compare_files (gconstpointer a, gconstpointer b) |
176 | { |
177 | GFile *file1 = G_FILE (a); |
178 | GFile *file2 = G_FILE (b); |
179 | char *path1, *path2; |
180 | int result; |
181 | |
182 | path1 = g_file_get_path (file: file1); |
183 | path2 = g_file_get_path (file: file2); |
184 | |
185 | result = strcmp (s1: path1, s2: path2); |
186 | |
187 | g_free (mem: path1); |
188 | g_free (mem: path2); |
189 | |
190 | return result; |
191 | } |
192 | |
193 | static void |
194 | add_tests_for_files_in_directory (GFile *dir) |
195 | { |
196 | GFileEnumerator *enumerator; |
197 | GFileInfo *info; |
198 | GList *files; |
199 | GError *error = NULL; |
200 | |
201 | enumerator = g_file_enumerate_children (file: dir, G_FILE_ATTRIBUTE_STANDARD_NAME, flags: 0, NULL, error: &error); |
202 | g_assert_no_error (error); |
203 | files = NULL; |
204 | |
205 | while ((info = g_file_enumerator_next_file (enumerator, NULL, error: &error))) |
206 | { |
207 | const char *filename; |
208 | |
209 | filename = g_file_info_get_name (info); |
210 | |
211 | if (!g_str_has_suffix (str: filename, suffix: ".ui" ) || |
212 | g_str_has_suffix (str: filename, suffix: ".nodes" )) |
213 | { |
214 | g_object_unref (object: info); |
215 | continue; |
216 | } |
217 | |
218 | files = g_list_prepend (list: files, data: g_file_get_child (file: dir, name: filename)); |
219 | |
220 | g_object_unref (object: info); |
221 | } |
222 | |
223 | g_assert_no_error (error); |
224 | g_object_unref (object: enumerator); |
225 | |
226 | files = g_list_sort (list: files, compare_func: compare_files); |
227 | g_list_foreach (list: files, func: (GFunc) add_test_for_file, NULL); |
228 | g_list_free_full (list: files, free_func: g_object_unref); |
229 | } |
230 | |
231 | int |
232 | main (int argc, char **argv) |
233 | { |
234 | g_setenv (variable: "GTK_CSS_DEBUG" , value: "1" , TRUE); |
235 | g_setenv (variable: "GTK_THEME" , value: "Empty" , TRUE); |
236 | |
237 | if (argc >= 3 && strcmp (s1: argv[1], s2: "--generate" ) == 0) |
238 | { |
239 | GFile *file = g_file_new_for_commandline_arg (arg: argv[2]); |
240 | |
241 | gtk_init (); |
242 | |
243 | g_object_set (object: gtk_settings_get_default (), first_property_name: "gtk-font-name" , "Sans" , NULL); |
244 | |
245 | load_ui_file (file, TRUE); |
246 | |
247 | g_object_unref (object: file); |
248 | |
249 | return 0; |
250 | } |
251 | |
252 | gtk_test_init (argcp: &argc, argvp: &argv); |
253 | g_object_set (object: gtk_settings_get_default (), first_property_name: "gtk-font-name" , "Sans" , NULL); |
254 | |
255 | if (argc < 2) |
256 | { |
257 | const char *basedir; |
258 | GFile *dir; |
259 | |
260 | basedir = g_test_get_dir (file_type: G_TEST_DIST); |
261 | dir = g_file_new_for_path (path: basedir); |
262 | add_tests_for_files_in_directory (dir); |
263 | |
264 | g_object_unref (object: dir); |
265 | } |
266 | else |
267 | { |
268 | guint i; |
269 | |
270 | for (i = 1; i < argc; i++) |
271 | { |
272 | GFile *file = g_file_new_for_commandline_arg (arg: argv[i]); |
273 | |
274 | add_test_for_file (file); |
275 | |
276 | g_object_unref (object: file); |
277 | } |
278 | } |
279 | |
280 | return g_test_run (); |
281 | } |
282 | |
283 | |