Logo Search packages:      
Sourcecode: libnih version File versions  Download package

test_com.netsplit.Nih.Test_proxy.c

/* libnih
 *
 * test_com.netsplit.Nih.Test_proxy.c - test suite for auto-generated
 * proxy bindings.
 *
 * Copyright © 2009 Scott James Remnant <scott@netsplit.com>.
 * Copyright © 2009 Canonical Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <nih/test.h>
#include <nih-dbus/test_dbus.h>

#include <dbus/dbus.h>

#include <sys/types.h>
#include <sys/wait.h>

#include <errno.h>
#include <signal.h>
#include <stdlib.h>

#include <nih/macros.h>
#include <nih/alloc.h>
#include <nih/signal.h>
#include <nih/main.h>
#include <nih/error.h>

#include <nih-dbus/dbus_connection.h>
#include <nih-dbus/dbus_object.h>
#include <nih-dbus/dbus_proxy.h>
#include <nih-dbus/dbus_error.h>
#include <nih-dbus/errors.h>

#include "tests/com.netsplit.Nih.Test_proxy.h"
#include "tests/com.netsplit.Nih.Test_object.h"
#include "tests/com.netsplit.Nih.Test_impl.h"


static int       error_handler_called;
static void *    last_data;
static NihError *last_error;

static void
my_error_handler (void *          data,
              NihDBusMessage *message)
{
      error_handler_called++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      last_data = data;

      last_error = nih_error_steal ();
}


static int ordinary_method_replied;
const char *last_str_value;

static void
my_ordinary_method_reply (void *          data,
                    NihDBusMessage *message,
                    const char *    output)
{
      ordinary_method_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_ordinary_method (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_ordinary_method");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            ordinary_method_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_ordinary_method (proxy,
                                             "she needs more of ze punishment",
                                             my_ordinary_method_reply,
                                             my_error_handler,
                                             parent,
                                             0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (ordinary_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "she needs more of ze punishment");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            ordinary_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_ordinary_method (proxy,
                                             "",
                                             my_ordinary_method_reply,
                                             my_error_handler,
                                             parent,
                                             0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (ordinary_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.OrdinaryMethod.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            ordinary_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_ordinary_method (proxy,
                                             "invalid",
                                             my_ordinary_method_reply,
                                             my_error_handler,
                                             parent,
                                             0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (ordinary_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            ordinary_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_ordinary_method (proxy,
                                             "she needs more of ze punishment",
                                             my_ordinary_method_reply,
                                             my_error_handler,
                                             parent,
                                             50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (ordinary_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            ordinary_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_ordinary_method (proxy,
                                             "she needs more of ze punishment",
                                             my_ordinary_method_reply,
                                             my_error_handler,
                                             parent,
                                             50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (ordinary_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            ordinary_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_ordinary_method (proxy,
                                             "she needs more of ze punishment",
                                             my_ordinary_method_reply,
                                             my_error_handler,
                                             parent,
                                             50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (ordinary_method_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_ordinary_method_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_ordinary_method_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_ordinary_method_sync (parent, proxy,
                                           "she needs more of ze punishment",
                                           &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "she needs more of ze punishment");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_ordinary_method_sync (parent, proxy,
                                           "",
                                           &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.OrdinaryMethod.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_ordinary_method_sync (parent, proxy,
                                           "invalid",
                                           &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_ordinary_method_sync (parent, proxy,
                                           "she needs more of ze punishment",
                                           &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int nameless_method_replied;

static void
my_nameless_method_reply (void *          data,
                    NihDBusMessage *message,
                    const char *    arg2)
{
      nameless_method_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (arg2, NULL);
      TEST_ALLOC_PARENT (arg2, message);

      last_data = data;
      last_str_value = arg2;
      nih_ref (last_str_value, last_data);
}

void
test_nameless_method (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_nameless_method");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            nameless_method_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_nameless_method (proxy,
                                             "she needs more of ze punishment",
                                             my_nameless_method_reply,
                                             my_error_handler,
                                             parent,
                                             0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (nameless_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "she needs more of ze punishment");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            nameless_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_nameless_method (proxy,
                                             "",
                                             my_nameless_method_reply,
                                             my_error_handler,
                                             parent,
                                             0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (nameless_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.NamelessMethod.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            nameless_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_nameless_method (proxy,
                                             "invalid",
                                             my_nameless_method_reply,
                                             my_error_handler,
                                             parent,
                                             0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (nameless_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            nameless_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_nameless_method (proxy,
                                             "she needs more of ze punishment",
                                             my_nameless_method_reply,
                                             my_error_handler,
                                             parent,
                                             50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (nameless_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            nameless_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_nameless_method (proxy,
                                             "she needs more of ze punishment",
                                             my_nameless_method_reply,
                                             my_error_handler,
                                             parent,
                                             50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (nameless_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            nameless_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_nameless_method (proxy,
                                             "she needs more of ze punishment",
                                             my_nameless_method_reply,
                                             my_error_handler,
                                             parent,
                                             50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (nameless_method_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_nameless_method_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_nameless_method_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_nameless_method_sync (parent, proxy,
                                           "she needs more of ze punishment",
                                           &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "she needs more of ze punishment");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_nameless_method_sync (parent, proxy,
                                           "",
                                           &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.NamelessMethod.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_nameless_method_sync (parent, proxy,
                                           "invalid",
                                           &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_nameless_method_sync (parent, proxy,
                                           "she needs more of ze punishment",
                                           &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int async_method_replied;

static void
my_async_method_reply (void *          data,
                   NihDBusMessage *message,
                   const char *    output)
{
      async_method_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_async_method (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_async_method");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            async_method_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_async_method (proxy,
                                          "she needs more of ze punishment",
                                          my_async_method_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            async_method_input = NULL;
            async_method_message = NULL;

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);

            assert (async_method_input != NULL);
            assert (async_method_message != NULL);

            TEST_ALLOC_SAFE {
                  assert0 (my_test_async_method_reply (
                               async_method_message,
                               async_method_input));
            }

            nih_free (async_method_message);
            nih_free (async_method_input);

            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (async_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "she needs more of ze punishment");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            async_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_async_method (proxy,
                                          "",
                                          my_async_method_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (async_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.AsyncMethod.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            async_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_async_method (proxy,
                                          "invalid",
                                          my_async_method_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (async_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            async_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_async_method (proxy,
                                          "she needs more of ze punishment",
                                          my_async_method_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (async_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            async_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_async_method (proxy,
                                          "she needs more of ze punishment",
                                          my_async_method_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (async_method_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            async_method_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_async_method (proxy,
                                          "she needs more of ze punishment",
                                          my_async_method_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (async_method_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_async_method_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_async_method_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            async_method_main_loop = TRUE;
            async_method_input = NULL;
            async_method_message = NULL;

            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_async_method_sync (parent, proxy,
                                        "she needs more of ze punishment",
                                        &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "she needs more of ze punishment");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_async_method_sync (parent, proxy,
                                        "",
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.AsyncMethod.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_async_method_sync (parent, proxy,
                                        "invalid",
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_async_method_sync (parent, proxy,
                                        "she needs more of ze punishment",
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int byte_to_str_replied;

static void
my_byte_to_str_reply (void *          data,
                  NihDBusMessage *message,
                  const char *    output)
{
      byte_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_byte_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_byte_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            byte_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_byte_to_str (proxy,
                                           97,
                                           my_byte_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (byte_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "97");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            byte_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_byte_to_str (proxy,
                                           0,
                                           my_byte_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (byte_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.ByteToStr.ZeroInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            byte_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_byte_to_str (proxy,
                                           4,
                                           my_byte_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (byte_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            byte_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_byte_to_str (proxy,
                                           97,
                                           my_byte_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (byte_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            byte_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_byte_to_str (proxy,
                                           97,
                                           my_byte_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (byte_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            byte_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_byte_to_str (proxy,
                                           97,
                                           my_byte_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (byte_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_byte_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_byte_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_byte_to_str_sync (parent, proxy,
                                       97,
                                       &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "97");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_byte_to_str_sync (parent, proxy,
                                       0,
                                       &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.ByteToStr.ZeroInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_byte_to_str_sync (parent, proxy,
                                       4,
                                       &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_byte_to_str_sync (parent, proxy,
                                       97,
                                       &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_byte_replied;
static uint8_t last_byte_value;

static void
my_str_to_byte_reply (void *          data,
                  NihDBusMessage *message,
                  uint8_t         output)
{
      str_to_byte_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      last_data = data;
      last_byte_value = output;
}

void
test_str_to_byte (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_byte");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_byte_replied = FALSE;
            last_data = NULL;
            last_byte_value = 0;

            pending_call = proxy_test_str_to_byte (proxy,
                                           "97",
                                           my_str_to_byte_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_byte_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ (last_byte_value, 97);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_byte_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_byte (proxy,
                                           "",
                                           my_str_to_byte_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_byte_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToByte.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_byte_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_byte (proxy,
                                           "invalid",
                                           my_str_to_byte_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_byte_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_byte_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_byte (proxy,
                                           "97",
                                           my_str_to_byte_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_byte_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_byte_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_byte (proxy,
                                           "97",
                                           my_str_to_byte_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_byte_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_byte_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_byte (proxy,
                                           "97",
                                           my_str_to_byte_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_byte_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_byte_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      uint8_t         byte_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_byte_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            byte_value = 0;

            ret = proxy_test_str_to_byte_sync (parent, proxy,
                                       "97",
                                       &byte_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_EQ (byte_value, 97);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_byte_sync (parent, proxy,
                                       "",
                                       &byte_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToByte.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_byte_sync (parent, proxy,
                                       "invalid",
                                       &byte_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_byte_sync (parent, proxy,
                                       "97",
                                       &byte_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int boolean_to_str_replied;

static void
my_boolean_to_str_reply (void *          data,
                   NihDBusMessage *message,
                   const char *    output)
{
      boolean_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_boolean_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_boolean_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            boolean_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_boolean_to_str (proxy,
                                            TRUE,
                                            my_boolean_to_str_reply,
                                            my_error_handler,
                                            parent,
                                            0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (boolean_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "True");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            boolean_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_boolean_to_str (proxy,
                                            FALSE,
                                            my_boolean_to_str_reply,
                                            my_error_handler,
                                            parent,
                                            0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (boolean_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.BooleanToStr.ZeroInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            boolean_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_boolean_to_str (proxy,
                                            TRUE,
                                            my_boolean_to_str_reply,
                                            my_error_handler,
                                            parent,
                                            50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (boolean_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            boolean_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_boolean_to_str (proxy,
                                            TRUE,
                                            my_boolean_to_str_reply,
                                            my_error_handler,
                                            parent,
                                            50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (boolean_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            boolean_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_boolean_to_str (proxy,
                                            TRUE,
                                            my_boolean_to_str_reply,
                                            my_error_handler,
                                            parent,
                                            50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (boolean_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_boolean_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_boolean_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_boolean_to_str_sync (parent, proxy,
                                          TRUE,
                                          &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "True");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_boolean_to_str_sync (parent, proxy,
                                          FALSE,
                                          &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.BooleanToStr.ZeroInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_boolean_to_str_sync (parent, proxy,
                                          TRUE,
                                          &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_boolean_replied;
static int last_boolean_value;

static void
my_str_to_boolean_reply (void *          data,
                   NihDBusMessage *message,
                   int             output)
{
      str_to_boolean_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      last_data = data;
      last_boolean_value = output;
}

void
test_str_to_boolean (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_boolean");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_boolean_replied = FALSE;
            last_data = NULL;
            last_boolean_value = 0;

            pending_call = proxy_test_str_to_boolean (proxy,
                                            "True",
                                            my_str_to_boolean_reply,
                                            my_error_handler,
                                            parent,
                                            0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_boolean_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ (last_boolean_value, TRUE);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_boolean_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_boolean (proxy,
                                            "",
                                            my_str_to_boolean_reply,
                                            my_error_handler,
                                            parent,
                                            0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_boolean_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToBoolean.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_boolean_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_boolean (proxy,
                                            "invalid",
                                            my_str_to_boolean_reply,
                                            my_error_handler,
                                            parent,
                                            0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_boolean_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_boolean_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_boolean (proxy,
                                            "True",
                                            my_str_to_boolean_reply,
                                            my_error_handler,
                                            parent,
                                            50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_boolean_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_boolean_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_boolean (proxy,
                                            "True",
                                            my_str_to_boolean_reply,
                                            my_error_handler,
                                            parent,
                                            50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_boolean_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_boolean_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_boolean (proxy,
                                            "True",
                                            my_str_to_boolean_reply,
                                            my_error_handler,
                                            parent,
                                            50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_boolean_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_boolean_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      int             boolean_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_boolean_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            boolean_value = 0;

            ret = proxy_test_str_to_boolean_sync (parent, proxy,
                                          "True",
                                          &boolean_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_EQ (boolean_value, TRUE);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_boolean_sync (parent, proxy,
                                          "",
                                          &boolean_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToBoolean.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_boolean_sync (parent, proxy,
                                          "invalid",
                                          &boolean_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_boolean_sync (parent, proxy,
                                          "True",
                                          &boolean_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int int16_to_str_replied;

static void
my_int16_to_str_reply (void *          data,
                   NihDBusMessage *message,
                   const char *    output)
{
      int16_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_int16_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_int16_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int16_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_int16_to_str (proxy,
                                          -42,
                                          my_int16_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (int16_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "-42");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int16_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int16_to_str (proxy,
                                          0,
                                          my_int16_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int16_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.Int16ToStr.ZeroInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int16_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int16_to_str (proxy,
                                          4,
                                          my_int16_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int16_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int16_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int16_to_str (proxy,
                                          -42,
                                          my_int16_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int16_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int16_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int16_to_str (proxy,
                                          -42,
                                          my_int16_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int16_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            int16_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int16_to_str (proxy,
                                          -42,
                                          my_int16_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (int16_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_int16_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_int16_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_int16_to_str_sync (parent, proxy,
                                        -42,
                                        &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "-42");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_int16_to_str_sync (parent, proxy,
                                        0,
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.Int16ToStr.ZeroInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_int16_to_str_sync (parent, proxy,
                                        4,
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_int16_to_str_sync (parent, proxy,
                                        -42,
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_int16_replied;
static int16_t last_int16_value;

static void
my_str_to_int16_reply (void *          data,
                   NihDBusMessage *message,
                   int16_t         output)
{
      str_to_int16_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      last_data = data;
      last_int16_value = output;
}

void
test_str_to_int16 (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_int16");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int16_replied = FALSE;
            last_data = NULL;
            last_int16_value = 0;

            pending_call = proxy_test_str_to_int16 (proxy,
                                          "-42",
                                          my_str_to_int16_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_int16_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ (last_int16_value, -42);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int16_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int16 (proxy,
                                          "",
                                          my_str_to_int16_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int16_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToInt16.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int16_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int16 (proxy,
                                          "invalid",
                                          my_str_to_int16_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int16_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int16_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int16 (proxy,
                                          "-42",
                                          my_str_to_int16_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int16_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int16_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int16 (proxy,
                                          "-42",
                                          my_str_to_int16_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int16_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_int16_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int16 (proxy,
                                          "-42",
                                          my_str_to_int16_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_int16_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_int16_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      int16_t         int16_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_int16_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            int16_value = 0;

            ret = proxy_test_str_to_int16_sync (parent, proxy,
                                        "-42",
                                        &int16_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_EQ (int16_value, -42);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_int16_sync (parent, proxy,
                                        "",
                                        &int16_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToInt16.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_int16_sync (parent, proxy,
                                        "invalid",
                                        &int16_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_int16_sync (parent, proxy,
                                        "-42",
                                        &int16_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int uint16_to_str_replied;

static void
my_uint16_to_str_reply (void *          data,
                  NihDBusMessage *message,
                  const char *    output)
{
      uint16_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_uint16_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_uint16_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint16_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_uint16_to_str (proxy,
                                           42,
                                           my_uint16_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (uint16_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "42");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint16_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint16_to_str (proxy,
                                           0,
                                           my_uint16_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint16_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.UInt16ToStr.ZeroInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint16_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint16_to_str (proxy,
                                           4,
                                           my_uint16_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint16_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint16_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint16_to_str (proxy,
                                           42,
                                           my_uint16_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint16_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint16_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint16_to_str (proxy,
                                           42,
                                           my_uint16_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint16_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            uint16_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint16_to_str (proxy,
                                           42,
                                           my_uint16_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (uint16_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_uint16_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_uint16_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_uint16_to_str_sync (parent, proxy,
                                         42,
                                         &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "42");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_uint16_to_str_sync (parent, proxy,
                                         0,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.UInt16ToStr.ZeroInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_uint16_to_str_sync (parent, proxy,
                                         4,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_uint16_to_str_sync (parent, proxy,
                                         42,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_uint16_replied;
static int16_t last_uint16_value;

static void
my_str_to_uint16_reply (void *          data,
                  NihDBusMessage *message,
                  uint16_t        output)
{
      str_to_uint16_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      last_data = data;
      last_uint16_value = output;
}

void
test_str_to_uint16 (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_uint16");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint16_replied = FALSE;
            last_data = NULL;
            last_uint16_value = 0;

            pending_call = proxy_test_str_to_uint16 (proxy,
                                           "42",
                                           my_str_to_uint16_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_uint16_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ (last_uint16_value, 42);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint16_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint16 (proxy,
                                           "",
                                           my_str_to_uint16_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint16_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToUInt16.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint16_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint16 (proxy,
                                           "invalid",
                                           my_str_to_uint16_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint16_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint16_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint16 (proxy,
                                           "42",
                                           my_str_to_uint16_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint16_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint16_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint16 (proxy,
                                           "42",
                                           my_str_to_uint16_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint16_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_uint16_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint16 (proxy,
                                           "42",
                                           my_str_to_uint16_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_uint16_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_uint16_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      uint16_t        uint16_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_uint16_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            uint16_value = 0;

            ret = proxy_test_str_to_uint16_sync (parent, proxy,
                                         "42",
                                         &uint16_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_EQ (uint16_value, 42);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_uint16_sync (parent, proxy,
                                         "",
                                         &uint16_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToUInt16.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_uint16_sync (parent, proxy,
                                         "invalid",
                                         &uint16_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_uint16_sync (parent, proxy,
                                         "42",
                                         &uint16_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int int32_to_str_replied;

static void
my_int32_to_str_reply (void *          data,
                   NihDBusMessage *message,
                   const char *    output)
{
      int32_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_int32_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_int32_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int32_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_int32_to_str (proxy,
                                          -1048576,
                                          my_int32_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (int32_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "-1048576");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int32_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_to_str (proxy,
                                          0,
                                          my_int32_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.Int32ToStr.ZeroInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int32_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_to_str (proxy,
                                          4,
                                          my_int32_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int32_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_to_str (proxy,
                                          -1048576,
                                          my_int32_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int32_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_to_str (proxy,
                                          -1048576,
                                          my_int32_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            int32_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_to_str (proxy,
                                          -1048576,
                                          my_int32_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (int32_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_int32_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_int32_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_int32_to_str_sync (parent, proxy,
                                        -1048576,
                                        &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "-1048576");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_int32_to_str_sync (parent, proxy,
                                        0,
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.Int32ToStr.ZeroInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_int32_to_str_sync (parent, proxy,
                                        4,
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_int32_to_str_sync (parent, proxy,
                                        -1048576,
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_int32_replied;
static int32_t last_int32_value;

static void
my_str_to_int32_reply (void *          data,
                   NihDBusMessage *message,
                   int32_t         output)
{
      str_to_int32_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      last_data = data;
      last_int32_value = output;
}

void
test_str_to_int32 (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_int32");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_replied = FALSE;
            last_data = NULL;
            last_int32_value = 0;

            pending_call = proxy_test_str_to_int32 (proxy,
                                          "-1048576",
                                          my_str_to_int32_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_int32_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ (last_int32_value, -1048576);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32 (proxy,
                                          "",
                                          my_str_to_int32_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToInt32.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32 (proxy,
                                          "invalid",
                                          my_str_to_int32_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32 (proxy,
                                          "-1048576",
                                          my_str_to_int32_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32 (proxy,
                                          "-1048576",
                                          my_str_to_int32_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_int32_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32 (proxy,
                                          "-1048576",
                                          my_str_to_int32_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_int32_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_int32_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      int32_t         int32_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_int32_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            int32_value = 0;

            ret = proxy_test_str_to_int32_sync (parent, proxy,
                                        "-1048576",
                                        &int32_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_EQ (int32_value, -1048576);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_int32_sync (parent, proxy,
                                        "",
                                        &int32_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToInt32.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_int32_sync (parent, proxy,
                                        "invalid",
                                        &int32_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_int32_sync (parent, proxy,
                                        "-1048576",
                                        &int32_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int uint32_to_str_replied;

static void
my_uint32_to_str_reply (void *          data,
                  NihDBusMessage *message,
                  const char *    output)
{
      uint32_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_uint32_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_uint32_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint32_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_uint32_to_str (proxy,
                                           1048576,
                                           my_uint32_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (uint32_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "1048576");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint32_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint32_to_str (proxy,
                                           0,
                                           my_uint32_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint32_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.UInt32ToStr.ZeroInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint32_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint32_to_str (proxy,
                                           4,
                                           my_uint32_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint32_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint32_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint32_to_str (proxy,
                                           1048576,
                                           my_uint32_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint32_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint32_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint32_to_str (proxy,
                                           1048576,
                                           my_uint32_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint32_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            uint32_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint32_to_str (proxy,
                                           1048576,
                                           my_uint32_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (uint32_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_uint32_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_uint32_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_uint32_to_str_sync (parent, proxy,
                                         1048576,
                                         &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "1048576");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_uint32_to_str_sync (parent, proxy,
                                         0,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.UInt32ToStr.ZeroInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_uint32_to_str_sync (parent, proxy,
                                         4,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_uint32_to_str_sync (parent, proxy,
                                         1048576,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_uint32_replied;
static int32_t last_uint32_value;

static void
my_str_to_uint32_reply (void *          data,
                  NihDBusMessage *message,
                  uint32_t        output)
{
      str_to_uint32_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      last_data = data;
      last_uint32_value = output;
}

void
test_str_to_uint32 (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_uint32");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint32_replied = FALSE;
            last_data = NULL;
            last_uint32_value = 0;

            pending_call = proxy_test_str_to_uint32 (proxy,
                                           "1048576",
                                           my_str_to_uint32_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_uint32_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ (last_uint32_value, 1048576);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint32_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint32 (proxy,
                                           "",
                                           my_str_to_uint32_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint32_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToUInt32.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint32_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint32 (proxy,
                                           "invalid",
                                           my_str_to_uint32_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint32_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint32_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint32 (proxy,
                                           "1048576",
                                           my_str_to_uint32_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint32_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint32_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint32 (proxy,
                                           "1048576",
                                           my_str_to_uint32_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint32_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_uint32_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint32 (proxy,
                                           "1048576",
                                           my_str_to_uint32_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_uint32_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_uint32_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      uint32_t        uint32_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_uint32_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            uint32_value = 0;

            ret = proxy_test_str_to_uint32_sync (parent, proxy,
                                         "1048576",
                                         &uint32_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_EQ (uint32_value, 1048576);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_uint32_sync (parent, proxy,
                                         "",
                                         &uint32_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToUInt32.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_uint32_sync (parent, proxy,
                                         "invalid",
                                         &uint32_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_uint32_sync (parent, proxy,
                                         "1048576",
                                         &uint32_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int int64_to_str_replied;

static void
my_int64_to_str_reply (void *          data,
                   NihDBusMessage *message,
                   const char *    output)
{
      int64_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_int64_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_int64_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int64_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_int64_to_str (proxy,
                                          -4815162342L,
                                          my_int64_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (int64_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "-4815162342");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int64_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int64_to_str (proxy,
                                          0,
                                          my_int64_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int64_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.Int64ToStr.ZeroInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int64_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int64_to_str (proxy,
                                          4,
                                          my_int64_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int64_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int64_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int64_to_str (proxy,
                                          -4815162342L,
                                          my_int64_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int64_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int64_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int64_to_str (proxy,
                                          -4815162342L,
                                          my_int64_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int64_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            int64_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int64_to_str (proxy,
                                          -4815162342L,
                                          my_int64_to_str_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (int64_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_int64_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_int64_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_int64_to_str_sync (parent, proxy,
                                        -4815162342L,
                                        &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "-4815162342");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_int64_to_str_sync (parent, proxy,
                                        0,
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.Int64ToStr.ZeroInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_int64_to_str_sync (parent, proxy,
                                        4,
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_int64_to_str_sync (parent, proxy,
                                        -4815162342L,
                                        &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_int64_replied;
static int64_t last_int64_value;

static void
my_str_to_int64_reply (void *          data,
                   NihDBusMessage *message,
                   int64_t         output)
{
      str_to_int64_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      last_data = data;
      last_int64_value = output;
}

void
test_str_to_int64 (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_int64");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int64_replied = FALSE;
            last_data = NULL;
            last_int64_value = 0;

            pending_call = proxy_test_str_to_int64 (proxy,
                                          "-4815162342",
                                          my_str_to_int64_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_int64_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ (last_int64_value, -4815162342L);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int64_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int64 (proxy,
                                          "",
                                          my_str_to_int64_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int64_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToInt64.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int64_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int64 (proxy,
                                          "invalid",
                                          my_str_to_int64_reply,
                                          my_error_handler,
                                          parent,
                                          NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int64_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int64_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int64 (proxy,
                                          "-4815162342",
                                          my_str_to_int64_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int64_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int64_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int64 (proxy,
                                          "-4815162342",
                                          my_str_to_int64_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int64_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_int64_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int64 (proxy,
                                          "-4815162342",
                                          my_str_to_int64_reply,
                                          my_error_handler,
                                          parent,
                                          50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_int64_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_int64_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      int64_t         int64_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_int64_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            int64_value = 0;

            ret = proxy_test_str_to_int64_sync (parent, proxy,
                                        "-4815162342",
                                        &int64_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_EQ (int64_value, -4815162342L);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_int64_sync (parent, proxy,
                                        "",
                                        &int64_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToInt64.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_int64_sync (parent, proxy,
                                        "invalid",
                                        &int64_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_int64_sync (parent, proxy,
                                        "-4815162342",
                                        &int64_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int uint64_to_str_replied;

static void
my_uint64_to_str_reply (void *          data,
                  NihDBusMessage *message,
                  const char *    output)
{
      uint64_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_uint64_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_uint64_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint64_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_uint64_to_str (proxy,
                                           4815162342L,
                                           my_uint64_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (uint64_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "4815162342");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint64_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint64_to_str (proxy,
                                           0,
                                           my_uint64_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint64_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.UInt64ToStr.ZeroInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint64_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint64_to_str (proxy,
                                           4,
                                           my_uint64_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint64_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint64_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint64_to_str (proxy,
                                           4815162342L,
                                           my_uint64_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint64_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            uint64_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint64_to_str (proxy,
                                           4815162342L,
                                           my_uint64_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (uint64_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            uint64_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_uint64_to_str (proxy,
                                           4815162342L,
                                           my_uint64_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (uint64_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_uint64_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_uint64_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_uint64_to_str_sync (parent, proxy,
                                         4815162342L,
                                         &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "4815162342");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_uint64_to_str_sync (parent, proxy,
                                         0,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.UInt64ToStr.ZeroInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_uint64_to_str_sync (parent, proxy,
                                         4,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_uint64_to_str_sync (parent, proxy,
                                         4815162342L,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_uint64_replied;
static int64_t last_uint64_value;

static void
my_str_to_uint64_reply (void *          data,
                  NihDBusMessage *message,
                  uint64_t        output)
{
      str_to_uint64_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      last_data = data;
      last_uint64_value = output;
}

void
test_str_to_uint64 (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_uint64");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint64_replied = FALSE;
            last_data = NULL;
            last_uint64_value = 0;

            pending_call = proxy_test_str_to_uint64 (proxy,
                                           "4815162342",
                                           my_str_to_uint64_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_uint64_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ (last_uint64_value, 4815162342L);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint64_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint64 (proxy,
                                           "",
                                           my_str_to_uint64_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint64_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToUInt64.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint64_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint64 (proxy,
                                           "invalid",
                                           my_str_to_uint64_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint64_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint64_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint64 (proxy,
                                           "4815162342",
                                           my_str_to_uint64_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint64_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_uint64_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint64 (proxy,
                                           "4815162342",
                                           my_str_to_uint64_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_uint64_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_uint64_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_uint64 (proxy,
                                           "4815162342",
                                           my_str_to_uint64_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_uint64_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_uint64_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      uint64_t        uint64_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_uint64_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            uint64_value = 0;

            ret = proxy_test_str_to_uint64_sync (parent, proxy,
                                         "4815162342",
                                         &uint64_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_EQ (uint64_value, 4815162342L);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_uint64_sync (parent, proxy,
                                         "",
                                         &uint64_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToUInt64.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_uint64_sync (parent, proxy,
                                         "invalid",
                                         &uint64_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_uint64_sync (parent, proxy,
                                         "4815162342",
                                         &uint64_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int double_to_str_replied;

static void
my_double_to_str_reply (void *          data,
                  NihDBusMessage *message,
                  const char *    output)
{
      double_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_double_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_double_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            double_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_double_to_str (proxy,
                                           3.141597,
                                           my_double_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (double_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "3.141597");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            double_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_double_to_str (proxy,
                                           0,
                                           my_double_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (double_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.DoubleToStr.ZeroInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            double_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_double_to_str (proxy,
                                           4,
                                           my_double_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (double_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            double_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_double_to_str (proxy,
                                           3.141597,
                                           my_double_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (double_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            double_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_double_to_str (proxy,
                                           3.141597,
                                           my_double_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (double_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            double_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_double_to_str (proxy,
                                           3.141597,
                                           my_double_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (double_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_double_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_double_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_double_to_str_sync (parent, proxy,
                                         3.141597,
                                         &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "3.141597");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_double_to_str_sync (parent, proxy,
                                         0,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.DoubleToStr.ZeroInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_double_to_str_sync (parent, proxy,
                                         4,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_double_to_str_sync (parent, proxy,
                                         3.141597,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_double_replied;
static double last_double_value;

static void
my_str_to_double_reply (void *          data,
                  NihDBusMessage *message,
                  double          output)
{
      str_to_double_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      last_data = data;
      last_double_value = output;
}

void
test_str_to_double (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_double");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_double_replied = FALSE;
            last_data = NULL;
            last_double_value = 0;

            pending_call = proxy_test_str_to_double (proxy,
                                           "3.141597",
                                           my_str_to_double_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_double_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ (last_double_value, 3.141597);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_double_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_double (proxy,
                                           "",
                                           my_str_to_double_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_double_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToDouble.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_double_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_double (proxy,
                                           "invalid",
                                           my_str_to_double_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_double_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_double_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_double (proxy,
                                           "3.141597",
                                           my_str_to_double_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_double_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_double_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_double (proxy,
                                           "3.141597",
                                           my_str_to_double_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_double_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_double_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_double (proxy,
                                           "3.141597",
                                           my_str_to_double_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_double_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_double_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      double          double_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_double_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            double_value = 0;

            ret = proxy_test_str_to_double_sync (parent, proxy,
                                         "3.141597",
                                         &double_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_EQ (double_value, 3.141597);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_double_sync (parent, proxy,
                                         "",
                                         &double_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToDouble.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_double_sync (parent, proxy,
                                         "invalid",
                                         &double_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_double_sync (parent, proxy,
                                         "3.141597",
                                         &double_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int object_path_to_str_replied;

static void
my_object_path_to_str_reply (void *          data,
                       NihDBusMessage *message,
                       const char *    output)
{
      object_path_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_object_path_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_object_path_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            object_path_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_object_path_to_str (proxy,
                                                "/com/netsplit/Nih/Test",
                                                my_object_path_to_str_reply,
                                                my_error_handler,
                                                parent,
                                                0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (object_path_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "/com/netsplit/Nih/Test");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            object_path_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_object_path_to_str (proxy,
                                                "/",
                                                my_object_path_to_str_reply,
                                                my_error_handler,
                                                parent,
                                                0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (object_path_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.ObjectPathToStr.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            object_path_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_object_path_to_str (proxy,
                                                "/invalid",
                                                my_object_path_to_str_reply,
                                                my_error_handler,
                                                parent,
                                                0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (object_path_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            object_path_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_object_path_to_str (proxy,
                                                "/com/netsplit/Nih/Test",
                                                my_object_path_to_str_reply,
                                                my_error_handler,
                                                parent,
                                                50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (object_path_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            object_path_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_object_path_to_str (proxy,
                                                "/com/netsplit/Nih/Test",
                                                my_object_path_to_str_reply,
                                                my_error_handler,
                                                parent,
                                                50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (object_path_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            object_path_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_object_path_to_str (proxy,
                                                "/com/netsplit/Nih/Test",
                                                my_object_path_to_str_reply,
                                                my_error_handler,
                                                parent,
                                                50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (object_path_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_object_path_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_object_path_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_object_path_to_str_sync (parent, proxy,
                                            "/com/netsplit/Nih/Test",
                                            &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "/com/netsplit/Nih/Test");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_object_path_to_str_sync (parent, proxy,
                                            "/",
                                            &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.ObjectPathToStr.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_object_path_to_str_sync (parent, proxy,
                                            "/invalid",
                                            &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_object_path_to_str_sync (parent, proxy,
                                            "/com/netsplit/Nih/Test",
                                            &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_object_path_replied;

static void
my_str_to_object_path_reply (void *          data,
                       NihDBusMessage *message,
                       const char *    output)
{
      str_to_object_path_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_str_to_object_path (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_object_path");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_object_path_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_str_to_object_path (proxy,
                                                "/com/netsplit/Nih/Test",
                                                my_str_to_object_path_reply,
                                                my_error_handler,
                                                parent,
                                                0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_object_path_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "/com/netsplit/Nih/Test");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_object_path_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_object_path (proxy,
                                                "",
                                                my_str_to_object_path_reply,
                                                my_error_handler,
                                                parent,
                                                0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_object_path_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToObjectPath.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_object_path_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_object_path (proxy,
                                                "invalid",
                                                my_str_to_object_path_reply,
                                                my_error_handler,
                                                parent,
                                                0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_object_path_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_object_path_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_object_path (proxy,
                                                "/com/netsplit/Nih/Test",
                                                my_str_to_object_path_reply,
                                                my_error_handler,
                                                parent,
                                                50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_object_path_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_object_path_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_object_path (proxy,
                                                "/com/netsplit/Nih/Test",
                                                my_str_to_object_path_reply,
                                                my_error_handler,
                                                parent,
                                                50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_object_path_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_object_path_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_object_path (proxy,
                                                "/com/netsplit/Nih/Test",
                                                my_str_to_object_path_reply,
                                                my_error_handler,
                                                parent,
                                                50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_object_path_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_object_path_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_object_path_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_str_to_object_path_sync (parent, proxy,
                                            "/com/netsplit/Nih/Test",
                                            &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "/com/netsplit/Nih/Test");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_object_path_sync (parent, proxy,
                                            "",
                                            &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToObjectPath.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_object_path_sync (parent, proxy,
                                            "invalid",
                                            &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_object_path_sync (parent, proxy,
                                            "/com/netsplit/Nih/Test",
                                            &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int signature_to_str_replied;

static void
my_signature_to_str_reply (void *          data,
                     NihDBusMessage *message,
                     const char *    output)
{
      signature_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_signature_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_signature_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            signature_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_signature_to_str (proxy,
                                              "a(ib)",
                                              my_signature_to_str_reply,
                                              my_error_handler,
                                              parent,
                                              0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (signature_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "a(ib)");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            signature_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_signature_to_str (proxy,
                                              "",
                                              my_signature_to_str_reply,
                                              my_error_handler,
                                              parent,
                                              0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (signature_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.SignatureToStr.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            signature_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_signature_to_str (proxy,
                                              "inva(x)id",
                                              my_signature_to_str_reply,
                                              my_error_handler,
                                              parent,
                                              0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (signature_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            signature_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_signature_to_str (proxy,
                                              "a(ib)",
                                              my_signature_to_str_reply,
                                              my_error_handler,
                                              parent,
                                              50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (signature_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            signature_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_signature_to_str (proxy,
                                              "a(ib)",
                                              my_signature_to_str_reply,
                                              my_error_handler,
                                              parent,
                                              50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (signature_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            signature_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_signature_to_str (proxy,
                                              "a(ib)",
                                              my_signature_to_str_reply,
                                              my_error_handler,
                                              parent,
                                              50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (signature_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_signature_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_signature_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_signature_to_str_sync (parent, proxy,
                                          "a(ib)",
                                          &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "a(ib)");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_signature_to_str_sync (parent, proxy,
                                          "",
                                          &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.SignatureToStr.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_signature_to_str_sync (parent, proxy,
                                          "inva(x)id",
                                          &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_signature_to_str_sync (parent, proxy,
                                          "a(ib)",
                                          &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_signature_replied;

static void
my_str_to_signature_reply (void *          data,
                     NihDBusMessage *message,
                     const char *    output)
{
      str_to_signature_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_str_to_signature (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_signature");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_signature_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_str_to_signature (proxy,
                                              "a(ib)",
                                              my_str_to_signature_reply,
                                              my_error_handler,
                                              parent,
                                              0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_signature_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "a(ib)");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_signature_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_signature (proxy,
                                              "",
                                              my_str_to_signature_reply,
                                              my_error_handler,
                                              parent,
                                              0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_signature_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToSignature.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_signature_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_signature (proxy,
                                              "invalid",
                                              my_str_to_signature_reply,
                                              my_error_handler,
                                              parent,
                                              0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_signature_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_signature_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_signature (proxy,
                                              "a(ib)",
                                              my_str_to_signature_reply,
                                              my_error_handler,
                                              parent,
                                              50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_signature_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_signature_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_signature (proxy,
                                              "a(ib)",
                                              my_str_to_signature_reply,
                                              my_error_handler,
                                              parent,
                                              50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_signature_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_signature_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_signature (proxy,
                                              "a(ib)",
                                              my_str_to_signature_reply,
                                              my_error_handler,
                                              parent,
                                              50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_signature_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_signature_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_signature_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_value = NULL;

            ret = proxy_test_str_to_signature_sync (parent, proxy,
                                          "a(ib)",
                                          &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "a(ib)");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_signature_sync (parent, proxy,
                                          "",
                                          &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToSignature.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_signature_sync (parent, proxy,
                                          "invalid",
                                          &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_signature_sync (parent, proxy,
                                          "a(ib)",
                                          &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int struct_to_str_replied;

static void
my_struct_to_str_reply (void *          data,
                  NihDBusMessage *message,
                  const char *    output)
{
      struct_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_struct_to_str (void)
{
      pid_t                      dbus_pid;
      DBusConnection *           client_conn;
      DBusConnection *           server_conn;
      DBusConnection *           flakey_conn;
      NihDBusObject *            object = NULL;
      NihDBusProxy *             proxy = NULL;
      void *                     parent = NULL;
      ProxyTestStructToStrInput *struct_value = NULL;
      DBusPendingCall *          pending_call;
      NihError *                 err;
      NihDBusError *             dbus_err;

      TEST_FUNCTION ("proxy_test_struct_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_value = nih_new (parent, ProxyTestStructToStrInput);
                  struct_value->item0 = "Joe";
                  struct_value->item1 = 34;
            }

            error_handler_called = FALSE;
            struct_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_struct_to_str (proxy,
                                           struct_value,
                                           my_struct_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (struct_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "Joe 34");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_value = nih_new (parent, ProxyTestStructToStrInput);
                  struct_value->item0 = "";
                  struct_value->item1 = 34;
            }

            error_handler_called = FALSE;
            struct_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_struct_to_str (proxy,
                                           struct_value,
                                           my_struct_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (struct_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StructToStr.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_value = nih_new (parent, ProxyTestStructToStrInput);
                  struct_value->item0 = "invalid";
                  struct_value->item1 = 34;
            }

            error_handler_called = FALSE;
            struct_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_struct_to_str (proxy,
                                           struct_value,
                                           my_struct_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (struct_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_value = nih_new (parent, ProxyTestStructToStrInput);
                  struct_value->item0 = "Joe";
                  struct_value->item1 = 34;
            }

            error_handler_called = FALSE;
            struct_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_struct_to_str (proxy,
                                           struct_value,
                                           my_struct_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (struct_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_value = nih_new (parent, ProxyTestStructToStrInput);
                  struct_value->item0 = "Joe";
                  struct_value->item1 = 34;
            }

            error_handler_called = FALSE;
            struct_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_struct_to_str (proxy,
                                           struct_value,
                                           my_struct_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (struct_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_value = nih_new (parent, ProxyTestStructToStrInput);
                  struct_value->item0 = "Joe";
                  struct_value->item1 = 34;
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            struct_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_struct_to_str (proxy,
                                           struct_value,
                                           my_struct_to_str_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (struct_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_struct_to_str_sync (void)
{
      pid_t                      dbus_pid;
      DBusConnection *           client_conn;
      DBusConnection *           server_conn;
      DBusConnection *           flakey_conn;
      pid_t                      server_pid;
      int                        wait_fd;
      NihDBusObject *            object = NULL;
      NihDBusProxy *             proxy = NULL;
      void *                     parent = NULL;
      ProxyTestStructToStrInput *struct_value = NULL;
      char *                     str_value;
      int                        ret;
      NihError *                 err;
      NihDBusError *             dbus_err;
      int                        status;

      TEST_FUNCTION ("proxy_test_struct_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_value = nih_new (parent, ProxyTestStructToStrInput);
                  struct_value->item0 = "Joe";
                  struct_value->item1 = 34;
            }

            str_value = NULL;

            ret = proxy_test_struct_to_str_sync (parent, proxy,
                                         struct_value,
                                         &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "Joe 34");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_value = nih_new (parent, ProxyTestStructToStrInput);
                  struct_value->item0 = "";
                  struct_value->item1 = 34;
            }

            ret = proxy_test_struct_to_str_sync (parent, proxy,
                                         struct_value,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StructToStr.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_value = nih_new (parent, ProxyTestStructToStrInput);
                  struct_value->item0 = "invalid";
                  struct_value->item1 = 34;
            }

            ret = proxy_test_struct_to_str_sync (parent, proxy,
                                         struct_value,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_value = nih_new (parent, ProxyTestStructToStrInput);
                  struct_value->item0 = "Joe";
                  struct_value->item1 = 34;
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_struct_to_str_sync (parent, proxy,
                                         struct_value,
                                         &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_struct_replied;
static const MyStruct *last_struct_value;

static void
my_str_to_struct_reply (void *          data,
                  NihDBusMessage *message,
                  const ProxyTestStrToStructOutput *output)
{
      str_to_struct_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_struct_value = (MyStruct *)output;
      nih_ref (last_struct_value, last_data);
}

void
test_str_to_struct (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_struct");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_struct_replied = FALSE;
            last_data = NULL;
            last_struct_value = NULL;

            pending_call = proxy_test_str_to_struct (proxy,
                                           "Joe 34",
                                           my_str_to_struct_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_struct_replied);

            TEST_EQ_P (last_data, parent);
            TEST_ALLOC_SIZE (last_struct_value, sizeof (MyStruct));
            TEST_ALLOC_PARENT (last_struct_value, parent);
            TEST_EQ_STR (last_struct_value->item0, "Joe");
            TEST_ALLOC_PARENT (last_struct_value->item0, last_struct_value);
            TEST_EQ (last_struct_value->item1, 34);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_struct_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_struct (proxy,
                                           "",
                                           my_str_to_struct_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_struct_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToStruct.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_struct_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_struct (proxy,
                                           "invalid",
                                           my_str_to_struct_reply,
                                           my_error_handler,
                                           parent,
                                           0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_struct_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_struct_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_struct (proxy,
                                           "Joe 34",
                                           my_str_to_struct_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_struct_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_struct_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_struct (proxy,
                                           "Joe 34",
                                           my_str_to_struct_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_struct_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_struct_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_struct (proxy,
                                           "Joe 34",
                                           my_str_to_struct_reply,
                                           my_error_handler,
                                           parent,
                                           50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_struct_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_struct_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      ProxyTestStrToStructOutput *struct_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_struct_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            struct_value = NULL;

            ret = proxy_test_str_to_struct_sync (parent, proxy,
                                         "Joe 34",
                                         &struct_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_ALLOC_SIZE (struct_value, sizeof (ProxyTestStrToStructOutput));
            TEST_ALLOC_PARENT (struct_value, parent);
            TEST_EQ_STR (struct_value->item0, "Joe");
            TEST_ALLOC_PARENT (struct_value->item0, struct_value);
            TEST_EQ (struct_value->item1, 34);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_struct_sync (parent, proxy,
                                         "",
                                         &struct_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToStruct.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_struct_sync (parent, proxy,
                                         "invalid",
                                         &struct_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_struct_sync (parent, proxy,
                                         "Joe 34",
                                         &struct_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int int32_array_to_str_replied;

static void
my_int32_array_to_str_reply (void *          data,
                       NihDBusMessage *message,
                       const char *    output)
{
      int32_array_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_int32_array_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      int32_t *        int32_array = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_int32_array_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array = nih_alloc (parent, sizeof (int32_t) * 6);
                  int32_array[0] = 4;
                  int32_array[1] = 8;
                  int32_array[2] = 15;
                  int32_array[3] = 16;
                  int32_array[4] = 23;
                  int32_array[5] = 42;
            }

            error_handler_called = FALSE;
            int32_array_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_int32_array_to_str (
                  proxy,
                  int32_array, 6,
                  my_int32_array_to_str_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (int32_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "4 8 15 16 23 42");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            int32_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_array_to_str (
                  proxy,
                  NULL, 0,
                  my_int32_array_to_str_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.Int32ArrayToStr.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array = nih_alloc (parent, sizeof (int32_t) * 4);
                  int32_array[0] = 4;
                  int32_array[1] = 8;
                  int32_array[2] = 15;
                  int32_array[3] = 16;
            }

            error_handler_called = FALSE;
            int32_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_array_to_str (
                  proxy,
                  int32_array, 4,
                  my_int32_array_to_str_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array = nih_alloc (parent, sizeof (int32_t) * 6);
                  int32_array[0] = 4;
                  int32_array[1] = 8;
                  int32_array[2] = 15;
                  int32_array[3] = 16;
                  int32_array[4] = 23;
                  int32_array[5] = 42;
            }

            error_handler_called = FALSE;
            int32_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_array_to_str (
                  proxy,
                  int32_array, 6,
                  my_int32_array_to_str_reply,
                  my_error_handler,
                  parent,
                  50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array = nih_alloc (parent, sizeof (int32_t) * 6);
                  int32_array[0] = 4;
                  int32_array[1] = 8;
                  int32_array[2] = 15;
                  int32_array[3] = 16;
                  int32_array[4] = 23;
                  int32_array[5] = 42;
            }

            error_handler_called = FALSE;
            int32_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_array_to_str (
                  proxy,
                  int32_array, 6,
                  my_int32_array_to_str_reply,
                  my_error_handler,
                  parent,
                  50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array = nih_alloc (parent, sizeof (int32_t) * 6);
                  int32_array[0] = 4;
                  int32_array[1] = 8;
                  int32_array[2] = 15;
                  int32_array[3] = 16;
                  int32_array[4] = 23;
                  int32_array[5] = 42;
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            int32_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_array_to_str (
                  proxy,
                  int32_array, 6,
                  my_int32_array_to_str_reply,
                  my_error_handler,
                  parent,
                  50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (int32_array_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_int32_array_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      int32_t *       int32_array = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_int32_array_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array = nih_alloc (parent, sizeof (int32_t) * 6);
                  int32_array[0] = 4;
                  int32_array[1] = 8;
                  int32_array[2] = 15;
                  int32_array[3] = 16;
                  int32_array[4] = 23;
                  int32_array[5] = 42;
            }

            str_value = NULL;

            ret = proxy_test_int32_array_to_str_sync (parent, proxy,
                                            int32_array, 6,
                                            &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "4 8 15 16 23 42");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_int32_array_to_str_sync (parent, proxy,
                                            NULL, 0,
                                            &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.Int32ArrayToStr.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array = nih_alloc (parent, sizeof (int32_t) * 6);
                  int32_array[0] = 4;
                  int32_array[1] = 8;
                  int32_array[2] = 15;
                  int32_array[3] = 16;
            }

            ret = proxy_test_int32_array_to_str_sync (parent, proxy,
                                            int32_array, 4,
                                            &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array = nih_alloc (parent, sizeof (int32_t) * 6);
                  int32_array[0] = 4;
                  int32_array[1] = 8;
                  int32_array[2] = 15;
                  int32_array[3] = 16;
                  int32_array[4] = 23;
                  int32_array[5] = 42;
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_int32_array_to_str_sync (parent, proxy,
                                            int32_array, 6,
                                            &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_int32_array_replied;
static const int32_t *last_int32_array;
static size_t         last_int32_array_len;

static void
my_str_to_int32_array_reply (void *          data,
                       NihDBusMessage *message,
                       const int32_t * output,
                       size_t          output_len)
{
      str_to_int32_array_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      if (output_len)
            TEST_NE_P (output, NULL);

      last_data = data;
      last_int32_array = output;
      nih_ref (last_int32_array, last_data);
      last_int32_array_len = output_len;
}

void
test_str_to_int32_array (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_int32_array");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_array_replied = FALSE;
            last_data = NULL;
            last_int32_array = NULL;
            last_int32_array_len = 0;

            pending_call = proxy_test_str_to_int32_array (proxy,
                                                "4 8 15 16 23 42",
                                                my_str_to_int32_array_reply,
                                                my_error_handler,
                                                parent,
                                                0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_int32_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ (last_int32_array_len, 6);
            TEST_ALLOC_SIZE (last_int32_array, sizeof (int32_t) * 6);
            TEST_ALLOC_PARENT (last_int32_array, parent);
            TEST_EQ (last_int32_array[0], 4);
            TEST_EQ (last_int32_array[1], 8);
            TEST_EQ (last_int32_array[2], 15);
            TEST_EQ (last_int32_array[3], 16);
            TEST_EQ (last_int32_array[4], 23);
            TEST_EQ (last_int32_array[5], 42);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32_array (proxy,
                                                "",
                                                my_str_to_int32_array_reply,
                                                my_error_handler,
                                                parent,
                                                0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToInt32Array.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32_array (proxy,
                                                "invalid",
                                                my_str_to_int32_array_reply,
                                                my_error_handler,
                                                parent,
                                                0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32_array (proxy,
                                                "4 8 15 16 23 42",
                                                my_str_to_int32_array_reply,
                                                my_error_handler,
                                                parent,
                                                50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32_array (proxy,
                                                "4 8 15 16 23 42",
                                                my_str_to_int32_array_reply,
                                                my_error_handler,
                                                parent,
                                                50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_int32_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32_array (proxy,
                                                "4 8 15 16 23 42",
                                                my_str_to_int32_array_reply,
                                                my_error_handler,
                                                parent,
                                                50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_int32_array_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_int32_array_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      int32_t *       int32_array;
      size_t          int32_array_len;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_int32_array_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            int32_array = NULL;
            int32_array_len = 0;

            ret = proxy_test_str_to_int32_array_sync (parent, proxy,
                                            "4 8 15 16 23 42",
                                            &int32_array,
                                            &int32_array_len);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_EQ (int32_array_len, 6);
            TEST_ALLOC_SIZE (int32_array, sizeof (int32_t) * 6);
            TEST_ALLOC_PARENT (int32_array, parent);
            TEST_EQ (int32_array[0], 4);
            TEST_EQ (int32_array[1], 8);
            TEST_EQ (int32_array[2], 15);
            TEST_EQ (int32_array[3], 16);
            TEST_EQ (int32_array[4], 23);
            TEST_EQ (int32_array[5], 42);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            int32_array = NULL;
            int32_array_len = 0;

            ret = proxy_test_str_to_int32_array_sync (parent, proxy,
                                            "",
                                            &int32_array,
                                            &int32_array_len);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToInt32Array.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            int32_array = NULL;
            int32_array_len = 0;

            ret = proxy_test_str_to_int32_array_sync (parent, proxy,
                                            "invalid",
                                            &int32_array,
                                            &int32_array_len);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_int32_array_sync (parent, proxy,
                                            "4 8 15 16 23 42",
                                            &int32_array,
                                            &int32_array_len);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_array_to_str_replied;

static void
my_str_array_to_str_reply (void *          data,
                       NihDBusMessage *message,
                       const char *    output)
{
      str_array_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_str_array_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      char **          str_array = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_array_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  str_array = nih_alloc (parent, sizeof (char *) * 7);
                  str_array[0] = "she";
                  str_array[1] = "needs";
                  str_array[2] = "more";
                  str_array[3] = "of";
                  str_array[4] = "ze";
                  str_array[5] = "punishment";
                  str_array[6] = NULL;
            }

            error_handler_called = FALSE;
            str_array_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_str_array_to_str (
                  proxy,
                  str_array,
                  my_str_array_to_str_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "she needs more of ze punishment");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  str_array = nih_alloc (parent, sizeof (char *) * 1);
                  str_array[0] = NULL;
            }

            error_handler_called = FALSE;
            str_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_array_to_str (
                  proxy,
                  str_array,
                  my_str_array_to_str_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrArrayToStr.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  str_array = nih_alloc (parent, sizeof (char *) * 5);
                  str_array[0] = "this";
                  str_array[1] = "is";
                  str_array[2] = "a";
                  str_array[3] = "test";
                  str_array[4] = NULL;
            }

            error_handler_called = FALSE;
            str_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_array_to_str (
                  proxy,
                  str_array,
                  my_str_array_to_str_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  str_array = nih_alloc (parent, sizeof (char *) * 7);
                  str_array[0] = "she";
                  str_array[1] = "needs";
                  str_array[2] = "more";
                  str_array[3] = "of";
                  str_array[4] = "ze";
                  str_array[5] = "punishment";
                  str_array[6] = NULL;
            }

            error_handler_called = FALSE;
            str_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_array_to_str (
                  proxy,
                  str_array,
                  my_str_array_to_str_reply,
                  my_error_handler,
                  parent,
                  50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  str_array = nih_alloc (parent, sizeof (char *) * 7);
                  str_array[0] = "she";
                  str_array[1] = "needs";
                  str_array[2] = "more";
                  str_array[3] = "of";
                  str_array[4] = "ze";
                  str_array[5] = "punishment";
                  str_array[6] = NULL;
            }

            error_handler_called = FALSE;
            str_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_array_to_str (
                  proxy,
                  str_array,
                  my_str_array_to_str_reply,
                  my_error_handler,
                  parent,
                  50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  str_array = nih_alloc (parent, sizeof (char *) * 7);
                  str_array[0] = "she";
                  str_array[1] = "needs";
                  str_array[2] = "more";
                  str_array[3] = "of";
                  str_array[4] = "ze";
                  str_array[5] = "punishment";
                  str_array[6] = NULL;
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_array_to_str (
                  proxy,
                  str_array,
                  my_str_array_to_str_reply,
                  my_error_handler,
                  parent,
                  50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_array_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_array_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char **         str_array = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_array_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  str_array = nih_alloc (parent, sizeof (char *) * 7);
                  str_array[0] = "she";
                  str_array[1] = "needs";
                  str_array[2] = "more";
                  str_array[3] = "of";
                  str_array[4] = "ze";
                  str_array[5] = "punishment";
                  str_array[6] = NULL;
            }

            str_value = NULL;

            ret = proxy_test_str_array_to_str_sync (parent, proxy,
                                          str_array,
                                          &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "she needs more of ze punishment");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  str_array = nih_alloc (parent, sizeof (char *) * 1);
                  str_array[0] = NULL;
            }

            ret = proxy_test_str_array_to_str_sync (parent, proxy,
                                          str_array,
                                          &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrArrayToStr.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  str_array = nih_alloc (parent, sizeof (char *) * 5);
                  str_array[0] = "this";
                  str_array[1] = "is";
                  str_array[2] = "a";
                  str_array[3] = "test";
                  str_array[4] = NULL;
            }

            ret = proxy_test_str_array_to_str_sync (parent, proxy,
                                          str_array,
                                          &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  str_array = nih_alloc (parent, sizeof (char *) * 7);
                  str_array[0] = "she";
                  str_array[1] = "needs";
                  str_array[2] = "more";
                  str_array[3] = "of";
                  str_array[4] = "ze";
                  str_array[5] = "punishment";
                  str_array[6] = NULL;
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_array_to_str_sync (parent, proxy,
                                          str_array,
                                          &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_str_array_replied;
static char * const *last_str_array;

static void
my_str_to_str_array_reply (void *          data,
                     NihDBusMessage *message,
                     char * const *  output)
{
      str_to_str_array_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_array = output;
      nih_ref (last_str_array, last_data);
}

void
test_str_to_str_array (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_str_array");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_str_array_replied = FALSE;
            last_data = NULL;
            last_str_array = NULL;

            pending_call = proxy_test_str_to_str_array (proxy,
                                              "she needs more of ze punishment",
                                              my_str_to_str_array_reply,
                                              my_error_handler,
                                              parent,
                                              0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_str_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_ALLOC_SIZE (last_str_array, sizeof (char *) * 7);
            TEST_ALLOC_PARENT (last_str_array, parent);
            TEST_EQ_STR (last_str_array[0], "she");
            TEST_EQ_STR (last_str_array[1], "needs");
            TEST_EQ_STR (last_str_array[2], "more");
            TEST_EQ_STR (last_str_array[3], "of");
            TEST_EQ_STR (last_str_array[4], "ze");
            TEST_EQ_STR (last_str_array[5], "punishment");
            TEST_EQ_P (last_str_array[6], NULL);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_str_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_str_array (proxy,
                                              "",
                                              my_str_to_str_array_reply,
                                              my_error_handler,
                                              parent,
                                              0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_str_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToStrArray.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_str_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_str_array (proxy,
                                              "invalid",
                                              my_str_to_str_array_reply,
                                              my_error_handler,
                                              parent,
                                              0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_str_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_str_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_str_array (proxy,
                                              "she needs more of ze punishment",
                                              my_str_to_str_array_reply,
                                              my_error_handler,
                                              parent,
                                              50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_str_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_str_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_str_array (proxy,
                                              "she needs more of ze punishment",
                                              my_str_to_str_array_reply,
                                              my_error_handler,
                                              parent,
                                              50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_str_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_str_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_str_array (proxy,
                                              "she needs more of ze punishment",
                                              my_str_to_str_array_reply,
                                              my_error_handler,
                                              parent,
                                              50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_str_array_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_str_array_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      char **         str_array;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_str_array_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_array = NULL;

            ret = proxy_test_str_to_str_array_sync (parent, proxy,
                                          "she needs more of ze punishment",
                                          &str_array);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_ALLOC_SIZE (str_array, sizeof (char *) * 7);
            TEST_ALLOC_PARENT (str_array, parent);
            TEST_EQ_STR (str_array[0], "she");
            TEST_EQ_STR (str_array[1], "needs");
            TEST_EQ_STR (str_array[2], "more");
            TEST_EQ_STR (str_array[3], "of");
            TEST_EQ_STR (str_array[4], "ze");
            TEST_EQ_STR (str_array[5], "punishment");
            TEST_EQ_P (str_array[6], NULL);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_array = NULL;

            ret = proxy_test_str_to_str_array_sync (parent, proxy,
                                          "",
                                          &str_array);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToStrArray.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            str_array = NULL;

            ret = proxy_test_str_to_str_array_sync (parent, proxy,
                                          "invalid",
                                          &str_array);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_str_array_sync (parent, proxy,
                                          "she needs more of ze punishment",
                                          &str_array);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int int32_array_array_to_str_replied;

static void
my_int32_array_array_to_str_reply (void *          data,
                           NihDBusMessage *message,
                           const char *    output)
{
      int32_array_array_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_int32_array_array_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      int32_t **       int32_array_array = NULL;
      size_t *         int32_array_array_len = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_int32_array_array_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array_array = nih_alloc (parent, sizeof (int32_t *) * 3);
                  int32_array_array_len = nih_alloc (parent, sizeof (size_t) * 2);

                  int32_array_array[0] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[0][0] = 4;
                  int32_array_array[0][1] = 8;
                  int32_array_array[0][2] = 15;
                  int32_array_array[0][3] = 16;
                  int32_array_array[0][4] = 23;
                  int32_array_array[0][5] = 42;
                  int32_array_array_len[0] = 6;

                  int32_array_array[1] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[1][0] = 1;
                  int32_array_array[1][1] = 1;
                  int32_array_array[1][2] = 2;
                  int32_array_array[1][3] = 3;
                  int32_array_array[1][4] = 5;
                  int32_array_array[1][5] = 8;
                  int32_array_array_len[1] = 6;

                  int32_array_array[2] = NULL;
            }

            error_handler_called = FALSE;
            int32_array_array_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_int32_array_array_to_str (
                  proxy,
                  int32_array_array, int32_array_array_len,
                  my_int32_array_array_to_str_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (int32_array_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "4 8 15 16 23 42\n1 1 2 3 5 8");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array_array = nih_alloc (parent, sizeof (int32_t *) * 1);
                  int32_array_array_len = NULL;

                  int32_array_array[0] = NULL;
            }

            error_handler_called = FALSE;
            int32_array_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_array_array_to_str (
                  proxy,
                  int32_array_array, int32_array_array_len,
                  my_int32_array_array_to_str_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_array_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.Int32ArrayArrayToStr.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array_array = nih_alloc (parent, sizeof (int32_t *) * 2);
                  int32_array_array_len = nih_alloc (parent, sizeof (size_t) * 1);

                  int32_array_array[0] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[0][0] = 4;
                  int32_array_array[0][1] = 8;
                  int32_array_array[0][2] = 15;
                  int32_array_array[0][3] = 16;
                  int32_array_array[0][4] = 23;
                  int32_array_array[0][5] = 42;
                  int32_array_array_len[0] = 6;

                  int32_array_array[1] = NULL;
            }

            error_handler_called = FALSE;
            int32_array_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_array_array_to_str (
                  proxy,
                  int32_array_array, int32_array_array_len,
                  my_int32_array_array_to_str_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_array_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array_array = nih_alloc (parent, sizeof (int32_t *) * 3);
                  int32_array_array_len = nih_alloc (parent, sizeof (size_t) * 2);

                  int32_array_array[0] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[0][0] = 4;
                  int32_array_array[0][1] = 8;
                  int32_array_array[0][2] = 15;
                  int32_array_array[0][3] = 16;
                  int32_array_array[0][4] = 23;
                  int32_array_array[0][5] = 42;
                  int32_array_array_len[0] = 6;

                  int32_array_array[1] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[1][0] = 1;
                  int32_array_array[1][1] = 1;
                  int32_array_array[1][2] = 2;
                  int32_array_array[1][3] = 3;
                  int32_array_array[1][4] = 5;
                  int32_array_array[1][5] = 8;
                  int32_array_array_len[1] = 6;

                  int32_array_array[2] = NULL;
            }

            error_handler_called = FALSE;
            int32_array_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_array_array_to_str (
                  proxy,
                  int32_array_array, int32_array_array_len,
                  my_int32_array_array_to_str_reply,
                  my_error_handler,
                  parent,
                  50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_array_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array_array = nih_alloc (parent, sizeof (int32_t *) * 3);
                  int32_array_array_len = nih_alloc (parent, sizeof (size_t) * 2);

                  int32_array_array[0] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[0][0] = 4;
                  int32_array_array[0][1] = 8;
                  int32_array_array[0][2] = 15;
                  int32_array_array[0][3] = 16;
                  int32_array_array[0][4] = 23;
                  int32_array_array[0][5] = 42;
                  int32_array_array_len[0] = 6;

                  int32_array_array[1] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[1][0] = 1;
                  int32_array_array[1][1] = 1;
                  int32_array_array[1][2] = 2;
                  int32_array_array[1][3] = 3;
                  int32_array_array[1][4] = 5;
                  int32_array_array[1][5] = 8;
                  int32_array_array_len[1] = 6;

                  int32_array_array[2] = NULL;
            }

            error_handler_called = FALSE;
            int32_array_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_array_array_to_str (
                  proxy,
                  int32_array_array, int32_array_array_len,
                  my_int32_array_array_to_str_reply,
                  my_error_handler,
                  parent,
                  50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (int32_array_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array_array = nih_alloc (parent, sizeof (int32_t *) * 3);
                  int32_array_array_len = nih_alloc (parent, sizeof (size_t) * 2);

                  int32_array_array[0] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[0][0] = 4;
                  int32_array_array[0][1] = 8;
                  int32_array_array[0][2] = 15;
                  int32_array_array[0][3] = 16;
                  int32_array_array[0][4] = 23;
                  int32_array_array[0][5] = 42;
                  int32_array_array_len[0] = 6;

                  int32_array_array[1] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[1][0] = 1;
                  int32_array_array[1][1] = 1;
                  int32_array_array[1][2] = 2;
                  int32_array_array[1][3] = 3;
                  int32_array_array[1][4] = 5;
                  int32_array_array[1][5] = 8;
                  int32_array_array_len[1] = 6;

                  int32_array_array[2] = NULL;
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            int32_array_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_int32_array_array_to_str (
                  proxy,
                  int32_array_array, int32_array_array_len,
                  my_int32_array_array_to_str_reply,
                  my_error_handler,
                  parent,
                  50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (int32_array_array_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_int32_array_array_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      int32_t **      int32_array_array = NULL;
      size_t *        int32_array_array_len = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_int32_array_array_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array_array = nih_alloc (parent, sizeof (int32_t *) * 3);
                  int32_array_array_len = nih_alloc (parent, sizeof (size_t) * 2);

                  int32_array_array[0] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[0][0] = 4;
                  int32_array_array[0][1] = 8;
                  int32_array_array[0][2] = 15;
                  int32_array_array[0][3] = 16;
                  int32_array_array[0][4] = 23;
                  int32_array_array[0][5] = 42;
                  int32_array_array_len[0] = 6;

                  int32_array_array[1] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[1][0] = 1;
                  int32_array_array[1][1] = 1;
                  int32_array_array[1][2] = 2;
                  int32_array_array[1][3] = 3;
                  int32_array_array[1][4] = 5;
                  int32_array_array[1][5] = 8;
                  int32_array_array_len[1] = 6;

                  int32_array_array[2] = NULL;
            }

            str_value = NULL;

            ret = proxy_test_int32_array_array_to_str_sync (
                  parent, proxy,
                  int32_array_array, int32_array_array_len,
                  &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "4 8 15 16 23 42\n1 1 2 3 5 8");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array_array = nih_alloc (parent, sizeof (int32_t *) * 1);
                  int32_array_array_len = NULL;

                  int32_array_array[0] = NULL;
            }

            ret = proxy_test_int32_array_array_to_str_sync (
                  parent, proxy,
                  int32_array_array, int32_array_array_len,
                  &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.Int32ArrayArrayToStr.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array_array = nih_alloc (parent, sizeof (int32_t *) * 2);
                  int32_array_array_len = nih_alloc (parent, sizeof (size_t) * 1);

                  int32_array_array[0] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[0][0] = 4;
                  int32_array_array[0][1] = 8;
                  int32_array_array[0][2] = 15;
                  int32_array_array[0][3] = 16;
                  int32_array_array[0][4] = 23;
                  int32_array_array[0][5] = 42;
                  int32_array_array_len[0] = 6;

                  int32_array_array[1] = NULL;
            }

            ret = proxy_test_int32_array_array_to_str_sync (
                  parent, proxy,
                  int32_array_array, int32_array_array_len,
                  &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  int32_array_array = nih_alloc (parent, sizeof (int32_t *) * 3);
                  int32_array_array_len = nih_alloc (parent, sizeof (size_t) * 2);

                  int32_array_array[0] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[0][0] = 4;
                  int32_array_array[0][1] = 8;
                  int32_array_array[0][2] = 15;
                  int32_array_array[0][3] = 16;
                  int32_array_array[0][4] = 23;
                  int32_array_array[0][5] = 42;
                  int32_array_array_len[0] = 6;

                  int32_array_array[1] = nih_alloc (int32_array_array, sizeof (int32_t) * 6);
                  int32_array_array[1][0] = 1;
                  int32_array_array[1][1] = 1;
                  int32_array_array[1][2] = 2;
                  int32_array_array[1][3] = 3;
                  int32_array_array[1][4] = 5;
                  int32_array_array[1][5] = 8;
                  int32_array_array_len[1] = 6;

                  int32_array_array[2] = NULL;
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_int32_array_array_to_str_sync (
                  parent, proxy,
                  int32_array_array, int32_array_array_len,
                  &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_int32_array_array_replied;
static int32_t * const *last_int32_array_array;
static const size_t *   last_int32_array_array_len;

static void
my_str_to_int32_array_array_reply (void *           data,
                           NihDBusMessage * message,
                           int32_t * const *output,
                           const size_t *   output_len)
{
      str_to_int32_array_array_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);
      if (*output) {
            TEST_NE_P (output_len, NULL);
            TEST_ALLOC_PARENT (output_len, output);
      }

      last_data = data;
      last_int32_array_array = output;
      nih_ref (last_int32_array_array, last_data);
      last_int32_array_array_len = output_len;
      nih_ref (last_int32_array_array_len, last_data);
}

void
test_str_to_int32_array_array (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_int32_array_array");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_array_array_replied = FALSE;
            last_data = NULL;
            last_int32_array_array = NULL;
            last_int32_array_array_len = NULL;

            pending_call = proxy_test_str_to_int32_array_array (
                  proxy,
                  "4 8 15 16 23 42\n1 1 2 3 5 8",
                  my_str_to_int32_array_array_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_int32_array_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_ALLOC_SIZE (last_int32_array_array_len, sizeof (size_t) * 2);
            TEST_ALLOC_PARENT (last_int32_array_array_len, parent);
            TEST_ALLOC_SIZE (last_int32_array_array, sizeof (int32_t *) * 3);

            TEST_EQ (last_int32_array_array_len[0], 6);
            TEST_ALLOC_SIZE (last_int32_array_array[0], sizeof (int32_t) * 6);
            TEST_ALLOC_PARENT (last_int32_array_array[0], last_int32_array_array);
            TEST_EQ (last_int32_array_array[0][0], 4);
            TEST_EQ (last_int32_array_array[0][1], 8);
            TEST_EQ (last_int32_array_array[0][2], 15);
            TEST_EQ (last_int32_array_array[0][3], 16);
            TEST_EQ (last_int32_array_array[0][4], 23);
            TEST_EQ (last_int32_array_array[0][5], 42);

            TEST_EQ (last_int32_array_array_len[1], 6);
            TEST_ALLOC_SIZE (last_int32_array_array[1], sizeof (int32_t) * 6);
            TEST_ALLOC_PARENT (last_int32_array_array[1], last_int32_array_array);
            TEST_EQ (last_int32_array_array[1][0], 1);
            TEST_EQ (last_int32_array_array[1][1], 1);
            TEST_EQ (last_int32_array_array[1][2], 2);
            TEST_EQ (last_int32_array_array[1][3], 3);
            TEST_EQ (last_int32_array_array[1][4], 5);
            TEST_EQ (last_int32_array_array[1][5], 8);

            TEST_EQ_P (last_int32_array_array[2], NULL);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_array_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32_array_array (
                  proxy,
                  "",
                  my_str_to_int32_array_array_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_array_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToInt32ArrayArray.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_array_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32_array_array (
                  proxy,
                  "invalid",
                  my_str_to_int32_array_array_reply,
                  my_error_handler,
                  parent,
                  NIH_DBUS_TIMEOUT_DEFAULT);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_array_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_array_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32_array_array (
                  proxy,
                  "4 8 15 16 23 42\n1 1 2 3 5 8",
                  my_str_to_int32_array_array_reply,
                  my_error_handler,
                  parent,
                  50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_array_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_int32_array_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32_array_array (
                  proxy,
                  "4 8 15 16 23 42\n1 1 2 3 5 8",
                  my_str_to_int32_array_array_reply,
                  my_error_handler,
                  parent,
                  50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_int32_array_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_int32_array_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_int32_array_array (
                  proxy,
                  "4 8 15 16 23 42\n1 1 2 3 5 8",
                  my_str_to_int32_array_array_reply,
                  my_error_handler,
                  parent,
                  50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_int32_array_array_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_int32_array_array_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      int32_t **      int32_array_array;
      size_t *        int32_array_array_len;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_int32_array_array_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            int32_array_array = NULL;
            int32_array_array_len = NULL;

            ret = proxy_test_str_to_int32_array_array_sync (
                  parent, proxy,
                  "4 8 15 16 23 42\n1 1 2 3 5 8",
                  &int32_array_array,
                  &int32_array_array_len);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);
            TEST_ALLOC_SIZE (int32_array_array_len, sizeof (size_t) * 2);
            TEST_ALLOC_PARENT (int32_array_array_len, int32_array_array);

            TEST_ALLOC_SIZE (int32_array_array, sizeof (int32_t) * 3);
            TEST_ALLOC_PARENT (int32_array_array, parent);

            TEST_EQ (int32_array_array_len[0], 6);
            TEST_ALLOC_SIZE (int32_array_array[0], sizeof (int32_t) * 6);
            TEST_ALLOC_PARENT (int32_array_array[0], int32_array_array);
            TEST_EQ (int32_array_array[0][0], 4);
            TEST_EQ (int32_array_array[0][1], 8);
            TEST_EQ (int32_array_array[0][2], 15);
            TEST_EQ (int32_array_array[0][3], 16);
            TEST_EQ (int32_array_array[0][4], 23);
            TEST_EQ (int32_array_array[0][5], 42);

            TEST_EQ (int32_array_array_len[1], 6);
            TEST_ALLOC_SIZE (int32_array_array[1], sizeof (int32_t) * 6);
            TEST_ALLOC_PARENT (int32_array_array[1], int32_array_array);
            TEST_EQ (int32_array_array[1][0], 1);
            TEST_EQ (int32_array_array[1][1], 1);
            TEST_EQ (int32_array_array[1][2], 2);
            TEST_EQ (int32_array_array[1][3], 3);
            TEST_EQ (int32_array_array[1][4], 5);
            TEST_EQ (int32_array_array[1][5], 8);

            TEST_EQ_P (int32_array_array[2], NULL);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            int32_array_array = NULL;
            int32_array_array_len = NULL;

            ret = proxy_test_str_to_int32_array_array_sync (
                  parent, proxy,
                  "",
                  &int32_array_array,
                  &int32_array_array_len);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToInt32ArrayArray.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            int32_array_array = NULL;
            int32_array_array_len = NULL;

            ret = proxy_test_str_to_int32_array_array_sync (
                  parent, proxy,
                  "invalid",
                  &int32_array_array,
                  &int32_array_array_len);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_str_to_int32_array_array_sync (
                  parent, proxy,
                  "4 8 15 16 23 42\n1 1 2 3 5 8",
                  &int32_array_array,
                  &int32_array_array_len);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int struct_array_to_str_replied;

static void
my_struct_array_to_str_reply (void *          data,
                        NihDBusMessage *message,
                        const char *    output)
{
      struct_array_to_str_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_str_value = output;
      nih_ref (last_str_value, last_data);
}

void
test_struct_array_to_str (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      ProxyTestStructArrayToStrInputElement **struct_array = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_struct_array_to_str");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_array = nih_alloc (parent, sizeof (ProxyTestStructArrayToStrInputElement) * 3);

                  struct_array[0] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[0]->item0 = "Joe";
                  struct_array[0]->item1 = 34;

                  struct_array[1] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[1]->item0 = "Paul";
                  struct_array[1]->item1 = 27;

                  struct_array[2] = NULL;
            }

            error_handler_called = FALSE;
            struct_array_to_str_replied = FALSE;
            last_data = NULL;
            last_str_value = NULL;

            pending_call = proxy_test_struct_array_to_str (proxy,
                                                 struct_array,
                                                 my_struct_array_to_str_reply,
                                                 my_error_handler,
                                                 parent,
                                                 0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (struct_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_EQ_STR (last_str_value, "Joe 34\nPaul 27");
            TEST_ALLOC_PARENT (last_str_value, parent);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_array = nih_alloc (parent, sizeof (ProxyTestStructArrayToStrInputElement) * 1);

                  struct_array[0] = NULL;
            }

            error_handler_called = FALSE;
            struct_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_struct_array_to_str (proxy,
                                                 struct_array,
                                                 my_struct_array_to_str_reply,
                                                 my_error_handler,
                                                 parent,
                                                 0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (struct_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StructArrayToStr.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_array = nih_alloc (parent, sizeof (ProxyTestStructArrayToStrInputElement) * 2);

                  struct_array[0] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[0]->item0 = "Joe";
                  struct_array[0]->item1 = 34;

                  struct_array[1] = NULL;
            }

            error_handler_called = FALSE;
            struct_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_struct_array_to_str (proxy,
                                                 struct_array,
                                                 my_struct_array_to_str_reply,
                                                 my_error_handler,
                                                 parent,
                                                 0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (struct_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_array = nih_alloc (parent, sizeof (ProxyTestStructArrayToStrInputElement) * 3);

                  struct_array[0] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[0]->item0 = "Joe";
                  struct_array[0]->item1 = 34;

                  struct_array[1] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[1]->item0 = "Paul";
                  struct_array[1]->item1 = 27;

                  struct_array[2] = NULL;
            }

            error_handler_called = FALSE;
            struct_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_struct_array_to_str (proxy,
                                                 struct_array,
                                                 my_struct_array_to_str_reply,
                                                 my_error_handler,
                                                 parent,
                                                 50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (struct_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_array = nih_alloc (parent, sizeof (ProxyTestStructArrayToStrInputElement) * 3);

                  struct_array[0] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[0]->item0 = "Joe";
                  struct_array[0]->item1 = 34;

                  struct_array[1] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[1]->item0 = "Paul";
                  struct_array[1]->item1 = 27;

                  struct_array[2] = NULL;
            }

            error_handler_called = FALSE;
            struct_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_struct_array_to_str (proxy,
                                                 struct_array,
                                                 my_struct_array_to_str_reply,
                                                 my_error_handler,
                                                 parent,
                                                 50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (struct_array_to_str_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_array = nih_alloc (parent, sizeof (ProxyTestStructArrayToStrInputElement) * 3);

                  struct_array[0] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[0]->item0 = "Joe";
                  struct_array[0]->item1 = 34;

                  struct_array[1] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[1]->item0 = "Paul";
                  struct_array[1]->item1 = 27;

                  struct_array[2] = NULL;
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            struct_array_to_str_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_struct_array_to_str (proxy,
                                                 struct_array,
                                                 my_struct_array_to_str_reply,
                                                 my_error_handler,
                                                 parent,
                                                 50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (struct_array_to_str_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_struct_array_to_str_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      ProxyTestStructArrayToStrInputElement **struct_array = NULL;
      char *          str_value;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_struct_array_to_str_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_array = nih_alloc (parent, sizeof (ProxyTestStructArrayToStrInputElement) * 3);

                  struct_array[0] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[0]->item0 = "Joe";
                  struct_array[0]->item1 = 34;

                  struct_array[1] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[1]->item0 = "Paul";
                  struct_array[1]->item1 = 27;

                  struct_array[2] = NULL;
            }

            str_value = NULL;

            ret = proxy_test_struct_array_to_str_sync (parent, proxy,
                                             struct_array,
                                             &str_value);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_EQ_STR (str_value, "Joe 34\nPaul 27");
            TEST_ALLOC_PARENT (str_value, parent);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_array = nih_alloc (parent, sizeof (ProxyTestStructArrayToStrInputElement) * 1);

                  struct_array[0] = NULL;
            }

            ret = proxy_test_struct_array_to_str_sync (parent, proxy,
                                             struct_array,
                                             &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StructArrayToStr.EmptyInput");

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a generic error is returned as a raised D-Bus error
       * with the "failed" name.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_array = nih_alloc (parent, sizeof (ProxyTestStructArrayToStrInputElement) * 2);

                  struct_array[0] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[0]->item0 = "Joe";
                  struct_array[0]->item1 = 34;

                  struct_array[1] = NULL;
            }

            ret = proxy_test_struct_array_to_str_sync (parent, proxy,
                                             struct_array,
                                             &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that attempting to send to a disconnected connection
       * returns a raised D-Bus disconnected error.
       */
      TEST_FEATURE ("with disconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);

                  struct_array = nih_alloc (parent, sizeof (ProxyTestStructArrayToStrInputElement) * 3);

                  struct_array[0] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[0]->item0 = "Joe";
                  struct_array[0]->item1 = 34;

                  struct_array[1] = nih_new (struct_array, ProxyTestStructArrayToStrInputElement);
                  struct_array[1]->item0 = "Paul";
                  struct_array[1]->item1 = 27;

                  struct_array[2] = NULL;
            }

            TEST_DBUS_CLOSE (flakey_conn);

            ret = proxy_test_struct_array_to_str_sync (parent, proxy,
                                             struct_array,
                                             &str_value);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            nih_free (parent);
            nih_free (proxy);
      }


      kill (server_pid, SIGTERM);
      waitpid (server_pid, &status, 0);
      TEST_TRUE (WIFEXITED (status));
      TEST_EQ (WEXITSTATUS (status), 0);

      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}


static int str_to_struct_array_replied;
static MyStruct * const *last_struct_array;

static void
my_str_to_struct_array_reply (void *          data,
                        NihDBusMessage *message,
                        ProxyTestStrToStructArrayOutputElement * const *output)
{
      str_to_struct_array_replied++;

      TEST_NE_P (data, NULL);
      TEST_ALLOC_SIZE (message, sizeof (NihDBusMessage));
      TEST_NE_P (message->connection, NULL);
      TEST_NE_P (message->message, NULL);

      TEST_NE_P (output, NULL);
      TEST_ALLOC_PARENT (output, message);

      last_data = data;
      last_struct_array = (MyStruct * const *)output;
      nih_ref (last_struct_array, last_data);
}

void
test_str_to_struct_array (void)
{
      pid_t            dbus_pid;
      DBusConnection * client_conn;
      DBusConnection * server_conn;
      DBusConnection * flakey_conn;
      NihDBusObject *  object = NULL;
      NihDBusProxy *   proxy = NULL;
      void *           parent = NULL;
      DBusPendingCall *pending_call;
      NihError *       err;
      NihDBusError *   dbus_err;

      TEST_FUNCTION ("proxy_test_str_to_struct_array");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);


      /* Check that we receive a pending call object after giving a
       * valid input, dispatching from the server side should result
       * in the call being completed and our handler being called with
       * the output arguments.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_struct_array_replied = FALSE;
            last_data = NULL;
            last_struct_array = NULL;

            pending_call = proxy_test_str_to_struct_array (proxy,
                                                 "Joe 34\nPaul 27",
                                                 my_str_to_struct_array_reply,
                                                 my_error_handler,
                                                 parent,
                                                 0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_FALSE (error_handler_called);
            TEST_TRUE (str_to_struct_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_ALLOC_SIZE (last_struct_array, sizeof (MyStruct *) * 3);
            TEST_ALLOC_PARENT (last_struct_array, parent);

            TEST_ALLOC_SIZE (last_struct_array[0], sizeof (MyStruct));
            TEST_ALLOC_PARENT (last_struct_array[0], last_struct_array);
            TEST_EQ_STR (last_struct_array[0]->item0, "Joe");
            TEST_ALLOC_PARENT (last_struct_array[0]->item0, last_struct_array[0]);
            TEST_EQ (last_struct_array[0]->item1, 34);

            TEST_ALLOC_SIZE (last_struct_array[1], sizeof (MyStruct));
            TEST_ALLOC_PARENT (last_struct_array[1], last_struct_array);
            TEST_EQ_STR (last_struct_array[1]->item0, "Paul");
            TEST_ALLOC_PARENT (last_struct_array[1]->item0, last_struct_array[1]);
            TEST_EQ (last_struct_array[1]->item1, 27);

            TEST_EQ_P (last_struct_array[2], NULL);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a D-Bus error is returned resulting in the error
       * handler being called with the error raised.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_struct_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_struct_array (proxy,
                                                 "",
                                                 my_str_to_struct_array_reply,
                                                 my_error_handler,
                                                 parent,
                                                 0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_struct_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToStructArray.EmptyInput");

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a generic error is returned resulting in the error
       * handler being called with the D-Bus "failed" error raised.
       */
      TEST_FEATURE ("with generic error");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_struct_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_struct_array (proxy,
                                                 "invalid",
                                                 my_str_to_struct_array_reply,
                                                 my_error_handler,
                                                 parent,
                                                 0);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_connection_flush (client_conn);
            TEST_DBUS_DISPATCH (server_conn);
            dbus_connection_flush (server_conn);
            TEST_DBUS_DISPATCH (client_conn);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_struct_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_FAILED);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a timeout (along with other D-Bus generated errors)
       * also results in the error handler being called with the D-Bus
       * error raised.
       */
      TEST_FEATURE ("with timeout");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_struct_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_struct_array (proxy,
                                                 "Joe 34\nPaul 27",
                                                 my_str_to_struct_array_reply,
                                                 my_error_handler,
                                                 parent,
                                                 50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_struct_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that a remote end disconnection (along with other D-Bus
       * generated errors) also results in the error handler being
       * called with the D-Bus error raised.
       */
      TEST_FEATURE ("with server disconnection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (flakey_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            error_handler_called = FALSE;
            str_to_struct_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_struct_array (proxy,
                                                 "Joe 34\nPaul 27",
                                                 my_str_to_struct_array_reply,
                                                 my_error_handler,
                                                 parent,
                                                 50);

            if (test_alloc_failed
                && (pending_call == NULL)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  TEST_DBUS_CLOSE (flakey_conn);
                  continue;
            }

            TEST_NE_P (pending_call, NULL);
            TEST_FALSE (dbus_pending_call_get_completed (pending_call));

            TEST_DBUS_CLOSE (flakey_conn);
            dbus_pending_call_block (pending_call);

            TEST_TRUE (dbus_pending_call_get_completed (pending_call));

            dbus_pending_call_unref (pending_call);

            TEST_TRUE (error_handler_called);
            TEST_FALSE (str_to_struct_array_replied);

            TEST_EQ_P (last_data, parent);
            TEST_NE_P (last_error, NULL);

            TEST_EQ (last_error->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (last_error, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)last_error;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_NO_REPLY);

            nih_free (last_error);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      /* Check that sending to a disconnected local end results in the
       * function returning NULL and the D-Bus disconnected error being
       * raised.
       */
      TEST_FEATURE ("with unconnected connection");
      TEST_ALLOC_FAIL {
            TEST_DBUS_OPEN (flakey_conn);

            TEST_ALLOC_SAFE {
                  object = nih_dbus_object_new (NULL, server_conn,
                                          "/com/netsplit/Nih/Test",
                                          my_interfaces,
                                          NULL);

                  proxy = nih_dbus_proxy_new (NULL, flakey_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            TEST_DBUS_CLOSE (flakey_conn);

            error_handler_called = FALSE;
            str_to_struct_array_replied = FALSE;
            last_data = NULL;
            last_error = NULL;

            pending_call = proxy_test_str_to_struct_array (proxy,
                                                 "Joe 34\nPaul 27",
                                                 my_str_to_struct_array_reply,
                                                 my_error_handler,
                                                 parent,
                                                 50);

            TEST_EQ_P (pending_call, NULL);

            err = nih_error_get ();
            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (err);
                  nih_free (parent);
                  nih_free (proxy);
                  nih_free (object);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, DBUS_ERROR_DISCONNECTED);

            nih_free (err);

            TEST_FALSE (error_handler_called);
            TEST_FALSE (str_to_struct_array_replied);

            nih_free (parent);
            nih_free (proxy);
            nih_free (object);
      }


      TEST_DBUS_CLOSE (client_conn);
      TEST_DBUS_CLOSE (server_conn);
      TEST_DBUS_END (dbus_pid);

      dbus_shutdown ();
}

void
test_str_to_struct_array_sync (void)
{
      pid_t           dbus_pid;
      DBusConnection *client_conn;
      DBusConnection *server_conn;
      DBusConnection *flakey_conn;
      pid_t           server_pid;
      int             wait_fd;
      NihDBusObject * object = NULL;
      NihDBusProxy *  proxy = NULL;
      void *          parent = NULL;
      ProxyTestStrToStructArrayOutputElement **struct_array;
      int             ret;
      NihError *      err;
      NihDBusError *  dbus_err;
      int             status;

      TEST_FUNCTION ("proxy_test_str_to_struct_array_sync");
      TEST_DBUS (dbus_pid);
      TEST_DBUS_OPEN (client_conn);
      TEST_DBUS_OPEN (server_conn);

      TEST_CHILD_WAIT (server_pid, wait_fd) {
            assert0 (nih_dbus_setup (server_conn, NULL));

            object = nih_dbus_object_new (NULL, server_conn,
                                    "/com/netsplit/Nih/Test",
                                    my_interfaces,
                                    NULL);

            nih_signal_set_handler (SIGTERM, nih_signal_handler);
            assert (nih_signal_add_handler (object, SIGTERM,
                                    nih_main_term_signal, NULL));

            TEST_CHILD_RELEASE (wait_fd);

            nih_main_loop ();

            nih_free (object);

            TEST_DBUS_CLOSE (client_conn);
            TEST_DBUS_CLOSE (server_conn);

            dbus_shutdown ();

            exit (0);
      }


      /* Check that when we give a valid input, we receive a valid
       * output in our pointer, allocated as a child of the parent.
       */
      TEST_FEATURE ("with valid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            struct_array = NULL;

            ret = proxy_test_str_to_struct_array_sync (parent, proxy,
                                             "Joe 34\nPaul 27",
                                             &struct_array);

            if (test_alloc_failed
                && (ret < 0)) {
                  err = nih_error_get ();
                  TEST_EQ (err->number, ENOMEM);
                  nih_free (err);

                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (ret, 0);

            TEST_ALLOC_SIZE (struct_array, sizeof (ProxyTestStrToStructArrayOutputElement *) * 3);
            TEST_ALLOC_PARENT (struct_array, parent);

            TEST_ALLOC_SIZE (struct_array[0], sizeof (ProxyTestStrToStructArrayOutputElement));
            TEST_ALLOC_PARENT (struct_array[0], struct_array);
            TEST_EQ_STR (struct_array[0]->item0, "Joe");
            TEST_ALLOC_PARENT (struct_array[0]->item0, struct_array[0]);
            TEST_EQ (struct_array[0]->item1, 34);

            TEST_ALLOC_SIZE (struct_array[1], sizeof (ProxyTestStrToStructArrayOutputElement));
            TEST_ALLOC_PARENT (struct_array[1], struct_array);
            TEST_EQ_STR (struct_array[1]->item0, "Paul");
            TEST_ALLOC_PARENT (struct_array[1]->item0, struct_array[1]);
            TEST_EQ (struct_array[1]->item1, 27);

            TEST_EQ_P (struct_array[2], NULL);

            nih_free (parent);
            nih_free (proxy);
      }


      /* Check that a D-Bus error is returned as a raised error, with
       * an appropriate return value.
       */
      TEST_FEATURE ("with invalid argument");
      TEST_ALLOC_FAIL {
            TEST_ALLOC_SAFE {
                  proxy = nih_dbus_proxy_new (NULL, client_conn,
                                        dbus_bus_get_unique_name (server_conn),
                                        "/com/netsplit/Nih/Test",
                                        NULL, NULL);

                  parent = nih_alloc (NULL, 0);
            }

            ret = proxy_test_str_to_struct_array_sync (parent, proxy,
                                             "",
                                             &struct_array);

            TEST_LT (ret, 0);

            err = nih_error_get ();

            if (test_alloc_failed
                && (err->number == ENOMEM)) {
                  nih_free (parent);
                  nih_free (proxy);
                  continue;
            }

            TEST_EQ (err->number, NIH_DBUS_ERROR);
            TEST_ALLOC_SIZE (err, sizeof (NihDBusError));

            dbus_err = (NihDBusError *)err;
            TEST_EQ_STR (dbus_err->name, "com.netsplit.Nih.Test.StrToStructA