1 | /* |
2 | * Copyright (C) 2020 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 void |
55 | style_context_changed (GtkWidget *window, const char **output) |
56 | { |
57 | GtkStyleContext *context; |
58 | |
59 | context = gtk_widget_get_style_context (widget: window); |
60 | |
61 | *output = gtk_style_context_to_string (context, flags: GTK_STYLE_CONTEXT_PRINT_RECURSE | |
62 | GTK_STYLE_CONTEXT_PRINT_SHOW_CHANGE); |
63 | |
64 | g_main_context_wakeup (NULL); |
65 | } |
66 | |
67 | static void |
68 | load_ui_file (GFile *file, gboolean generate) |
69 | { |
70 | GtkBuilder *builder; |
71 | GtkWidget *window; |
72 | char *output, *diff; |
73 | char *ui_file, *css_file, *reference_file; |
74 | GtkCssProvider *provider; |
75 | GError *error = NULL; |
76 | |
77 | ui_file = g_file_get_path (file); |
78 | |
79 | css_file = test_get_other_file (ui_file, extension: ".css" ); |
80 | g_assert_nonnull (css_file); |
81 | |
82 | provider = gtk_css_provider_new (); |
83 | gtk_css_provider_load_from_path (css_provider: provider, path: css_file); |
84 | gtk_style_context_add_provider_for_display (display: gdk_display_get_default (), |
85 | GTK_STYLE_PROVIDER (provider), |
86 | GTK_STYLE_PROVIDER_PRIORITY_FORCE); |
87 | |
88 | builder = gtk_builder_new_from_file (filename: ui_file); |
89 | window = GTK_WIDGET (gtk_builder_get_object (builder, "window1" )); |
90 | if (window == NULL) |
91 | window = GTK_WIDGET (gtk_builder_get_object (builder, "window" )); |
92 | |
93 | g_assert_nonnull (window); |
94 | |
95 | |
96 | output = NULL; |
97 | g_signal_connect (window, "map" , G_CALLBACK (style_context_changed), &output); |
98 | |
99 | gtk_widget_show (widget: window); |
100 | |
101 | while (!output) |
102 | g_main_context_iteration (NULL, FALSE); |
103 | |
104 | if (generate) |
105 | { |
106 | g_print (format: "%s" , output); |
107 | goto out; |
108 | } |
109 | |
110 | reference_file = test_get_other_file (ui_file, extension: ".nodes" ); |
111 | |
112 | diff = diff_with_file (file1: reference_file, text: output, len: -1, error: &error); |
113 | g_assert_no_error (error); |
114 | |
115 | if (diff && diff[0]) |
116 | { |
117 | g_test_message (format: "Resulting output doesn't match reference:\n%s" , diff); |
118 | g_test_fail (); |
119 | } |
120 | g_free (mem: reference_file); |
121 | g_free (mem: diff); |
122 | |
123 | out: |
124 | gtk_style_context_remove_provider_for_display (display: gdk_display_get_default (), |
125 | GTK_STYLE_PROVIDER (provider)); |
126 | g_object_unref (object: provider); |
127 | |
128 | g_free (mem: output); |
129 | g_free (mem: ui_file); |
130 | g_free (mem: css_file); |
131 | } |
132 | |
133 | static void |
134 | test_ui_file (GFile *file) |
135 | { |
136 | load_ui_file (file, FALSE); |
137 | } |
138 | |
139 | static void |
140 | add_test_for_file (GFile *file) |
141 | { |
142 | char *path; |
143 | |
144 | path = g_file_get_path (file); |
145 | |
146 | g_test_add_vtable (testpath: path, |
147 | data_size: 0, |
148 | g_object_ref (file), |
149 | NULL, |
150 | data_test: (GTestFixtureFunc) test_ui_file, |
151 | data_teardown: (GTestFixtureFunc) g_object_unref); |
152 | |
153 | g_free (mem: path); |
154 | } |
155 | |
156 | static int |
157 | compare_files (gconstpointer a, gconstpointer b) |
158 | { |
159 | GFile *file1 = G_FILE (a); |
160 | GFile *file2 = G_FILE (b); |
161 | char *path1, *path2; |
162 | int result; |
163 | |
164 | path1 = g_file_get_path (file: file1); |
165 | path2 = g_file_get_path (file: file2); |
166 | |
167 | result = strcmp (s1: path1, s2: path2); |
168 | |
169 | g_free (mem: path1); |
170 | g_free (mem: path2); |
171 | |
172 | return result; |
173 | } |
174 | |
175 | static void |
176 | add_tests_for_files_in_directory (GFile *dir) |
177 | { |
178 | GFileEnumerator *enumerator; |
179 | GFileInfo *info; |
180 | GList *files; |
181 | GError *error = NULL; |
182 | |
183 | enumerator = g_file_enumerate_children (file: dir, G_FILE_ATTRIBUTE_STANDARD_NAME, flags: 0, NULL, error: &error); |
184 | g_assert_no_error (error); |
185 | files = NULL; |
186 | |
187 | while ((info = g_file_enumerator_next_file (enumerator, NULL, error: &error))) |
188 | { |
189 | const char *filename; |
190 | |
191 | filename = g_file_info_get_name (info); |
192 | |
193 | if (!g_str_has_suffix (str: filename, suffix: ".ui" ) || |
194 | g_str_has_suffix (str: filename, suffix: ".nodes" )) |
195 | { |
196 | g_object_unref (object: info); |
197 | continue; |
198 | } |
199 | |
200 | files = g_list_prepend (list: files, data: g_file_get_child (file: dir, name: filename)); |
201 | |
202 | g_object_unref (object: info); |
203 | } |
204 | |
205 | g_assert_no_error (error); |
206 | g_object_unref (object: enumerator); |
207 | |
208 | files = g_list_sort (list: files, compare_func: compare_files); |
209 | g_list_foreach (list: files, func: (GFunc) add_test_for_file, NULL); |
210 | g_list_free_full (list: files, free_func: g_object_unref); |
211 | } |
212 | |
213 | int |
214 | main (int argc, char **argv) |
215 | { |
216 | g_setenv (variable: "GTK_CSS_DEBUG" , value: "1" , TRUE); |
217 | g_setenv (variable: "GTK_THEME" , value: "Empty" , TRUE); |
218 | g_setenv (variable: "GSETTINGS_BACKEND" , value: "memory" , TRUE); |
219 | |
220 | if (argc >= 3 && strcmp (s1: argv[1], s2: "--generate" ) == 0) |
221 | { |
222 | gtk_init (); |
223 | |
224 | GFile *file = g_file_new_for_commandline_arg (arg: argv[2]); |
225 | |
226 | load_ui_file (file, TRUE); |
227 | |
228 | g_object_unref (object: file); |
229 | |
230 | return 0; |
231 | } |
232 | |
233 | gtk_test_init (argcp: &argc, argvp: &argv); |
234 | |
235 | if (argc < 2) |
236 | { |
237 | const char *basedir; |
238 | GFile *dir; |
239 | |
240 | basedir = g_test_get_dir (file_type: G_TEST_DIST); |
241 | dir = g_file_new_for_path (path: basedir); |
242 | add_tests_for_files_in_directory (dir); |
243 | |
244 | g_object_unref (object: dir); |
245 | } |
246 | else |
247 | { |
248 | guint i; |
249 | |
250 | for (i = 1; i < argc; i++) |
251 | { |
252 | GFile *file = g_file_new_for_commandline_arg (arg: argv[i]); |
253 | |
254 | add_test_for_file (file); |
255 | |
256 | g_object_unref (object: file); |
257 | } |
258 | } |
259 | |
260 | return g_test_run (); |
261 | } |
262 | |
263 | |