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 
25 #ifdef G_OS_WIN32
26 # include <io.h>
27 #endif
28 
29 
30 static char *
test_get_reference_file(const char * ui_file)31 test_get_reference_file (const char *ui_file)
32 {
33   GString *file = g_string_new (NULL);
34 
35   if (g_str_has_suffix (ui_file, ".ui"))
36     g_string_append_len (file, ui_file, strlen (ui_file) - 3);
37   else
38     g_string_append (file, ui_file);
39 
40   g_string_append (file, ".nodes");
41 
42   if (!g_file_test (file->str, G_FILE_TEST_EXISTS))
43     {
44       g_string_free (file, TRUE);
45       return g_strdup (ui_file);
46     }
47 
48   return g_string_free (file, FALSE);
49 }
50 
51 static char *
diff_with_file(const char * file1,char * text,gssize len,GError ** error)52 diff_with_file (const char  *file1,
53                 char        *text,
54                 gssize       len,
55                 GError     **error)
56 {
57   const char *command[] = { "diff", "-u", file1, NULL, NULL };
58   char *diff, *tmpfile;
59   int fd;
60 
61   diff = NULL;
62 
63   if (len < 0)
64     len = strlen (text);
65 
66   /* write the text buffer to a temporary file */
67   fd = g_file_open_tmp (NULL, &tmpfile, error);
68   if (fd < 0)
69     return NULL;
70 
71   if (write (fd, text, len) != (int) len)
72     {
73       close (fd);
74       g_set_error (error,
75                    G_FILE_ERROR, G_FILE_ERROR_FAILED,
76                    "Could not write data to temporary file '%s'", tmpfile);
77       goto done;
78     }
79   close (fd);
80   command[3] = tmpfile;
81 
82   /* run diff command */
83   g_spawn_sync (NULL,
84                 (char **) command,
85                 NULL,
86                 G_SPAWN_SEARCH_PATH,
87                 NULL, NULL,
88 	        &diff,
89                 NULL, NULL,
90                 error);
91 
92 done:
93   g_unlink (tmpfile);
94   g_free (tmpfile);
95 
96   return diff;
97 }
98 
99 static void
load_ui_file(GFile * file,gboolean generate)100 load_ui_file (GFile *file, gboolean generate)
101 {
102   GtkBuilder *builder;
103   GtkWidget *window;
104   GtkStyleContext *context;
105   char *output, *diff;
106   char *ui_file, *reference_file;
107   GError *error = NULL;
108 
109   ui_file = g_file_get_path (file);
110 
111   if (g_str_has_suffix (ui_file, ".rtl.ui"))
112     gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
113   else
114     gtk_widget_set_default_direction (GTK_TEXT_DIR_LTR);
115 
116   builder = gtk_builder_new_from_file (ui_file);
117   window = GTK_WIDGET (gtk_builder_get_object (builder, "window1"));
118 
119   g_assert (window != NULL);
120 
121   context = gtk_widget_get_style_context (window);
122 
123   output = gtk_style_context_to_string (context, GTK_STYLE_CONTEXT_PRINT_RECURSE);
124 
125   if (generate)
126     {
127       g_print ("%s", output);
128       goto out;
129     }
130 
131   reference_file = test_get_reference_file (ui_file);
132 
133   diff = diff_with_file (reference_file, output, -1, &error);
134   g_assert_no_error (error);
135 
136   if (diff && diff[0])
137     {
138       g_test_message ("Resulting output doesn't match reference:\n%s", diff);
139       g_test_fail ();
140     }
141   g_free (reference_file);
142   g_free (diff);
143 
144 out:
145   g_free (output);
146   g_free (ui_file);
147 }
148 
149 static void
test_ui_file(GFile * file)150 test_ui_file (GFile *file)
151 {
152   load_ui_file (file, FALSE);
153 }
154 
155 static void
add_test_for_file(GFile * file)156 add_test_for_file (GFile *file)
157 {
158   char *path;
159 
160   path = g_file_get_path (file);
161 
162   g_test_add_vtable (path,
163                      0,
164                      g_object_ref (file),
165                      NULL,
166                      (GTestFixtureFunc) test_ui_file,
167                      (GTestFixtureFunc) g_object_unref);
168 
169   g_free (path);
170 }
171 
172 static int
compare_files(gconstpointer a,gconstpointer b)173 compare_files (gconstpointer a, gconstpointer b)
174 {
175   GFile *file1 = G_FILE (a);
176   GFile *file2 = G_FILE (b);
177   char *path1, *path2;
178   int result;
179 
180   path1 = g_file_get_path (file1);
181   path2 = g_file_get_path (file2);
182 
183   result = strcmp (path1, path2);
184 
185   g_free (path1);
186   g_free (path2);
187 
188   return result;
189 }
190 
191 static void
add_tests_for_files_in_directory(GFile * dir)192 add_tests_for_files_in_directory (GFile *dir)
193 {
194   GFileEnumerator *enumerator;
195   GFileInfo *info;
196   GList *files;
197   GError *error = NULL;
198 
199   enumerator = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &error);
200   g_assert_no_error (error);
201   files = NULL;
202 
203   while ((info = g_file_enumerator_next_file (enumerator, NULL, &error)))
204     {
205       const char *filename;
206 
207       filename = g_file_info_get_name (info);
208 
209       if (!g_str_has_suffix (filename, ".ui") ||
210           g_str_has_suffix (filename, ".nodes"))
211         {
212           g_object_unref (info);
213           continue;
214         }
215 
216       files = g_list_prepend (files, g_file_get_child (dir, filename));
217 
218       g_object_unref (info);
219     }
220 
221   g_assert_no_error (error);
222   g_object_unref (enumerator);
223 
224   files = g_list_sort (files, compare_files);
225   g_list_foreach (files, (GFunc) add_test_for_file, NULL);
226   g_list_free_full (files, g_object_unref);
227 }
228 
229 int
main(int argc,char ** argv)230 main (int argc, char **argv)
231 {
232   g_setenv ("GTK_CSS_DEBUG", "1", TRUE);
233 
234   gtk_test_init (&argc, &argv);
235 
236   if (argc < 2)
237     {
238       const char *basedir;
239       GFile *dir;
240 
241       basedir = g_test_get_dir (G_TEST_DIST);
242       dir = g_file_new_for_path (basedir);
243       add_tests_for_files_in_directory (dir);
244 
245       g_object_unref (dir);
246     }
247   else if (strcmp (argv[1], "--generate") == 0)
248     {
249       if (argc >= 3)
250         {
251           GFile *file = g_file_new_for_commandline_arg (argv[2]);
252 
253           load_ui_file (file, TRUE);
254 
255           g_object_unref (file);
256         }
257     }
258   else
259     {
260       guint i;
261 
262       for (i = 1; i < argc; i++)
263         {
264           GFile *file = g_file_new_for_commandline_arg (argv[i]);
265 
266           add_test_for_file (file);
267 
268           g_object_unref (file);
269         }
270     }
271 
272   return g_test_run ();
273 }
274 
275