1#include <stdlib.h>
2
3#include <gtk/gtk.h>
4
5typedef gboolean (* ValueCompareFunc) (GValue *v1, GValue *v2);
6
7typedef struct {
8 GOutputStream *ostream;
9 GInputStream *istream;
10 const char *mime_type;
11 GValue value;
12 ValueCompareFunc compare;
13 gboolean done;
14} TestData;
15
16static gboolean
17compare_string_values (GValue *v1, GValue *v2)
18{
19 return G_VALUE_TYPE (v1) == G_TYPE_STRING &&
20 G_VALUE_TYPE (v2) == G_TYPE_STRING &&
21 strcmp (s1: g_value_get_string (value: v1), s2: g_value_get_string (value: v2)) == 0;
22}
23
24static gboolean
25compare_rgba_values (GValue *v1, GValue *v2)
26{
27 return G_VALUE_TYPE (v1) == GDK_TYPE_RGBA &&
28 G_VALUE_TYPE (v2) == GDK_TYPE_RGBA &&
29 gdk_rgba_equal (p1: (GdkRGBA *)g_value_get_boxed (value: v1),
30 p2: (GdkRGBA *)g_value_get_boxed (value: v2));
31}
32
33static gboolean
34textures_equal (GdkTexture *t1, GdkTexture *t2)
35{
36 guchar *d1, *d2;
37 int width, height;
38 gboolean ret;
39
40 width = gdk_texture_get_width (texture: t1);
41 height = gdk_texture_get_height (texture: t1);
42
43 if (width != gdk_texture_get_width (texture: t2))
44 return FALSE;
45 if (height != gdk_texture_get_height (texture: t2))
46 return FALSE;
47
48 d1 = g_malloc (n_bytes: width * height * 4);
49 d2 = g_malloc (n_bytes: width * height * 4);
50
51 gdk_texture_download (texture: t1, data: d1, stride: width * 4);
52 gdk_texture_download (texture: t2, data: d2, stride: width * 4);
53
54 ret = memcmp (s1: d1, s2: d2, n: width * height * 4) == 0;
55
56 if (!ret)
57 {
58 gdk_texture_save_to_png (texture: t1, filename: "texture1.png");
59 gdk_texture_save_to_png (texture: t2, filename: "texture2.png");
60 }
61 g_free (mem: d1);
62 g_free (mem: d2);
63
64 return ret;
65}
66
67static gboolean
68compare_texture_values (GValue *v1, GValue *v2)
69{
70 return G_VALUE_TYPE (v1) == GDK_TYPE_TEXTURE &&
71 G_VALUE_TYPE (v2) == GDK_TYPE_TEXTURE &&
72 textures_equal (t1: (GdkTexture *)g_value_get_object (value: v1),
73 t2: (GdkTexture *)g_value_get_object (value: v2));
74}
75
76static gboolean
77compare_file_values (GValue *v1, GValue *v2)
78{
79 return G_VALUE_TYPE (v1) == G_TYPE_FILE &&
80 G_VALUE_TYPE (v2) == G_TYPE_FILE &&
81 g_file_equal (file1: (GFile *)g_value_get_object (value: v1),
82 file2: (GFile *)g_value_get_object (value: v2));
83}
84
85static gboolean
86compare_file_list_values (GValue *v1, GValue *v2)
87{
88 GSList *s1, *s2, *l1, *l2;
89
90 if (G_VALUE_TYPE (v1) != GDK_TYPE_FILE_LIST ||
91 G_VALUE_TYPE (v2) != GDK_TYPE_FILE_LIST)
92 return FALSE;
93
94 s1 = g_value_get_boxed (value: v1);
95 s2 = g_value_get_boxed (value: v2);
96
97 if (g_slist_length (list: s1) != g_slist_length (list: s2))
98 return FALSE;
99
100 for (l1 = s1, l2 = s2; l1 != NULL; l1 = l1->next, l2 = l2->next)
101 {
102 GFile *f1 = l1->data;
103 GFile *f2 = l2->data;
104 if (!g_file_equal (file1: f1, file2: f2))
105 return FALSE;
106 }
107
108 return TRUE;
109}
110
111static void
112deserialize_done (GObject *source,
113 GAsyncResult *result,
114 gpointer user_data)
115{
116 GError *error = NULL;
117 gboolean res;
118 TestData *data = user_data;
119 GValue value = G_VALUE_INIT;
120
121 g_value_init (value: &value, G_VALUE_TYPE (&data->value));
122
123 res = gdk_content_deserialize_finish (result, value: &value, error: &error);
124 g_assert_true (res);
125 g_assert_no_error (error);
126
127 g_assert_true (data->compare (&data->value, &value));
128
129 g_value_unset (value: &value);
130
131 data->done = TRUE;
132 g_main_context_wakeup (NULL);
133}
134
135static void
136serialize_done (GObject *source,
137 GAsyncResult *result,
138 gpointer user_data)
139{
140 GError *error = NULL;
141 gboolean res;
142 TestData *data = user_data;
143 gpointer serialized_data;
144 gsize serialized_len;
145
146 res = gdk_content_serialize_finish (result, error: &error);
147 g_assert_true (res);
148 g_assert_no_error (error);
149
150 serialized_data = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (data->ostream));
151 serialized_len = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (data->ostream));
152 data->istream = g_memory_input_stream_new_from_data (data: serialized_data, len: serialized_len, NULL);
153
154 gdk_content_deserialize_async (stream: data->istream,
155 mime_type: data->mime_type,
156 G_VALUE_TYPE (&data->value),
157 G_PRIORITY_DEFAULT,
158 NULL,
159 callback: deserialize_done,
160 user_data: data);
161}
162
163static void
164test_content_roundtrip (const GValue *value,
165 const char *mime_type,
166 ValueCompareFunc compare)
167{
168 TestData data = { 0, };
169
170 data.ostream = g_memory_output_stream_new_resizable ();
171 data.mime_type = mime_type;
172 g_value_init (value: &data.value, G_VALUE_TYPE (value));
173 g_value_copy (src_value: value, dest_value: &data.value);
174 data.compare = compare;
175 data.done = FALSE;
176
177 gdk_content_serialize_async (stream: data.ostream,
178 mime_type: data.mime_type,
179 value: &data.value,
180 G_PRIORITY_DEFAULT,
181 NULL,
182 callback: serialize_done,
183 user_data: &data);
184
185 while (!data.done)
186 g_main_context_iteration (NULL, TRUE);
187
188 g_object_unref (object: data.ostream);
189 g_object_unref (object: data.istream);
190 g_value_unset (value: &data.value);
191}
192
193static void
194test_content_text_plain_utf8 (void)
195{
196 GValue value = G_VALUE_INIT;
197
198 g_value_init (value: &value, G_TYPE_STRING);
199 g_value_set_string (value: &value, v_string: "ABCDEF12345");
200 test_content_roundtrip (value: &value, mime_type: "text/plain;charset=utf-8", compare: compare_string_values);
201 g_value_unset (value: &value);
202}
203
204static void
205test_content_text_plain (void)
206{
207 GValue value = G_VALUE_INIT;
208
209 g_value_init (value: &value, G_TYPE_STRING);
210 g_value_set_string (value: &value, v_string: "ABCDEF12345");
211 test_content_roundtrip (value: &value, mime_type: "text/plain", compare: compare_string_values);
212 g_value_unset (value: &value);
213}
214
215static void
216test_content_color (void)
217{
218 GdkRGBA color;
219 GValue value = G_VALUE_INIT;
220
221 gdk_rgba_parse (rgba: &color, spec: "magenta");
222 g_value_init (value: &value, GDK_TYPE_RGBA);
223 g_value_set_boxed (value: &value, v_boxed: &color);
224 test_content_roundtrip (value: &value, mime_type: "application/x-color", compare: compare_rgba_values);
225 g_value_unset (value: &value);
226}
227
228static void
229test_content_texture (gconstpointer data)
230{
231 const char *mimetype = data;
232 GValue value = G_VALUE_INIT;
233 char *path;
234 GFile *file;
235 GdkTexture *texture;
236 GError *error = NULL;
237
238 path = g_test_build_filename (file_type: G_TEST_DIST, first_path: "image-data", "image.png", NULL);
239 file = g_file_new_for_path (path);
240 texture = gdk_texture_new_from_file (file, error: &error);
241 g_assert_no_error (error);
242 g_object_unref (object: file);
243 g_free (mem: path);
244
245 g_value_init (value: &value, GDK_TYPE_TEXTURE);
246 g_value_set_object (value: &value, v_object: texture);
247 test_content_roundtrip (value: &value, mime_type: mimetype, compare: compare_texture_values);
248 g_value_unset (value: &value);
249 g_object_unref (object: texture);
250}
251
252static void
253test_content_file (void)
254{
255 GFile *file;
256 GValue value = G_VALUE_INIT;
257
258 file = g_file_new_for_path (path: "/etc/passwd");
259 g_value_init (value: &value, G_TYPE_FILE);
260 g_value_set_object (value: &value, v_object: file);
261 test_content_roundtrip (value: &value, mime_type: "text/uri-list", compare: compare_file_values);
262 g_value_unset (value: &value);
263 g_object_unref (object: file);
264}
265
266static void
267test_content_files (void)
268{
269 GSList *files;
270 GValue value = G_VALUE_INIT;
271
272 files = NULL;
273 files = g_slist_append (list: files, data: g_file_new_for_path (path: "/etc/passwd"));
274 files = g_slist_append (list: files, data: g_file_new_for_path (path: "/boot/ostree"));
275
276 g_value_init (value: &value, GDK_TYPE_FILE_LIST);
277 g_value_set_boxed (value: &value, v_boxed: files);
278 test_content_roundtrip (value: &value, mime_type: "text/uri-list", compare: compare_file_list_values);
279 g_value_unset (value: &value);
280 g_slist_free_full (list: files, free_func: g_object_unref);
281}
282
283#define MY_TYPE_INT_LIST (my_int_list_get_type ())
284GType my_int_list_get_type (void) G_GNUC_CONST;
285
286typedef gpointer MyIntList;
287
288static gpointer
289my_int_list_copy (gpointer list)
290{
291 int size;
292 gpointer copy;
293
294 size = *(int *)list;
295 copy = g_malloc (n_bytes: (size + 1) * sizeof (int));
296 memcpy (dest: copy, src: list, n: (size + 1) * sizeof (int));
297
298 return copy;
299}
300
301static void
302my_int_list_free (gpointer list)
303{
304 g_free (mem: list);
305}
306
307G_DEFINE_BOXED_TYPE (MyIntList, my_int_list, my_int_list_copy, my_int_list_free)
308
309static void
310int_list_serializer_finish (GObject *source,
311 GAsyncResult *result,
312 gpointer serializer)
313{
314 GOutputStream *stream = G_OUTPUT_STREAM (source);
315 GError *error = NULL;
316
317 if (!g_output_stream_write_all_finish (stream, result, NULL, error: &error))
318 gdk_content_serializer_return_error (serializer, error);
319 else
320 gdk_content_serializer_return_success (serializer);
321}
322
323static void
324int_list_serializer (GdkContentSerializer *serializer)
325{
326 GString *str;
327 const GValue *value;
328 int *data;
329
330 str = g_string_new (NULL);
331 value = gdk_content_serializer_get_value (serializer);
332
333 data = g_value_get_boxed (value);
334 for (int i = 0; i < data[0] + 1; i++)
335 {
336 if (i > 0)
337 g_string_append_c (str, ' ');
338 g_string_append_printf (string: str, format: "%d", data[i]);
339 }
340
341 g_output_stream_write_all_async (stream: gdk_content_serializer_get_output_stream (serializer),
342 buffer: str->str,
343 count: str->len,
344 io_priority: gdk_content_serializer_get_priority (serializer),
345 cancellable: gdk_content_serializer_get_cancellable (serializer),
346 callback: int_list_serializer_finish,
347 user_data: serializer);
348 gdk_content_serializer_set_task_data (serializer, data: g_string_free (string: str, FALSE), notify: g_free);
349}
350
351static void
352int_list_deserializer_finish (GObject *source,
353 GAsyncResult *result,
354 gpointer deserializer)
355{
356 GOutputStream *stream = G_OUTPUT_STREAM (source);
357 GError *error = NULL;
358 gssize written;
359 GValue *value;
360 char *str;
361 char **strv;
362 int size;
363 int *data;
364
365 written = g_output_stream_splice_finish (stream, result, error: &error);
366 if (written < 0)
367 {
368 gdk_content_deserializer_return_error (deserializer, error);
369 return;
370 }
371
372 /* write terminating NULL */
373 if (g_output_stream_write (stream, buffer: "", count: 1, NULL, error: &error) < 0 ||
374 !g_output_stream_close (stream, NULL, error: &error))
375 {
376 gdk_content_deserializer_return_error (deserializer, error);
377 return;
378 }
379
380 str = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (stream));
381 strv = g_strsplit (string: str, delimiter: " ", max_tokens: -1);
382 size = atoi (nptr: strv[0]);
383 if (size + 1 != g_strv_length (str_array: strv))
384 {
385 g_set_error_literal (err: &error, G_IO_ERROR, code: G_IO_ERROR_FAILED, message: "Int list corrupt");
386 gdk_content_deserializer_return_error (deserializer, error);
387 g_free (mem: str);
388 g_strfreev (str_array: strv);
389 return;
390 }
391
392 data = g_malloc (n_bytes: (size + 1) * sizeof (int));
393 for (int i = 0; i < size + 1; i++)
394 data[i] = atoi (nptr: strv[i]);
395 g_free (mem: str);
396 g_strfreev (str_array: strv);
397
398 value = gdk_content_deserializer_get_value (deserializer);
399 g_value_take_boxed (value, v_boxed: data);
400
401 gdk_content_deserializer_return_success (deserializer);
402}
403
404static void
405int_list_deserializer (GdkContentDeserializer *deserializer)
406{
407 GOutputStream *output;
408
409 output = g_memory_output_stream_new_resizable ();
410
411 g_output_stream_splice_async (stream: output,
412 source: gdk_content_deserializer_get_input_stream (deserializer),
413 flags: G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
414 io_priority: gdk_content_deserializer_get_priority (deserializer),
415 cancellable: gdk_content_deserializer_get_cancellable (deserializer),
416 callback: int_list_deserializer_finish,
417 user_data: deserializer);
418 g_object_unref (object: output);
419}
420
421static gboolean
422compare_int_list_values (GValue *v1, GValue *v2)
423{
424 int *d1, *d2;
425
426 if (G_VALUE_TYPE (v1) != MY_TYPE_INT_LIST ||
427 G_VALUE_TYPE (v2) != MY_TYPE_INT_LIST)
428 return FALSE;
429
430 d1 = g_value_get_boxed (value: v1);
431 d2 = g_value_get_boxed (value: v2);
432
433 if (d1[0] != d2[0])
434 return FALSE;
435
436 return memcmp (s1: d1, s2: d2, n: (d1[0] + 1) * sizeof (int)) == 0;
437}
438
439static void
440test_custom_format (void)
441{
442 int *data;
443 GValue value = G_VALUE_INIT;
444
445 gdk_content_register_serializer (MY_TYPE_INT_LIST,
446 mime_type: "application/x-int-list",
447 serialize: int_list_serializer,
448 NULL, NULL);
449 gdk_content_register_deserializer (mime_type: "application/x-int-list",
450 MY_TYPE_INT_LIST,
451 deserialize: int_list_deserializer,
452 NULL, NULL);
453
454 data = g_malloc (n_bytes: 3 * sizeof (int));
455 data[0] = 2;
456 data[1] = 3;
457 data[2] = 5;
458
459 g_value_init (value: &value, MY_TYPE_INT_LIST);
460 g_value_set_boxed (value: &value, v_boxed: data);
461 test_content_roundtrip (value: &value, mime_type: "application/x-int-list", compare: compare_int_list_values);
462 g_value_unset (value: &value);
463 g_free (mem: data);
464}
465
466int
467main (int argc, char *argv[])
468{
469 (g_test_init) (argc: &argc, argv: &argv, NULL);
470
471 gtk_init ();
472
473 g_test_add_func (testpath: "/content/text_plain_utf8", test_func: test_content_text_plain_utf8);
474 g_test_add_func (testpath: "/content/text_plain", test_func: test_content_text_plain);
475 g_test_add_func (testpath: "/content/color", test_func: test_content_color);
476 g_test_add_data_func (testpath: "/content/texture/png", test_data: "image/png", test_func: test_content_texture);
477 g_test_add_data_func (testpath: "/content/texture/tiff", test_data: "image/tiff", test_func: test_content_texture);
478 g_test_add_func (testpath: "/content/file", test_func: test_content_file);
479 g_test_add_func (testpath: "/content/files", test_func: test_content_files);
480 g_test_add_func (testpath: "/content/custom", test_func: test_custom_format);
481
482 return g_test_run ();
483}
484

source code of gtk/testsuite/gdk/contentserializer.c