[gcr] gcr: Implement prompt-close in GcrSystemPrompt and GcrSystemPrompter



commit d042d43ae1f44c37334fd620ad1e5625f06aa88f
Author: Stef Walter <stefw gnome org>
Date:   Fri Sep 21 10:23:23 2012 +0200

    gcr: Implement prompt-close in GcrSystemPrompt and GcrSystemPrompter
    
     * Properly relay the prompt-close signal from GcrSystemPrompter
       back to GcrSystemPrompt by firing the PromptDone callback method
       on the caller's DBus interface.
     * Make sure GcrSystemPrompt emits prompt-close appropriately for
       all the various paths that can close the prompt.
     * Add testing of the above, and changes in the mock prompter for this.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=678611

 docs/reference/gcr/gcr-sections.txt |    1 +
 gcr/gcr-base.symbols                |    1 +
 gcr/gcr-mock-prompter.c             |   44 +++++++
 gcr/gcr-mock-prompter.h             |    2 +
 gcr/gcr-system-prompt.c             |  212 +++++++++++++++++++++--------------
 gcr/gcr-system-prompter.c           |   58 ++++++++--
 gcr/tests/frob-system-prompt.c      |    3 +
 gcr/tests/test-system-prompt.c      |   99 ++++++++++++++++
 8 files changed, 324 insertions(+), 96 deletions(-)
---
diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt
index 2b21ca9..b39923e 100644
--- a/docs/reference/gcr/gcr-sections.txt
+++ b/docs/reference/gcr/gcr-sections.txt
@@ -851,6 +851,7 @@ gcr_mock_prompter_expect_confirm_cancel
 gcr_mock_prompter_expect_confirm_ok
 gcr_mock_prompter_expect_password_cancel
 gcr_mock_prompter_expect_password_ok
+gcr_mock_prompter_expect_close
 gcr_mock_prompter_get_delay_msec
 gcr_mock_prompter_set_delay_msec
 gcr_mock_prompter_is_expecting
diff --git a/gcr/gcr-base.symbols b/gcr/gcr-base.symbols
index bcf7bfb..c794829 100644
--- a/gcr/gcr-base.symbols
+++ b/gcr/gcr-base.symbols
@@ -100,6 +100,7 @@ gcr_import_interaction_supplement
 gcr_import_interaction_supplement_async
 gcr_import_interaction_supplement_finish
 gcr_import_interaction_supplement_prep
+gcr_mock_prompter_expect_close
 gcr_mock_prompter_expect_confirm_cancel
 gcr_mock_prompter_expect_confirm_ok
 gcr_mock_prompter_expect_password_cancel
diff --git a/gcr/gcr-mock-prompter.c b/gcr/gcr-mock-prompter.c
index 1ef31e0..564ed2d 100644
--- a/gcr/gcr-mock-prompter.c
+++ b/gcr/gcr-mock-prompter.c
@@ -88,6 +88,7 @@ struct _GcrMockPromptClass {
 };
 
 typedef struct {
+	gboolean close;
 	gboolean proceed;
 	gchar *password;
 	GList *properties;
@@ -403,6 +404,17 @@ on_timeout_complete (gpointer data)
 	return FALSE;
 }
 
+static gboolean
+on_timeout_complete_and_close (gpointer data)
+{
+	GSimpleAsyncResult *res = data;
+	GcrPrompt *prompt = GCR_PROMPT (g_async_result_get_source_object (data));
+	g_simple_async_result_complete (res);
+	gcr_prompt_close (prompt);
+	g_object_unref (prompt);
+	return FALSE;
+}
+
 static void
 gcr_mock_prompt_confirm_async (GcrPrompt *prompt,
                                GCancellable *cancellable,
@@ -428,6 +440,10 @@ gcr_mock_prompt_confirm_async (GcrPrompt *prompt,
 		g_critical ("password prompt requested, but not expected");
 		g_simple_async_result_set_op_res_gboolean (res, FALSE);
 
+	} else if (response->close) {
+		complete_func = on_timeout_complete_and_close;
+		g_simple_async_result_set_op_res_gboolean (res, FALSE);
+
 	} else if (response->password) {
 		g_critical ("confirmation prompt requested, but password prompt expected");
 		g_simple_async_result_set_op_res_gboolean (res, FALSE);
@@ -504,6 +520,10 @@ gcr_mock_prompt_password_async (GcrPrompt *prompt,
 		g_critical ("password prompt requested, but not expected");
 		g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
 
+	} else if (response->close) {
+		g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
+		complete_func = on_timeout_complete_and_close;
+
 	} else if (!response->password) {
 		g_critical ("password prompt requested, but confirmation prompt expected");
 		g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
@@ -798,6 +818,30 @@ gcr_mock_prompter_expect_password_cancel (void)
 }
 
 /**
+ * gcr_mock_prompter_expect_close:
+ *
+ * Queue an expected response on the mock prompter.
+ *
+ * Expects any prompt, and closes the prompt when it gets it.
+ */
+void
+gcr_mock_prompter_expect_close (void)
+{
+	MockResponse *response;
+
+	g_assert (running != NULL);
+
+	g_mutex_lock (running->mutex);
+
+	response = g_new0 (MockResponse, 1);
+	response->close = TRUE;
+
+	g_queue_push_tail (&running->responses, response);
+
+	g_mutex_unlock (running->mutex);
+}
+
+/**
  * gcr_mock_prompter_is_expecting:
  *
  * Check if the mock prompter is expecting a response. This will be %TRUE
diff --git a/gcr/gcr-mock-prompter.h b/gcr/gcr-mock-prompter.h
index 43c53cb..3e2cf26 100644
--- a/gcr/gcr-mock-prompter.h
+++ b/gcr/gcr-mock-prompter.h
@@ -55,6 +55,8 @@ void                 gcr_mock_prompter_expect_password_ok        (const gchar *p
 
 void                 gcr_mock_prompter_expect_password_cancel    (void);
 
+void                 gcr_mock_prompter_expect_close              (void);
+
 gboolean             gcr_mock_prompter_is_expecting              (void);
 
 G_END_DECLS
diff --git a/gcr/gcr-system-prompt.c b/gcr/gcr-system-prompt.c
index b66e30e..640aa77 100644
--- a/gcr/gcr-system-prompt.c
+++ b/gcr/gcr-system-prompt.c
@@ -390,15 +390,100 @@ gcr_system_prompt_constructed (GObject *obj)
 }
 
 static void
+on_prompter_stop_prompting (GObject *source,
+                            GAsyncResult *result,
+                            gpointer user_data)
+{
+	GSimpleAsyncResult *async = NULL;
+	GError *error = NULL;
+	GVariant *retval;
+
+	retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
+	if (error != NULL) {
+		_gcr_debug ("failed to stop prompting: %s", egg_error_message (error));
+		g_clear_error (&error);
+	}
+
+	if (retval)
+		g_variant_unref (retval);
+
+	if (user_data) {
+		async = G_SIMPLE_ASYNC_RESULT (user_data);
+		g_simple_async_result_complete (async);
+		g_object_unref (async);
+	}
+}
+
+static void
+perform_close (GcrSystemPrompt *self,
+               GSimpleAsyncResult *async,
+               GCancellable *cancellable)
+{
+	GSimpleAsyncResult *res;
+	CallClosure *closure;
+	gboolean called = FALSE;
+	gboolean closed;
+
+	closed = self->pv->closed;
+	self->pv->closed = TRUE;
+
+	if (!closed)
+		_gcr_debug ("closing prompt");
+
+	if (self->pv->pending) {
+		res = g_object_ref (self->pv->pending);
+		g_clear_object (&self->pv->pending);
+		closure = g_simple_async_result_get_op_res_gpointer (res);
+		g_cancellable_cancel (closure->cancellable);
+		g_simple_async_result_complete_in_idle (res);
+		g_object_unref (res);
+	}
+
+	if (self->pv->prompt_registered) {
+		g_dbus_connection_unregister_object (self->pv->connection,
+		                                     self->pv->prompt_registered);
+		self->pv->prompt_registered = 0;
+	}
+
+	if (self->pv->begun_prompting) {
+		if (self->pv->connection && self->pv->prompt_path) {
+			_gcr_debug ("Calling the prompter %s method", GCR_DBUS_PROMPTER_METHOD_STOP);
+			g_dbus_connection_call (self->pv->connection,
+			                        self->pv->prompter_bus_name,
+			                        GCR_DBUS_PROMPTER_OBJECT_PATH,
+			                        GCR_DBUS_PROMPTER_INTERFACE,
+			                        GCR_DBUS_PROMPTER_METHOD_STOP,
+			                        g_variant_new ("(o)", self->pv->prompt_path),
+			                        G_VARIANT_TYPE ("()"),
+			                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
+			                        -1, cancellable,
+			                        on_prompter_stop_prompting,
+			                        async ? g_object_ref (async) : NULL);
+			called = TRUE;
+		}
+		self->pv->begun_prompting = FALSE;
+	}
+
+	g_free (self->pv->prompt_path);
+	self->pv->prompt_path = NULL;
+
+	g_clear_object (&self->pv->connection);
+
+	if (!called && async)
+		g_simple_async_result_complete_in_idle (async);
+
+	/* Emit the signal if necessary, after closed */
+	if (!closed)
+		gcr_prompt_close (GCR_PROMPT (self));
+}
+
+static void
 gcr_system_prompt_dispose (GObject *obj)
 {
 	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
 
 	g_clear_object (&self->pv->exchange);
-
-	_gcr_debug ("closing prompt asynchronously: %s", self->pv->prompt_path);
-	if (self->pv->connection)
-		gcr_system_prompt_close_async (self, NULL, NULL, NULL);
+	perform_close (self, NULL, NULL);
 
 	g_hash_table_remove_all (self->pv->properties);
 	g_hash_table_remove_all (self->pv->dirty_properties);
@@ -559,17 +644,15 @@ prompt_method_done (GcrSystemPrompt *self,
                     GDBusMethodInvocation *invocation,
                     GVariant *parameters)
 {
-	GSimpleAsyncResult *res;
-
-	g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (self->pv->pending));
-
 	g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
 
-	res = g_object_ref (self->pv->pending);
-	g_clear_object (&self->pv->pending);
-	g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
-	g_simple_async_result_complete (res);
-	g_object_unref (res);
+	/*
+	 * At this point we're done prompting, and calling StopPrompting
+	 * on the prompter is no longer necessary. It may have already been
+	 * called, or the prompter may have stopped on its own accord.
+	 */
+	self->pv->begun_prompting = FALSE;
+	perform_close (self, NULL, NULL);
 }
 
 static void
@@ -951,8 +1034,9 @@ on_perform_prompt_complete (GObject *source,
 
 	retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
 	if (error != NULL) {
+		self->pv->pending = NULL;
 		g_simple_async_result_take_error (res, error);
-		g_simple_async_result_complete_in_idle (res);
+		g_simple_async_result_complete (res);
 	}
 
 	if (retval)
@@ -984,13 +1068,20 @@ perform_prompt_async (GcrSystemPrompt *self,
 		return;
 	}
 
-	_gcr_debug ("prompting for password");
-
 	res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, source_tag);
 	closure = g_new0 (CallClosure, 1);
 	closure->cancellable = cancellable ? g_object_ref (cancellable) : cancellable;
 	g_simple_async_result_set_op_res_gpointer (res, closure, call_closure_free);
 
+	if (self->pv->closed) {
+		self->pv->last_response = g_strdup (GCR_DBUS_PROMPT_REPLY_NONE);
+		g_simple_async_result_complete_in_idle (res);
+		g_object_unref (res);
+		return;
+	}
+
+	_gcr_debug ("prompting for password");
+
 	exchange = gcr_system_prompt_get_secret_exchange (self);
 	if (self->pv->received)
 		sent = gcr_secret_exchange_send (exchange, NULL, 0);
@@ -1107,12 +1198,29 @@ gcr_system_prompt_confirm_finish (GcrPrompt *prompt,
 }
 
 static void
+gcr_system_prompt_real_close (GcrPrompt *prompt)
+{
+	GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (prompt);
+
+	/*
+	 * Setting this before calling close_async allows us to prevent firing
+	 * this signal again in a loop.
+	 */
+
+	if (!self->pv->closed) {
+		self->pv->closed = TRUE;
+		perform_close (self, NULL, NULL);
+	}
+}
+
+static void
 gcr_system_prompt_prompt_iface (GcrPromptIface *iface)
 {
 	iface->prompt_password_async = gcr_system_prompt_password_async;
 	iface->prompt_password_finish = gcr_system_prompt_password_finish;
 	iface->prompt_confirm_async = gcr_system_prompt_confirm_async;
 	iface->prompt_confirm_finish = gcr_system_prompt_confirm_finish;
+	iface->prompt_close = gcr_system_prompt_real_close;
 }
 
 /**
@@ -1299,8 +1407,8 @@ gcr_system_prompt_open_for_prompter (const gchar *prompter_name,
  * @cancellable: an optional cancellation object
  * @error: location to place an error on failure
  *
- * Close this prompt. After calling this function, no further methods may be
- * called on this object. The prompt object is not unreferenced by this
+ * Close this prompt. After calling this function, no further prompts will
+ * succeed on this object. The prompt object is not unreferenced by this
  * function, and you must unreference it once done.
  *
  * This call may block, use the gcr_system_prompt_close_async() to perform
@@ -1335,28 +1443,6 @@ gcr_system_prompt_close (GcrSystemPrompt *self,
 	return result;
 }
 
-static void
-on_prompter_stop_prompting (GObject *source,
-                            GAsyncResult *result,
-                            gpointer user_data)
-{
-	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
-	GError *error = NULL;
-	GVariant *retval;
-
-	retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
-	if (error != NULL) {
-		_gcr_debug ("failed to stop prompting: %s", egg_error_message (error));
-		g_clear_error (&error);
-	}
-
-	if (retval)
-		g_variant_unref (retval);
-
-	g_simple_async_result_complete (res);
-	g_object_unref (res);
-}
-
 /**
  * gcr_system_prompt_close_async:
  * @self: the prompt
@@ -1378,25 +1464,9 @@ gcr_system_prompt_close_async (GcrSystemPrompt *self,
 {
 	GSimpleAsyncResult *res;
 	CallClosure *closure;
-	gboolean called = FALSE;
 
 	g_return_if_fail (GCR_SYSTEM_PROMPT (self));
 	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
-	g_return_if_fail (self->pv->connection != NULL);
-	g_return_if_fail (self->pv->prompt_path != NULL);
-	g_return_if_fail (self->pv->closed == FALSE);
-
-	_gcr_debug ("closing prompt");
-	self->pv->closed = TRUE;
-
-	if (self->pv->pending) {
-		res = g_object_ref (self->pv->pending);
-		g_clear_object (&self->pv->pending);
-		closure = g_simple_async_result_get_op_res_gpointer (res);
-		g_cancellable_cancel (closure->cancellable);
-		g_simple_async_result_complete_in_idle (res);
-		g_object_unref (res);
-	}
 
 	res = g_simple_async_result_new (NULL, callback, user_data,
 	                                 gcr_system_prompt_close_async);
@@ -1407,36 +1477,8 @@ gcr_system_prompt_close_async (GcrSystemPrompt *self,
 		g_main_context_ref (closure->context);
 	g_simple_async_result_set_op_res_gpointer (res, closure, call_closure_free);
 
-	if (self->pv->prompt_registered) {
-		g_dbus_connection_unregister_object (self->pv->connection,
-		                                     self->pv->prompt_registered);
-		self->pv->prompt_registered = 0;
-	}
+	perform_close (self, res, closure->cancellable);
 
-	if (self->pv->begun_prompting) {
-		if (self->pv->connection) {
-			g_dbus_connection_call (self->pv->connection,
-			                        self->pv->prompter_bus_name,
-			                        GCR_DBUS_PROMPTER_OBJECT_PATH,
-			                        GCR_DBUS_PROMPTER_INTERFACE,
-			                        GCR_DBUS_PROMPTER_METHOD_STOP,
-			                        g_variant_new ("(o)", self->pv->prompt_path),
-			                        G_VARIANT_TYPE ("()"),
-			                        G_DBUS_CALL_FLAGS_NO_AUTO_START,
-			                        -1, closure->cancellable,
-			                        on_prompter_stop_prompting,
-			                        g_object_ref (res));
-			called = TRUE;
-		}
-	}
-
-	g_free (self->pv->prompt_path);
-	self->pv->prompt_path = NULL;
-
-	g_clear_object (&self->pv->connection);
-
-	if (!called)
-		g_simple_async_result_complete_in_idle (res);
 	g_object_unref (res);
 }
 
diff --git a/gcr/gcr-system-prompter.c b/gcr/gcr-system-prompter.c
index fb03ec8..ba16dcc 100644
--- a/gcr/gcr-system-prompter.c
+++ b/gcr/gcr-system-prompter.c
@@ -121,6 +121,8 @@ typedef struct {
 	GHashTable *changed;
 	GcrSecretExchange *exchange;
 	gboolean received;
+	gboolean closed;
+	guint close_sig;
 } ActivePrompt;
 
 static void    prompt_send_ready               (ActivePrompt *active,
@@ -151,6 +153,14 @@ on_prompt_notify (GObject *object,
 	g_hash_table_replace (active->changed, key, key);
 }
 
+static void
+on_prompt_close (GcrPrompt *prompt,
+                 gpointer user_data)
+{
+	ActivePrompt *active = user_data;
+	prompt_stop_prompting (active->prompter, active->callback, TRUE, FALSE);
+}
+
 static Callback *
 callback_dup (Callback *original)
 {
@@ -195,9 +205,13 @@ active_prompt_unref (gpointer data)
 	ActivePrompt *active = data;
 
 	if (g_atomic_int_dec_and_test (&active->refs)) {
+		callback_free (active->callback);
 		g_object_unref (active->prompter);
 		g_object_unref (active->cancellable);
-		g_signal_handlers_disconnect_by_func (active->prompt, on_prompt_notify, active);
+		if (g_signal_handler_is_connected (active->prompt, active->notify_sig))
+			g_signal_handler_disconnect (active->prompt, active->notify_sig);
+		if (g_signal_handler_is_connected (active->prompt, active->close_sig))
+			g_signal_handler_disconnect (active->prompt, active->close_sig);
 		g_object_unref (active->prompt);
 		g_hash_table_destroy (active->changed);
 		if (active->exchange)
@@ -221,17 +235,15 @@ active_prompt_create (GcrSystemPrompter *self,
 	ActivePrompt *active;
 
 	active = g_slice_new0 (ActivePrompt);
-	if (!g_hash_table_lookup_extended (self->pv->callbacks, lookup,
-	                                   (gpointer *)&active->callback, NULL))
-		g_return_val_if_reached (NULL);
-
 	active->refs = 1;
+	active->callback = callback_dup (lookup);
 	active->prompter = g_object_ref (self);
 	active->cancellable = g_cancellable_new ();
 	g_signal_emit (self, signals[NEW_PROMPT], 0, &active->prompt);
 	g_return_val_if_fail (active->prompt != NULL, NULL);
 
 	active->notify_sig = g_signal_connect (active->prompt, "notify", G_CALLBACK (on_prompt_notify), active);
+	active->close_sig = g_signal_connect (active->prompt, "prompt-close", G_CALLBACK (on_prompt_close), active);
 	active->changed = g_hash_table_new (g_direct_hash, g_direct_equal);
 
 	/* Insert us into the active hash table */
@@ -481,11 +493,26 @@ prompt_stop_prompting (GcrSystemPrompter *self,
 {
 	ActivePrompt *active;
 	GVariant *retval;
+	gpointer watch;
+
+	_gcr_debug ("stopping prompting for operation %s %s",
+	            callback->path, callback->name);
 
 	/* Get a pointer to our actual callback */
 	if (!g_hash_table_lookup_extended (self->pv->callbacks, callback,
-	                                   (gpointer *)&callback, NULL))
+	                                   (gpointer *)&callback, &watch)) {
+		_gcr_debug ("couldn't find the callback for prompting operation %s %s",
+		            callback->path, callback->name);
 		return;
+	}
+
+	/*
+	 * We remove these from the callbacks hash table so that we don't
+	 * do this stuff more than once. However we still need the callback
+	 * to be valid.
+	 */
+	if (!g_hash_table_steal (self->pv->callbacks, callback))
+		g_assert_not_reached ();
 
 	/* Removed from the waiting queue */
 	g_queue_remove (&self->pv->waiting, callback);
@@ -493,16 +520,19 @@ prompt_stop_prompting (GcrSystemPrompter *self,
 	/* Close any active prompt */
 	active = g_hash_table_lookup (self->pv->active, callback);
 	if (active != NULL) {
+		active_prompt_ref (active);
+		g_hash_table_remove (self->pv->active, callback);
+
 		if (!active->ready) {
 			_gcr_debug ("cancelling active prompting operation for %s %s",
 			            callback->path, callback->name);
 			g_cancellable_cancel (active->cancellable);
 		}
 
-		_gcr_debug ("disposing the prompt");
-
+		_gcr_debug ("closing the prompt");
+		gcr_prompt_close (active->prompt);
 		g_object_run_dispose (G_OBJECT (active->prompt));
-		g_hash_table_remove (self->pv->active, callback);
+		active_prompt_unref (active);
 	}
 
 	/* Notify the caller */
@@ -540,8 +570,14 @@ prompt_stop_prompting (GcrSystemPrompter *self,
 		                        -1, NULL, NULL, NULL);
 	}
 
-	/* And all traces gone, including watch */
-	g_hash_table_remove (self->pv->callbacks, callback);
+	/*
+	 * And all traces gone, including watch. We stole these values from
+	 * the callbacks hashtable above. Now free them
+	 */
+
+	callback_free (callback);
+	unwatch_name (watch);
+
 	g_object_notify (G_OBJECT (self), "prompting");
 }
 
diff --git a/gcr/tests/frob-system-prompt.c b/gcr/tests/frob-system-prompt.c
index 39919b3..b743adf 100644
--- a/gcr/tests/frob-system-prompt.c
+++ b/gcr/tests/frob-system-prompt.c
@@ -25,6 +25,8 @@
 
 #include "gcr/gcr.h"
 
+#include "egg/egg-testing.h"
+
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
 
@@ -67,6 +69,7 @@ on_prompt_clicked (GtkToolButton *button,
 
 	g_print ("password: %s\n", password);
 	g_object_unref (prompt);
+	egg_assert_not_object (prompt);
 }
 
 static gboolean
diff --git a/gcr/tests/test-system-prompt.c b/gcr/tests/test-system-prompt.c
index d0450b5..0d48124 100644
--- a/gcr/tests/test-system-prompt.c
+++ b/gcr/tests/test-system-prompt.c
@@ -513,6 +513,15 @@ test_prompt_close (Test *test,
 }
 
 static void
+on_prompt_close (GcrPrompt *prompt,
+                 gpointer user_data)
+{
+	gboolean *prompt_closed = (gboolean *)user_data;
+	g_assert (*prompt_closed == FALSE);
+	*prompt_closed = TRUE;
+}
+
+static void
 test_close_cancels (Test *test,
                     gconstpointer unused)
 {
@@ -520,6 +529,7 @@ test_close_cancels (Test *test,
 	GError *error = NULL;
 	const gchar *password = NULL;
 	GAsyncResult *result = NULL;
+	gboolean prompt_closed;
 
 	gcr_mock_prompter_set_delay_msec (3000);
 	gcr_mock_prompter_expect_password_ok ("booo", NULL);
@@ -528,11 +538,15 @@ test_close_cancels (Test *test,
 	g_assert_no_error (error);
 	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
 
+	prompt_closed = FALSE;
+	g_signal_connect_after (prompt, "prompt-close", G_CALLBACK (on_prompt_close), &prompt_closed);
+
 	gcr_prompt_password_async (prompt, NULL, on_async_result, &result);
 
 	gcr_system_prompt_close (GCR_SYSTEM_PROMPT (prompt), NULL, &error);
 	g_assert_no_error (error);
 
+	g_assert (prompt_closed == TRUE);
 	egg_test_wait ();
 
 	password = gcr_prompt_password_finish (prompt, result, &error);
@@ -544,6 +558,89 @@ test_close_cancels (Test *test,
 	egg_assert_not_object (prompt);
 }
 
+static void
+test_close_from_prompter (Test *test,
+                          gconstpointer unused)
+{
+	GcrPrompt *prompt;
+	GError *error = NULL;
+	gboolean ret;
+	const gchar *password;
+	gboolean prompt_closed;
+
+	gcr_mock_prompter_expect_close ();
+
+	prompt = gcr_system_prompt_open_for_prompter (test->prompter_name, 1, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
+
+	prompt_closed = FALSE;
+	g_signal_connect_after (prompt, "prompt-close", G_CALLBACK (on_prompt_close), &prompt_closed);
+
+	ret = gcr_prompt_confirm_run (prompt, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (ret == GCR_PROMPT_REPLY_CANCEL);
+
+	/* The prompt should be closed now, these shouldn't reach the mock prompter */
+
+	while (!prompt_closed)
+		g_main_context_iteration (NULL, TRUE);
+
+	ret = gcr_prompt_confirm_run (prompt, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (ret == GCR_PROMPT_REPLY_CANCEL);
+
+	password = gcr_prompt_password_run (prompt, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (password == NULL);
+
+	g_object_unref (prompt);
+	egg_assert_not_object (prompt);
+}
+
+static void
+test_after_close_dismisses (Test *test,
+                            gconstpointer unused)
+{
+	GcrPrompt *prompt;
+	GError *error = NULL;
+	gboolean ret;
+	const gchar *password;
+	gboolean prompt_closed;
+
+	gcr_mock_prompter_expect_confirm_ok (NULL);
+
+	prompt = gcr_system_prompt_open_for_prompter (test->prompter_name, 1, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
+
+	prompt_closed = FALSE;
+	g_signal_connect_after (prompt, "prompt-close", G_CALLBACK (on_prompt_close), &prompt_closed);
+
+
+	ret = gcr_prompt_confirm_run (prompt, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (ret == GCR_PROMPT_REPLY_CONTINUE);
+
+	gcr_prompt_close (prompt);
+	g_assert (prompt_closed);
+
+	/* These should never even reach the mock prompter */
+
+	ret = gcr_prompt_confirm_run (prompt, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (ret == GCR_PROMPT_REPLY_CANCEL);
+
+	password = gcr_prompt_password_run (prompt, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (password == NULL);
+
+	while (g_main_context_iteration (NULL, FALSE));
+
+	g_object_unref (prompt);
+	egg_assert_not_object (prompt);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -568,6 +665,8 @@ main (int argc, char **argv)
 	g_test_add ("/gcr/system-prompt/properties-reset", Test, NULL, setup, test_prompt_properties_reset, teardown);
 	g_test_add ("/gcr/system-prompt/close", Test, NULL, setup, test_prompt_close, teardown);
 	g_test_add ("/gcr/system-prompt/close-cancels", Test, NULL, setup, test_close_cancels, teardown);
+	g_test_add ("/gcr/system-prompt/after-close-dismisses", Test, NULL, setup, test_after_close_dismisses, teardown);
+	g_test_add ("/gcr/system-prompt/close-from-prompter", Test, NULL, setup, test_close_from_prompter, teardown);
 
 	return egg_tests_run_with_loop ();
 }



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