[gnome-keyring] gcr: Display photo as icon for Gnupg keys.



commit 9938f392505415b5d8c622424a9c57e492c43298
Author: Stef Walter <stefw collabora co uk>
Date:   Fri May 13 21:30:29 2011 +0200

    gcr: Display photo as icon for Gnupg keys.
    
     * Load the first photo uid and use as icon for Gnupg Keys.
     * Add GcrMemoryIcon which is a GLoadableIcon which loads from
       data in memory.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=650433

 .gitignore                          |    1 +
 docs/reference/gcr/gcr-sections.txt |   10 ++
 gcr/Makefile.am                     |    1 +
 gcr/gcr-gnupg-key.c                 |   85 ++++++++++++++-
 gcr/gcr-gnupg-key.h                 |    2 +
 gcr/gcr-memory-icon.c               |  195 +++++++++++++++++++++++++++++++++
 gcr/gcr-memory-icon.h               |   67 +++++++++++
 gcr/gcr-record.c                    |   14 +++
 gcr/gcr-record.h                    |    4 +
 gcr/tests/Makefile.am               |    1 +
 gcr/tests/test-memory-icon.c        |  206 +++++++++++++++++++++++++++++++++++
 gcr/tests/test-record.c             |   18 +++-
 12 files changed, 599 insertions(+), 5 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index fac973e..1721d51 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,6 +126,7 @@ run-tests
 /gcr/tests/test-gnupg-collection
 /gcr/tests/test-gnupg-key
 /gcr/tests/test-gnupg-process
+/gcr/tests/test-memory-icon
 /gcr/tests/test-parser
 /gcr/tests/test-pkcs11-certificate
 /gcr/tests/test-record
diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt
index 8d8b22b..adbdb3f 100644
--- a/docs/reference/gcr/gcr-sections.txt
+++ b/docs/reference/gcr/gcr-sections.txt
@@ -498,4 +498,14 @@ GcrGnupgKey
 GcrGnupgKeyClass
 GcrGnupgKeyPrivate
 GcrLineCallback
+GCR_IS_MEMORY_ICON
+GCR_IS_MEMORY_ICON_CLASS
+GCR_MEMORY_ICON
+GCR_MEMORY_ICON_CLASS
+GCR_MEMORY_ICON_COLUMNS
+GCR_MEMORY_ICON_GET_CLASS
+GCR_TYPE_MEMORY_ICON
+GcrMemoryIcon
+GcrMemoryIconClass
+GcrMemoryIconPrivate
 </SECTION>
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index 496f9d3..a6164df 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -99,6 +99,7 @@ libgcr_ GCR_MAJOR@_la_SOURCES = \
 	gcr-key-renderer.c gcr-key-renderer.h \
 	gcr-key-widget.c gcr-key-widget.h \
 	gcr-library.c gcr-library.h \
+	gcr-memory-icon.c gcr-memory-icon.h \
 	gcr-parser.c gcr-parser.h \
 	gcr-pkcs11-certificate.c gcr-pkcs11-certificate.h \
 	gcr-record.c gcr-record.h \
diff --git a/gcr/gcr-gnupg-key.c b/gcr/gcr-gnupg-key.c
index 3bf768f..1274ec6 100644
--- a/gcr/gcr-gnupg-key.c
+++ b/gcr/gcr-gnupg-key.c
@@ -21,8 +21,9 @@
 
 #include "config.h"
 
-#include "gcr-record.h"
 #include "gcr-gnupg-key.h"
+#include "gcr-record.h"
+#include "gcr-memory-icon.h"
 
 #include "gck/gck.h"
 
@@ -37,12 +38,14 @@ enum {
 	PROP_LABEL,
 	PROP_MARKUP,
 	PROP_DESCRIPTION,
-	PROP_SHORT_KEYID
+	PROP_SHORT_KEYID,
+	PROP_ICON
 };
 
 struct _GcrGnupgKeyPrivate {
 	GPtrArray *public_records;
 	GPtrArray *secret_records;
+	GIcon *icon;
 };
 
 G_DEFINE_TYPE (GcrGnupgKey, _gcr_gnupg_key, G_TYPE_OBJECT);
@@ -159,6 +162,9 @@ _gcr_gnupg_key_get_property (GObject *obj, guint prop_id, GValue *value,
 	case PROP_SHORT_KEYID:
 		g_value_set_string (value, calculate_short_keyid (self));
 		break;
+	case PROP_ICON:
+		g_value_set_object (value, _gcr_gnupg_key_get_icon (self));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
@@ -240,6 +246,15 @@ _gcr_gnupg_key_class_init (GcrGnupgKeyClass *klass)
 	g_object_class_install_property (gobject_class, PROP_SHORT_KEYID,
 	         g_param_spec_string ("short-keyid", "Short Key ID", "Display key identifier",
 	                              "", G_PARAM_READABLE));
+
+	/**
+	 * GcrGnupgKey::icon:
+	 *
+	 * Icon for this key.
+	 */
+	g_object_class_install_property (gobject_class, PROP_ICON,
+	         g_param_spec_object ("icon", "Icon", "Icon for this key",
+	                              G_TYPE_ICON, G_PARAM_READABLE));
 }
 
 /**
@@ -421,6 +436,66 @@ _gcr_gnupg_key_get_fingerprint_for_records (GPtrArray *records)
 	return NULL;
 }
 
+#define TYPE_IMAGE 0x01
+#define IMAGE_HEADER_LEN 0x10
+#define IMAGE_JPEG_SIG "\x10\x00\x01\x01"
+#define IMAGE_JPEG_SIG_LEN 4
+
+static GIcon*
+load_user_attribute_icon (GcrGnupgKey *self)
+{
+	GcrRecord *record;
+	guchar *data;
+	gsize n_data;
+	guint type;
+	guint i;
+
+	for (i = 0; i < self->pv->public_records->len; i++) {
+		record = self->pv->public_records->pdata[i];
+		if (GCR_RECORD_SCHEMA_XA1 != _gcr_record_get_schema (record))
+			continue;
+		if (!_gcr_record_get_uint (record, GCR_RECORD_XA1_TYPE, &type))
+			continue;
+		if (type != TYPE_IMAGE)
+			continue;
+
+		/* TODO: Validity? */
+
+		data = _gcr_record_get_base64 (record, GCR_RECORD_XA1_DATA, &n_data);
+		g_return_val_if_fail (data != NULL, NULL);
+
+		/* Header is 16 bytes long */
+		if (n_data <= IMAGE_HEADER_LEN)
+			continue;
+
+		/* These are the header bytes. See gnupg doc/DETAILS */
+		g_assert (IMAGE_JPEG_SIG_LEN < IMAGE_HEADER_LEN);
+		if (memcmp (data, IMAGE_JPEG_SIG, IMAGE_JPEG_SIG_LEN) != 0)
+			continue;
+
+		/* We have a valid header */
+		return G_ICON (_gcr_memory_icon_new_full ("image/jpeg", data,
+		                                          n_data, IMAGE_HEADER_LEN,
+		                                          g_free));
+	}
+
+	return NULL;
+}
+
+GIcon*
+_gcr_gnupg_key_get_icon (GcrGnupgKey *self)
+{
+	g_return_val_if_fail (GCR_IS_GNUPG_KEY (self), NULL);
+
+	if (self->pv->icon == NULL) {
+		self->pv->icon = load_user_attribute_icon (self);
+		if (self->pv->icon == NULL)
+			self->pv->icon = g_themed_icon_new ("folder"); /* TODO: proper icon */
+	}
+
+	return self->pv->icon;
+}
+
 /**
  * _gcr_gnupg_key_get_columns:
  *
@@ -432,12 +507,14 @@ const GcrColumn*
 _gcr_gnupg_key_get_columns (void)
 {
 	static GcrColumn columns[] = {
+		{ "icon", /* later */ 0, /* later */ 0, NULL, 0, NULL, 0 },
 		{ "label", G_TYPE_STRING, G_TYPE_STRING, NC_("column", "Name"),
-		  GCR_COLUMN_SORTABLE },
+		  GCR_COLUMN_SORTABLE, NULL, 0 },
 		{ "short-keyid", G_TYPE_STRING, G_TYPE_STRING, NC_("column", "Key ID"),
-		  GCR_COLUMN_SORTABLE },
+		  GCR_COLUMN_SORTABLE, NULL, 0 },
 		{ NULL }
 	};
 
+	columns[0].property_type = columns[0].column_type = G_TYPE_ICON;
 	return columns;
 }
diff --git a/gcr/gcr-gnupg-key.h b/gcr/gcr-gnupg-key.h
index 271bb90..745bec2 100644
--- a/gcr/gcr-gnupg-key.h
+++ b/gcr/gcr-gnupg-key.h
@@ -76,6 +76,8 @@ GPtrArray*          _gcr_gnupg_key_get_secret_records            (GcrGnupgKey *s
 void                _gcr_gnupg_key_set_secret_records            (GcrGnupgKey *self,
                                                                   GPtrArray *records);
 
+GIcon*              _gcr_gnupg_key_get_icon                      (GcrGnupgKey *self);
+
 const gchar*        _gcr_gnupg_key_get_keyid_for_records         (GPtrArray *records);
 
 const gchar*        _gcr_gnupg_key_get_fingerprint_for_records   (GPtrArray *records);
diff --git a/gcr/gcr-memory-icon.c b/gcr/gcr-memory-icon.c
new file mode 100644
index 0000000..83fb5ea
--- /dev/null
+++ b/gcr/gcr-memory-icon.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gcr-memory-icon.h"
+
+#include <string.h>
+
+struct _GcrMemoryIconPrivate {
+	gpointer data;
+	gsize n_data;
+	goffset offset;
+	gchar *image_type;
+	GDestroyNotify destroy;
+};
+
+/* Forward declarations */
+static void _gcr_memory_icon_iface_icon (GIconIface *iface);
+static void _gcr_memory_icon_iface_loadable_icon (GLoadableIconIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcrMemoryIcon, _gcr_memory_icon, G_TYPE_OBJECT,
+	G_IMPLEMENT_INTERFACE (G_TYPE_ICON, _gcr_memory_icon_iface_icon);
+	G_IMPLEMENT_INTERFACE (G_TYPE_LOADABLE_ICON, _gcr_memory_icon_iface_loadable_icon);
+);
+
+
+static void
+_gcr_memory_icon_init (GcrMemoryIcon *self)
+{
+	self->pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_MEMORY_ICON, GcrMemoryIconPrivate));
+}
+
+static void
+_gcr_memory_icon_finalize (GObject *obj)
+{
+	GcrMemoryIcon *self = GCR_MEMORY_ICON (obj);
+
+	if (self->pv->destroy)
+		(self->pv->destroy) (self->pv->data);
+	g_free (self->pv->image_type);
+
+	G_OBJECT_CLASS (_gcr_memory_icon_parent_class)->finalize (obj);
+}
+
+static void
+_gcr_memory_icon_class_init (GcrMemoryIconClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	_gcr_memory_icon_parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GcrMemoryIconPrivate));
+
+	gobject_class->finalize = _gcr_memory_icon_finalize;
+}
+
+static gboolean
+_gcr_memory_icon_equal (GIcon *icon1, GIcon *icon2)
+{
+	GcrMemoryIcon *one = GCR_MEMORY_ICON (icon1);
+	GcrMemoryIcon *two = GCR_MEMORY_ICON (icon2);
+
+	if (icon1 == icon2)
+		return TRUE;
+	if (!g_str_equal (one->pv->image_type, two->pv->image_type))
+		return FALSE;
+	if ((one->pv->n_data - one->pv->offset) != (two->pv->n_data - two->pv->offset))
+		return FALSE;
+	return memcmp ((guchar*)one->pv->data + one->pv->offset,
+	               (guchar*)two->pv->data + two->pv->offset,
+	               one->pv->n_data - one->pv->offset) == 0;
+}
+
+static guint
+_gcr_memory_icon_hash (GIcon *icon)
+{
+	GcrMemoryIcon *self = GCR_MEMORY_ICON (icon);
+	const signed char *p, *end;
+	guint32 hash;
+
+	hash = g_str_hash (self->pv->image_type);
+
+	/* Adapted from g_str_hash */
+	p = self->pv->data;
+	end = p + self->pv->n_data;
+	p += self->pv->offset;
+	while (p < end)
+		hash = (hash << 5) + hash + *(p++);
+
+	return hash;
+}
+
+static void
+_gcr_memory_icon_iface_icon (GIconIface *iface)
+{
+	iface->equal = _gcr_memory_icon_equal;
+	iface->hash = _gcr_memory_icon_hash;
+}
+
+static GInputStream *
+_gcr_memory_icon_load (GLoadableIcon *icon, int size, gchar **type,
+                       GCancellable *cancellable, GError **error)
+{
+	GcrMemoryIcon *self = GCR_MEMORY_ICON (icon);
+	GInputStream *is;
+
+	if (type != NULL)
+		*type = g_strdup (self->pv->image_type);
+
+	is = g_memory_input_stream_new_from_data ((guchar*)self->pv->data + self->pv->offset,
+	                                          self->pv->n_data, NULL);
+	g_object_set_data_full (G_OBJECT (is), "back-reference", g_object_ref (self),
+	                        g_object_unref);
+
+	return is;
+}
+
+static void
+_gcr_memory_icon_load_async (GLoadableIcon *icon, int size, GCancellable *cancellable,
+                             GAsyncReadyCallback callback, gpointer user_data)
+{
+	GSimpleAsyncResult *res;
+
+	res = g_simple_async_result_new (G_OBJECT (icon), callback, user_data,
+	                                 _gcr_memory_icon_load_async);
+
+	g_simple_async_result_complete_in_idle (res);
+	g_object_unref (res);
+}
+
+static GInputStream*
+_gcr_memory_icon_finish (GLoadableIcon *icon, GAsyncResult *res, char **type,
+                         GError **error)
+{
+	g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (icon),
+	                      _gcr_memory_icon_load_async), NULL);
+	return _gcr_memory_icon_load (icon, 0, type, NULL, error);
+}
+
+
+static void
+_gcr_memory_icon_iface_loadable_icon (GLoadableIconIface *iface)
+{
+	iface->load = _gcr_memory_icon_load;
+	iface->load_async = _gcr_memory_icon_load_async;
+	iface->load_finish = _gcr_memory_icon_finish;
+}
+
+GIcon*
+_gcr_memory_icon_new (const gchar *image_type, gconstpointer data, gsize n_data)
+{
+	g_return_val_if_fail (image_type != NULL, NULL);
+	g_return_val_if_fail (data != NULL, NULL);
+
+	return _gcr_memory_icon_new_full (image_type, g_memdup (data, n_data),
+	                                  n_data, 0, g_free);
+}
+
+GIcon*
+_gcr_memory_icon_new_full (const gchar *image_type, gpointer data, gsize n_data,
+                           goffset offset, GDestroyNotify destroy)
+{
+	GcrMemoryIcon *self;
+
+	g_return_val_if_fail (image_type != NULL, NULL);
+	g_return_val_if_fail (data != NULL, NULL);
+	g_return_val_if_fail (offset <= n_data, NULL);
+
+	self = g_object_new (GCR_TYPE_MEMORY_ICON, NULL);
+	self->pv->data = data;
+	self->pv->n_data = n_data;
+	self->pv->offset = offset;
+	self->pv->destroy = destroy;
+	self->pv->image_type = g_strdup (image_type);
+
+	return G_ICON (self);
+}
diff --git a/gcr/gcr-memory-icon.h b/gcr/gcr-memory-icon.h
new file mode 100644
index 0000000..c02767f
--- /dev/null
+++ b/gcr/gcr-memory-icon.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef GCR_MEMORY_ICON_H
+#define GCR_MEMORY_ICON_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GCR_MEMORY_ICON_COLUMNS            (_gcr_memory_icon_get_columns ())
+#define GCR_TYPE_MEMORY_ICON               (_gcr_memory_icon_get_type ())
+#define GCR_MEMORY_ICON(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_MEMORY_ICON, GcrMemoryIcon))
+#define GCR_MEMORY_ICON_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_MEMORY_ICON, GcrMemoryIconClass))
+#define GCR_IS_MEMORY_ICON(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_MEMORY_ICON))
+#define GCR_IS_MEMORY_ICON_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_MEMORY_ICON))
+#define GCR_MEMORY_ICON_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_MEMORY_ICON, GcrMemoryIconClass))
+
+typedef struct _GcrMemoryIcon GcrMemoryIcon;
+typedef struct _GcrMemoryIconClass GcrMemoryIconClass;
+typedef struct _GcrMemoryIconPrivate GcrMemoryIconPrivate;
+
+struct _GcrMemoryIcon {
+	GObject parent;
+
+	/*< private >*/
+	GcrMemoryIconPrivate *pv;
+};
+
+struct _GcrMemoryIconClass {
+	GObjectClass parent_class;
+};
+
+GType               _gcr_memory_icon_get_type             (void);
+
+GIcon*              _gcr_memory_icon_new                  (const gchar *image_type,
+                                                           gconstpointer data,
+                                                           gsize n_data);
+
+GIcon*              _gcr_memory_icon_new_full             (const gchar *image_type,
+                                                           gpointer data,
+                                                           gsize n_data,
+                                                           goffset offset,
+                                                           GDestroyNotify destroy);
+
+G_END_DECLS
+
+#endif /* __GCR_MEMORY_ICON_H__ */
diff --git a/gcr/gcr-record.c b/gcr/gcr-record.c
index cd33b2d..a417d0d 100644
--- a/gcr/gcr-record.c
+++ b/gcr/gcr-record.c
@@ -216,6 +216,20 @@ _gcr_record_get_uint (GcrRecord *record, guint column, guint *value)
 	return TRUE;
 }
 
+gpointer
+_gcr_record_get_base64 (GcrRecord *record, guint column, gsize *n_data)
+{
+	const gchar *raw;
+
+	g_return_val_if_fail (record, NULL);
+
+	raw = _gcr_record_get_raw (record, column);
+	if (raw == NULL)
+		return NULL;
+
+	return g_base64_decode (raw, n_data);
+}
+
 const gchar*
 _gcr_record_get_raw (GcrRecord *record, guint column)
 {
diff --git a/gcr/gcr-record.h b/gcr/gcr-record.h
index 5b1b69a..937ef75 100644
--- a/gcr/gcr-record.h
+++ b/gcr/gcr-record.h
@@ -155,6 +155,10 @@ gboolean       _gcr_record_get_uint             (GcrRecord *record,
                                                  guint column,
                                                  guint *value);
 
+gpointer       _gcr_record_get_base64           (GcrRecord *record,
+                                                 guint column,
+                                                 gsize *n_data);
+
 const gchar*   _gcr_record_get_raw              (GcrRecord *record,
                                                  guint column);
 
diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am
index e2ece38..91fd093 100644
--- a/gcr/tests/Makefile.am
+++ b/gcr/tests/Makefile.am
@@ -29,6 +29,7 @@ TEST_PROGS = \
 	test-trust \
 	test-parser \
 	test-record \
+	test-memory-icon \
 	test-gnupg-key \
 	test-gnupg-collection \
 	test-gnupg-process
diff --git a/gcr/tests/test-memory-icon.c b/gcr/tests/test-memory-icon.c
new file mode 100644
index 0000000..ebf200b
--- /dev/null
+++ b/gcr/tests/test-memory-icon.c
@@ -0,0 +1,206 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+   Copyright (C) 2011 Collabora Ltd
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stefw collabora co uk>
+*/
+
+#include "config.h"
+
+#include "gcr/gcr-memory-icon.h"
+
+#include "egg/egg-testing.h"
+
+#include <glib.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+	GIcon *icon;
+	GAsyncResult *result;
+} Test;
+
+static const unsigned char test_data[256] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+	16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+	32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+	48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+	64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+	80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+	96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+	112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+	128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+	144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+	160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+	176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+	192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+	208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+	224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+	240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
+};
+
+static void
+setup (Test *test, gconstpointer unused)
+{
+	test->icon = _gcr_memory_icon_new ("application/octet-stream",
+	                                   test_data, sizeof (test_data));
+}
+
+static void
+teardown (Test *test, gconstpointer unused)
+{
+	g_object_unref (test->icon);
+	if (test->result)
+		g_object_unref (test->result);
+}
+
+static void
+test_equal_same (Test *test, gconstpointer unused)
+{
+	g_assert (g_icon_equal (test->icon, test->icon) == TRUE);
+}
+
+static void
+test_equal_not (Test *test, gconstpointer unused)
+{
+	GIcon *icon;
+
+	icon = g_themed_icon_new ("folder");
+	g_assert (g_icon_equal (test->icon, icon) == FALSE);
+	g_object_unref (icon);
+}
+
+static void
+test_equal_data (Test *test, gconstpointer unused)
+{
+	GIcon *icon;
+
+	icon = _gcr_memory_icon_new ("application/octet-stream",
+	                             test_data, sizeof (test_data));
+	g_assert (g_icon_equal (test->icon, icon) == TRUE);
+	g_assert_cmpuint (g_icon_hash (test->icon), ==, g_icon_hash (icon));
+	g_object_unref (icon);
+}
+
+static void
+test_different_type (Test *test, gconstpointer unused)
+{
+	GIcon *icon;
+
+	icon = _gcr_memory_icon_new ("application/x-other",
+	                             test_data, sizeof (test_data));
+	g_assert (g_icon_equal (test->icon, icon) == FALSE);
+	g_assert_cmpuint (g_icon_hash (test->icon), !=, g_icon_hash (icon));
+	g_object_unref (icon);
+}
+
+static void
+test_different_offset (Test *test, gconstpointer unused)
+{
+	GIcon *icon;
+
+	icon = _gcr_memory_icon_new_full ("application/octet-stream", (gpointer)test_data,
+	                                  sizeof (test_data) - 16, 16, NULL);
+	g_assert (g_icon_equal (test->icon, icon) == FALSE);
+	g_assert_cmpuint (g_icon_hash (test->icon), !=, g_icon_hash (icon));
+	g_object_unref (icon);
+}
+
+static void
+test_load_sync (Test *test, gconstpointer unused)
+{
+	GError *error = NULL;
+	GInputStream *is;
+	gchar buf[1024];
+	gsize length;
+	gchar *type;
+
+	is = g_loadable_icon_load (G_LOADABLE_ICON (test->icon), 0, &type, NULL, &error);
+	g_assert (is != NULL);
+	g_assert_no_error (error);
+	g_assert_cmpstr (type, ==, "application/octet-stream");
+
+	if (!g_input_stream_read_all (is, buf, sizeof (buf), &length, NULL, &error))
+		g_assert_not_reached ();
+	g_assert_no_error (error);
+	egg_assert_cmpsize (length, ==, sizeof (test_data));
+	egg_assert_cmpmem (buf, length, ==, test_data, sizeof (test_data));
+
+	g_free (type);
+	g_object_unref (is);
+}
+
+static void
+on_async_ready (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+	Test *test = user_data;
+
+	g_assert (G_OBJECT (test->icon) == source);
+	g_assert (test->result == NULL);
+	g_assert (g_async_result_get_source_object (result) == source);
+
+	test->result = g_object_ref (result);
+	egg_test_wait_stop ();
+}
+
+static void
+test_load_async (Test *test, gconstpointer unused)
+{
+	GError *error = NULL;
+	GInputStream *is;
+	gchar buf[1024];
+	gsize length;
+	gchar *type;
+
+	g_loadable_icon_load_async (G_LOADABLE_ICON (test->icon), 0, NULL, on_async_ready, test);
+	egg_test_wait_until (500);
+
+	g_assert (test->result);
+	is = g_loadable_icon_load_finish (G_LOADABLE_ICON (test->icon), test->result, &type, &error);
+	g_assert (is != NULL);
+	g_assert_no_error (error);
+	g_assert_cmpstr (type, ==, "application/octet-stream");
+
+	if (!g_input_stream_read_all (is, buf, sizeof (buf), &length, NULL, &error))
+		g_assert_not_reached ();
+	g_assert_no_error (error);
+	egg_assert_cmpsize (length, ==, sizeof (test_data));
+	egg_assert_cmpmem (buf, length, ==, test_data, sizeof (test_data));
+
+	g_free (type);
+	g_object_unref (is);
+}
+
+int
+main (int argc, char **argv)
+{
+	g_type_init ();
+	g_test_init (&argc, &argv, NULL);
+
+	g_test_add ("/gcr/memory-icon/equal_same", Test, NULL, setup, test_equal_same, teardown);
+	g_test_add ("/gcr/memory-icon/equal_not", Test, NULL, setup, test_equal_not, teardown);
+	g_test_add ("/gcr/memory-icon/equal_data", Test, NULL, setup, test_equal_data, teardown);
+	g_test_add ("/gcr/memory-icon/different_type", Test, NULL, setup, test_different_type, teardown);
+	g_test_add ("/gcr/memory-icon/different_offset", Test, NULL, setup, test_different_offset, teardown);
+	g_test_add ("/gcr/memory-icon/load_sync", Test, NULL, setup, test_load_sync, teardown);
+	g_test_add ("/gcr/memory-icon/load_async", Test, NULL, setup, test_load_async, teardown);
+
+	return egg_tests_run_in_thread_with_loop ();
+}
diff --git a/gcr/tests/test-record.c b/gcr/tests/test-record.c
index 265d677..4c132ff 100644
--- a/gcr/tests/test-record.c
+++ b/gcr/tests/test-record.c
@@ -35,7 +35,7 @@ typedef struct {
 static void
 setup (Test *test, gconstpointer unused)
 {
-	test->record = _gcr_record_parse_colons ("one:tab\\there::four:f\xfc""nf:3533333:-88", -1);
+	test->record = _gcr_record_parse_colons ("one:tab\\there::YW9ldTM4Mzg=:f\xfc""nf:3533333:-88", -1);
 }
 
 static void
@@ -215,6 +215,21 @@ test_get_uint_invalid (Test *test, gconstpointer unused)
 }
 
 static void
+test_get_base64 (Test *test, gconstpointer unused)
+{
+	gchar *value;
+	gsize n_value;
+
+	value = _gcr_record_get_base64 (test->record, 3, &n_value);
+	g_assert (value);
+	egg_assert_cmpsize (n_value, ==, 8);
+	g_assert (memcmp (value, "aoeu3838", n_value) == 0);
+
+	g_free (value);
+}
+
+
+static void
 test_free_null (void)
 {
 	_gcr_record_free (NULL);
@@ -294,6 +309,7 @@ main (int argc, char **argv)
 	g_test_add ("/gcr/record/get_uint", Test, NULL, setup, test_get_uint, teardown);
 	g_test_add ("/gcr/record/get_uint_invalid", Test, NULL, setup, test_get_uint_invalid, teardown);
 	g_test_add ("/gcr/record/get_uint_range", Test, NULL, setup, test_get_uint_range, teardown);
+	g_test_add ("/gcr/record/get_base64", Test, NULL, setup, test_get_base64, teardown);
 	g_test_add ("/gcr/record/get_schema", Test, NULL, setup, test_get_schema, teardown);
 
 	return g_test_run ();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]