484 lines
13 KiB
C
484 lines
13 KiB
C
|
#include <stdlib.h>
|
||
|
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
typedef gboolean (* ValueCompareFunc) (GValue *v1, GValue *v2);
|
||
|
|
||
|
typedef struct {
|
||
|
GOutputStream *ostream;
|
||
|
GInputStream *istream;
|
||
|
const char *mime_type;
|
||
|
GValue value;
|
||
|
ValueCompareFunc compare;
|
||
|
gboolean done;
|
||
|
} TestData;
|
||
|
|
||
|
static gboolean
|
||
|
compare_string_values (GValue *v1, GValue *v2)
|
||
|
{
|
||
|
return G_VALUE_TYPE (v1) == G_TYPE_STRING &&
|
||
|
G_VALUE_TYPE (v2) == G_TYPE_STRING &&
|
||
|
strcmp (g_value_get_string (v1), g_value_get_string (v2)) == 0;
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
compare_rgba_values (GValue *v1, GValue *v2)
|
||
|
{
|
||
|
return G_VALUE_TYPE (v1) == GDK_TYPE_RGBA &&
|
||
|
G_VALUE_TYPE (v2) == GDK_TYPE_RGBA &&
|
||
|
gdk_rgba_equal ((GdkRGBA *)g_value_get_boxed (v1),
|
||
|
(GdkRGBA *)g_value_get_boxed (v2));
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
textures_equal (GdkTexture *t1, GdkTexture *t2)
|
||
|
{
|
||
|
guchar *d1, *d2;
|
||
|
int width, height;
|
||
|
gboolean ret;
|
||
|
|
||
|
width = gdk_texture_get_width (t1);
|
||
|
height = gdk_texture_get_height (t1);
|
||
|
|
||
|
if (width != gdk_texture_get_width (t2))
|
||
|
return FALSE;
|
||
|
if (height != gdk_texture_get_height (t2))
|
||
|
return FALSE;
|
||
|
|
||
|
d1 = g_malloc (width * height * 4);
|
||
|
d2 = g_malloc (width * height * 4);
|
||
|
|
||
|
gdk_texture_download (t1, d1, width * 4);
|
||
|
gdk_texture_download (t2, d2, width * 4);
|
||
|
|
||
|
ret = memcmp (d1, d2, width * height * 4) == 0;
|
||
|
|
||
|
if (!ret)
|
||
|
{
|
||
|
gdk_texture_save_to_png (t1, "texture1.png");
|
||
|
gdk_texture_save_to_png (t2, "texture2.png");
|
||
|
}
|
||
|
g_free (d1);
|
||
|
g_free (d2);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
compare_texture_values (GValue *v1, GValue *v2)
|
||
|
{
|
||
|
return G_VALUE_TYPE (v1) == GDK_TYPE_TEXTURE &&
|
||
|
G_VALUE_TYPE (v2) == GDK_TYPE_TEXTURE &&
|
||
|
textures_equal ((GdkTexture *)g_value_get_object (v1),
|
||
|
(GdkTexture *)g_value_get_object (v2));
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
compare_file_values (GValue *v1, GValue *v2)
|
||
|
{
|
||
|
return G_VALUE_TYPE (v1) == G_TYPE_FILE &&
|
||
|
G_VALUE_TYPE (v2) == G_TYPE_FILE &&
|
||
|
g_file_equal ((GFile *)g_value_get_object (v1),
|
||
|
(GFile *)g_value_get_object (v2));
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
compare_file_list_values (GValue *v1, GValue *v2)
|
||
|
{
|
||
|
GSList *s1, *s2, *l1, *l2;
|
||
|
|
||
|
if (G_VALUE_TYPE (v1) != GDK_TYPE_FILE_LIST ||
|
||
|
G_VALUE_TYPE (v2) != GDK_TYPE_FILE_LIST)
|
||
|
return FALSE;
|
||
|
|
||
|
s1 = g_value_get_boxed (v1);
|
||
|
s2 = g_value_get_boxed (v2);
|
||
|
|
||
|
if (g_slist_length (s1) != g_slist_length (s2))
|
||
|
return FALSE;
|
||
|
|
||
|
for (l1 = s1, l2 = s2; l1 != NULL; l1 = l1->next, l2 = l2->next)
|
||
|
{
|
||
|
GFile *f1 = l1->data;
|
||
|
GFile *f2 = l2->data;
|
||
|
if (!g_file_equal (f1, f2))
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
deserialize_done (GObject *source,
|
||
|
GAsyncResult *result,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
GError *error = NULL;
|
||
|
gboolean res;
|
||
|
TestData *data = user_data;
|
||
|
GValue value = G_VALUE_INIT;
|
||
|
|
||
|
g_value_init (&value, G_VALUE_TYPE (&data->value));
|
||
|
|
||
|
res = gdk_content_deserialize_finish (result, &value, &error);
|
||
|
g_assert_true (res);
|
||
|
g_assert_no_error (error);
|
||
|
|
||
|
g_assert_true (data->compare (&data->value, &value));
|
||
|
|
||
|
g_value_unset (&value);
|
||
|
|
||
|
data->done = TRUE;
|
||
|
g_main_context_wakeup (NULL);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
serialize_done (GObject *source,
|
||
|
GAsyncResult *result,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
GError *error = NULL;
|
||
|
gboolean res;
|
||
|
TestData *data = user_data;
|
||
|
gpointer serialized_data;
|
||
|
gsize serialized_len;
|
||
|
|
||
|
res = gdk_content_serialize_finish (result, &error);
|
||
|
g_assert_true (res);
|
||
|
g_assert_no_error (error);
|
||
|
|
||
|
serialized_data = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (data->ostream));
|
||
|
serialized_len = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (data->ostream));
|
||
|
data->istream = g_memory_input_stream_new_from_data (serialized_data, serialized_len, NULL);
|
||
|
|
||
|
gdk_content_deserialize_async (data->istream,
|
||
|
data->mime_type,
|
||
|
G_VALUE_TYPE (&data->value),
|
||
|
G_PRIORITY_DEFAULT,
|
||
|
NULL,
|
||
|
deserialize_done,
|
||
|
data);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_content_roundtrip (const GValue *value,
|
||
|
const char *mime_type,
|
||
|
ValueCompareFunc compare)
|
||
|
{
|
||
|
TestData data = { 0, };
|
||
|
|
||
|
data.ostream = g_memory_output_stream_new_resizable ();
|
||
|
data.mime_type = mime_type;
|
||
|
g_value_init (&data.value, G_VALUE_TYPE (value));
|
||
|
g_value_copy (value, &data.value);
|
||
|
data.compare = compare;
|
||
|
data.done = FALSE;
|
||
|
|
||
|
gdk_content_serialize_async (data.ostream,
|
||
|
data.mime_type,
|
||
|
&data.value,
|
||
|
G_PRIORITY_DEFAULT,
|
||
|
NULL,
|
||
|
serialize_done,
|
||
|
&data);
|
||
|
|
||
|
while (!data.done)
|
||
|
g_main_context_iteration (NULL, TRUE);
|
||
|
|
||
|
g_object_unref (data.ostream);
|
||
|
g_object_unref (data.istream);
|
||
|
g_value_unset (&data.value);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_content_text_plain_utf8 (void)
|
||
|
{
|
||
|
GValue value = G_VALUE_INIT;
|
||
|
|
||
|
g_value_init (&value, G_TYPE_STRING);
|
||
|
g_value_set_string (&value, "ABCDEF12345");
|
||
|
test_content_roundtrip (&value, "text/plain;charset=utf-8", compare_string_values);
|
||
|
g_value_unset (&value);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_content_text_plain (void)
|
||
|
{
|
||
|
GValue value = G_VALUE_INIT;
|
||
|
|
||
|
g_value_init (&value, G_TYPE_STRING);
|
||
|
g_value_set_string (&value, "ABCDEF12345");
|
||
|
test_content_roundtrip (&value, "text/plain", compare_string_values);
|
||
|
g_value_unset (&value);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_content_color (void)
|
||
|
{
|
||
|
GdkRGBA color;
|
||
|
GValue value = G_VALUE_INIT;
|
||
|
|
||
|
gdk_rgba_parse (&color, "magenta");
|
||
|
g_value_init (&value, GDK_TYPE_RGBA);
|
||
|
g_value_set_boxed (&value, &color);
|
||
|
test_content_roundtrip (&value, "application/x-color", compare_rgba_values);
|
||
|
g_value_unset (&value);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_content_texture (gconstpointer data)
|
||
|
{
|
||
|
const char *mimetype = data;
|
||
|
GValue value = G_VALUE_INIT;
|
||
|
char *path;
|
||
|
GFile *file;
|
||
|
GdkTexture *texture;
|
||
|
GError *error = NULL;
|
||
|
|
||
|
path = g_test_build_filename (G_TEST_DIST, "image-data", "image.png", NULL);
|
||
|
file = g_file_new_for_path (path);
|
||
|
texture = gdk_texture_new_from_file (file, &error);
|
||
|
g_assert_no_error (error);
|
||
|
g_object_unref (file);
|
||
|
g_free (path);
|
||
|
|
||
|
g_value_init (&value, GDK_TYPE_TEXTURE);
|
||
|
g_value_set_object (&value, texture);
|
||
|
test_content_roundtrip (&value, mimetype, compare_texture_values);
|
||
|
g_value_unset (&value);
|
||
|
g_object_unref (texture);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_content_file (void)
|
||
|
{
|
||
|
GFile *file;
|
||
|
GValue value = G_VALUE_INIT;
|
||
|
|
||
|
file = g_file_new_for_path ("/etc/passwd");
|
||
|
g_value_init (&value, G_TYPE_FILE);
|
||
|
g_value_set_object (&value, file);
|
||
|
test_content_roundtrip (&value, "text/uri-list", compare_file_values);
|
||
|
g_value_unset (&value);
|
||
|
g_object_unref (file);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_content_files (void)
|
||
|
{
|
||
|
GSList *files;
|
||
|
GValue value = G_VALUE_INIT;
|
||
|
|
||
|
files = NULL;
|
||
|
files = g_slist_append (files, g_file_new_for_path ("/etc/passwd"));
|
||
|
files = g_slist_append (files, g_file_new_for_path ("/boot/ostree"));
|
||
|
|
||
|
g_value_init (&value, GDK_TYPE_FILE_LIST);
|
||
|
g_value_set_boxed (&value, files);
|
||
|
test_content_roundtrip (&value, "text/uri-list", compare_file_list_values);
|
||
|
g_value_unset (&value);
|
||
|
g_slist_free_full (files, g_object_unref);
|
||
|
}
|
||
|
|
||
|
#define MY_TYPE_INT_LIST (my_int_list_get_type ())
|
||
|
GType my_int_list_get_type (void) G_GNUC_CONST;
|
||
|
|
||
|
typedef gpointer MyIntList;
|
||
|
|
||
|
static gpointer
|
||
|
my_int_list_copy (gpointer list)
|
||
|
{
|
||
|
int size;
|
||
|
gpointer copy;
|
||
|
|
||
|
size = *(int *)list;
|
||
|
copy = g_malloc ((size + 1) * sizeof (int));
|
||
|
memcpy (copy, list, (size + 1) * sizeof (int));
|
||
|
|
||
|
return copy;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
my_int_list_free (gpointer list)
|
||
|
{
|
||
|
g_free (list);
|
||
|
}
|
||
|
|
||
|
G_DEFINE_BOXED_TYPE (MyIntList, my_int_list, my_int_list_copy, my_int_list_free)
|
||
|
|
||
|
static void
|
||
|
int_list_serializer_finish (GObject *source,
|
||
|
GAsyncResult *result,
|
||
|
gpointer serializer)
|
||
|
{
|
||
|
GOutputStream *stream = G_OUTPUT_STREAM (source);
|
||
|
GError *error = NULL;
|
||
|
|
||
|
if (!g_output_stream_write_all_finish (stream, result, NULL, &error))
|
||
|
gdk_content_serializer_return_error (serializer, error);
|
||
|
else
|
||
|
gdk_content_serializer_return_success (serializer);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
int_list_serializer (GdkContentSerializer *serializer)
|
||
|
{
|
||
|
GString *str;
|
||
|
const GValue *value;
|
||
|
int *data;
|
||
|
|
||
|
str = g_string_new (NULL);
|
||
|
value = gdk_content_serializer_get_value (serializer);
|
||
|
|
||
|
data = g_value_get_boxed (value);
|
||
|
for (int i = 0; i < data[0] + 1; i++)
|
||
|
{
|
||
|
if (i > 0)
|
||
|
g_string_append_c (str, ' ');
|
||
|
g_string_append_printf (str, "%d", data[i]);
|
||
|
}
|
||
|
|
||
|
g_output_stream_write_all_async (gdk_content_serializer_get_output_stream (serializer),
|
||
|
str->str,
|
||
|
str->len,
|
||
|
gdk_content_serializer_get_priority (serializer),
|
||
|
gdk_content_serializer_get_cancellable (serializer),
|
||
|
int_list_serializer_finish,
|
||
|
serializer);
|
||
|
gdk_content_serializer_set_task_data (serializer, g_string_free (str, FALSE), g_free);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
int_list_deserializer_finish (GObject *source,
|
||
|
GAsyncResult *result,
|
||
|
gpointer deserializer)
|
||
|
{
|
||
|
GOutputStream *stream = G_OUTPUT_STREAM (source);
|
||
|
GError *error = NULL;
|
||
|
gssize written;
|
||
|
GValue *value;
|
||
|
char *str;
|
||
|
char **strv;
|
||
|
int size;
|
||
|
int *data;
|
||
|
|
||
|
written = g_output_stream_splice_finish (stream, result, &error);
|
||
|
if (written < 0)
|
||
|
{
|
||
|
gdk_content_deserializer_return_error (deserializer, error);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* write terminating NULL */
|
||
|
if (g_output_stream_write (stream, "", 1, NULL, &error) < 0 ||
|
||
|
!g_output_stream_close (stream, NULL, &error))
|
||
|
{
|
||
|
gdk_content_deserializer_return_error (deserializer, error);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
str = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (stream));
|
||
|
strv = g_strsplit (str, " ", -1);
|
||
|
size = atoi (strv[0]);
|
||
|
if (size + 1 != g_strv_length (strv))
|
||
|
{
|
||
|
g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "Int list corrupt");
|
||
|
gdk_content_deserializer_return_error (deserializer, error);
|
||
|
g_free (str);
|
||
|
g_strfreev (strv);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
data = g_malloc ((size + 1) * sizeof (int));
|
||
|
for (int i = 0; i < size + 1; i++)
|
||
|
data[i] = atoi (strv[i]);
|
||
|
g_free (str);
|
||
|
g_strfreev (strv);
|
||
|
|
||
|
value = gdk_content_deserializer_get_value (deserializer);
|
||
|
g_value_take_boxed (value, data);
|
||
|
|
||
|
gdk_content_deserializer_return_success (deserializer);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
int_list_deserializer (GdkContentDeserializer *deserializer)
|
||
|
{
|
||
|
GOutputStream *output;
|
||
|
|
||
|
output = g_memory_output_stream_new_resizable ();
|
||
|
|
||
|
g_output_stream_splice_async (output,
|
||
|
gdk_content_deserializer_get_input_stream (deserializer),
|
||
|
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
|
||
|
gdk_content_deserializer_get_priority (deserializer),
|
||
|
gdk_content_deserializer_get_cancellable (deserializer),
|
||
|
int_list_deserializer_finish,
|
||
|
deserializer);
|
||
|
g_object_unref (output);
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
compare_int_list_values (GValue *v1, GValue *v2)
|
||
|
{
|
||
|
int *d1, *d2;
|
||
|
|
||
|
if (G_VALUE_TYPE (v1) != MY_TYPE_INT_LIST ||
|
||
|
G_VALUE_TYPE (v2) != MY_TYPE_INT_LIST)
|
||
|
return FALSE;
|
||
|
|
||
|
d1 = g_value_get_boxed (v1);
|
||
|
d2 = g_value_get_boxed (v2);
|
||
|
|
||
|
if (d1[0] != d2[0])
|
||
|
return FALSE;
|
||
|
|
||
|
return memcmp (d1, d2, (d1[0] + 1) * sizeof (int)) == 0;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_custom_format (void)
|
||
|
{
|
||
|
int *data;
|
||
|
GValue value = G_VALUE_INIT;
|
||
|
|
||
|
gdk_content_register_serializer (MY_TYPE_INT_LIST,
|
||
|
"application/x-int-list",
|
||
|
int_list_serializer,
|
||
|
NULL, NULL);
|
||
|
gdk_content_register_deserializer ("application/x-int-list",
|
||
|
MY_TYPE_INT_LIST,
|
||
|
int_list_deserializer,
|
||
|
NULL, NULL);
|
||
|
|
||
|
data = g_malloc (3 * sizeof (int));
|
||
|
data[0] = 2;
|
||
|
data[1] = 3;
|
||
|
data[2] = 5;
|
||
|
|
||
|
g_value_init (&value, MY_TYPE_INT_LIST);
|
||
|
g_value_set_boxed (&value, data);
|
||
|
test_content_roundtrip (&value, "application/x-int-list", compare_int_list_values);
|
||
|
g_value_unset (&value);
|
||
|
g_free (data);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main (int argc, char *argv[])
|
||
|
{
|
||
|
(g_test_init) (&argc, &argv, NULL);
|
||
|
|
||
|
gtk_init ();
|
||
|
|
||
|
g_test_add_func ("/content/text_plain_utf8", test_content_text_plain_utf8);
|
||
|
g_test_add_func ("/content/text_plain", test_content_text_plain);
|
||
|
g_test_add_func ("/content/color", test_content_color);
|
||
|
g_test_add_data_func ("/content/texture/png", "image/png", test_content_texture);
|
||
|
g_test_add_data_func ("/content/texture/tiff", "image/tiff", test_content_texture);
|
||
|
g_test_add_func ("/content/file", test_content_file);
|
||
|
g_test_add_func ("/content/files", test_content_files);
|
||
|
g_test_add_func ("/content/custom", test_custom_format);
|
||
|
|
||
|
return g_test_run ();
|
||
|
}
|