1#include <string.h>
2#include <glib/gstdio.h>
3#include <gtk/gtk.h>
4#include <stdlib.h>
5#include "../reftests/reftest-compare.h"
6
7static char *arg_output_dir = NULL;
8
9static const char *
10get_output_dir (void)
11{
12 static const char *output_dir = NULL;
13 GError *error = NULL;
14 GFile *file;
15
16 if (output_dir)
17 return output_dir;
18
19 if (arg_output_dir)
20 {
21 GFile *arg_file = g_file_new_for_commandline_arg (arg: arg_output_dir);
22 const char *subdir;
23
24 subdir = g_getenv (variable: "TEST_OUTPUT_SUBDIR");
25 if (subdir)
26 {
27 GFile *child = g_file_get_child (file: arg_file, name: subdir);
28 g_object_unref (object: arg_file);
29 arg_file = child;
30 }
31
32 output_dir = g_file_get_path (file: arg_file);
33 g_object_unref (object: arg_file);
34 }
35 else
36 {
37 output_dir = g_get_tmp_dir ();
38 }
39
40 /* Just try to create the output directory.
41 * If it already exists, that's exactly what we wanted to check,
42 * so we can happily skip that error.
43 */
44 file = g_file_new_for_path (path: output_dir);
45 if (!g_file_make_directory_with_parents (file, NULL, error: &error))
46 {
47 g_object_unref (object: file);
48
49 if (!g_error_matches (error, G_IO_ERROR, code: G_IO_ERROR_EXISTS))
50 {
51 g_error ("Failed to create output dir: %s", error->message);
52 g_error_free (error);
53 return NULL;
54 }
55 g_error_free (error);
56 }
57 else
58 g_object_unref (object: file);
59
60 return output_dir;
61}
62
63static char *
64file_replace_extension (const char *old_file,
65 const char *old_ext,
66 const char *new_ext)
67{
68 GString *file = g_string_new (NULL);
69
70 if (g_str_has_suffix (str: old_file, suffix: old_ext))
71 g_string_append_len (string: file, val: old_file, len: strlen (s: old_file) - strlen (s: old_ext));
72 else
73 g_string_append (string: file, val: old_file);
74
75 g_string_append (string: file, val: new_ext);
76
77 return g_string_free (string: file, FALSE);
78}
79
80static char *
81get_output_file (const char *file,
82 const char *orig_ext,
83 const char *new_ext)
84{
85 const char *dir;
86 char *result, *base;
87 char *name;
88
89 dir = get_output_dir ();
90 base = g_path_get_basename (file_name: file);
91 name = file_replace_extension (old_file: base, old_ext: orig_ext, new_ext);
92
93 result = g_strconcat (string1: dir, G_DIR_SEPARATOR_S, name, NULL);
94
95 g_free (mem: base);
96 g_free (mem: name);
97
98 return result;
99}
100
101static void
102save_image (GdkTexture *texture,
103 const char *test_name,
104 const char *extension)
105{
106 char *filename = get_output_file (file: test_name, orig_ext: ".node", new_ext: extension);
107 gboolean result;
108
109 g_print (format: "Storing test result image at %s\n", filename);
110 result = gdk_texture_save_to_png (texture, filename);
111 g_assert_true (result);
112 g_free (mem: filename);
113}
114
115static void
116deserialize_error_func (const GskParseLocation *start,
117 const GskParseLocation *end,
118 const GError *error,
119 gpointer user_data)
120{
121 GString *string = g_string_new (init: "<data>");
122
123 g_string_append_printf (string, format: ":%zu:%zu",
124 start->lines + 1, start->line_chars + 1);
125 if (start->lines != end->lines || start->line_chars != end->line_chars)
126 {
127 g_string_append (string, val: "-");
128 if (start->lines != end->lines)
129 g_string_append_printf (string, format: "%zu:", end->lines + 1);
130 g_string_append_printf (string, format: "%zu", end->line_chars + 1);
131 }
132
133 g_warning ("Error at %s: %s", string->str, error->message);
134
135 g_string_free (string, TRUE);
136}
137
138static const GOptionEntry options[] = {
139 { "output", 0, 0, G_OPTION_ARG_FILENAME, &arg_output_dir,
140 "Directory to save image files to", "DIR" },
141 { NULL }
142};
143
144/*
145 * Non-option arguments:
146 * 1) .node file to compare
147 * 2) .png file to compare the rendered .node file to
148 */
149int
150main (int argc, char **argv)
151{
152 GdkTexture *reference_texture;
153 GdkTexture *rendered_texture;
154 GskRenderer *renderer;
155 GdkSurface *window;
156 GskRenderNode *node;
157 const char *node_file;
158 const char *png_file;
159 gboolean success = TRUE;
160 GError *error = NULL;
161 GOptionContext *context;
162
163 (g_test_init) (argc: &argc, argv: &argv, NULL);
164
165 context = g_option_context_new (parameter_string: "NODE REF - run GSK node tests");
166 g_option_context_add_main_entries (context, entries: options, NULL);
167 g_option_context_set_ignore_unknown_options (context, TRUE);
168
169 if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error))
170 {
171 g_error ("Option parsing failed: %s\n", error->message);
172 return 1;
173 }
174 else if (argc != 3)
175 {
176 char *help = g_option_context_get_help (context, TRUE, NULL);
177 g_print (format: "%s", help);
178 return 1;
179 }
180
181 g_option_context_free (context);
182
183 gtk_init ();
184
185 node_file = argv[1];
186 png_file = argv[2];
187
188 window = gdk_surface_new_toplevel (display: gdk_display_get_default());
189 renderer = gsk_renderer_new_for_surface (surface: window);
190
191 g_print (format: "Node file: '%s'\n", node_file);
192 g_print (format: "PNG file: '%s'\n", png_file);
193
194 /* Load the render node from the given .node file */
195 {
196 GBytes *bytes;
197 gsize len;
198 char *contents;
199
200 if (!g_file_get_contents (filename: node_file, contents: &contents, length: &len, error: &error))
201 {
202 g_print (format: "Could not open node file: %s\n", error->message);
203 g_clear_error (err: &error);
204 return 1;
205 }
206
207 bytes = g_bytes_new_take (data: contents, size: len);
208 node = gsk_render_node_deserialize (bytes, error_func: deserialize_error_func, user_data: &success);
209 g_bytes_unref (bytes);
210
211 g_assert_no_error (error);
212 g_assert_nonnull (node);
213 }
214
215 /* Render the .node file and download to cairo surface */
216 rendered_texture = gsk_renderer_render_texture (renderer, root: node, NULL);
217 g_assert_nonnull (rendered_texture);
218
219 /* Load the given reference png file */
220 reference_texture = gdk_texture_new_from_filename (path: png_file, error: &error);
221 if (reference_texture == NULL)
222 {
223 g_print (format: "Error loading reference surface: %s\n", error->message);
224 g_clear_error (err: &error);
225 success = FALSE;
226 }
227 else
228 {
229 GdkTexture *diff_texture;
230
231 /* Now compare the two */
232 diff_texture = reftest_compare_textures (texture1: rendered_texture, texture2: reference_texture);
233
234 if (diff_texture)
235 {
236 save_image (texture: diff_texture, test_name: node_file, extension: ".diff.png");
237 g_object_unref (object: diff_texture);
238 success = FALSE;
239 }
240 }
241
242 save_image (texture: rendered_texture, test_name: node_file, extension: ".out.png");
243
244 g_object_unref (object: reference_texture);
245 g_object_unref (object: rendered_texture);
246
247 gsk_render_node_unref (node);
248
249 return success ? 0 : 1;
250}
251

source code of gtk/testsuite/gsk/compare-render.c