[gcr] gck: Enumerator can now retrieve object attributes



commit 074c6666bb3bb5264d9a0833c136c44ee9492b5e
Author: Stef Walter <stefw collabora co uk>
Date:   Wed Oct 12 11:18:05 2011 +0200

    gck: Enumerator can now retrieve object attributes
    
     * New interface called GckObjectAttributes. Callers derive from
       GckObject, implement GckObjectAttributes and set fields in the
       iface to denote which attributes are interested in.
     * Caller passes type of derived object to gck_enumerator_set_object_type()
       and then enumerator will retrieve attributes set in iface, and return
       objects of that type
     * GckObjectAttributes has attributes property.

 .gitignore                          |    1 -
 docs/reference/gck/Makefile.am      |    2 +-
 docs/reference/gck/gck-docs.sgml    |    1 +
 docs/reference/gck/gck-sections.txt |   24 ++-
 docs/reference/gck/gck.interfaces   |    1 +
 docs/reference/gcr/gcr.interfaces   |   39 ++++
 gck/Makefile.am                     |    1 +
 gck/gck-enumerator.c                |  413 +++++++++++++++++++++++++++--------
 gck/gck-object-attributes.c         |  106 +++++++++
 gck/gck-object.c                    |   35 ++--
 gck/gck-private.h                   |    3 +
 gck/gck.h                           |   41 ++++-
 gck/gck.symbols                     |    5 +
 gck/tests/Makefile.am               |    2 +-
 gck/tests/test-gck-enumerator.c     |  137 ++++++++++++-
 15 files changed, 694 insertions(+), 117 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index db54d73..c45aa9e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,7 +54,6 @@ stamp-*
 /docs/reference/*/*-unused.txt
 /docs/reference/*/*.args
 /docs/reference/*/*.hierarchy
-/docs/reference/*/*.interfaces
 /docs/reference/*/*.prerequisites
 /docs/reference/*/*.signals
 /docs/reference/*/html
diff --git a/docs/reference/gck/Makefile.am b/docs/reference/gck/Makefile.am
index 72ea16f..98cbc34 100644
--- a/docs/reference/gck/Makefile.am
+++ b/docs/reference/gck/Makefile.am
@@ -64,7 +64,7 @@ IGNORE_HFILES= \
 	pkcs11n.h \
 	pkcs11x.h \
 	pkcs11i.h \
-	tests/mock-interaction.h
+	mock-interaction.h
 
 # Images to copy into HTML directory.
 # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
diff --git a/docs/reference/gck/gck-docs.sgml b/docs/reference/gck/gck-docs.sgml
index f5668f3..e70290a 100644
--- a/docs/reference/gck/gck-docs.sgml
+++ b/docs/reference/gck/gck-docs.sgml
@@ -21,6 +21,7 @@
 		<xi:include href="xml/gck-object.xml"/>
 		<xi:include href="xml/gck-attribute.xml"/>
 		<xi:include href="xml/gck-attributes.xml"/>
+		<xi:include href="xml/gck-object-attributes.xml"/>
 		<xi:include href="xml/gck-enumerator.xml"/>
 		<xi:include href="xml/gck-modules.xml"/>
 		<xi:include href="xml/gck-error.xml"/>
diff --git a/docs/reference/gck/gck-sections.txt b/docs/reference/gck/gck-sections.txt
index a26f2c1..6482a6d 100644
--- a/docs/reference/gck/gck-sections.txt
+++ b/docs/reference/gck/gck-sections.txt
@@ -61,10 +61,10 @@ gck_attributes_unref
 gck_attributes_contains
 gck_attributes_dump
 gck_attributes_new_full
+GCK_TYPE_ATTRIBUTES
 GckAllocator
 <SUBSECTION Standard>
 gck_attributes_get_type
-GCK_TYPE_ATTRIBUTES
 gck_attributes_get_boxed_type
 </SECTION>
 
@@ -255,6 +255,7 @@ gck_session_info_get_type
 <SECTION>
 <FILE>gck-object</FILE>
 GckObject
+GckObjectClass
 gck_object_from_handle
 gck_objects_from_handle_array
 gck_object_equal
@@ -283,7 +284,6 @@ gck_object_set_template
 gck_object_set_template_async
 gck_object_set_template_finish
 <SUBSECTION Standard>
-GckObjectClass
 gck_object_get_type
 GCK_IS_OBJECT
 GCK_IS_OBJECT_CLASS
@@ -295,6 +295,20 @@ GckObjectPrivate
 </SECTION>
 
 <SECTION>
+<FILE>gck-object-attributes</FILE>
+GckObjectAttributes
+GckObjectAttributesIface
+gck_object_attributes_get_attributes
+gck_object_attributes_set_attributes
+<SUBSECTION Standard>
+gck_object_attributes_get_type
+GCK_IS_OBJECT_ATTRIBUTES
+GCK_OBJECT_ATTRIBUTES
+GCK_OBJECT_ATTRIBUTES_GET_INTERFACE
+GCK_TYPE_OBJECT_ATTRIBUTES
+</SECTION>
+
+<SECTION>
 <FILE>gck-error</FILE>
 GCK_VENDOR_CODE
 GCK_ERROR
@@ -329,12 +343,14 @@ gck_uri_flags_get_type
 <SECTION>
 <FILE>gck-enumerator</FILE>
 GckEnumerator
-gck_enumerator_get_interaction
-gck_enumerator_set_interaction
 gck_enumerator_next
 gck_enumerator_next_async
 gck_enumerator_next_finish
 gck_enumerator_next_n
+gck_enumerator_get_interaction
+gck_enumerator_set_interaction
+gck_enumerator_get_object_type
+gck_enumerator_set_object_type
 <SUBSECTION Standard>
 GckEnumeratorClass
 gck_enumerator_get_type
diff --git a/docs/reference/gck/gck.interfaces b/docs/reference/gck/gck.interfaces
new file mode 100644
index 0000000..5fdaecc
--- /dev/null
+++ b/docs/reference/gck/gck.interfaces
@@ -0,0 +1 @@
+gck_object_attributes_get_type
diff --git a/docs/reference/gcr/gcr.interfaces b/docs/reference/gcr/gcr.interfaces
new file mode 100644
index 0000000..5af264f
--- /dev/null
+++ b/docs/reference/gcr/gcr.interfaces
@@ -0,0 +1,39 @@
+GcrCertificateRenderer GcrRenderer GcrComparableIface GcrCertificate
+GtkWidget AtkImplementorIface GtkBuildable
+GtkContainer AtkImplementorIface GtkBuildable
+GtkBin AtkImplementorIface GtkBuildable
+GtkAlignment AtkImplementorIface GtkBuildable
+GcrCertificateWidget AtkImplementorIface GtkBuildable
+GcrKeyWidget AtkImplementorIface GtkBuildable
+GcrUnlockOptionsWidget AtkImplementorIface GtkBuildable
+GtkComboBox AtkImplementorIface GtkBuildable GtkCellLayout GtkCellEditable
+GcrComboSelector AtkImplementorIface GtkBuildable GtkCellLayout GtkCellEditable
+GtkButton AtkImplementorIface GtkBuildable GtkActivatable
+GcrImportButton AtkImplementorIface GtkBuildable GtkActivatable
+GtkScrolledWindow AtkImplementorIface GtkBuildable
+GcrDisplayScrolled AtkImplementorIface GtkBuildable GcrViewer
+GcrViewerWidget AtkImplementorIface GtkBuildable GcrViewer
+GtkWindow AtkImplementorIface GtkBuildable
+GcrViewerWindow AtkImplementorIface GtkBuildable
+GtkTreeView AtkImplementorIface GtkBuildable GtkScrollable
+GcrListSelector AtkImplementorIface GtkBuildable GtkScrollable
+GcrTreeSelector AtkImplementorIface GtkBuildable GtkScrollable
+GtkCellArea GtkCellLayout GtkBuildable
+GtkTreeViewColumn GtkCellLayout GtkBuildable
+GcrCollectionModel GtkTreeModel GtkTreeSortable
+GcrKeyRenderer GcrRenderer
+GcrPkcs11Certificate GcrComparableIface GcrCertificate
+GcrSimpleCertificate GcrComparableIface GcrCertificate
+GcrSimpleCollection GcrCollection
+GcrUnionCollection GcrCollection
+GtkWidgetAccessible AtkComponent
+GtkContainerAccessible AtkComponent
+GtkComboBoxAccessible AtkComponent AtkAction AtkSelection
+GtkButtonAccessible AtkComponent AtkAction AtkImage
+GtkTreeViewAccessible AtkComponent AtkTable AtkSelection GtkCellAccessibleParent
+GtkScrolledWindowAccessible AtkComponent
+GtkWindowAccessible AtkComponent AtkWindow
+GtkAction GtkBuildable
+GdkPixbuf GIcon
+GApplication GActionGroup
+GtkApplication GActionGroup
diff --git a/gck/Makefile.am b/gck/Makefile.am
index c43f306..5856f0f 100644
--- a/gck/Makefile.am
+++ b/gck/Makefile.am
@@ -41,6 +41,7 @@ PUBLIC_FILES = \
 	gck-module.c \
 	gck-modules.c \
 	gck-object.c \
+	gck-object-attributes.c \
 	gck-password.c \
 	gck-session.c \
 	gck-slot.c \
diff --git a/gck/gck-enumerator.c b/gck/gck-enumerator.c
index c57297e..add0bee 100644
--- a/gck/gck-enumerator.c
+++ b/gck/gck-enumerator.c
@@ -45,7 +45,8 @@
 
 enum {
 	PROP_0,
-	PROP_INTERACTION
+	PROP_INTERACTION,
+	PROP_OBJECT_TYPE
 };
 
 /**
@@ -55,9 +56,16 @@ enum {
  * An object that allows enumerating of objects across modules, tokens.
  */
 
+typedef struct _GckEnumeratorResult {
+	gulong handle;
+	GckSession *session;
+	GckAttributes *attrs;
+} GckEnumeratorResult;
+
 typedef struct _GckEnumeratorState GckEnumeratorState;
 
-typedef gpointer (*GckEnumeratorFunc) (GckEnumeratorState *args, gboolean forward);
+typedef gpointer (*GckEnumeratorFunc)     (GckEnumeratorState *args,
+                                           gboolean forward);
 
 struct _GckEnumeratorState {
 	/* For the current call */
@@ -72,6 +80,11 @@ struct _GckEnumeratorState {
 	GckSessionOptions session_options;
 	GTlsInteraction *interaction;
 
+	/* The type of objects to create */
+	GType object_type;
+	gpointer object_class;
+	GckObjectAttributesIface *object_iface;
+
 	/* state_slots */
 	GList *slots;
 
@@ -84,30 +97,50 @@ struct _GckEnumeratorState {
 	GckSession *session;
 
 	/* state_results */
-	GArray *objects;
-
-	/* Output from enumerator */
-	GList *results;
+	GQueue *results;
 };
 
 struct _GckEnumeratorPrivate {
 	GMutex *mutex;
 	GckEnumeratorState *the_state;
 	GTlsInteraction *interaction;
+	GType object_type;
+	GckObjectClass *object_class;
 };
 
 G_DEFINE_TYPE (GckEnumerator, gck_enumerator, G_TYPE_OBJECT);
 
-static gpointer state_modules        (GckEnumeratorState *args, gboolean forward);
-static gpointer state_slots          (GckEnumeratorState *args, gboolean forward);
-static gpointer state_slot           (GckEnumeratorState *args, gboolean forward);
-static gpointer state_session        (GckEnumeratorState *args, gboolean forward);
-static gpointer state_authenticated  (GckEnumeratorState *args, gboolean forward);
-static gpointer state_results        (GckEnumeratorState *args, gboolean forward);
+static gpointer state_modules        (GckEnumeratorState *args,
+                                      gboolean forward);
 
-/* ----------------------------------------------------------------------------
- * INTERNAL
- */
+static gpointer state_slots          (GckEnumeratorState *args,
+                                      gboolean forward);
+
+static gpointer state_slot           (GckEnumeratorState *args,
+                                      gboolean forward);
+
+static gpointer state_session        (GckEnumeratorState *args,
+                                      gboolean forward);
+
+static gpointer state_authenticated  (GckEnumeratorState *args,
+                                      gboolean forward);
+
+static gpointer state_results        (GckEnumeratorState *args,
+                                      gboolean forward);
+
+static gpointer state_attributes     (GckEnumeratorState *args,
+                                      gboolean forward);
+
+
+static void
+_gck_enumerator_result_free (gpointer data)
+{
+	GckEnumeratorResult *result = data;
+	g_object_unref (result->session);
+	if (result->attrs)
+		gck_attributes_unref (result->attrs);
+	g_slice_free (GckEnumeratorResult, result);
+}
 
 static gpointer
 rewind_state (GckEnumeratorState *args, GckEnumeratorFunc handler)
@@ -144,19 +177,22 @@ cleanup_state (GckEnumeratorState *args)
 	g_assert (!args->session);
 
 	/* state_results */
-	if (args->objects)
-		g_array_free (args->objects, TRUE);
-	args->objects = NULL;
-
-	/* Other cleanup */
-	gck_list_unref_free (args->results);
-	args->results = NULL;
+	if (args->results) {
+		g_queue_foreach (args->results, (GFunc) _gck_enumerator_result_free, NULL);
+		g_queue_free (args->results);
+		args->results = NULL;
+	}
 
 	gck_list_unref_free (args->modules);
 	args->modules = NULL;
 
 	g_clear_object (&args->interaction);
 
+	if (args->object_class)
+		g_type_class_unref (args->object_class);
+	args->object_class = NULL;
+	args->object_type = 0;
+
 	if (args->match) {
 		if (args->match->attributes)
 			_gck_attributes_unlock (args->match->attributes);
@@ -368,7 +404,8 @@ state_authenticated (GckEnumeratorState *args, gboolean forward)
 	CK_OBJECT_HANDLE objects[128];
 	CK_SESSION_HANDLE session;
 	CK_ATTRIBUTE_PTR attrs;
-	CK_ULONG n_attrs, count;
+	CK_ULONG n_attrs, i,count;
+	GckEnumeratorResult *result;
 	CK_RV rv;
 
 	/* Just go back, no logout */
@@ -377,9 +414,12 @@ state_authenticated (GckEnumeratorState *args, gboolean forward)
 
 	/* This is where we do the actual searching */
 
-	g_assert (args->session);
-	g_assert (args->want_objects);
-	g_assert (args->funcs);
+	g_assert (args->session != NULL);
+	g_assert (args->want_objects > 0);
+	g_assert (args->funcs != NULL);
+
+	if (!args->results)
+		args->results = g_queue_new ();
 
 	if (args->match->attributes) {
 		attrs = _gck_attributes_commit_out (args->match->attributes, &n_attrs);
@@ -406,80 +446,133 @@ state_authenticated (GckEnumeratorState *args, gboolean forward)
 			if (rv != CKR_OK || count == 0)
 				break;
 
-			if (!args->objects)
-				args->objects = g_array_new (FALSE, TRUE, sizeof (CK_OBJECT_HANDLE));
 			_gck_debug ("matched %lu objects", count);
-			g_array_append_vals (args->objects, objects, count);
+
+			for (i = 0; i < count; i++) {
+				result = g_slice_new0 (GckEnumeratorResult);
+				result->handle = objects[i];
+				result->session = g_object_ref (args->session);
+				g_queue_push_tail (args->results, result);
+			}
 		}
 
 		(args->funcs->C_FindObjectsFinal) (session);
 	}
 
 	_gck_debug ("finding objects completed with: %s", _gck_stringize_rv (rv));
-	return state_results;
+	return state_attributes;
 }
 
-static GckObject*
-extract_result (GckEnumeratorState *args)
+static gpointer
+state_attributes (GckEnumeratorState *args,
+                  gboolean forward)
 {
-	CK_OBJECT_HANDLE handle;
+	GckEnumeratorResult *result;
+	GckAttributes *attrs;
+	CK_ATTRIBUTE_PTR template;
+	CK_ULONG n_template;
+	CK_SESSION_HANDLE session;
+	gint count;
+	GList *l;
+	gint i;
+	CK_RV rv;
 
-	if (!args->objects || !args->objects->len)
-		return NULL;
+	g_assert (args->funcs != NULL);
+	g_assert (args->object_class != NULL);
+	g_assert (args->results != NULL);
 
-	g_assert (args->session);
+	/* No cleanup, just unwind */
+	if (!forward)
+		return state_authenticated;
+
+	/* If no request for attributes, just go forward */
+	if (args->object_iface == NULL ||
+	    args->object_iface->n_attribute_types == 0)
+		return state_results;
 
-	handle = g_array_index (args->objects, CK_OBJECT_HANDLE, 0);
-	g_array_remove_index_fast (args->objects, 0);
+	session = gck_session_get_handle (args->session);
+	g_return_val_if_fail (session, NULL);
+
+	count = 0;
+
+	/* Get the attributes for want_objects */
+	for (count = 0, l = args->results->head;
+	     l != NULL && count < args->want_objects;
+	     l = g_list_next (l), count++) {
+		result = l->data;
+
+		attrs = gck_attributes_new ();
+		for (i = 0; i < args->object_iface->n_attribute_types; ++i)
+			gck_attributes_add_empty (attrs, args->object_iface->attribute_types[i]);
+		_gck_attributes_lock (attrs);
+
+		/* Ask for attribute sizes */
+		template = _gck_attributes_prepare_in (attrs, &n_template);
+
+		rv = (args->funcs->C_GetAttributeValue) (session, result->handle, template, n_template);
+		if (GCK_IS_GET_ATTRIBUTE_RV_OK (rv)) {
+
+			/* Allocate memory for each value */
+			template = _gck_attributes_commit_in (attrs, &n_template);
+
+			/* Now get the actual values */
+			rv = (args->funcs->C_GetAttributeValue) (session, result->handle, template, n_template);
+		}
+
+		_gck_attributes_unlock (attrs);
+
+		if (GCK_IS_GET_ATTRIBUTE_RV_OK (rv)) {
+			if (_gck_debugging) {
+				gchar *string = _gck_attributes_format (attrs);
+				_gck_debug ("retrieved attributes for object %lu: %s",
+				            result->handle, string);
+				g_free (string);
+			}
+			result->attrs = attrs;
+			rv = CKR_OK;
+
+		} else {
+			g_message ("couldn't retrieve attributes when enumerating: %s",
+			           gck_message_from_rv (rv));
+			gck_attributes_unref (attrs);
+		}
+	}
 
-	return gck_object_from_handle (args->session, handle);
+	return state_results;
 }
 
 static gpointer
-state_results (GckEnumeratorState *args, gboolean forward)
+state_results (GckEnumeratorState *args,
+               gboolean forward)
 {
-	GckObject *object;
-	guint have;
-
-	g_assert (args->session);
+	g_assert (args->results != NULL);
 
 	/* No cleanup, just unwind */
 	if (!forward)
 		return state_authenticated;
 
-	/* Create result objects from what we have */
-	have = g_list_length (args->results);
-
-	while (have < args->want_objects) {
-
-		object = extract_result (args);
-		if (!object) {
-			_gck_debug ("wanted %d objects, have %d, looking for more",
-			            args->want_objects, have);
-			return rewind_state (args, state_slots);
-		}
-
-		args->results = g_list_append (args->results, object);
-		++have;
+	while (args->want_objects > g_queue_get_length (args->results)) {
+		_gck_debug ("wanted %d objects, have %d, looking for more",
+		            args->want_objects, g_queue_get_length (args->results));
+		return rewind_state (args, state_slots);
 	}
 
 	_gck_debug ("wanted %d objects, returned %d objects",
-	            args->want_objects, have);
+	            args->want_objects, g_queue_get_length (args->results));
 
 	/* We got all the results we wanted */
 	return NULL;
 }
 
-/* ----------------------------------------------------------------------------
- * OBJECT
- */
-
 static void
 gck_enumerator_init (GckEnumerator *self)
 {
 	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_ENUMERATOR, GckEnumeratorPrivate);
 	self->pv->mutex = g_mutex_new ();
 	self->pv->the_state = g_new0 (GckEnumeratorState, 1);
+	self->pv->object_type = GCK_TYPE_OBJECT;
+	self->pv->object_class = g_type_class_ref (self->pv->object_type);
+	g_assert (self->pv->object_class);
 }
 
 static void
@@ -494,6 +587,9 @@ gck_enumerator_get_property (GObject *obj,
 	case PROP_INTERACTION:
 		g_value_take_object (value, gck_enumerator_get_interaction (self));
 		break;
+	case PROP_OBJECT_TYPE:
+		g_value_set_gtype (value, gck_enumerator_get_object_type (self));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
@@ -512,6 +608,9 @@ gck_enumerator_set_property (GObject *obj,
 	case PROP_INTERACTION:
 		gck_enumerator_set_interaction (self, g_value_get_object (value));
 		break;
+	case PROP_OBJECT_TYPE:
+		gck_enumerator_set_object_type (self, g_value_get_gtype (value));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
@@ -540,6 +639,7 @@ gck_enumerator_finalize (GObject *obj)
 	g_free (self->pv->the_state);
 
 	g_mutex_free (self->pv->mutex);
+	g_type_class_unref (self->pv->object_class);
 
 	G_OBJECT_CLASS (gck_enumerator_parent_class)->finalize (obj);
 }
@@ -566,6 +666,16 @@ gck_enumerator_class_init (GckEnumeratorClass *klass)
 	g_object_class_install_property (gobject_class, PROP_INTERACTION,
 		g_param_spec_object ("interaction", "Interaction", "Interaction asking for pins",
 		                     G_TYPE_TLS_INTERACTION, G_PARAM_READWRITE));
+
+	/**
+	 * GckEnumerator:object-type:
+	 *
+	 * The type of objects that are created by the enumerator. Must be
+	 * GckObject or derived from it.
+	 */
+	g_object_class_install_property (gobject_class, PROP_OBJECT_TYPE,
+		g_param_spec_gtype ("object-type", "Object Type", "Type of objects created",
+		                    GCK_TYPE_OBJECT, G_PARAM_READWRITE));
 }
 
 /* ----------------------------------------------------------------------------
@@ -648,6 +758,69 @@ free_enumerate_next (EnumerateNext *args)
 }
 
 /**
+ * gck_enumerator_get_object_type:
+ * @self: an enumerator
+ *
+ * Get the type of objects created by this enumerator. The type will always
+ * either be #GckObject or derived from it.
+ *
+ * Returns: (transfer none): the type of objects created
+ */
+GType
+gck_enumerator_get_object_type (GckEnumerator *self)
+{
+	GType result;
+
+	g_return_val_if_fail (GCK_IS_ENUMERATOR (self), 0);
+
+	g_mutex_lock (self->pv->mutex);
+
+		result = self->pv->object_type;
+
+	g_mutex_unlock (self->pv->mutex);
+
+	return result;
+}
+
+/**
+ * gck_enumerator_set_object_type:
+ * @self: an enumerator
+ * @object_type: the type of objects to create
+ *
+ * Set the type of objects to be created by this enumerator. The type must
+ * always be either #GckObject or derived from it.
+ *
+ * If the #GckObjectClass:attribute_types and #GckObjectClass:n_attribute_types
+ * are set in a derived class, then the derived class must have a property
+ * called 'attributes' of boxed type GCK_TYPE_ATTRIBUTE.
+ */
+void
+gck_enumerator_set_object_type (GckEnumerator *self,
+                                GType object_type)
+{
+	gpointer klass;
+
+	g_return_if_fail (GCK_IS_ENUMERATOR (self));
+
+	if (!g_type_is_a (object_type, GCK_TYPE_OBJECT)) {
+		g_warning ("the object_type '%s' is not a derived type of GckObject",
+		           g_type_name (object_type));
+		return;
+	}
+
+	klass = g_type_class_ref (object_type);
+
+	g_mutex_lock (self->pv->mutex);
+
+		if (self->pv->object_type)
+			g_type_class_unref (self->pv->object_class);
+		self->pv->object_type = object_type;
+		self->pv->object_class = klass;
+
+	g_mutex_unlock (self->pv->mutex);
+}
+
+/**
  * gck_enumerator_get_interaction:
  * @self: the enumerator
  *
@@ -717,6 +890,17 @@ check_out_enumerator_state (GckEnumerator *self)
 			g_clear_object (&state->interaction);
 			if (self->pv->interaction)
 				state->interaction = g_object_ref (self->pv->interaction);
+
+			if (state->object_class)
+				g_type_class_unref (state->object_class);
+
+			/* Must already be holding a reference, state also holds a ref */
+			state->object_type = self->pv->object_type;
+			state->object_class = g_type_class_peek (state->object_type);
+			g_assert (state->object_class == self->pv->object_class);
+			state->object_iface = g_type_interface_peek (state->object_class,
+			                                             GCK_TYPE_OBJECT_ATTRIBUTES);
+			g_type_class_ref (state->object_type);
 		}
 
 	g_mutex_unlock (self->pv->mutex);
@@ -739,6 +923,57 @@ check_in_enumerator_state (GckEnumerator *self,
 	g_mutex_unlock (self->pv->mutex);
 }
 
+static GckObject *
+extract_result (GckEnumeratorState *state)
+{
+	GckEnumeratorResult *result;
+	GckModule *module;
+	GckObject *object;
+
+	g_assert (state != NULL);
+
+	if (state->results == NULL)
+		return NULL;
+
+	result = g_queue_pop_head (state->results);
+	if (result == NULL)
+		return NULL;
+
+	module = gck_session_get_module (result->session);
+	object = g_object_new (state->object_type,
+	                       "module", module,
+	                       "handle", result->handle,
+	                       "session", result->session,
+	                       result->attrs ? "attributes" : NULL, result->attrs,
+	                       NULL);
+	g_object_unref (module);
+
+	_gck_enumerator_result_free (result);
+	return object;
+}
+
+static GList *
+extract_results (GckEnumeratorState *state,
+                 gint *want_objects)
+{
+	GList *objects = NULL;
+	GckObject *object;
+	gint i;
+
+	g_assert (state != NULL);
+	g_assert (want_objects != NULL);
+
+	for (i = 0; i < *want_objects; i++) {
+		object = extract_result (state);
+		if (object == NULL)
+			break;
+		objects = g_list_prepend (objects, object);
+	}
+
+	*want_objects -= i;
+	return g_list_reverse (objects);
+}
+
 /**
  * gck_enumerator_next:
  * @self: The enumerator
@@ -753,8 +988,10 @@ check_in_enumerator_state (GckEnumerator *self,
  * Returns: (transfer full) (allow-none): The next object, which must be released
  * using g_object_unref, or %NULL.
  */
-GckObject*
-gck_enumerator_next (GckEnumerator *self, GCancellable *cancellable, GError **error)
+GckObject *
+gck_enumerator_next (GckEnumerator *self,
+                     GCancellable *cancellable,
+                     GError **error)
 {
 	EnumerateNext args = { GCK_ARGUMENTS_INIT, NULL, };
 	GckObject *result = NULL;
@@ -767,18 +1004,12 @@ gck_enumerator_next (GckEnumerator *self, GCancellable *cancellable, GError **er
 
 	/* A result from a previous run? */
 	result = extract_result (args.state);
-	if (!result) {
+	if (result == NULL) {
 		args.state->want_objects = 1;
 
 		/* Run the operation and steal away the results */
-		if (_gck_call_sync (NULL, perform_enumerate_next, NULL, &args, cancellable, error)) {
-			if (args.state->results) {
-				g_assert (g_list_length (args.state->results) == 1);
-				result = g_object_ref (args.state->results->data);
-				gck_list_unref_free (args.state->results);
-				args.state->results = NULL;
-			}
-		}
+		if (_gck_call_sync (NULL, perform_enumerate_next, NULL, &args, cancellable, error))
+			result = extract_result (args.state);
 
 		args.state->want_objects = 0;
 	}
@@ -806,12 +1037,15 @@ gck_enumerator_next (GckEnumerator *self, GCancellable *cancellable, GError **er
  * Returns: (transfer full) (element-type Gck.Object): A list of objects, which
  * should be freed using gck_list_unref_free().
  */
-GList*
-gck_enumerator_next_n (GckEnumerator *self, gint max_objects, GCancellable *cancellable,
+GList *
+gck_enumerator_next_n (GckEnumerator *self,
+                       gint max_objects,
+                       GCancellable *cancellable,
                        GError **error)
 {
 	EnumerateNext args = { GCK_ARGUMENTS_INIT, NULL, };
 	GList *results = NULL;
+	gint want_objects;
 
 	g_return_val_if_fail (GCK_IS_ENUMERATOR (self), NULL);
 	g_return_val_if_fail (max_objects == -1 || max_objects > 0, NULL);
@@ -821,15 +1055,22 @@ gck_enumerator_next_n (GckEnumerator *self, gint max_objects, GCancellable *canc
 	args.state = check_out_enumerator_state (self);
 	g_return_val_if_fail (args.state != NULL, NULL);
 
-	args.state->want_objects = max_objects <= 0 ? G_MAXINT : max_objects;
+	want_objects = max_objects <= 0 ? G_MAXINT : max_objects;
+
+	/* A result from a previous run? */
+	results = extract_results (args.state, &want_objects);
+	if (want_objects > 0) {
+		args.state->want_objects = want_objects;
+
+		/* Run the operation and steal away the results */
+		if (_gck_call_sync (NULL, perform_enumerate_next, NULL, &args, cancellable, error))
+			results = g_list_concat (results, extract_results (args.state, &want_objects));
 
-	/* Run the operation and steal away the results */
-	if (_gck_call_sync (NULL, perform_enumerate_next, NULL, &args, cancellable, error)) {
-		results = args.state->results;
-		args.state->results = NULL;
+		args.state->want_objects = 0;
 	}
 
-	args.state->want_objects = 0;
+	if (results)
+		g_clear_error (error);
 
 	/* Put the state back */
 	check_in_enumerator_state (self, args.state);
@@ -895,18 +1136,18 @@ gck_enumerator_next_finish (GckEnumerator *self, GAsyncResult *result, GError **
 	EnumerateNext *args;
 	GckEnumeratorState *state;
 	GList *results = NULL;
+	gint want_objects;
 
 	g_object_ref (self);
 
 	args = _gck_call_arguments (result, EnumerateNext);
 	state = args->state;
 	args->state = NULL;
+	want_objects = state->want_objects;
 	state->want_objects = 0;
 
-	if (_gck_call_basic_finish (result, error)) {
-		results = state->results;
-		state->results = NULL;
-	}
+	if (_gck_call_basic_finish (result, error))
+		results = extract_results (state, &want_objects);
 
 	/* Put the state back */
 	check_in_enumerator_state (self, state);
diff --git a/gck/gck-object-attributes.c b/gck/gck-object-attributes.c
new file mode 100644
index 0000000..fad6055
--- /dev/null
+++ b/gck/gck-object-attributes.c
@@ -0,0 +1,106 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-object-attributes.c - the GObject PKCS#11 wrapper library
+
+   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 "gck.h"
+#include "gck-private.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gck-object-attributes
+ * @title: GckObjectAttributes
+ * @short_description: An interface which holds attributes for a PKCS\#11 object
+ *
+ * #GckObjectAttributes is an interface implemented by derived classes of
+ * #GckObject to indicate which attributes they'd like an enumerator to retrieve.
+ * These attributes are then cached on the object and can be retrieved through
+ * the #GckObjectAttributes:attributes property.
+ */
+
+/**
+ * GckObjectAttributesIface:
+ * @interface: parent interface
+ * @attribute_types: (array length=n_attribute_types): attribute types that an
+ *                   enumerator should retrieve
+ * @n_attribute_types: number of attribute types to be retrieved
+ *
+ * Interface for #GckObjectAttributes. If the @attribute_types field is set by
+ * a implementing class, then the a #GckEnumerator which has been setup using
+ * gck_enumerator_set_object_type()
+ */
+
+typedef GckObjectAttributesIface GckObjectAttributesInterface;
+G_DEFINE_INTERFACE (GckObjectAttributes, gck_object_attributes, GCK_TYPE_OBJECT);
+
+static void
+gck_object_attributes_default_init (GckObjectAttributesIface *iface)
+{
+	static volatile gsize initialized = 0;
+	if (g_once_init_enter (&initialized)) {
+
+		/**
+		 * GckObjectAttributes:attributes:
+		 *
+		 * The attributes cached on this object.
+		 */
+		g_object_interface_install_property (iface,
+		         g_param_spec_boxed ("attributes", "Attributes", "PKCS#11 Attributes",
+		                             GCK_TYPE_ATTRIBUTES, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+		g_once_init_leave (&initialized, 1);
+	}
+}
+
+/**
+ * gck_object_attributes_get_attributes:
+ * @object: an object with attributes
+ *
+ * Gets the attributes cached on this object.
+ *
+ * Returns: (transfer full) (allow-none): the attributes
+ */
+GckAttributes *
+gck_object_attributes_get_attributes (GckObjectAttributes *object)
+{
+	GckAttributes *attributes = NULL;
+	g_return_val_if_fail (GCK_IS_OBJECT_ATTRIBUTES (object), NULL);
+	g_object_get (object, "attributes", &attributes, NULL);
+	return attributes;
+}
+
+/**
+ * gck_object_attributes_set_attributes:
+ * @object: an object with attributes
+ * @attributes: (allow-none): the attributes to cache
+ *
+ * Sets the attributes cached on this object.
+ */
+void
+gck_object_attributes_set_attributes (GckObjectAttributes *object,
+                                      GckAttributes *attributes)
+{
+	g_return_if_fail (GCK_IS_OBJECT_ATTRIBUTES (object));
+	g_object_set (object, "attributes", attributes, NULL);
+}
diff --git a/gck/gck-object.c b/gck/gck-object.c
index 7798db2..c7b1eb0 100644
--- a/gck/gck-object.c
+++ b/gck/gck-object.c
@@ -44,6 +44,19 @@
  * Represents a PKCS11 object handle such as a key or certifiacte.
  */
 
+/**
+ * GckObjectClass:
+ * @parent: derived from this
+ *
+ * The class for a #GckObject.
+ *
+ * If the @attribute_types field is set by a derived class, then the a
+ * #GckEnumerator which has been setup using gck_enumerator_set_object_type()
+ * with this derived type will retrieve these attributes when enumerating. In
+ * this case the class must implement an 'attributes' property of boxed type
+ * GCK_TYPE_ATTRIBUTES.
+ */
+
 /*
  * MT safe -- Nothing in GckObjectData changes between
  * init and finalize. All GckObjectPrivate access between init
@@ -588,24 +601,6 @@ typedef struct _GetAttributes {
 	GckAttributes *attrs;
 } GetAttributes;
 
-/*
- * Certain failure return values only apply to individual attributes
- * being retrieved. These are ignored, since the attribute should
- * already have -1 set as the length.
- */
-static gboolean
-is_ok_get_attributes_rv (CK_RV rv)
-{
-	switch (rv) {
-	case CKR_OK:
-	case CKR_ATTRIBUTE_SENSITIVE:
-	case CKR_ATTRIBUTE_TYPE_INVALID:
-		return TRUE;
-	default:
-		return FALSE;
-	}
-}
-
 static CK_RV
 perform_get_attributes (GetAttributes *args)
 {
@@ -622,7 +617,7 @@ perform_get_attributes (GetAttributes *args)
 	/* Get the size of each value */
 	rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object,
 	                                               attrs, n_attrs);
-	if (!is_ok_get_attributes_rv (rv))
+	if (!GCK_IS_GET_ATTRIBUTE_RV_OK (rv))
 		return rv;
 
 	/* Allocate memory for each value */
@@ -632,7 +627,7 @@ perform_get_attributes (GetAttributes *args)
 	rv = (args->base.pkcs11->C_GetAttributeValue) (args->base.handle, args->object,
 	                                               attrs, n_attrs);
 
-	if (is_ok_get_attributes_rv (rv))
+	if (GCK_IS_GET_ATTRIBUTE_RV_OK (rv))
 		rv = CKR_OK;
 
 	return rv;
diff --git a/gck/gck-private.h b/gck/gck-private.h
index c2919cf..4caf079 100644
--- a/gck/gck-private.h
+++ b/gck/gck-private.h
@@ -32,6 +32,9 @@
 
 G_BEGIN_DECLS
 
+#define GCK_IS_GET_ATTRIBUTE_RV_OK(rv) \
+	((rv) == CKR_OK || (rv) == CKR_ATTRIBUTE_SENSITIVE || (rv) == CKR_ATTRIBUTE_TYPE_INVALID)
+
 /* ---------------------------------------------------------------------------
  * ATTRIBUTE INTERNALS
  */
diff --git a/gck/gck.h b/gck/gck.h
index c62e1de..4b02220 100644
--- a/gck/gck.h
+++ b/gck/gck.h
@@ -287,6 +287,7 @@ typedef struct _GckSlot GckSlot;
 typedef struct _GckModule GckModule;
 typedef struct _GckSession GckSession;
 typedef struct _GckObject GckObject;
+typedef struct _GckObjectAttributes GckObjectAttributes;
 typedef struct _GckEnumerator GckEnumerator;
 typedef struct _GckUriData GckUriData;
 
@@ -455,7 +456,12 @@ GTlsInteraction *     gck_enumerator_get_interaction          (GckEnumerator *se
 void                  gck_enumerator_set_interaction          (GckEnumerator *self,
                                                                GTlsInteraction *interaction);
 
-GckObject*            gck_enumerator_next                     (GckEnumerator *self,
+GType                 gck_enumerator_get_object_type          (GckEnumerator *self);
+
+void                  gck_enumerator_set_object_type          (GckEnumerator *self,
+                                                               GType object_type);
+
+GckObject *           gck_enumerator_next                     (GckEnumerator *self,
                                                                GCancellable *cancellable,
                                                                GError **error);
 
@@ -1090,7 +1096,7 @@ GckObject*          gck_session_derive_key_finish            (GckSession *self,
 
 #define GCK_TYPE_OBJECT             (gck_object_get_type())
 #define GCK_OBJECT(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GCK_TYPE_OBJECT, GckObject))
-#define GCK_OBJECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_OBJECT, GckObject))
+#define GCK_OBJECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GCK_TYPE_OBJECT, GckObjectClass))
 #define GCK_IS_OBJECT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCK_TYPE_OBJECT))
 #define GCK_IS_OBJECT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GCK_TYPE_OBJECT))
 #define GCK_OBJECT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), GCK_TYPE_OBJECT, GckObjectClass))
@@ -1108,6 +1114,8 @@ struct _GckObject {
 
 struct _GckObjectClass {
 	GObjectClass parent;
+
+	/*< private >*/
 	gpointer reserved[8];
 };
 
@@ -1239,6 +1247,35 @@ GckAttributes*      gck_object_get_template_finish          (GckObject *self,
                                                              GError **error);
 
 /* ------------------------------------------------------------------------
+ * OBJECT ATTRIBUTES
+ */
+
+#define GCK_TYPE_OBJECT_ATTRIBUTES                    (gck_object_attributes_get_type ())
+#define GCK_OBJECT_ATTRIBUTES(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_OBJECT_ATTRIBUTES, GckObjectAttributes))
+#define GCK_IS_OBJECT_ATTRIBUTES(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_OBJECT_ATTRIBUTES))
+#define GCK_OBJECT_ATTRIBUTES_GET_INTERFACE(inst)     (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GCK_TYPE_OBJECT_ATTRIBUTES, GckObjectAttributesIface))
+
+typedef struct _GckObjectAttributes GckObjectAttributes;
+typedef struct _GckObjectAttributesIface GckObjectAttributesIface;
+
+struct _GckObjectAttributesIface {
+	GTypeInterface interface;
+
+	const gulong *attribute_types;
+	gint n_attribute_types;
+
+	/*< private >*/
+	gpointer reserved[6];
+};
+
+GType               gck_object_attributes_get_type          (void) G_GNUC_CONST;
+
+GckAttributes *     gck_object_attributes_get_attributes    (GckObjectAttributes *object);
+
+void                gck_object_attributes_set_attributes    (GckObjectAttributes *object,
+                                                             GckAttributes *attributes);
+
+/* ------------------------------------------------------------------------
  * PASSWORD
  */
 
diff --git a/gck/gck.symbols b/gck/gck.symbols
index 818ce22..3b573c4 100644
--- a/gck/gck.symbols
+++ b/gck/gck.symbols
@@ -58,12 +58,14 @@ gck_attributes_set_string
 gck_attributes_set_ulong
 gck_attributes_unref
 gck_enumerator_get_interaction
+gck_enumerator_get_object_type
 gck_enumerator_get_type
 gck_enumerator_next
 gck_enumerator_next_async
 gck_enumerator_next_finish
 gck_enumerator_next_n
 gck_enumerator_set_interaction
+gck_enumerator_set_object_type
 gck_error_get_quark
 gck_error_get_type
 gck_get_error_quark
@@ -100,6 +102,9 @@ gck_modules_object_for_uri
 gck_modules_objects_for_uri
 gck_modules_token_for_uri
 gck_modules_tokens_for_uri
+gck_object_attributes_get_attributes
+gck_object_attributes_get_type
+gck_object_attributes_set_attributes
 gck_object_destroy
 gck_object_destroy_async
 gck_object_destroy_finish
diff --git a/gck/tests/Makefile.am b/gck/tests/Makefile.am
index 734e9d8..abddc6a 100644
--- a/gck/tests/Makefile.am
+++ b/gck/tests/Makefile.am
@@ -3,7 +3,7 @@ INCLUDES = \
 	-I$(top_builddir) \
 	-I$(top_srcdir) \
 	-DSRCDIR="\"@abs_srcdir \"" \
-	-DBUILDDIR="\"$(builddir)\"" \
+	-DBUILDDIR="\"@abs_builddir \"" \
 	-DGCK_API_SUBJECT_TO_CHANGE \
 	$(GLIB_CFLAGS)
 
diff --git a/gck/tests/test-gck-enumerator.c b/gck/tests/test-gck-enumerator.c
index d5d669e..a70bfa4 100644
--- a/gck/tests/test-gck-enumerator.c
+++ b/gck/tests/test-gck-enumerator.c
@@ -71,11 +71,16 @@ static void
 test_create (Test *test, gconstpointer unused)
 {
 	GckUriData *uri_data;
+	GType object_type;
 	GckEnumerator *en;
 
 	uri_data = gck_uri_data_new ();
 	en = _gck_enumerator_new (test->modules, 0, uri_data);
 	g_assert (GCK_IS_ENUMERATOR (en));
+
+	g_object_get (en, "object-type", &object_type, NULL);
+	g_assert (object_type == GCK_TYPE_OBJECT);
+
 	g_object_unref (en);
 }
 
@@ -221,7 +226,7 @@ test_next_async (Test *test, gconstpointer unused)
 }
 
 static void
-test_attributes (Test *test, gconstpointer unused)
+test_attribute_match (Test *test, gconstpointer unused)
 {
 	GckUriData *uri_data;
 	GError *error = NULL;
@@ -335,6 +340,133 @@ test_token_match (Test *test, gconstpointer unused)
 	g_object_unref (en);
 }
 
+enum {
+	PROP_0,
+	PROP_ATTRIBUTES,
+};
+
+static const gulong mock_attribute_types[] = {
+	CKA_CLASS,
+	CKA_ID,
+	CKA_LABEL,
+};
+
+typedef struct {
+	GckObject parent;
+	GckAttributes *attrs;
+} MockObject;
+
+typedef struct {
+	GckObjectClass parent;
+} MockObjectClass;
+
+GType mock_object_get_type (void) G_GNUC_CONST;
+static void mock_object_attributes_init (GckObjectAttributesIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (MockObject, mock_object, GCK_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GCK_TYPE_OBJECT_ATTRIBUTES,
+                                                mock_object_attributes_init)
+);
+
+static void
+mock_object_init (MockObject *self)
+{
+
+}
+
+static void
+mock_object_get_property (GObject *obj,
+                          guint prop_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+	MockObject *self = (MockObject *)obj;
+
+	switch (prop_id) {
+	case PROP_ATTRIBUTES:
+		g_value_set_boxed (value, self->attrs);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+mock_object_set_property (GObject *obj,
+                          guint prop_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+	MockObject *self = (MockObject *)obj;
+
+	switch (prop_id) {
+	case PROP_ATTRIBUTES:
+		g_assert (self->attrs == NULL);
+		self->attrs = g_value_dup_boxed (value);
+		g_assert (self->attrs != NULL);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+mock_object_finalize (GObject *obj)
+{
+	MockObject *self = (MockObject *)obj;
+	gck_attributes_unref (self->attrs);
+	G_OBJECT_CLASS (mock_object_parent_class)->finalize (obj);
+}
+
+static void
+mock_object_class_init (MockObjectClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	gobject_class->get_property = mock_object_get_property;
+	gobject_class->set_property = mock_object_set_property;
+	gobject_class->finalize = mock_object_finalize;
+
+	g_object_class_override_property (gobject_class, PROP_ATTRIBUTES, "attributes");
+}
+
+static void
+mock_object_attributes_init (GckObjectAttributesIface *iface)
+{
+	iface->attribute_types = mock_attribute_types;
+	iface->n_attribute_types = G_N_ELEMENTS (mock_attribute_types);
+}
+
+static void
+test_attribute_get (Test *test,
+                    gconstpointer unused)
+{
+	GckUriData *uri_data;
+	GError *error = NULL;
+	GckEnumerator *en;
+	GList *objects, *l;
+	MockObject *mock;
+
+	uri_data = gck_uri_data_new ();
+	en = _gck_enumerator_new (test->modules, 0, uri_data);
+	g_object_set (en, "object-type", mock_object_get_type (), NULL);
+
+	objects = gck_enumerator_next_n (en, -1, NULL, &error);
+	g_assert_no_error (error);
+	g_assert_cmpint (g_list_length (objects), ==, 5);
+
+	for (l = objects; l != NULL; l = g_list_next (l)) {
+		mock = l->data;
+		g_assert (G_TYPE_CHECK_INSTANCE_TYPE (mock, mock_object_get_type ()));
+		g_assert (mock->attrs != NULL);
+	}
+
+	gck_list_unref_free (objects);
+	g_object_unref (en);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -352,8 +484,9 @@ main (int argc, char **argv)
 	g_test_add ("/gck/enumerator/next_async", Test, NULL, setup, test_next_async, teardown);
 	g_test_add ("/gck/enumerator/authenticate-interaction", Test, NULL, setup, test_authenticate_interaction, teardown);
 	g_test_add ("/gck/enumerator/authenticate-compat", Test, NULL, setup, test_authenticate_compat, teardown);
-	g_test_add ("/gck/enumerator/attributes", Test, NULL, setup, test_attributes, teardown);
+	g_test_add ("/gck/enumerator/attribute_match", Test, NULL, setup, test_attribute_match, teardown);
 	g_test_add ("/gck/enumerator/token_match", Test, NULL, setup, test_token_match, teardown);
+	g_test_add ("/gck/enumerator/attribute_get", Test, NULL, setup, test_attribute_get, teardown);
 
 	return egg_tests_run_in_thread_with_loop ();
 }



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