store the GSource in priv, don't ref the session. Otherwise the session
authorDan Winship <danw@src.gnome.org>
Tue, 30 Sep 2008 15:43:17 +0000 (15:43 +0000)
committerDan Winship <danw@src.gnome.org>
Tue, 30 Sep 2008 15:43:17 +0000 (15:43 +0000)
* libsoup/soup-session-async.c (do_idle_run_queue): store the
GSource in priv, don't ref the session. Otherwise the session
won't get destroyed if you abort it and then don't return to its
main loop. (addendum to #498509, Arnout Vandecappelle)
(finalize): Destroy the idle_run_queue source when finalizing.
(run_queue, got_connection): Ref the session when calling
soup_connection_connect_async(), and do a
do_idle_run_queue()+unref in got_connection, to ensure correct
handling regardless of what the application does with its own ref
on the session.
(final_finished): Likewise, ref/do_idle_run_queue/unref rather
than calling run_queue directly and playing with weak pointers.

* libsoup/soup-session.c (connect_result): ref the session around
the cancel-if-error loop

Fixes #533473, crash in seahorse when connecting to a
non-responsive key server.

* tests/misc-test.c (do_callback_unref_test): Add a test for the
bug in #533473.

* tests/test-utils.c (soup_test_session_abort_unref): abort and
unref a SoupSession, and consider it an error if the session still
exists afterward. Suggested by Arnout Vandecappelle.
(test_server_shutdown): Likewise, consider it an error if the
server is leaked.

* tests/*.c: Use soup_test_session_abort_unref().

svn path=/trunk/; revision=1168

14 files changed:
ChangeLog
libsoup/soup-session-async.c
libsoup/soup-session.c
tests/auth-test.c
tests/chunk-test.c
tests/context-test.c
tests/continue-test.c
tests/misc-test.c
tests/ntlm-test.c
tests/pull-api.c
tests/redirect-test.c
tests/test-utils.c
tests/test-utils.h
tests/xmlrpc-test.c

index 678388b..6273797 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2008-09-30  Dan Winship  <danw@gnome.org>
+
+       * libsoup/soup-session-async.c (do_idle_run_queue): store the
+       GSource in priv, don't ref the session. Otherwise the session
+       won't get destroyed if you abort it and then don't return to its
+       main loop. (addendum to #498509, Arnout Vandecappelle)
+       (finalize): Destroy the idle_run_queue source when finalizing.
+       (run_queue, got_connection): Ref the session when calling
+       soup_connection_connect_async(), and do a
+       do_idle_run_queue()+unref in got_connection, to ensure correct
+       handling regardless of what the application does with its own ref
+       on the session.
+       (final_finished): Likewise, ref/do_idle_run_queue/unref rather
+       than calling run_queue directly and playing with weak pointers.
+
+       * libsoup/soup-session.c (connect_result): ref the session around
+       the cancel-if-error loop
+
+       Fixes #533473, crash in seahorse when connecting to a
+       non-responsive key server.
+
+       * tests/misc-test.c (do_callback_unref_test): Add a test for the
+       bug in #533473.
+
+       * tests/test-utils.c (soup_test_session_abort_unref): abort and
+       unref a SoupSession, and consider it an error if the session still
+       exists afterward. Suggested by Arnout Vandecappelle.
+       (test_server_shutdown): Likewise, consider it an error if the
+       server is leaked.
+
+       * tests/*.c: Use soup_test_session_abort_unref().
+
 2008-09-26  Dan Winship  <danw@gnome.org>
 
        * libsoup/soup-auth-manager-ntlm.c
index 6ec97fb..c8624a3 100644 (file)
@@ -24,7 +24,7 @@
  **/
 
 static gboolean run_queue (SoupSessionAsync *sa);
-static void do_idle_run_queue (SoupSession *session);
+static void do_idle_run_queue (SoupSessionAsync *sa);
 
 static void  queue_message   (SoupSession *session, SoupMessage *req,
                              SoupSessionCallback callback, gpointer user_data);
@@ -32,19 +32,41 @@ static guint send_message    (SoupSession *session, SoupMessage *req);
 
 G_DEFINE_TYPE (SoupSessionAsync, soup_session_async, SOUP_TYPE_SESSION)
 
+typedef struct {
+       GSource *idle_run_queue_source;
+} SoupSessionAsyncPrivate;
+#define SOUP_SESSION_ASYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_ASYNC, SoupSessionAsyncPrivate))
+
 static void
 soup_session_async_init (SoupSessionAsync *sa)
 {
 }
 
 static void
+finalize (GObject *object)
+{
+       SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (object);
+
+       if (priv->idle_run_queue_source)
+               g_source_destroy (priv->idle_run_queue_source);
+
+       G_OBJECT_CLASS (soup_session_async_parent_class)->finalize (object);
+}
+
+static void
 soup_session_async_class_init (SoupSessionAsyncClass *soup_session_async_class)
 {
        SoupSessionClass *session_class = SOUP_SESSION_CLASS (soup_session_async_class);
+       GObjectClass *object_class = G_OBJECT_CLASS (session_class);
+
+       g_type_class_add_private (soup_session_async_class,
+                                 sizeof (SoupSessionAsyncPrivate));
 
        /* virtual method override */
        session_class->queue_message = queue_message;
        session_class->send_message = send_message;
+
+       object_class->finalize = finalize;
 }
 
 
@@ -86,12 +108,12 @@ soup_session_async_new_with_options (const char *optname1, ...)
 
 
 static void
-connection_closed (SoupConnection *conn, gpointer session)
+connection_closed (SoupConnection *conn, gpointer sa)
 {
        /* Run the queue in case anyone was waiting for a connection
         * to be closed.
         */
-       do_idle_run_queue (session);
+       do_idle_run_queue (sa);
 }
 
 static void
@@ -99,34 +121,29 @@ got_connection (SoupConnection *conn, guint status, gpointer user_data)
 {
        SoupSessionAsync *sa = user_data;
 
-       if (status != SOUP_STATUS_OK) {
-               /* The connection attempt failed, and thus @conn was
-                * closed and the open connection count for the
-                * session has been decremented. (If the failure was
-                * fatal, then SoupSession itself will have dealt
-                * with cancelling any pending messages for that
-                * host, so we don't need to worry about that here.)
-                * However, there may be other messages in the
-                * queue that were waiting for the connection count
-                * to go down, so run the queue now.
+       if (status == SOUP_STATUS_OK) {
+               g_signal_connect (conn, "disconnected",
+                                 G_CALLBACK (connection_closed), sa);
+
+               /* @conn has been marked reserved by SoupSession, but
+                * we don't actually have any specific message in mind
+                * for it. (In particular, the message we were
+                * originally planning to queue on it may have already
+                * been queued on some other connection that became
+                * available while we were waiting for this one to
+                * connect.) So we release the connection into the
+                * idle pool and then just run the queue and see what
+                * happens.
                 */
-               run_queue (sa);
-               return;
+               soup_connection_release (conn);
        }
 
-       g_signal_connect (conn, "disconnected",
-                         G_CALLBACK (connection_closed), sa);
-
-       /* @conn has been marked reserved by SoupSession, but we don't
-        * actually have any specific message in mind for it. (In
-        * particular, the message we were originally planning to
-        * queue on it may have already been queued on some other
-        * connection that became available while we were waiting for
-        * this one to connect.) So we release the connection into the
-        * idle pool and then just run the queue and see what happens.
+       /* Even if the connection failed, we run the queue, since
+        * there may have been messages waiting for the connection
+        * count to go down.
         */
-       soup_connection_release (conn);
-       run_queue (sa);
+       do_idle_run_queue (sa);
+       g_object_unref (sa);
 }
 
 static gboolean
@@ -158,7 +175,7 @@ run_queue (SoupSessionAsync *sa)
 
                if (is_new) {
                        soup_connection_connect_async (conn, got_connection,
-                                                      session);
+                                                      g_object_ref (session));
                } else
                        soup_connection_send_request (conn, msg);
        }
@@ -195,46 +212,42 @@ final_finished (SoupMessage *req, gpointer user_data)
        SoupSessionAsyncQueueData *saqd = user_data;
        SoupSessionAsync *sa = saqd->sa;
 
-       g_object_add_weak_pointer (G_OBJECT (sa), (gpointer)&sa);
-
+       g_object_ref (sa);
        if (!SOUP_MESSAGE_IS_STARTING (req)) {
                g_signal_handlers_disconnect_by_func (req, final_finished, saqd);
                if (saqd->callback) {
                        saqd->callback ((SoupSession *)sa, req,
                                        saqd->callback_data);
-                       /* callback might destroy sa */
                }
 
                g_object_unref (req);
                g_slice_free (SoupSessionAsyncQueueData, saqd);
        }
 
-       if (sa) {
-               g_object_remove_weak_pointer (G_OBJECT (sa), (gpointer)&sa);
-               run_queue (sa);
-       }
+       do_idle_run_queue (sa);
+       g_object_unref (sa);
 }
 
 static gboolean
-idle_run_queue (gpointer user_data)
+idle_run_queue (gpointer sa)
 {
-       SoupSessionAsync *sa = user_data;
+       SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (sa);
 
-       g_object_add_weak_pointer (G_OBJECT (sa), (gpointer)&sa);
-       g_object_unref (sa);
-
-       if (sa) {
-               g_object_remove_weak_pointer (G_OBJECT (sa), (gpointer)&sa);
-               run_queue (sa);
-       }
+       priv->idle_run_queue_source = NULL;
+       run_queue (sa);
        return FALSE;
 }
 
 static void
-do_idle_run_queue (SoupSession *session)
+do_idle_run_queue (SoupSessionAsync *sa)
 {
-       soup_add_completion (soup_session_get_async_context (session),
-                            idle_run_queue, g_object_ref (session));
+       SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (sa);
+
+       if (!priv->idle_run_queue_source) {
+               priv->idle_run_queue_source = soup_add_completion (
+                       soup_session_get_async_context ((SoupSession *)sa),
+                       idle_run_queue, sa);
+       }
 }
 
 static void
@@ -255,7 +268,7 @@ queue_message (SoupSession *session, SoupMessage *req,
                                G_CALLBACK (final_finished), saqd);
 
        SOUP_SESSION_CLASS (soup_session_async_parent_class)->queue_message (session, req, callback, user_data);
-       do_idle_run_queue (session);
+       do_idle_run_queue (sa);
 }
 
 static guint
index 9f23e89..41134cf 100644 (file)
@@ -960,6 +960,7 @@ connect_result (SoupConnection *conn, guint status, gpointer user_data)
         * any messages waiting for this host, since they're out
         * of luck.
         */
+       g_object_ref (session);
        for (msg = soup_message_queue_first (priv->queue, &iter); msg; msg = soup_message_queue_next (priv->queue, &iter)) {
                if (get_host_for_message (session, msg) == host) {
                        if (status == SOUP_STATUS_TRY_AGAIN) {
@@ -971,6 +972,7 @@ connect_result (SoupConnection *conn, guint status, gpointer user_data)
                        }
                }
        }
+       g_object_unref (session);
 }
 
 /**
index 08dfda3..96a4979 100644 (file)
@@ -503,8 +503,7 @@ do_async_auth_test (const char *base_uri)
                errors++;
        }
 
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        g_object_unref (msg1);
        g_object_unref (msg3);
@@ -537,8 +536,7 @@ do_async_auth_test (const char *base_uri)
        g_main_loop_run (loop);
        g_signal_handler_disconnect (session, auth_id);
 
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        g_object_unref (msg1);
 
@@ -607,8 +605,7 @@ main (int argc, char **argv)
 
                g_object_unref (msg);
        }
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        /* And now for some regression tests */
        loop = g_main_loop_new (NULL, TRUE);
@@ -633,8 +630,7 @@ main (int argc, char **argv)
        g_free (uri);
 
        g_main_loop_run (loop);
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        debug_printf (1, "\nTesting digest nonce expiration:\n");
 
@@ -726,8 +722,7 @@ main (int argc, char **argv)
        do_digest_nonce_test (session, "Fourth", uri, FALSE, FALSE);
        g_free (uri);
 
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        /* Async auth */
        do_async_auth_test (base_uri);
index e79dc93..13238cf 100644 (file)
@@ -248,8 +248,7 @@ do_chunk_tests (SoupURI *base_uri)
        do_request_test (session, base_uri);
        debug_printf (2, "\n\n");
        do_response_test (session, base_uri);
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 }
 
 static void
index c226051..6cd9ec7 100644 (file)
@@ -194,8 +194,7 @@ test1_thread (gpointer user_data)
        }
        g_object_unref (msg);
 
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
        g_free (uri);
 
        g_cond_signal (test1_cond);
@@ -240,8 +239,7 @@ do_test2 (void)
        }
        g_object_unref (msg);
 
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
        g_free (uri);
 
        g_source_remove (idle);
index 5fbcbe3..d0905ce 100644 (file)
@@ -109,8 +109,7 @@ do_message (const char *path, gboolean long_body,
        events = NULL;
        session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
        soup_session_send_message (session, msg);
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        va_start (ap, auth);
        while ((expected_event = va_arg (ap, const char *))) {
index 765d70b..d5985b8 100644 (file)
@@ -46,8 +46,8 @@ server_callback (SoupServer *server, SoupMessage *msg,
 
 /* Host header handling: client must be able to override the default
  * value, server must be able to recognize different Host values.
+ * #539803.
  */
-
 static void
 do_host_test (void)
 {
@@ -65,8 +65,7 @@ do_host_test (void)
        soup_session_send_message (session, one);
        soup_session_send_message (session, two);
 
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        if (!SOUP_STATUS_IS_SUCCESSFUL (one->status_code)) {
                debug_printf (1, "  Message 1 failed: %d %s\n",
@@ -91,6 +90,96 @@ do_host_test (void)
        g_object_unref (two);
 }
 
+/* Dropping the application's ref on the session from a callback
+ * should not cause the session to be freed at an incorrect time.
+ * (This test will crash if it fails.) #533473
+ */
+static void
+cu_one_completed (SoupSession *session, SoupMessage *msg, gpointer loop)
+{
+       debug_printf (2, "  Message 1 completed\n");
+       if (msg->status_code != SOUP_STATUS_CANT_CONNECT) {
+               debug_printf (1, "  Unexpected status on Message 1: %d %s\n",
+                             msg->status_code, msg->reason_phrase);
+               errors++;
+       }
+       g_object_unref (session);
+}
+
+static gboolean
+cu_idle_quit (gpointer loop)
+{
+       g_main_loop_quit (loop);
+       return FALSE;
+}
+
+static void
+cu_two_completed (SoupSession *session, SoupMessage *msg, gpointer loop)
+{
+       debug_printf (2, "  Message 2 completed\n");
+       if (msg->status_code != SOUP_STATUS_CANT_CONNECT) {
+               debug_printf (1, "  Unexpected status on Message 2: %d %s\n",
+                             msg->status_code, msg->reason_phrase);
+               errors++;
+       }
+       g_idle_add (cu_idle_quit, loop); 
+}
+
+static void
+do_callback_unref_test (void)
+{
+       SoupServer *bad_server;
+       SoupSession *session;
+       SoupMessage *one, *two;
+       GMainLoop *loop;
+       char *bad_uri;
+
+       debug_printf (1, "Callback unref handling\n");
+
+       /* Get a guaranteed-bad URI */
+       bad_server = soup_server_new (NULL, NULL);
+       bad_uri = g_strdup_printf ("http://localhost:%u/",
+                                  soup_server_get_port (bad_server));
+       g_object_unref (bad_server);
+
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+       g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
+
+       loop = g_main_loop_new (NULL, TRUE);
+
+       one = soup_message_new ("GET", bad_uri);
+       g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
+       two = soup_message_new ("GET", bad_uri);
+       g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two);
+
+       soup_session_queue_message (session, one, cu_one_completed, loop);
+       soup_session_queue_message (session, two, cu_two_completed, loop);
+
+       g_main_loop_run (loop);
+       g_main_loop_unref (loop);
+
+       if (session) {
+               g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
+               debug_printf (1, "  Session not destroyed?\n");
+               errors++;
+               g_object_unref (session);
+       }
+       if (one) {
+               g_object_remove_weak_pointer (G_OBJECT (one), (gpointer *)&one);
+               debug_printf (1, "  Message 1 not destroyed?\n");
+               errors++;
+               g_object_unref (one);
+       }
+       if (two) {
+               g_object_remove_weak_pointer (G_OBJECT (two), (gpointer *)&two);
+               debug_printf (1, "  Message 2 not destroyed?\n");
+               errors++;
+               g_object_unref (two);
+       }
+
+       /* Otherwise, if we haven't crashed, we're ok. */
+}
+
 int
 main (int argc, char **argv)
 {
@@ -104,6 +193,7 @@ main (int argc, char **argv)
                                    soup_server_get_port (server));
 
        do_host_test ();
+       do_callback_unref_test ();
 
        g_free (base_uri);
 
index fc4c082..6c6d6d3 100644 (file)
@@ -385,8 +385,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user)
                    user != NULL ? SOUP_STATUS_OK :
                    SOUP_STATUS_UNAUTHORIZED);
 
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 }
 
 static void
index 906a5e9..d47ce91 100644 (file)
@@ -41,8 +41,7 @@ get_correct_response (const char *uri)
        correct_response = soup_message_body_flatten (msg->response_body);
 
        g_object_unref (msg);
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 }
 
 /* Pull API version 1: fully-async. More like a "poke" API. Rather
@@ -508,8 +507,7 @@ main (int argc, char **argv)
                             TRUE, SOUP_STATUS_UNAUTHORIZED);
        do_fully_async_test (session, base_uri, "/Basic/realm2/",
                             TRUE, SOUP_STATUS_OK);
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        debug_printf (1, "\nFully async, slow requests\n");
        session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
@@ -521,8 +519,7 @@ main (int argc, char **argv)
                             FALSE, SOUP_STATUS_UNAUTHORIZED);
        do_fully_async_test (session, base_uri, "/Basic/realm2/",
                             FALSE, SOUP_STATUS_OK);
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        debug_printf (1, "\nSynchronously async\n");
        session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
@@ -534,9 +531,7 @@ main (int argc, char **argv)
                                     SOUP_STATUS_UNAUTHORIZED);
        do_synchronously_async_test (session, base_uri, "/Basic/realm2/",
                                     SOUP_STATUS_OK);
-
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        soup_buffer_free (correct_response);
 
index 048104e..5bb37e9 100644 (file)
@@ -189,15 +189,13 @@ do_redirect_tests (SoupURI *base_uri)
        debug_printf (1, "Async session\n");
        for (n = 0; n < n_tests; n++)
                do_test (session, base_uri, n);
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
        debug_printf (1, "Sync session\n");
        for (n = 0; n < n_tests; n++)
                do_test (session, base_uri, n);
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 }
 
 static void
index f0f2860..0bb4a89 100644 (file)
@@ -100,14 +100,6 @@ test_init (int argc, char **argv, GOptionEntry *entries)
 void
 test_cleanup (void)
 {
-       debug_printf (1, "\n");
-       if (errors) {
-               printf ("%s: %d error(s).%s\n",
-                       g_get_prgname (), errors,
-                       debug_level == 0 ? " Run with '-d' for details" : "");
-       } else
-               printf ("%s: OK\n", g_get_prgname ());
-
 #ifdef HAVE_APACHE
        if (apache_running)
                apache_cleanup ();
@@ -119,6 +111,14 @@ test_cleanup (void)
                g_object_unref (logger);
 
        g_main_context_unref (g_main_context_default ());
+
+       debug_printf (1, "\n");
+       if (errors) {
+               printf ("%s: %d error(s).%s\n",
+                       g_get_prgname (), errors,
+                       debug_level == 0 ? " Run with '-d' for details" : "");
+       } else
+               printf ("%s: OK\n", g_get_prgname ());
 }
 
 void
@@ -225,6 +225,21 @@ soup_test_session_new (GType type, ...)
        return session;
 }
 
+void
+soup_test_session_abort_unref (SoupSession *session)
+{
+       g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
+
+       soup_session_abort (session);
+       g_object_unref (session);
+
+       if (session) {
+               errors++;
+               debug_printf (1, "leaked SoupSession!\n");
+               g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
+       }
+}
+
 static gpointer run_server_thread (gpointer user_data);
 
 SoupServer *
@@ -271,6 +286,9 @@ idle_quit_server (gpointer server)
 static void
 test_server_shutdown (void)
 {
+       g_object_add_weak_pointer (G_OBJECT (test_server),
+                                  (gpointer *)&test_server);
+
        if (server_thread) {
                soup_add_completion (soup_server_get_async_context (test_server),
                                     idle_quit_server, test_server);
@@ -278,6 +296,11 @@ test_server_shutdown (void)
        } else
                soup_server_quit (test_server);
        g_object_unref (test_server);
-}
-
 
+       if (test_server) {
+               errors++;
+               debug_printf (1, "leaked SoupServer!\n");
+               g_object_remove_weak_pointer (G_OBJECT (test_server),
+                                             (gpointer *)&test_server);
+       }
+}
index 535172f..ff9f472 100644 (file)
@@ -15,5 +15,7 @@ void apache_init    (void);
 void apache_cleanup (void);
 #endif
 
-SoupSession *soup_test_session_new (GType type, ...);
+SoupSession *soup_test_session_new         (GType type, ...);
+void         soup_test_session_abort_unref (SoupSession *session);
+
 SoupServer  *soup_test_server_new  (gboolean in_own_thread);
index b773549..372a6f6 100644 (file)
@@ -468,8 +468,7 @@ main (int argc, char **argv)
        if (!test_fault_args ())
                errors++;
 
-       soup_session_abort (session);
-       g_object_unref (session);
+       soup_test_session_abort_unref (session);
 
        test_cleanup ();
        return errors != 0;