/*
 * This file is part of Cockpit.
 *
 * Copyright (C) 2013 Red Hat, Inc.
 *
 * Cockpit is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Cockpit 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include "cockpitdbusinternal.h"

#include "common/cockpittest.h"

#include <gio/gio.h>
#include <glib/gstdio.h>

extern const gchar *cockpit_bridge_path_passwd;
extern const gchar *cockpit_bridge_path_group;
extern const gchar *cockpit_bridge_path_shadow;
extern const gchar *cockpit_bridge_path_newusers;
extern const gchar *cockpit_bridge_path_chpasswd;
extern const gchar *cockpit_bridge_path_usermod;
extern gboolean     cockpit_bridge_have_newusers_crypt_method;

typedef struct {
  GDBusConnection *connection;
} TestCase;

static void
setup (TestCase *tc,
       gconstpointer unused)
{
  cockpit_dbus_internal_startup (FALSE);

  cockpit_dbus_setup_startup ();
  while (g_main_context_iteration (NULL, FALSE));

  tc->connection = cockpit_dbus_internal_client();
}

static void
teardown (TestCase *tc,
          gconstpointer unused)
{
  cockpit_assert_expected ();
  g_object_unref (tc->connection);
  cockpit_dbus_internal_cleanup ();
}

static void
on_complete_get_result (GObject *source,
                        GAsyncResult *result,
                        gpointer user_data)
{
  GAsyncResult **ret = user_data;
  g_assert (ret != NULL);
  g_assert (*ret == NULL);
  *ret = g_object_ref (result);
}

static GVariant *
dbus_call_with_main_loop (TestCase *tc,
                          const gchar *object_path,
                          const gchar *interface_name,
                          const gchar *method_name,
                          GVariant *parameters,
                          const GVariantType *reply_type,
                          GError **error)
{
  GAsyncResult *result = NULL;
  GVariant *retval;

  g_dbus_connection_call (tc->connection, NULL, object_path,
                          interface_name, method_name, parameters,
                          reply_type, G_DBUS_CALL_FLAGS_NONE, -1,
                          NULL, on_complete_get_result, &result);

  while (result == NULL)
    g_main_context_iteration (NULL, TRUE);

  retval = g_dbus_connection_call_finish (tc->connection, result, error);
  g_object_unref (result);

  return retval;
}

static void
test_get_properties (TestCase *tc,
                     gconstpointer unused)
{
  GVariant *retval;
  GError *error = NULL;
  gchar *string;

  retval = dbus_call_with_main_loop (tc, "/setup", "org.freedesktop.DBus.Properties", "GetAll",
                                     g_variant_new ("(s)", "cockpit.Setup"),
                                     G_VARIANT_TYPE ("(a{sv})"), &error);

  g_assert_no_error (error);
  string = g_variant_print (retval, FALSE);
  g_assert_cmpstr ("({'Mechanisms': <['passwd1']>},)", ==, string);
  g_free (string);
  g_variant_unref (retval);
}

static void
test_prepare_passwd1 (TestCase *tc,
                      gconstpointer unused)
{
  GVariant *retval;
  GError *error = NULL;
  gchar *string;

  cockpit_bridge_path_passwd = SRCDIR "/src/bridge/mock-setup/remote-passwd";
  cockpit_bridge_path_group = SRCDIR "/src/bridge/mock-setup/remote-group";

  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Prepare",
                                     g_variant_new ("(s)", "passwd1"),
                                     G_VARIANT_TYPE ("(v)"), &error);

  g_assert_no_error (error);
  string = g_variant_print (retval, FALSE);
  g_assert_cmpstr (string, ==, "(<(['root', 'janice', 'scruffy'], ['root', 'wheel', 'docker'])>,)");
  g_free (string);
  g_variant_unref (retval);
}

static void
test_prepare_unsupported (TestCase *tc,
                          gconstpointer unused)
{
  GVariant *retval;
  GError *error = NULL;

  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Prepare",
                                     g_variant_new ("(s)", "blah"),
                                     G_VARIANT_TYPE ("(v)"), &error);

  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED);
  g_assert (retval == NULL);
  g_error_free (error);
}

static void
test_prepare_fail (TestCase *tc,
                   gconstpointer unused)
{
  GVariant *retval;
  GError *error = NULL;

  cockpit_bridge_path_passwd = SRCDIR "/src/bridge/mock-setup/non-existent";

  cockpit_expect_message ("unable to open*");

  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Prepare",
                                     g_variant_new ("(s)", "passwd1"),
                                     G_VARIANT_TYPE ("(v)"), &error);

  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED);
  g_assert (retval == NULL);
  g_error_free (error);
}

static void
test_transfer_passwd1 (TestCase *tc,
                      gconstpointer unused)
{
  GVariant *retval;
  GVariant *prepared;
  GVariant *passwd_and_group;
  GVariant *child;
  GError *error = NULL;
  const gchar **passwds;
  gsize passwd_len;
  const gchar **groups;
  gsize group_len;

  const gchar *empty[] = { NULL };

  cockpit_bridge_path_passwd = SRCDIR "/src/bridge/mock-setup/local-passwd";
  cockpit_bridge_path_group = SRCDIR "/src/bridge/mock-setup/local-group";
  cockpit_bridge_path_shadow = SRCDIR "/src/bridge/mock-setup/local-shadow";

  prepared = g_variant_new ("(@as@as)", g_variant_new_strv (empty, -1), g_variant_new_strv (empty, -1));
  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Transfer",
                                     g_variant_new ("(sv)", "passwd1", prepared),
                                     G_VARIANT_TYPE ("(v)"), &error);
  g_assert_no_error (error);

  /* retval is a boxed (v) consisting of (asas): passwd strv, and group strv */
  child = g_variant_get_child_value(retval, 0);
  passwd_and_group = g_variant_get_variant (child);
  g_variant_unref (child);

  child = g_variant_get_child_value (passwd_and_group, 0);;
  passwds = g_variant_get_strv (child, &passwd_len);
  g_variant_unref (child);
  g_assert_cmpint (passwd_len, ==, 3);

  g_assert (g_strv_contains (passwds, "root:$6$RBjDivsC$mlwBspq8QVmDe92lS/uVFiCHnw69KO.v7BQ69TE50CUMx6AKwfOZJ9gjU0y846UkQt9NrLlChu6j0z9V2//0b/:::Root:/root:/bin/bash"));
  g_assert (g_strv_contains (passwds, "scruffy:$6$kiB.xr6x$xDzRjU5dHnwqds7Vs1iRe7NWKRI2AvK38DbGF2DIOfI9MtqHL.hDwL6GhBxEyliTGQi3FyEVR0y2pG6xuEGJ81:::Scruffy the Janitor:/home/scruffy:/bin/bash"));
  g_assert (g_strv_contains (passwds, "hermes:$6$vK.Xvf4y$8PI2sHG7VVexATp2uyqHyhqRMeCisGL0Zer2fs.Suy4Q.eg9OWCoPGIeSDbxhOLvpfQKGorAaQIRLuVJH5uUO.:::Hermes Conrad:/home/hermes:/bin/sh"));
  g_free (passwds);

  child = g_variant_get_child_value (passwd_and_group, 1);
  groups = g_variant_get_strv (child, &group_len);
  g_variant_unref (child);
  g_assert_cmpint (group_len, ==, 3);
  g_assert (g_strv_contains (groups, "docker:::hermes"));
  g_assert (g_strv_contains (groups, "wheel:::scruffy,hermes"));
  g_assert (g_strv_contains (groups, "root:::root"));
  g_free (groups);

  g_variant_unref (passwd_and_group);
  g_variant_unref (retval);
}

static void
test_transfer_unsupported (TestCase *tc,
                           gconstpointer unused)
{
  GVariant *retval;
  GVariant *prepared;
  GError *error = NULL;

  const gchar *users[] = { "janice", "scruffy", NULL };

  prepared = g_variant_new_strv (users, -1);
  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Transfer",
                                     g_variant_new ("(sv)", "blah", prepared),
                                     G_VARIANT_TYPE ("(v)"), &error);

  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED);
  g_assert (retval == NULL);
  g_error_free (error);
}

static void
test_transfer_bad (TestCase *tc,
                           gconstpointer unused)
{
  GVariant *retval;
  GVariant *prepared;
  GError *error = NULL;

  prepared = g_variant_new_string ("blah");
  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Transfer",
                                     g_variant_new ("(sv)", "passwd1", prepared),
                                     G_VARIANT_TYPE ("(v)"), &error);

  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
  g_assert (retval == NULL);
  g_error_free (error);
}

static void
test_transfer_fail (TestCase *tc,
                    gconstpointer unused)
{
  GVariant *retval;
  GVariant *prepared;
  GError *error = NULL;

  const gchar *empty[] = { NULL };

  cockpit_bridge_path_passwd = SRCDIR "/src/bridge/mock-setup/non-existent";

  cockpit_expect_message ("unable to open*");

  prepared = g_variant_new ("(@as@as)", g_variant_new_strv (empty, -1), g_variant_new_strv (empty, -1));
  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Transfer",
                                     g_variant_new ("(sv)", "passwd1", prepared),
                                     G_VARIANT_TYPE ("(v)"), &error);

  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED);
  g_assert (retval == NULL);
  g_error_free (error);
}

static const gchar *passwd_data[] = {
  "root:$6$RBjDivsC$mlwBspq8QVmDe92lS/uVFiCHnw69KO.v7BQ69TE50CUMx6AKwfOZJ9gjU0y846UkQt9NrLlChu6j0z9V2//0b/:::Root:/root:/bin/bash",
  "scruffy:$6$kiB.xr6x$xDzRjU5dHnwqds7Vs1iRe7NWKRI2AvK38DbGF2DIOfI9MtqHL.hDwL6GhBxEyliTGQi3FyEVR0y2pG6xuEGJ81:::Scruffy the Janitor:/home/scruffy:/bin/bash",
  "hermes:$6$vK.Xvf4y$8PI2sHG7VVexATp2uyqHyhqRMeCisGL0Zer2fs.Suy4Q.eg9OWCoPGIeSDbxhOLvpfQKGorAaQIRLuVJH5uUO.:::Hermes Conrad:/home/hermes:/bin/sh']>,)",
  NULL
};

static const gchar *group_data[] = {
  "wheel:::hermes,scruffy",
  "root:::root",
  "unsupported:::hermes,scruffy",
  "docker:::hermes",
  NULL
};

static void
test_commit_passwd1 (TestCase *tc,
                     gconstpointer unused)
{
  GVariant *retval;
  GVariant *transferred;
  GError *error = NULL;
  gchar *directory;
  gchar *string;
  gchar *contents;

  cockpit_bridge_path_passwd = SRCDIR "/src/bridge/mock-setup/remote-passwd";
  cockpit_bridge_path_group = SRCDIR "/src/bridge/mock-setup/remote-group";
  cockpit_bridge_path_shadow = SRCDIR "/src/bridge/mock-setup/remote-shadow";
  cockpit_bridge_path_newusers = SRCDIR "/src/bridge/mock-setup/newusers";
  cockpit_bridge_path_chpasswd = SRCDIR "/src/bridge/mock-setup/chpasswd";
  cockpit_bridge_path_usermod = SRCDIR "/src/bridge/mock-setup/usermod";
  cockpit_bridge_have_newusers_crypt_method = TRUE;

  directory = g_strdup ("/tmp/test-cockpit-setup.XXXXXX");
  directory = g_mkdtemp (directory);
  g_assert (directory != NULL);

  g_setenv ("MOCK_OUTPUT", directory, TRUE);

  transferred = g_variant_new ("(@as@as)",
                               g_variant_new_strv (passwd_data, -1),
                               g_variant_new_strv (group_data, -1));
  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Commit",
                                     g_variant_new ("(sv)", "passwd1", transferred),
                                     G_VARIANT_TYPE ("()"), &error);

  g_assert_no_error (error);
  string = g_variant_print (retval, FALSE);
  g_assert_cmpstr (string, ==, "()");
  g_free (string);
  g_variant_unref (retval);

  string = g_build_filename (directory, "newusers", NULL);
  g_assert (g_file_get_contents (string, &contents, NULL, NULL));
  g_assert_cmpstr (contents, ==, "hermes:$6$vK.Xvf4y$8PI2sHG7VVexATp2uyqHyhqRMeCisGL0Zer2fs.Suy4Q.eg9OWCoPGIeSDbxhOLvpfQKGorAaQIRLuVJH5uUO.:::Hermes Conrad:/home/hermes:/bin/sh']>,)\n");
  g_free (contents);
  g_assert (g_unlink (string) >= 0);
  g_free (string);

  string = g_build_filename (directory, "chpasswd", NULL);
  g_assert (g_file_get_contents (string, &contents, NULL, NULL));
  g_assert_cmpstr (contents, ==, "root:$6$RBjDivsC$mlwBspq8QVmDe92lS/uVFiCHnw69KO.v7BQ69TE50CUMx6AKwfOZJ9gjU0y846UkQt9NrLlChu6j0z9V2//0b/\nscruffy:$6$kiB.xr6x$xDzRjU5dHnwqds7Vs1iRe7NWKRI2AvK38DbGF2DIOfI9MtqHL.hDwL6GhBxEyliTGQi3FyEVR0y2pG6xuEGJ81\n");
  g_free (contents);
  g_assert (g_unlink (string) >= 0);
  g_free (string);

  string = g_build_filename (directory, "usermod", NULL);
  g_assert (g_file_get_contents (string, &contents, NULL, NULL));
  g_assert_cmpstr (contents, ==, "hermes --append --group wheel,docker\nroot --append --group root\nscruffy --append --group wheel\n");
  g_free (contents);
  g_assert (g_unlink (string) >= 0);
  g_free (string);

  g_assert (g_rmdir (directory) >= 0);
  g_free (directory);
}

static void
test_commit_passwd1_no_crypt_method (TestCase *tc,
                                     gconstpointer unused)
{
  GVariant *retval;
  GVariant *transferred;
  GError *error = NULL;
  gchar *directory;
  gchar *string;
  gchar *contents;

  /* Same as test_commit_passwd1, but the new password for hermes will
   * be set via chpasswd.
   */

  cockpit_bridge_path_passwd = SRCDIR "/src/bridge/mock-setup/remote-passwd";
  cockpit_bridge_path_group = SRCDIR "/src/bridge/mock-setup/remote-group";
  cockpit_bridge_path_shadow = SRCDIR "/src/bridge/mock-setup/remote-shadow";
  cockpit_bridge_path_newusers = SRCDIR "/src/bridge/mock-setup/newusers";
  cockpit_bridge_path_chpasswd = SRCDIR "/src/bridge/mock-setup/chpasswd";
  cockpit_bridge_path_usermod = SRCDIR "/src/bridge/mock-setup/usermod";
  cockpit_bridge_have_newusers_crypt_method = FALSE;

  directory = g_strdup ("/tmp/test-cockpit-setup.XXXXXX");
  directory = g_mkdtemp (directory);
  g_assert (directory != NULL);

  g_setenv ("MOCK_OUTPUT", directory, TRUE);

  transferred = g_variant_new ("(@as@as)",
                               g_variant_new_strv (passwd_data, -1),
                               g_variant_new_strv (group_data, -1));
  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Commit",
                                     g_variant_new ("(sv)", "passwd1", transferred),
                                     G_VARIANT_TYPE ("()"), &error);

  g_assert_no_error (error);
  string = g_variant_print (retval, FALSE);
  g_assert_cmpstr (string, ==, "()");
  g_free (string);
  g_variant_unref (retval);

  string = g_build_filename (directory, "newusers", NULL);
  g_assert (g_file_get_contents (string, &contents, NULL, NULL));
  g_assert_cmpstr (contents, ==, "hermes:$6$vK.Xvf4y$8PI2sHG7VVexATp2uyqHyhqRMeCisGL0Zer2fs.Suy4Q.eg9OWCoPGIeSDbxhOLvpfQKGorAaQIRLuVJH5uUO.:::Hermes Conrad:/home/hermes:/bin/sh']>,)\n");
  g_free (contents);
  g_assert (g_unlink (string) >= 0);
  g_free (string);

  string = g_build_filename (directory, "chpasswd", NULL);
  g_assert (g_file_get_contents (string, &contents, NULL, NULL));
  g_assert_cmpstr (contents, ==, "root:$6$RBjDivsC$mlwBspq8QVmDe92lS/uVFiCHnw69KO.v7BQ69TE50CUMx6AKwfOZJ9gjU0y846UkQt9NrLlChu6j0z9V2//0b/\nscruffy:$6$kiB.xr6x$xDzRjU5dHnwqds7Vs1iRe7NWKRI2AvK38DbGF2DIOfI9MtqHL.hDwL6GhBxEyliTGQi3FyEVR0y2pG6xuEGJ81\nhermes:$6$vK.Xvf4y$8PI2sHG7VVexATp2uyqHyhqRMeCisGL0Zer2fs.Suy4Q.eg9OWCoPGIeSDbxhOLvpfQKGorAaQIRLuVJH5uUO.\n");
  g_free (contents);
  g_assert (g_unlink (string) >= 0);
  g_free (string);

  string = g_build_filename (directory, "usermod", NULL);
  g_assert (g_file_get_contents (string, &contents, NULL, NULL));
  g_assert_cmpstr (contents, ==, "hermes --append --group wheel,docker\nroot --append --group root\nscruffy --append --group wheel\n");
  g_free (contents);
  g_assert (g_unlink (string) >= 0);
  g_free (string);

  g_assert (g_rmdir (directory) >= 0);
  g_free (directory);
}

static void
test_commit_fail_newusers (TestCase *tc,
                           gconstpointer unused)
{
  GVariant *retval;
  GVariant *transferred;
  GError *error = NULL;
  gchar *directory;

  cockpit_expect_message ("couldn't run newusers command*");

  cockpit_bridge_path_passwd = SRCDIR "/src/bridge/mock-setup/remote-passwd";
  cockpit_bridge_path_newusers = "/bin/false";
  cockpit_bridge_path_chpasswd = SRCDIR "/src/bridge/mock-setup/chpasswd";

  directory = g_strdup ("/tmp/test-cockpit-setup.XXXXXX");
  directory = g_mkdtemp (directory);
  g_assert (directory != NULL);

  g_setenv ("MOCK_OUTPUT", directory, TRUE);

  transferred = g_variant_new ("(@as@as)",
                               g_variant_new_strv (passwd_data, -1),
                               g_variant_new_strv (group_data, -1));
  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Commit",
                                     g_variant_new ("(sv)", "passwd1", transferred),
                                     G_VARIANT_TYPE ("()"), &error);

  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED);
  g_assert (retval == NULL);
  g_error_free (error);

  g_assert (g_rmdir (directory) >= 0);
  g_free (directory);
}

static void
test_commit_fail_chpasswd (TestCase *tc,
                           gconstpointer unused)
{
  GVariant *retval;
  GVariant *transferred;
  GError *error = NULL;
  gchar *directory;
  gchar *string;

  cockpit_expect_message ("couldn't run chpasswd command*");

  cockpit_bridge_path_passwd = SRCDIR "/src/bridge/mock-setup/remote-passwd";
  cockpit_bridge_path_chpasswd = "/bin/false";
  cockpit_bridge_path_newusers = SRCDIR "/src/bridge/mock-setup/newusers";
  cockpit_bridge_have_newusers_crypt_method = TRUE;

  directory = g_strdup ("/tmp/test-cockpit-setup.XXXXXX");
  directory = g_mkdtemp (directory);
  g_assert (directory != NULL);

  g_setenv ("MOCK_OUTPUT", directory, TRUE);

  transferred = g_variant_new ("(@as@as)",
                               g_variant_new_strv (passwd_data, -1),
                               g_variant_new_strv (group_data, -1));
  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Commit",
                                     g_variant_new ("(sv)", "passwd1", transferred),
                                     G_VARIANT_TYPE ("()"), &error);

  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED);
  g_assert (retval == NULL);
  g_error_free (error);

  string = g_build_filename (directory, "newusers", NULL);
  g_unlink (string);
  g_free (string);

  g_assert (g_rmdir (directory) >= 0);
  g_free (directory);
}


static void
test_commit_unsupported (TestCase *tc,
                         gconstpointer unused)
{
  GVariant *retval;
  GVariant *transferred;
  GError *error = NULL;

  const gchar *data[] = { "one", "two", NULL };

  transferred = g_variant_new_strv (data, -1);
  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Commit",
                                     g_variant_new ("(sv)", "blah", transferred),
                                     G_VARIANT_TYPE ("(v)"), &error);

  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED);
  g_assert (retval == NULL);
  g_error_free (error);
}

static void
test_commit_bad (TestCase *tc,
                 gconstpointer unused)
{
  GVariant *retval;
  GVariant *transferred;
  GError *error = NULL;

  transferred = g_variant_new_string ("blah");
  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Commit",
                                     g_variant_new ("(sv)", "passwd1", transferred),
                                     G_VARIANT_TYPE ("()"), &error);

  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
  g_assert (retval == NULL);
  g_error_free (error);
}

static void
test_commit_fail_passwd (TestCase *tc,
                         gconstpointer unused)
{
  GVariant *retval;
  GVariant *transferred;
  GError *error = NULL;

  const gchar *empty[] = { NULL };

  cockpit_bridge_path_passwd = SRCDIR "/src/bridge/mock-setup/non-existent";

  cockpit_expect_message ("unable to open*");

  transferred = g_variant_new ("(@as@as)", g_variant_new_strv (empty, -1), g_variant_new_strv (empty, -1));
  retval = dbus_call_with_main_loop (tc, "/setup", "cockpit.Setup", "Commit",
                                     g_variant_new ("(sv)", "passwd1", transferred),
                                     G_VARIANT_TYPE ("(v)"), &error);

  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED);
  g_assert (retval == NULL);
  g_error_free (error);
}

int
main (int argc,
      char *argv[])
{
  cockpit_test_init (&argc, &argv);

  g_test_add ("/setup/get-properties", TestCase, NULL,
              setup, test_get_properties, teardown);

  g_test_add ("/setup/prepare/passwd1", TestCase, NULL,
              setup, test_prepare_passwd1, teardown);
  g_test_add ("/setup/prepare/unsupported", TestCase, NULL,
              setup, test_prepare_unsupported, teardown);
  g_test_add ("/setup/prepare/fail", TestCase, NULL,
              setup, test_prepare_fail, teardown);

  g_test_add ("/setup/transfer/passwd1", TestCase, NULL,
              setup, test_transfer_passwd1, teardown);
  g_test_add ("/setup/transfer/unsupported", TestCase, NULL,
              setup, test_transfer_unsupported, teardown);
  g_test_add ("/setup/transfer/bad", TestCase, NULL,
              setup, test_transfer_bad, teardown);
  g_test_add ("/setup/transfer/fail", TestCase, NULL,
              setup, test_transfer_fail, teardown);

  g_test_add ("/setup/commit/passwd1", TestCase, NULL,
              setup, test_commit_passwd1, teardown);
  g_test_add ("/setup/commit/passwd1-no-crypt-method", TestCase, NULL,
              setup, test_commit_passwd1_no_crypt_method, teardown);
  g_test_add ("/setup/commit/unsupported", TestCase, NULL,
              setup, test_commit_unsupported, teardown);
  g_test_add ("/setup/commit/bad", TestCase, NULL,
              setup, test_commit_bad, teardown);
  g_test_add ("/setup/commit/fail-passwd", TestCase, NULL,
              setup, test_commit_fail_passwd, teardown);
  g_test_add ("/setup/commit/fail-newusers", TestCase, NULL,
              setup, test_commit_fail_newusers, teardown);
  g_test_add ("/setup/commit/fail-chpasswd", TestCase, NULL,
              setup, test_commit_fail_chpasswd, teardown);

  return g_test_run ();
}
