gcr: Implement prompt-close in GcrSystemPrompt and GcrSystemPrompter
authorStef Walter <stefw@gnome.org>
Fri, 21 Sep 2012 08:23:23 +0000 (10:23 +0200)
committerStef Walter <stefw@gnome.org>
Mon, 22 Oct 2012 13:58:55 +0000 (15:58 +0200)
 * 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
gcr/gcr-base.symbols
gcr/gcr-mock-prompter.c
gcr/gcr-mock-prompter.h
gcr/gcr-system-prompt.c
gcr/gcr-system-prompter.c
gcr/tests/frob-system-prompt.c
gcr/tests/test-system-prompt.c

index 2b21ca9..b39923e 100644 (file)
@@ -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
index bcf7bfb..c794829 100644 (file)
@@ -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
index 1ef31e0..564ed2d 100644 (file)
@@ -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
index 43c53cb..3e2cf26 100644 (file)
@@ -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
index b66e30e..640aa77 100644 (file)
@@ -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);
 }
 
index fb03ec8..ba16dcc 100644 (file)
@@ -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");
 }
 
index 39919b3..b743adf 100644 (file)
@@ -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
index d0450b5..0d48124 100644 (file)
@@ -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 ();
 }